我有一个具有唯一 Participant_ID 的数据集,每个 Participant_ID 均由两个不同的 Rater_ID 对许多不同变量进行评分(此处为 Q1、Q2 和 Q3)。我正在尝试找到一种方法来计算一个变量,该变量指示两个评分者的评分是否相差在 1 分之内。这是我正在使用的数据的简化版本:
library(tidyverse)
Participant_ID <- rep(1:3,2)
Rater_ID <- c(rep("A",3),rep("B",3))
Q1 <- c(5, 2, 1,3, 3, 4)
Q2 <- c(4, 2, 2,3, 5, 2)
Q3 <- c(4, 3, 3,3, 4, 5)
df <- tibble(Participant_ID, Rater_ID, Q1, Q2, Q3)
我可以通过使用以下内容拼写出代码的每次迭代来做到这一点:
df <- df %>% group_by(Participant_ID) %>%
mutate(Check_Q1= ifelse((abs(Q1[1]-Q1[2]) > 1), 1, 0),
Check_Q2= ifelse((abs(Q2[1]-Q2[2]) > 1), 1, 0),
Check_Q3= ifelse((abs(Q3[1]-Q3[2]) > 1), 1, 0)) %>% ungroup()
Q1 被标记为参与者 1(分配为 1),Q2 被标记为参与者 2,Q1 和 Q3 都被标记为参与者 3,因为评级差异 > 1。
然而,在我的真实数据中,“Q”变量不仅有3个,还有很多。另外,我希望这段代码能够在 Q 变量数量发生变化的各种情况下使用。用户将在运行代码之前指定问题数。我一直试图弄清楚如何使用 for 循环来做到这一点,但我无法弄清楚。据我所知,这是:
number_of_questions <- 3
questions <- grep("Q", names(df), value=TRUE)
df <- df %>% group_by(Participant_ID)
for(q in questions){
for(x in 1:number_of_questions){
check_varname <- paste0("Check_Q",x)
df <- df %>%
mutate(!!check_varname := ifelse((abs(get(q)[1]-get(q)[2]) > 1), 1, 0))
}}
df <- df %>% ungroup()
我没有收到任何错误,但输出不正确。它为 Participant_ID 3 的 Q1、Q2 和 Q3 分配 1。任何人都可以帮助我理解我做错了什么吗?
您可以使用
across
和 .names
函数来完成此操作。
df %>%
mutate(across(starts_with("Q"), ~ +(abs(.[1] - .[2]) > 1), # thanks @r2evans for improved code
.names = "Check_{.col}"), .by = Participant_ID)
输出:
Participant_ID Rater_ID Q1 Q2 Q3 Check_Q1 Check_Q2 Check_Q3
<int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 A 5 4 4 1 0 0
2 2 A 2 2 3 0 1 0
3 3 A 1 2 3 1 0 1
4 1 B 3 3 3 1 0 0
5 2 B 3 5 4 0 1 0
6 3 B 4 2 5 1 0 1
(请注意,我假设所有问题都以“Q”开头。如果情况并非如此,您可以使用
across(all_of(2:4), ...)
等内容轻松修改第 2 列到第 4 列)
重塑数据可能会更有效和/或更具可读性,以便每个问题都位于行上,评分者位于列上:
df_long <- df %>%
pivot_longer(-c(Participant_ID, Rater_ID)) %>%
pivot_wider(names_from = Rater_ID, values_from = value)
Participant_ID name A B
<int> <chr> <dbl> <dbl>
1 1 Q1 5 3
2 1 Q2 4 3
3 1 Q3 4 3
4 2 Q1 2 3
5 2 Q2 2 5
6 2 Q3 3 4
7 3 Q1 1 4
8 3 Q2 2 2
9 3 Q3 3 5
从那里,可以轻松创建检查列:
df_long %>%
mutate(check = abs(A - B) > 1)
Participant_ID name A B check
<int> <chr> <dbl> <dbl> <lgl>
1 1 Q1 5 3 TRUE
2 1 Q2 4 3 FALSE
3 1 Q3 4 3 FALSE
4 2 Q1 2 3 FALSE
5 2 Q2 2 5 TRUE
6 2 Q3 3 4 FALSE
7 3 Q1 1 4 TRUE
8 3 Q2 2 2 FALSE
9 3 Q3 3 5 TRUE
这可以转变为更广泛的格式:
df_long %>%
mutate(check = abs(A - B) > 1) %>%
select(-c(A, B)) %>%
pivot_wider(names_from = name, values_from = check, names_prefix = 'check_')
Participant_ID check_Q1 check_Q2 check_Q3
<int> <lgl> <lgl> <lgl>
1 1 TRUE FALSE FALSE
2 2 FALSE TRUE FALSE
3 3 TRUE FALSE TRUE