如何循环遍历数据集中的每一列,检查特定值是否存在于任何一列中,创建新列,并在该值存在时将1输入新列,否则输入0?

7
我正在做一个研究项目,其中一张表格的输入方式还不太适合进行分析,因此我正在尝试重新组织它。目前,每一行都是一个测试者,每一列都是他们回答错误的问题,按升序输入。因此,对于第一行,条目可能会读取“Q1”“Q3”“Q9”等,对应于第一、二、三列。总共有25个问题。
我的目标是重新组织数据,使每个问题都有一个列。如果测试者回答了问题,则相应列的条目为1,否则为0。
有一种暴力方法似乎可以解决问题。可以单独变换每一列,并检查每个值的每一列。然而,有25个问题,所有这些打字似乎极其低效,所以我怀疑肯定有更好的方法。
暴力代码如下:
df %>%
  mutate(Q3 == ifelse(col1 == "Q3" | col2 == "Q3" | col3 == "Q3", 0, 1))

这里,col1、col2、col3都是可能包含Q3的列,而Q3可能是测试者答错的问题。如果它们中的任何一个包含Q3,则我们输入0。否则,输入1。

当有25个问题时,代码会变得太长。

编辑:数据帧的示例如下。

sample <- "ID   Col1  Col2  Col3  Col4
1          100   Q1     
2          101   Q3    Q4
3          102   Q2    Q3    Q4   
4          103   
5          104   Q4
6          105   Q1    Q2    Q3    Q4 "

期望的输出如下:
sample <- "ID    Q1    Q2    Q3    Q4
1          100   0     1     1     1
2          101   1     1     0     0
3          102   1     0     0     0   
4          103   1     1     1     1
5          104   1     1     1     0 
6          105   0     0     0     0 "

1
你能分享一些输入数据的样本(不是所有列和所有行),以及基于此的期望输出吗?阅读此线程以了解如何使用可重现的示例提问:https://dev59.com/eG025IYBdhLWcg3whGSx - M--
5个回答

3

这是我的解决方案 - 将数据从宽转换为长,然后再转回去。

s <- reshape2::melt(sample, id.vars = "ID", value.name = "Q")
s$variable <- 1
s <- subset(s, complete.cases(s))
s <- reshape(s, idvar = "ID", timevar = "Q", direction = "wide")
s <- apply(s, 2, function(x) ifelse(is.na(x), 0, x))

2

1) 假设在结尾的注释中,DF已经被展示出来了,请使用sapply创建一个指标矩阵,并将其与ID列进行cbind。最后使名称更加友好。不需要使用任何软件包。

ques <- function(i) paste0("Q", 1:25) %in% unlist(DF[i, -1])
DFout <- cbind(DF[1], +t(sapply(1:nrow(DF), ques)))
names(DFout)[-1] <- paste0("Q", names(DFout[-1]))

前五列是:

> DFout[1:5]

   ID Q1 Q2 Q3 Q4
1 100  1  0  0  0
2 101  0  0  1  1
3 102  0  1  1  1
4 103  0  0  0  0
5 104  0  0  0  1
6 105  1  1  1  1

2) 另一种可能性是将输入转换为长格式,然后使用xtabs从中创建表格。

library(dplyr)
library(tidyr)

tab <- DF %>% 
  gather(key, Question, -ID) %>%
  filter(nzchar(Question)) %>%
  mutate(Question = factor(Question, paste0("Q", 1:25))) %>%
  xtabs(~ ID + Question, .)

在这个表格中,我们展示了前5列:

> tab[, 1:5]

     Question
ID    Q1 Q2 Q3 Q4
  100  1  0  0  0
  101  0  0  1  1
  102  0  1  1  1
  104  0  0  0  1
  105  1  1  1  1

如果需要结果为数据框,则添加以下代码:
library(tibble)

tab %>% 
  as.data.frame.matrix %>% 
  rownames_to_column(var = "ID")    

注意

sample <- "rows ID   Col1  Col2  Col3  Col4
1          100   Q1     
2          101   Q3    Q4
3          102   Q2    Q3    Q4   
4          103   
5          104   Q4
6          105   Q1    Q2    Q3    Q4"
DF <- read.table(text = sample, header = TRUE, fill = TRUE, as.is = TRUE,
  strip.white = TRUE)[-1]

2

@G.Grothendieck提供了一个非常好的解决方案。这是那个答案的一个变化,它将为测试中的每个问题生成一个值,即使每个学生都回答正确。不可否认,这个方法略微不够优雅。另外请注意,我使用缺失值构建数据,而不是空字符串,因此过滤器略有不同。

dat <- data.frame(ID = c(100:105), 
                  Col1 = c("Q1", "Q3", "Q2", NA, "Q4", "Q1"), 
                  Col2 = c(NA, "Q4", "Q3", NA, NA, "Q2"), 
                  Col3 = c(NA, NA, "Q4", NA, NA, "Q3"), 
                  Col4 = c(NA, NA, NA, NA, NA, "Q4"), 
                  stringsAsFactors = FALSE)

dat %>%
  gather(key = col, val = wrong, -ID) %>%
  select(-col) %>%
  mutate(tmp = 1) %>%
  complete(wrong = paste0("Q", 1:25)) %>%
  filter(!is.na(wrong)) %>%
  spread(wrong, tmp, fill = 0) %>%
  select(ID, paste0("Q", 1:25)) %>%
  filter(!is.na(ID)) %>%
  data.frame

2

这是一种类似于其他方法的将数据转换为长格式的方法,但是使用了data.table

library(data.table)
setDT(df)

dcast(melt(df, 'ID'), ID ~ value, fun.aggregate = length)[, V1 := NULL][]
#     ID Q1 Q2 Q3 Q4
# 1: 100  1  0  0  0
# 2: 101  0  0  1  1
# 3: 102  0  1  1  1
# 4: 104  0  0  0  1
# 5: 105  1  1  1  1

0

这将是使用简单的for循环的一种方法。

让我们从上面获取这些数据:

sample <- "rows ID   Col1  Col2  Col3  Col4
1          100   Q1     
2          101   Q3    Q4
3          102   Q2    Q3    Q4   
4          103   
5          104   Q4
6          105   Q1    Q2    Q3    Q4"
DF <- read.table(text = sample, header = TRUE, fill = TRUE, as.is = TRUE,
                 strip.white = TRUE)[-1]

这是一种方法。它填充现有的数据框以进行双重检查,以确保重新编码是否成功:

vars <- paste0("Q", 1:4)

for (i in vars){
  DF[i] = rowSums(ifelse(DF[, grep( "Col", names(DF))]==i, 1, 0))
}  

之后可以删除不需要的列:

DF <- DF[, -grep( "Col", names(DF))]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接