Formalises the completed E3 analysis (PLAN.md §5, frozen v1.0). Authoritative data = OSF package of the AIED manuscript (data/externi/osf-aied-ndtkz/), column Reseni_gen_AI: 0 = AI, 1 = teacher. This notebook (a) reproduces the manuscript’s LMMs (Table 3), (b) runs the pre-specified CLMM sensitivity, (c) computes inter-rater agreement with cluster-bootstrap 95% CIs, and (d) writes the numbers manifest vystupy/tabulky/kap7_k2_shoda_cisla.csv.
suppressPackageStartupMessages ({
library (dplyr); library (tidyr); library (lmerTest); library (ordinal); library (irr); library (digest)
})
SEED <- 20260714
set.seed (SEED)
ROOT <- normalizePath (file.path (getwd (), ".." )) # analyzy/
H2 <- normalizePath (file.path (ROOT, ".." )) # habilitace-2/
CSV <- file.path (H2, "data/externi/osf-aied-ndtkz/anotace_final_data_bezdiakritiky.csv" )
PUB <- file.path (H2, "data/processed/kap7_hodnoceni_pseudo.csv" )
DATA_SHA <- "ba464aaa3aa7223ba6a14009ec38dfb576db9af250079ed821bb6f9992478927"
# Studie 3 potřebuje k výpočtu jen HODNOCENÍ, ne texty kazuistik. Když je surový
# OSF soubor přítomen (autor), pseudonymizujeme anotátorky, ponecháme jen
# analytické sloupce (BEZ textů Popis/Anamneza/Řešení) a zapíšeme veřejnou
# tabulku; když chybí (Binder), čteme ji. Náhrada jmen 1:1 nemění žádný výsledek
# (anotátorka vstupuje jen jako náhodný efekt), texty korpusu zůstávají privátní.
KEEP <- c ("Id" , "anotator_name" , "Otazka" , "Hodnoceni" , "Reseni_gen_AI" ,
"Pristup.I" , "Pristup.II" , "Pristup.III" )
df <- read.csv (PUB, header = TRUE , sep = "," , encoding = "UTF-8" )
DATA_SHA <- digest:: digest (file = PUB, algo = "sha256" )
stopifnot (! anyDuplicated (df[c ("Id" , "anotator_name" , "Otazka" , "Reseni_gen_AI" )]))
stopifnot (length (unique (df$ Id)) == 324 ,
all (sort (unique (na.omit (df$ Hodnoceni))) == - 2 : 2 ))
dims <- c (vhodnost = "Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci." ,
reaktivni = "Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni." ,
humanisticke = "Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane." ,
systemove = "Jak.moc.je.popisovane.reseni.systemove.nebo.situacni." )
manifest <- list ()
put <- function (metric, value) manifest[[length (manifest) + 1 ]] <<-
data.frame (metric = metric, value = round (as.numeric (value), 4 ))
put ("n_kazuistik" , length (unique (df$ Id)))
put ("n_anotatorek" , length (unique (df$ anotator_name)))
put ("n_odpovedi_dimenze" , nrow (df))
put ("n_missing_dimenze" , sum (is.na (df$ Hodnoceni)))
Descriptives by source
desc <- df |>
filter (! is.na (Hodnoceni)) |>
mutate (dim = names (dims)[match (Otazka, dims)],
src = ifelse (Reseni_gen_AI == 0 , "AI" , "teacher" )) |>
group_by (dim, src) |>
summarise (n = n (), mean = mean (Hodnoceni), sd = sd (Hodnoceni), .groups = "drop" )
knitr:: kable (desc, digits = 2 )
humanisticke
AI
1727
-0.44
1.00
humanisticke
teacher
1737
0.34
1.25
reaktivni
AI
1730
0.69
1.02
reaktivni
teacher
1741
-0.32
1.24
systemove
AI
1730
0.35
1.07
systemove
teacher
1741
0.17
1.39
vhodnost
AI
1729
1.56
0.61
vhodnost
teacher
1741
0.71
0.95
for (i in seq_len (nrow (desc)))
put (paste0 ("mean_" , desc$ src[i], "_" , desc$ dim[i]), desc$ mean[i])
Primary models: LMM (manuscript reproduction)
lmm_rows <- lapply (names (dims), function (nm) {
d <- df[df$ Otazka == dims[[nm]], ]
m <- lmer (Hodnoceni ~ Reseni_gen_AI + (1 | Id) + (1 | anotator_name),
data = d, REML = FALSE )
fe <- summary (m)$ coefficients
ci <- confint (m, parm = "beta_" , method = "Wald" )
put (paste0 ("lmm_b0_AI_" , nm), fe[1 , 1 ])
put (paste0 ("lmm_b1_ucitel_" , nm), fe[2 , 1 ])
put (paste0 ("lmm_b1_se_" , nm), fe[2 , 2 ])
put (paste0 ("lmm_b1_ci_lo_" , nm), ci[2 , 1 ])
put (paste0 ("lmm_b1_ci_hi_" , nm), ci[2 , 2 ])
data.frame (dim = nm, b0_AI = fe[1 , 1 ], b1_teacher = fe[2 , 1 ],
se = fe[2 , 2 ], ci_lo = ci[2 , 1 ], ci_hi = ci[2 , 2 ], p = fe[2 , 5 ])
})
knitr:: kable (bind_rows (lmm_rows), digits = 3 )
vhodnost
1.547
-0.855
0.022
-0.898
-0.811
0
reaktivni
0.683
-1.010
0.029
-1.066
-0.954
0
humanisticke
-0.410
0.772
0.029
0.714
0.830
0
systemove
0.321
-0.172
0.032
-0.233
-0.110
0
Expected (AIED Table 3): appropriateness −0.85; reactive −1.01; humanistic +0.77; systemic −0.17 — reproduced exactly.
Sensitivity: CLMM (pre-specified)
clmm_rows <- lapply (names (dims), function (nm) {
d <- df[df$ Otazka == dims[[nm]] & ! is.na (df$ Hodnoceni), ]
m <- clmm (factor (Hodnoceni, ordered = TRUE ) ~ Reseni_gen_AI +
(1 | Id) + (1 | anotator_name), data = d)
co <- summary (m)$ coefficients
b <- co[grep ("Reseni_gen_AI" , rownames (co)), , drop = FALSE ]
put (paste0 ("clmm_b1_ucitel_" , nm), b[1 , 1 ])
put (paste0 ("clmm_or_ucitel_" , nm), exp (b[1 , 1 ]))
# Wald 95% CI na škále OR (pro tabulku 7.2 a figuru)
lo <- b[1 , 1 ] - 1.96 * b[1 , 2 ]; hi <- b[1 , 1 ] + 1.96 * b[1 , 2 ]
put (paste0 ("clmm_or_ci_lo_" , nm), exp (lo))
put (paste0 ("clmm_or_ci_hi_" , nm), exp (hi))
data.frame (dim = nm, b1_teacher = b[1 , 1 ], SE = b[1 , 2 ],
OR = exp (b[1 , 1 ]), OR_lo = exp (lo), OR_hi = exp (hi), p = b[1 , 4 ])
})
knitr:: kable (bind_rows (clmm_rows), digits = 3 )
vhodnost
-2.790
0.089
0.061
0.052
0.073
0
reaktivni
-2.252
0.077
0.105
0.091
0.122
0
humanisticke
1.683
0.071
5.383
4.684
6.187
0
systemove
-0.256
0.065
0.775
0.682
0.880
0
Sensitivity check: CLMM must agree in direction with LMM for all four dimensions.
Sensitivity: LMM with a random slope for source by case
The design is paired within case (both solutions of the same case are rated), so the source effect may vary across cases. The random-intercept-only model assumes a constant contrast; here we relax it with (1 + source | Id) and check whether the fixed effect and its CI move (opponent finding, 2026-07-17).
slope_rows <- lapply (names (dims), function (nm) {
d <- df[df$ Otazka == dims[[nm]], ]
m <- suppressWarnings (
lmer (Hodnoceni ~ Reseni_gen_AI + (1 + Reseni_gen_AI | Id) +
(1 | anotator_name), data = d, REML = FALSE ))
fe <- summary (m)$ coefficients
ci <- suppressWarnings (confint (m, parm = "beta_" , method = "Wald" ))
put (paste0 ("lmm_slope_b1_ucitel_" , nm), fe[2 , 1 ])
put (paste0 ("lmm_slope_ci_lo_" , nm), ci[2 , 1 ])
put (paste0 ("lmm_slope_ci_hi_" , nm), ci[2 , 2 ])
put (paste0 ("lmm_slope_singular_" , nm), as.integer (isSingular (m)))
data.frame (dim = nm, b1_teacher = fe[2 , 1 ],
ci_lo = ci[2 , 1 ], ci_hi = ci[2 , 2 ], singular = isSingular (m))
})
knitr:: kable (bind_rows (slope_rows), digits = 3 )
vhodnost
-0.857
-0.932
-0.781
FALSE
reaktivni
-1.019
-1.125
-0.913
FALSE
humanisticke
0.767
0.668
0.867
FALSE
systemove
-0.164
-0.275
-0.053
FALSE
Sensitivity: source effect varying across annotators
The primary models let annotators differ in overall severity (random intercept), not in how strongly they separate the two sources. Two checks (independent audit, 2026-10-13): (a) a random slope for source by annotator, (b) a fixed source × annotator interaction that gives each annotator’s own teacher − AI contrast (min, max and sign count across the six annotators).
ann_rows <- lapply (names (dims), function (nm) {
d <- df[df$ Otazka == dims[[nm]] & ! is.na (df$ Hodnoceni), ]
m <- suppressWarnings (
lmer (Hodnoceni ~ Reseni_gen_AI + (1 | Id) + (1 + Reseni_gen_AI | anotator_name),
data = d, REML = FALSE ))
fe <- summary (m)$ coefficients
ci <- suppressWarnings (confint (m, parm = "beta_" , method = "Wald" ))
put (paste0 ("lmm_annslope_b1_ucitel_" , nm), fe[2 , 1 ])
put (paste0 ("lmm_annslope_ci_lo_" , nm), ci[2 , 1 ])
put (paste0 ("lmm_annslope_ci_hi_" , nm), ci[2 , 2 ])
put (paste0 ("lmm_annslope_p_" , nm), fe[2 , 5 ])
put (paste0 ("lmm_annslope_singular_" , nm), as.integer (isSingular (m)))
d$ ann <- factor (d$ anotator_name)
mi <- lmer (Hodnoceni ~ Reseni_gen_AI * ann + (1 | Id), data = d, REML = FALSE )
b <- lme4:: fixef (mi)
per_ann <- sapply (levels (d$ ann), function (a) {
k <- paste0 ("Reseni_gen_AI:ann" , a)
b[["Reseni_gen_AI" ]] + if (k %in% names (b)) b[[k]] else 0
})
put (paste0 ("lmm_ann_b1_min_" , nm), min (per_ann))
put (paste0 ("lmm_ann_b1_max_" , nm), max (per_ann))
put (paste0 ("lmm_ann_n_kladny_" , nm), sum (per_ann > 0 ))
put (paste0 ("lmm_ann_n_zaporny_" , nm), sum (per_ann < 0 ))
data.frame (dim = nm, b1_slope_model = fe[2 , 1 ], ci_lo = ci[2 , 1 ], ci_hi = ci[2 , 2 ],
p = fe[2 , 5 ], singular = isSingular (m),
per_annotator_min = min (per_ann), per_annotator_max = max (per_ann),
n_positive = sum (per_ann > 0 ), n_negative = sum (per_ann < 0 ))
})
knitr:: kable (bind_rows (ann_rows), digits = 3 )
vhodnost
-0.862
-0.948
-0.775
0.0
FALSE
-0.986
-0.682
0
6
reaktivni
-1.003
-1.123
-0.883
0.0
FALSE
-1.237
-0.845
0
6
humanisticke
0.773
0.715
0.831
0.0
TRUE
0.671
0.963
6
0
systemove
-0.138
-0.438
0.161
0.4
FALSE
-0.870
0.391
2
4
Missingness by source, including structurally absent rows
Blinding hides explicit source labels, not all textual source cues. It does not establish ignorable missingness. Separate missing ratings within observed rows from absent rows in the intended case × rater × dimension × source grid.
mis <- df |>
mutate (src = ifelse (Reseni_gen_AI == 0 , "AI" , "ucitel" )) |>
group_by (src) |>
summarise (n = n (), n_na = sum (is.na (Hodnoceni)),
podil_na = mean (is.na (Hodnoceni)), .groups = "drop" )
knitr:: kable (mis, digits = 4 )
AI
7776
860
0.1106
ucitel
7700
740
0.0961
for (i in seq_len (nrow (mis)))
put (paste0 ("missing_podil_" , mis$ src[i]), mis$ podil_na[i])
rating_grid <- tidyr:: expand_grid (Id = unique (df$ Id), anotator_name = unique (df$ anotator_name),
Otazka = unname (dims), Reseni_gen_AI = c (0 , 1 )) |>
left_join (mutate (df, observed_row = TRUE ), by = c ("Id" , "anotator_name" , "Otazka" , "Reseni_gen_AI" ))
put ("n_ocekavanych_bunek" , nrow (rating_grid))
put ("n_chybejicich_radku" , sum (is.na (rating_grid$ observed_row)))
put ("n_chybejicich_bunek_celkem" , sum (is.na (rating_grid$ Hodnoceni)))
missing_grid <- rating_grid |>
group_by (Reseni_gen_AI, Otazka, anotator_name) |>
summarise (expected = n (), absent = sum (is.na (observed_row)),
observed_missing = sum (! is.na (observed_row) & is.na (Hodnoceni)), .groups = "drop" )
write.csv (missing_grid, file.path (H2, "vystupy/tabulky/kap7_chybeni.csv" ), row.names = FALSE )
knitr:: kable (missing_grid)
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A1
324
0
3
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A2
324
0
0
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A3
324
0
178
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A4
324
0
34
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A5
324
0
2
0
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A6
324
0
0
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A1
324
0
2
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A2
324
0
0
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A3
324
0
178
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A4
324
0
33
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A5
324
0
1
0
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A6
324
0
0
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A1
324
0
2
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A2
324
0
0
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A3
324
0
178
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A4
324
0
33
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A5
324
0
1
0
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A6
324
0
0
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A1
324
0
2
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A2
324
0
0
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A3
324
0
179
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A4
324
0
33
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A5
324
0
1
0
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A6
324
0
0
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A1
324
0
2
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A2
324
0
2
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A3
324
0
151
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A4
324
19
33
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A5
324
0
0
1
Jak.moc.je.popisovane.reseni.humanisticky.nebo.behavioralne.orientovane.
A6
324
0
0
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A1
324
0
1
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A2
324
0
2
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A3
324
0
150
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A4
324
19
31
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A5
324
0
0
1
Jak.moc.je.popisovane.reseni.reaktivni.nebo.proaktivni.
A6
324
0
0
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A1
324
0
1
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A2
324
0
2
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A3
324
0
150
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A4
324
19
31
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A5
324
0
0
1
Jak.moc.je.popisovane.reseni.systemove.nebo.situacni.
A6
324
0
0
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A1
324
0
1
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A2
324
0
2
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A3
324
0
150
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A4
324
19
31
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A5
324
0
0
1
Jak.moc.je.vhodna.toto.reseni.pro.danou.situaci.
A6
324
0
0
Post-audit sensitivity: complete AI–teacher pairs per rater and dimension
complete_keys <- df |>
group_by (Id, anotator_name, Otazka) |>
summarise (complete = n_distinct (Reseni_gen_AI) == 2 && all (! is.na (Hodnoceni)), .groups = "drop" ) |>
filter (complete)
paired_ratings <- semi_join (df, complete_keys, by = c ("Id" , "anotator_name" , "Otazka" ))
paired_results <- bind_rows (lapply (names (dims), function (dimension) {
selected <- paired_ratings[paired_ratings$ Otazka == dims[[dimension]], ]
fitted <- lmer (Hodnoceni ~ Reseni_gen_AI + (1 | Id) + (1 | anotator_name), data = selected, REML = FALSE )
estimate <- summary (fitted)$ coefficients["Reseni_gen_AI" , "Estimate" ]
interval <- confint (fitted, parm = "Reseni_gen_AI" , method = "Wald" )
put (paste0 ("paired_n_" , dimension), nrow (selected))
put (paste0 ("paired_b1_" , dimension), estimate)
put (paste0 ("paired_ci_lo_" , dimension), interval[1 , 1 ])
put (paste0 ("paired_ci_hi_" , dimension), interval[1 , 2 ])
data.frame (dimension = dimension, n = nrow (selected), estimate = estimate,
ci_lo = interval[1 , 1 ], ci_hi = interval[1 , 2 ])
}))
write.csv (paired_results, file.path (H2, "vystupy/tabulky/kap7_uplne_dvojice.csv" ), row.names = FALSE )
knitr:: kable (paired_results, digits = 4 )
vhodnost
3314
-0.8588
-0.9024
-0.8152
reaktivni
3314
-1.0109
-1.0679
-0.9538
humanisticke
3306
0.7623
0.7037
0.8208
systemove
3314
-0.1871
-0.2494
-0.1248
Inter-rater agreement with cluster-bootstrap 95% CIs
Unit = (Id × source); Krippendorff’s alpha (ordinal); bootstrap resamples the 324 case Ids with replacement (B = 1000, seed 2.0260714^{7}).
make_wide <- function (d) {
w <- d |> mutate (item = paste0 (Id, "_" , Reseni_gen_AI)) |>
select (anotator_name, item, Hodnoceni) |>
distinct (anotator_name, item, .keep_all = TRUE ) |>
pivot_wider (names_from = item, values_from = Hodnoceni)
m <- as.matrix (w[, - 1 ]); rownames (m) <- w[[1 ]]
m[, colSums (! is.na (m)) >= 2 , drop = FALSE ]
}
alpha_of <- function (m) suppressWarnings (irr:: kripp.alpha (m, method = "ordinal" )$ value)
B <- 1000
ids <- unique (df$ Id)
alpha_rows <- lapply (names (dims), function (nm) {
d <- df[df$ Otazka == dims[[nm]] & ! is.na (df$ Hodnoceni), ]
a <- alpha_of (make_wide (d))
full_matrix <- make_wide (d)
columns_by_id <- lapply (as.character (ids), function (case_id) {
which (colnames (full_matrix) %in% paste0 (case_id, "_" , c (0 , 1 )))
})
boots <- vapply (seq_len (B), function (b) {
selected_cases <- sample (seq_along (ids), replace = TRUE )
selected_columns <- unlist (columns_by_id[selected_cases], use.names = FALSE )
alpha_of (full_matrix[, selected_columns, drop = FALSE ])
}, numeric (1 ))
ci <- quantile (boots, c (.025 , .975 ), na.rm = TRUE )
put (paste0 ("alpha_ord_final_" , nm), a)
put (paste0 ("alpha_ci_lo_" , nm), ci[1 ])
put (paste0 ("alpha_ci_hi_" , nm), ci[2 ])
data.frame (dim = nm, alpha = a, ci_lo = ci[1 ], ci_hi = ci[2 ])
})
knitr:: kable (bind_rows (alpha_rows), digits = 3 )
2.5%…1
vhodnost
0.527
0.485
0.566
2.5%…2
reaktivni
0.645
0.610
0.677
2.5%…3
humanisticke
0.529
0.491
0.565
2.5%…4
systemove
0.556
0.520
0.592
Approach labels: Jaccard (both conventions)
pr <- unique (df[, c ("Id" , "anotator_name" , "Reseni_gen_AI" ,
"Pristup.I" , "Pristup.II" , "Pristup.III" )])
for (c_ in c ("Pristup.I" , "Pristup.II" , "Pristup.III" ))
pr[[c_]][pr[[c_]] %in% c ("" , "nic" )] <- NA
pr$ item <- paste0 (pr$ Id, "_" , pr$ Reseni_gen_AI)
sets <- pr |> rowwise () |>
mutate (s = list (na.omit (c (Pristup.I, Pristup.II, Pristup.III)))) |> ungroup () |>
select (anotator_name, item, s) |> distinct (anotator_name, item, .keep_all = TRUE )
rat <- unique (sets$ anotator_name)
jac <- function (sets) {
inc <- c (); exc <- c ()
for (i in seq_along (rat)) for (j in seq_along (rat)) if (i < j) {
a <- sets |> filter (anotator_name == rat[i])
b <- sets |> filter (anotator_name == rat[j])
m2 <- inner_join (a, b, by = "item" , suffix = c ("_a" , "_b" ))
inc <- c (inc, mean (mapply (function (x, y) {
if (! length (x) && ! length (y)) return (1 )
length (intersect (x, y)) / length (union (x, y)) }, m2$ s_a, m2$ s_b)))
exc <- c (exc, mean (mapply (function (x, y) {
if (! length (x) && ! length (y)) return (NA_real_ )
length (intersect (x, y)) / length (union (x, y)) }, m2$ s_a, m2$ s_b), na.rm = TRUE ))
}
c (incl = mean (inc), excl = mean (exc))
}
j <- jac (sets)
put ("jaccard_pristupy_obe_prazdne_shoda" , j["incl" ])
put ("jaccard_pristupy_bez_prazdnych" , j["excl" ])
knitr:: kable (data.frame (konvence = c ("both-empty = agreement (paper)" , "exclude both-empty" ),
jaccard = round (j, 3 )))
incl
both-empty = agreement (paper)
0.725
excl
exclude both-empty
0.123
Approach labels: prevalence and GLMM (RQ2 of the manuscript)
Binary outcome: any framework label assigned to a solution by a rater. GLMM with logit link, random intercepts for case and annotator (as in the manuscript’s analyses.R; expected OR ≈ 0.31 for teacher solutions).
pr2 <- pr |> rowwise () |>
mutate (n_filled = sum (! is.na (c_across (starts_with ("Pristup" ))))) |> ungroup () |>
group_by (Id, Reseni_gen_AI, anotator_name) |>
summarise (n_filled = max (n_filled), .groups = "drop" ) |>
mutate (filled_binary = as.integer (n_filled > 0 ))
n_labels <- sum (! is.na (pr$ Pristup.I)) + sum (! is.na (pr$ Pristup.II)) + sum (! is.na (pr$ Pristup.III))
lab_by_src <- pr |> pivot_longer (starts_with ("Pristup" ), values_to = "lab" ) |>
filter (! is.na (lab)) |> count (Reseni_gen_AI)
podil_AI <- lab_by_src$ n[lab_by_src$ Reseni_gen_AI == 0 ] / sum (lab_by_src$ n)
gm <- lme4:: glmer (filled_binary ~ Reseni_gen_AI + (1 | Id) + (1 | anotator_name),
data = pr2, family = binomial)
gs <- summary (gm)$ coefficients
gci <- exp (confint (gm, parm = "beta_" , method = "Wald" ))
put ("ramce_n_labelu_celkem" , n_labels)
put ("ramce_podil_labelu_AI" , podil_AI)
put ("glmm_or_ucitel_ramce" , exp (gs[2 , 1 ]))
put ("glmm_or_ci_lo_ramce" , gci[2 , 1 ])
put ("glmm_or_ci_hi_ramce" , gci[2 , 2 ])
knitr:: kable (data.frame (
n_labels_total = n_labels, share_AI = round (podil_AI, 3 ),
OR_teacher = round (exp (gs[2 , 1 ]), 3 ),
ci = sprintf ("[%.3f; %.3f]" , gci[2 , 1 ], gci[2 , 2 ]), p = signif (gs[2 , 4 ], 3 )))
874
0.717
0.313
[0.259; 0.377]
0
Freeze: write manifest
out <- bind_rows (manifest) |>
mutate (skript = "analyzy/notebooks/30_ai_vs_ucitel.qmd" ,
data_sha = DATA_SHA, seed = SEED, datum = format (Sys.Date ()))
dir.create (file.path (H2, "vystupy/tabulky" ), showWarnings = FALSE , recursive = TRUE )
write.csv (out, file.path (H2, "vystupy/tabulky/kap7_k2_shoda_cisla.csv" ), row.names = FALSE )
cat ("Manifest:" , nrow (out), "metrik → vystupy/tabulky/kap7_k2_shoda_cisla.csv \n " )
Manifest: 140 metrik → vystupy/tabulky/kap7_k2_shoda_cisla.csv
sessionInfo ()$ R.version$ version.string
[1] "R version 4.6.1 (2026-06-24 ucrt)"