Here is an alternative, data-driven approach which uses a two dimensional lookup table to describe the category of two consecutive triggers. Subsequently, observed data are joined with the lookup table to categorize and count the sequence of events.
This approach
- avoids hardcoded
case_when() statements or summation of hardcoded conditions
- covers all possible combinations of two consecutive triggers thereby exhibiting gaps in the definition of categories
- is easily adapted to different definitions of categories
From the question and OP's comment I understand that all values in trigger fall into one of three groups:
resp = participant has answered
Sc = triggers ending with the letter c like Sc, or SBc, L1c, H1c, etc
other = all other triggers which are neither resp nor ending with the letter c
With this simplification, we can define the categories of consecutive triggers as shown in the 2-D lookup table below:
|
other |
Sc |
resp |
| other |
cr |
cr |
fa |
| Sc |
miss |
miss |
hit |
| resp |
NA |
NA |
NA |
Rows refer to the preceding trigger and columns to the subsequent trigger. So, other followed by resp belongs to category fa (false alarm).
NA indicates that this sequence is not categorized yet and should not be counted. The OP has not explicitely specified how to categorize trigger sequences after resp so I have deliberately chosen to ignore them to be in line with Sinh Nguyen's answer. But see below for how to modify the categorisation easily.
library(data.table)
# define categories
lut2D <- fread(
"t1 other Sc resp
other cr cr fa
Sc miss miss hit
resp NA NA NA"
)
# reshape to long format
lut1D <-
melt(lut2D, id.var = "t1", variable.name = "t2", value.name = "category", na.rm = TRUE)[
# order categories as expected by the OP
, category := factor(category, levels = c("hit", "miss", "fa", "cr", "dr"))][]
# prepare trigger values
tmp <- as.data.table(df)[
# re-group trigger values: all values ending with "c"
trigger %like% "c$", trigger := "Sc"][
# re-group trigger values: all other values
!trigger %in% c("Sc", "resp"), trigger := "other"][
# create overlapping subsequences of length 2 in wide format
# and summaries per ID
, .(t1 = head(trigger, -1L), t2 = tail(trigger, -1L),
Nb_trig = .N, Nb_Sc = sum(trigger == "Sc")), keyby = ID]
# right join with lookup table to get matching categories, drop undefined categories
categorised <- lut1D[tmp, on = .(t1, t2), nomatch = NULL]
# count categories
dcast(categorised, ID + Nb_trig + Nb_Sc ~ category, fun = length)
With Sinh Nguyen's data we get
ID Nb_trig Nb_Sc hit miss fa cr
1: P1 96 21 4 17 15 33
2: P2 104 25 6 19 17 33
Please, note that I have added a count of trigger values Nb_trig per ID. So for P1 96 trigger values were recorded which yield 95 subsequences of length 2.
Additional explanations
- I have used
data.table as I am more acquainted with it. dplyr & tidyr may work as well.
- The code uses
data.table's command chaining.
- The categorisation is applied on subsequences of length 2. In order to join the observed data and the lookup table both datasets have to be reshaped so that they include a column
t1 for the preceding trigger value and a column t2 for the subsequent trigger value.
- Thus, the reshaped lookup table
lut1d looks as follows
t1 t2 category
1: other other cr
2: Sc other miss
3: other Sc cr
4: Sc Sc miss
5: other resp fa
6: Sc resp hit
- and the reshaped observed dataset
tmp have become
ID t1 t2 Nb_trig Nb_Sc
1: P1 Sc Sc 96 21
2: P1 Sc other 96 21
3: P1 other Sc 96 21
4: P1 Sc other 96 21
5: P1 other other 96 21
---
194: P2 other other 104 25
195: P2 other other 104 25
196: P2 other resp 104 25
197: P2 resp other 104 25
198: P2 other resp 104 25
- After the join, the categorised observed dataset
categorised is
t1 t2 category ID Nb_trig Nb_Sc
1: Sc Sc miss P1 96 21
2: Sc other miss P1 96 21
3: other Sc cr P1 96 21
4: Sc other miss P1 96 21
5: other other cr P1 96 21
---
140: other resp fa P2 104 25
141: other other cr P2 104 25
142: other other cr P2 104 25
143: other resp fa P2 104 25
144: other resp fa P2 104 25
- Note that undefined categories like
197: P2 resp other 104 25
have been dropped from the join result and are not counted.
Modifying the categorisation
The 2-D lookup table is easily modified. For instance, we can introduce a new category dr (duplicated responses) to cover cases where a participant has pressed the button two or more times in a row.
lut2D <- fread(
"t1 other Sc resp
other cr cr fa
Sc miss miss hit
resp NA NA dr"
)
With the modified categorisation and Sinh Nguyen's data we get
ID Nb_trig Nb_Sc hit miss fa cr dr
1: P1 96 21 4 17 15 33 8
2: P2 104 25 6 19 17 33 6
This result can help to explain the differences between Sinh Nguyen's answer and AnilGoyals's answer as discussed in Sinh Nguyen's comment.