
Vom Testcenter zum Skalierungsdatensatz
standard-workflow.RmdDieser Workflow führt von Testcenter-Antworten zu kodierten Daten, psychometrischen Kennwerten und einem Personen-×-Items-Datensatz. Das Beispiel umfasst einen Testzeitpunkt und einen Kompetenzbereich (Lesen) mit Autocode-Kodierschemata. Für Daten ohne solche Schemata, etwa aus StarS, siehe den Hinweis in Abschnitt 2.
| Quelle | Benötigte Daten |
|---|---|
| Studio | Units mit Kodierschemata, Item-Metadaten und Seiteninformationen |
| Testcenter | Antworten sowie Testdesign aus Testpersonen und Testheften |
Dateipfade, Serveradresse, Version, Workspace-IDs und Testmodus sind Platzhalter. Die folgenden Blöcke sind für die Ausführung mit eigenen Daten vorgesehen.
1. Studio: Units und Kodierschemata laden
Die Units müssen zur eingesetzten Testversion passen.
metadata = TRUE liefert die Item-Verknüpfungen,
unit_definition = TRUE die Seiteninformationen.
Ein Kodierschema legt für jede Variable fest, welche Antwort welchen Code und wie viele Punkte erhält, etwa „richtige Auswahl → Code 1 → 1 Punkt“. Es kann auch Regeln zur Ableitung weiterer Variablen enthalten.
login_studio()
meldet am Studio an, access_workspace()
wählt den Arbeitsbereich. get_units() lädt die
Units; add_coding_scheme()
bereitet ihre vorhandenen Kodierschemata für die Auswertung auf.
Das Beispiel verwendet Studio-Version 20.0.1. Falls auf
deiner Studio-Anmeldeseite eine andere Version steht, trage diese bei
app_version ein.
studio_login <- login_studio(app_version = "20.0.1")
studio <- access_workspace(studio_login, ws_id = 123)
units <- get_units(studio, metadata = TRUE, unit_definition = TRUE)
units <- add_coding_scheme(units)2. Testcenter: Antworten und Design bereitstellen
Testcenter-Verbindung herstellen
Auch beim CSV-Import wird hier Testcenter-Zugriff
benötigt: get_design() lädt das vollständige
Testdesign. Erst damit lassen sich später fehlende Antworten als Not
reached oder Auslassung einordnen. read_responses() allein
reicht dafür nicht aus.
Anmeldung mit login_testcenter(),
Auswahl des Arbeitsbereichs mit access_workspace().
testcenter_login <- login_testcenter(base_url = "https://TESTCENTER-ADRESSE/")
testcenter <- access_workspace(testcenter_login, ws_id = 1)Antworten: eine der beiden Alternativen wählen
A – direkt aus dem Testcenter mit get_responses():
responses <- get_responses(testcenter)B – bereits heruntergeladenen CSV-Export einlesen
mit read_responses():
responses <- read_responses("daten/Responses.csv")Ohne Autocode-Kodierschema (z. B. StarS): prepare_responses(responses)
entpackt die Rohantworten zur eigenen Weiterverarbeitung; es vergibt
keine Codes oder Scores. Bereits im Export gespeicherte Kodierungen
lassen sich mit prepare_coded(responses)
entpacken. Die folgenden Schritte zeigen den Weg mit
Kodierschemata; die entpackten Daten sind dafür kein direkter
Ersatz.
Das vollständige Testdesign laden
get_design()
liefert die vorgesehenen Person–Testheft–Variablen-Zuordnungen.
Im gewählten Testcenter-Workspace müssen dafür die Materialien der Erhebung liegen:
-
Testpersonen-XML (z. B.
testtakers.xml): Gruppen, Logins, Codes, Anmeldemodus und Zuordnung zu den Testheften. - Die zugehörigen Testheft-XML-Dateien (Booklets): referenzierte Units und ihre Reihenfolge innerhalb der Testlets.
Die Dateien müssen dem Stand der Durchführung entsprechen. Die
Variablen und Kodierschemata kommen hier aus dem zuvor aus Studio
geladenen Objekt units.
design <- get_design(testcenter, units = units, mode = "run-hot-return")mode muss zum Anmeldemodus der Erhebung passen.
units muss alle relevanten Aufgaben enthalten; Antworten
und Design müssen dieselbe Erhebung abdecken.
3. Automatisch kodieren und Ergebnis prüfen
code_responses()
nutzt das R-Paket eatAutoCode, das
den IQB-Autocoder
(@iqb/responses) einbindet. Dieser vergibt anhand des
Kodierschemas Codes (code_id) und Punkte
(code_score). prepare = TRUE ergänzt Metadaten
für die weiteren Schritte. Die abschließende Missing-Zuweisung
folgt erst in Abschnitt 5.
coded <- code_responses(responses, units, prepare = TRUE)
count(coded, code_status, code_type)Entscheidend sind die Variablen, die später als Items ausgewertet werden:
code_status |
Bewertung und nächster Schritt |
|---|---|
CODING_COMPLETE |
In Ordnung: Die Itemvariable ist fertig kodiert. |
CODING_INCOMPLETE,
DERIVE_PENDING
|
Noch offen: Kodierung bzw. benötigte Ausgangscodes prüfen; ggf. manuelle Codes ergänzen (Abschnitt 4). |
UNSET, CODING_ERROR,
DERIVE_ERROR
|
Prüfen: Kodierschema und Antworten kontrollieren; nach Korrekturen Units neu laden und erneut kodieren. |
DISPLAYED, PARTLY_DISPLAYED,
NOT_REACHED, INVALID
|
In der Regel in Ordnung: erwartbare Missing-Status, die in Abschnitt 5 behandelt werden. Unerwartete Häufungen prüfen. |
Untervariablen, die selbst keine Items werden, können andere Status behalten, ohne dass ein Problem vorliegt. Zu klären sind offene Kodierungen und Fehler, die die Itemvariablen oder deren Berechnung betreffen.
Zur Information: die voreingestellte Missing-Zuordnung
Das Objekt muss nicht selbst angelegt oder übergeben
werden. Die folgende Tabelle zeigt nur zur Information die
Standardwerte von complete_design(): Auslassungen und
ungültige Antworten erhalten 0 Punkte, Not reached und Kodierfehler
NA. Die Beispielaufrufe verwenden diese Voreinstellung
automatisch.
missings <- tibble::tribble(
~code_id, ~code_status, ~code_score, ~code_type,
-96, "NOT_REACHED", NA_real_, "MISSING_NOT_REACHED",
-97, "CODING_ERROR", NA_real_, "MISSING_CODING_IMPOSSIBLE",
-98, "INVALID", 0, "MISSING_INVALID_RESPONSE",
-99, "DISPLAYED", 0, "MISSING_BY_OMISSION")Nur für eigene Regeln wird missings vor Abschnitt 4
angelegt und mit missings = missings an die Aufrufe in
Abschnitt 4 und 5 übergeben.
Die beiden Funktionen nutzen Missing-Zuordnungen in unterschiedlichen Richtungen:
-
code_responses(..., codes_manual = ...): Ein manuell gelieferter Missing-Code incode_idwird in Status und Score übersetzt, z. B.-99→DISPLAYEDund 0 Punkte. Ohnecodes_manualwird diese Zuordnung hier nicht angewendet. Die abschließenden Missing-Scores weist erstcomplete_design()zu. -
complete_design(...): Die Funktion bestimmt zuerst die Missing-Art und ordnet ihr übercode_typeden Code und Score aus der Tabelle zu,- B.
MISSING_BY_OMISSION→-99und 0 Punkte. Das gilt auch ohne manuelle Kodierung; fehlende Status werden ergänzt.
- B.
4. Optional: manuelle Codes einfügen
Nur nötig, wenn Antworten manuell kodiert werden. Sonst
weiter mit Abschnitt 5. Die fertige Tabelle
codes_manual benötigt group_id,
login_name, login_code,
booklet_id, unit_key, variable_id
und code_id: genau einen Code je zu kodierender Antwort,
passend zum Kodierschema. code_id enthält den Code, nicht
die Punktzahl.
# Kennungen als Text, code_id als Ganzzahl einlesen
codes_manual <- readr::read_csv("daten/codes_manual.csv",
col_types = readr::cols(.default = "c", code_id = "i"))
coded <- code_responses(responses, units, prepare = TRUE,
codes_manual = codes_manual)
count(coded, code_status, code_type)Erneut die ursprünglichen responses übergeben: So werden
die manuellen Codes eingefügt und davon abhängige Variablen neu
berechnet. Anschließend die Status wie in Abschnitt 3 prüfen.
5. Design vervollständigen und Missings zuweisen
Der Antwortexport allein enthält nicht alle vorgesehenen
Antworten. complete_design()
ergänzt fehlende Einträge aus dem vollständigen Design. Anhand der
Unit-Reihenfolge innerhalb eines Testlets werden nicht erreichte
Aufgaben am Ende von Auslassungen vor später bearbeiteten Units
unterschieden. Bereits als Auslassung kodierte Antworten bleiben
standardmäßig Auslassungen.
design_coded <- complete_design(coded = coded, units = units, design = design)
count(design_coded, id_used, code_type)
analysis_data <- filter(design_coded, id_used)id_used kennzeichnet Personen mit mindestens einem
gespeicherten Kodierstatus. Mit
recode_omissions_to_not_reached = TRUE können zusätzlich
abschließende Auslassungen als nicht erreicht eingeordnet werden.
Aufgaben, die laut Design gar nicht vorgesehen waren, sind
Missing by Design, nicht Not reached; ihre Zellen
bleiben beim späteren Umformen NA.
6. Psychometrische Kennwerte berechnen
Im Beispiel gehören alle Units zum Bereich Lesen; bei mehreren
Bereichen enthält domains die jeweilige Zuordnung (ein
Bereich je Unit). evaluate_psychometrics()
berechnet die Kennwerte.
domains <- distinct(units, unit_key)
domains$domain <- "Lesen"
psychometrics <- evaluate_psychometrics(analysis_data, units, domains = domains)
psychometrics <- add_item_id(psychometrics, units)
psychometrics |>
select(item_id, unit_key, variable_id, code_id, code_score,
code_n, code_p_valid, code_pbc) |>
distinct()code_n zählt einen Code, code_p_valid ist
sein Anteil unter Antworten mit Score ungleich NA
(einschließlich als 0 gewerteter Missings). code_pbc ist
die Korrelation seines Auftretens mit dem mittleren Itemscore des
Bereichs (nicht part-whole-korrigiert).
7. Personen × Items für eatModel
add_item_id()
ergänzt die Item-ID aus den Studio-Metadaten über unit_key
und variable_id. Variablen ohne Item-Verknüpfung erhalten
NA; im Beispiel werden nur verknüpfte Itemvariablen
übernommen.
Für die Spaltennamen wird item nach der Konvention
unit_key__item_id gebildet, z. B. DH001__08.
item_id bleibt dabei eine Zeichenkette: Führende Nullen
müssen erhalten bleiben und dürfen nicht durch eine Umwandlung in Zahlen
verloren gehen.
item_data <- add_item_id(analysis_data, units)
item_data <- filter(item_data, !is.na(item_id))Optional: fehlende führende Nullen ergänzen. Falls
die Studio-Metadaten einstellige Item-IDs wie "8"
enthalten, diesen Chunk vor dem nächsten Schritt ausführen. Nach der
Konvention besteht item_id hier aus genau zwei Ziffern:
"8" wird zu "08", "08" und
"12" bleiben unverändert. Die Prüfung bricht bei anderen
Formaten ab, damit keine ungültigen IDs entstehen.
stopifnot(all(grepl("^[0-9]{1,2}$", item_data$item_id)))
item_data <- mutate(item_data,
item_id = stringr::str_pad(item_id, width = 2, pad = "0"))Anschließend die Itemnamen bilden und ins weite Format umformen:
item_data <- mutate(item_data, item = paste0(unit_key, "__", item_id))
scaling_data <- item_data |>
select(any_of(c("group_id", "login_name", "login_code", "IDSTUD")),
item, code_score) |>
pivot_wider(names_from = item, values_from = code_score)pivot_wider()
erzeugt eine Zeile je Person und eine Scorespalte je Item; die
vorhandenen Kennungen (group_id, login_name,
login_code, IDSTUD) bleiben erhalten.
any_of() überspringt fehlende Kennungsspalten.
Voraussetzung sind eindeutige Itemnamen und genau ein Score je Person
und Item. Fehlende Werte bleiben NA, sie werden nicht mit 0
aufgefüllt. analysis_data aufbewahren: Darin bleiben die
Missing-Arten unterscheidbar.
Die weiteren Schritte zur Skalierung beschreibt die eatModel-Dokumentation.