Takes the tibble returned by read_WEXTOR_log and collapses it into
'tidy' format: one row per participant. Every WEXTOR variable submitted anywhere in the study
becomes its own column, keyed by a single unique id per row and the resulting data frame
contains the commonly relevant paradata variables.
Internally this happens:
For each participant, their (usually many) rows are collapsed into one by taking, for every column, the first non-missing value in reverse chronological order by default – i.e. the last value submitted/logged for that column. This matters if a participant navigated back and resubmitted a page: the latest value wins, consistent with how
read_WEXTOR_logitself resolves repeated keys within a single request. Setduplicates = "first"to keep the earliest value instead (e.g. the participant's very firsttimestamp/client_ip). Empty-string answers ("", submitted but left blank) are turned intoNAin the final output, same as a variable that was never submitted at all.Participants who share an IP address with at least one other participant are flagged, in a
anomaly_flagcolumn added at the very end, containing"double IP address"if so,NAotherwise. This is a common sign of one person completing the study more than once from the same computer/network – worth a manual check before treating those rows as independent observations. It only catches an exact IP match; it won't catch someone who switched networks between attempts.
Usage
tidy_WEXTOR_log(data, id_col = "id", duplicates = c("last", "first"))Arguments
- data
A tibble as returned by
read_WEXTOR_log, i.e. containingquery,timestamp,client_ip,user_agentandduration_mscolumns.- id_col
Name of the participant identifier variable inside
query. Defaults to"id", WEXTOR's standard variable name.- duplicates
How to resolve a participant having more than one non-missing value logged for the same column.
"last"(default) keeps the most recently submitted/logged value;"first"keeps the earliest.
Value
A tibble with one row per unique participant id (sorted
numerically when every id looks numeric, alphabetically otherwise),
columns id, timestamp, client_ip, user_agent
and duration_ms (each keeping its original type from data),
one column per WEXTOR variable collected anywhere in the study
(character; convert types such as SUS/UEQ items to numeric downstream
as needed), and a final anomaly_flag column as described above.
Examples
log <- read_WEXTOR_log(path_to_file("fake_wextor_log.txt"))
data <- tidy_WEXTOR_log(log)
# anything worth a manual look:
dplyr::filter(data, !is.na(anomaly_flag))
#> # A tibble: 6 × 19
#> id timestamp client_ip user_agent duration_ms pagetrail var1
#> <chr> <dttm> <chr> <chr> <int> <chr[1d]> <chr>
#> 1 22097 2000-01-01 10:02:03 203.0.113.109 Mozilla/5… 333 start.ht… 1
#> 2 42092 2000-01-01 10:28:47 198.51.100.1… Mozilla/5… 436 start.ht… 54
#> 3 46048 2000-01-01 09:03:50 203.0.113.29 Mozilla/5… 324 start.ht… 18
#> 4 51588 2000-01-01 10:33:35 198.51.100.1… Mozilla/5… 346 start.ht… 55
#> 5 65057 2000-01-01 11:01:52 203.0.113.29 Mozilla/5… 462 start.ht… 56
#> 6 79163 2000-01-01 09:31:51 203.0.113.109 Mozilla/5… 267 start.ht… 97
#> # ℹ 12 more variables: var2 <chr>, var3 <chr>, var4 <chr>, var5 <chr>,
#> # var6 <chr>, var7 <chr>, var8 <chr>, var9 <chr>, var10 <chr>, var11 <chr>,
#> # var12 <chr>, anomaly_flag <chr>