使用 tidyverse 清理排名选择调查
Using tidyverse to clean up rank-choice survey
我在 R 中有这样的调查数据,其中我向人们展示了两组动作 - 高和低 - 并要求他们对每个动作进行排名。每组包含独特的动作,用字母标记(总共 6 个动作)。
id A_High B_High C_High D_Low E_Low F_Low
001 5 2 1 6 4 3
002 6 4 3 5 2 1
003 3 1 6 2 4 5
004 6 5 2 1 3 4
我需要一个如下所示的新 df,其中每个高操作都分配了一个新的数字等级(介于 0 和 3 之间),对应于排名在 下方的低操作项目的数量 高动作。
例如,id
001 的人排名 A_High
排名第 5,B_High
排名第 2,C_High
排名第 1。A_High
' s 的新排名将是 1(因为只有 1 个低行动,D_Low
排名低于 A_High
),B_High
的新排名将是 3(因为所有 3 个低行动都排名低于 B_High),C_High
的新排名将是 3(因为所有 3 个低级动作的排名都低于 C_High
)。
id A_High_rank B_High_rank C_High_rank
001 1 3 3
002 0 1 1
003 2 3 0
004 0 0 2
我觉得这可以通过 if/else 语句来完成,但怀疑应该有一种更有效的方法来使用 tidyverse 来实现这一点。在真实数据集中,我有 1000 多行和 12 个动作(6 个高和 6 个低)。如果对此有任何帮助,我将不胜感激。
谢谢!
数据:
"id A_High B_High C_High D_Low E_Low F_Low
001 5 2 1 6 4 3
002 6 4 3 5 2 1
003 3 1 6 2 4 5
004 6 5 2 1 3 4"
A base R
选项将遍历 'High' 列,通过检查它是否小于 'Low' 列来获取创建的逻辑矩阵的 rowSums
,并通过将 _rank
附加为后缀
来重命名这些输出
out <- cbind(df1[1], sapply(df1[2:4],
function(x) rowSums(x < df1[endsWith(names(df1), 'Low')])))
names(out)[-1] <- paste0(names(out)[-1], "_rank")
-输出
out
# id A_High_rank B_High_rank C_High_rank
#1 1 1 3 3
#2 2 0 1 1
#3 3 2 3 0
#4 4 0 0 2
或使用dplyr
library(dplyr)
df1 %>%
transmute(id, across(ends_with('High'),
~ rowSums(. < select(df1, ends_with('Low'))), .names = '{.col}_rank'))
# id A_High_rank B_High_rank C_High_rank
#1 1 1 3 3
#2 2 0 1 1
#3 3 2 3 0
#4 4 0 0 2
数据
df1 <- structure(list(id = 1:4, A_High = c(5L, 6L, 3L, 6L), B_High = c(2L,
4L, 1L, 5L), C_High = c(1L, 3L, 6L, 2L), D_Low = c(6L, 5L, 2L,
1L), E_Low = c(4L, 2L, 4L, 3L), F_Low = c(3L, 1L, 5L, 4L)),
class = "data.frame", row.names = c(NA,
-4L))
历经千辛万苦,这就是我想出的tidyverse
解决方案。这很有趣!
library(tidyverse)
data %>%
pivot_longer(cols = ends_with("_High"), names_to = "High Variables", values_to = "High") %>%
pivot_longer(cols = ends_with("_Low"), names_to = "Low Variables", values_to = "Low") %>%
filter(High-Low < 0) %>%
group_by(`High Variables`, `id`) %>%
summarise(Count = n()) %>%
pivot_wider(names_from = `High Variables`, values_from = Count) %>%
arrange(id)
翻译:
前两行创建两对列并保持 id
不变。每对都有两列,一列包含原始列名,另一列包含值。每对列代表 High
或 Low
.
然后,我过滤了所有行,只保留 Low
大于 High
的行。然后我计算每个 id
还剩多少,然后倒转格式。
现在我只需要弄清楚如何将那些 NA
变成 0
。
这是输出:
> data %>%
+ pivot_longer(cols = ends_with("_High"), names_to = "High Variables", values_to = "High") %>%
+ pivot_longer(cols = ends_with("_Low"), names_to = "Low Variables", values_to = "Low") %>%
+ filter(High < Low) %>%
+ group_by(`High Variables`, `id`) %>%
+ summarise(Count = n()) %>%
+ pivot_wider(names_from = `High Variables`, values_from = Count) %>%
+ arrange(id)
`summarise()` regrouping output by 'High Variables' (override with `.groups` argument)
# A tibble: 4 x 4
id A_High B_High C_High
<int> <int> <int> <int>
1 1 1 3 3
2 2 NA 1 1
3 3 2 3 NA
4 4 NA NA 2
我在 R 中有这样的调查数据,其中我向人们展示了两组动作 - 高和低 - 并要求他们对每个动作进行排名。每组包含独特的动作,用字母标记(总共 6 个动作)。
id A_High B_High C_High D_Low E_Low F_Low
001 5 2 1 6 4 3
002 6 4 3 5 2 1
003 3 1 6 2 4 5
004 6 5 2 1 3 4
我需要一个如下所示的新 df,其中每个高操作都分配了一个新的数字等级(介于 0 和 3 之间),对应于排名在 下方的低操作项目的数量 高动作。
例如,id
001 的人排名 A_High
排名第 5,B_High
排名第 2,C_High
排名第 1。A_High
' s 的新排名将是 1(因为只有 1 个低行动,D_Low
排名低于 A_High
),B_High
的新排名将是 3(因为所有 3 个低行动都排名低于 B_High),C_High
的新排名将是 3(因为所有 3 个低级动作的排名都低于 C_High
)。
id A_High_rank B_High_rank C_High_rank
001 1 3 3
002 0 1 1
003 2 3 0
004 0 0 2
我觉得这可以通过 if/else 语句来完成,但怀疑应该有一种更有效的方法来使用 tidyverse 来实现这一点。在真实数据集中,我有 1000 多行和 12 个动作(6 个高和 6 个低)。如果对此有任何帮助,我将不胜感激。
谢谢!
数据:
"id A_High B_High C_High D_Low E_Low F_Low
001 5 2 1 6 4 3
002 6 4 3 5 2 1
003 3 1 6 2 4 5
004 6 5 2 1 3 4"
A base R
选项将遍历 'High' 列,通过检查它是否小于 'Low' 列来获取创建的逻辑矩阵的 rowSums
,并通过将 _rank
附加为后缀
out <- cbind(df1[1], sapply(df1[2:4],
function(x) rowSums(x < df1[endsWith(names(df1), 'Low')])))
names(out)[-1] <- paste0(names(out)[-1], "_rank")
-输出
out
# id A_High_rank B_High_rank C_High_rank
#1 1 1 3 3
#2 2 0 1 1
#3 3 2 3 0
#4 4 0 0 2
或使用dplyr
library(dplyr)
df1 %>%
transmute(id, across(ends_with('High'),
~ rowSums(. < select(df1, ends_with('Low'))), .names = '{.col}_rank'))
# id A_High_rank B_High_rank C_High_rank
#1 1 1 3 3
#2 2 0 1 1
#3 3 2 3 0
#4 4 0 0 2
数据
df1 <- structure(list(id = 1:4, A_High = c(5L, 6L, 3L, 6L), B_High = c(2L,
4L, 1L, 5L), C_High = c(1L, 3L, 6L, 2L), D_Low = c(6L, 5L, 2L,
1L), E_Low = c(4L, 2L, 4L, 3L), F_Low = c(3L, 1L, 5L, 4L)),
class = "data.frame", row.names = c(NA,
-4L))
历经千辛万苦,这就是我想出的tidyverse
解决方案。这很有趣!
library(tidyverse)
data %>%
pivot_longer(cols = ends_with("_High"), names_to = "High Variables", values_to = "High") %>%
pivot_longer(cols = ends_with("_Low"), names_to = "Low Variables", values_to = "Low") %>%
filter(High-Low < 0) %>%
group_by(`High Variables`, `id`) %>%
summarise(Count = n()) %>%
pivot_wider(names_from = `High Variables`, values_from = Count) %>%
arrange(id)
翻译:
前两行创建两对列并保持 id
不变。每对都有两列,一列包含原始列名,另一列包含值。每对列代表 High
或 Low
.
然后,我过滤了所有行,只保留 Low
大于 High
的行。然后我计算每个 id
还剩多少,然后倒转格式。
现在我只需要弄清楚如何将那些 NA
变成 0
。
这是输出:
> data %>%
+ pivot_longer(cols = ends_with("_High"), names_to = "High Variables", values_to = "High") %>%
+ pivot_longer(cols = ends_with("_Low"), names_to = "Low Variables", values_to = "Low") %>%
+ filter(High < Low) %>%
+ group_by(`High Variables`, `id`) %>%
+ summarise(Count = n()) %>%
+ pivot_wider(names_from = `High Variables`, values_from = Count) %>%
+ arrange(id)
`summarise()` regrouping output by 'High Variables' (override with `.groups` argument)
# A tibble: 4 x 4
id A_High B_High C_High
<int> <int> <int> <int>
1 1 1 3 3
2 2 NA 1 1
3 3 2 3 NA
4 4 NA NA 2