如何遍历各列,检查某列中是否存在特定值,对新列进行突变,如果存在则输入1,否则输入0?

时间:2019-09-18 15:37:02

标签: r dataframe dplyr data-cleaning

我正在研究一个项目,并且输入的表格中的一种还不太适合分析,因此我试图对其进行重组。当前,每一行都是考生,每一列都是他们错误回答的问题,以升序输入。因此,对于第一行,条目可以分别读取第一,第二和第三列的“ Q1”,“ Q3”,“ Q9”等。总共有25个问题。

我的目标是重组数据,以便每个问题都有一列。如果考生正确回答了该问题,则相应列的条目为1,否则为0。

似乎有一种蛮力的方法。可以分别更改每列并检查每列中的每个值。但是,这里有25个问题,所有这些键入似乎效率都很低,所以我怀疑必须有更好的方法。

蛮力代码类似于:

df %>%
  mutate(Q3 == ifelse(col1 == "Q3" | col2 == "Q3" | col3 == "Q3", 0, 1))

这里,col1,col2,col3都是可能包含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 "

5 个答案:

答案 0 :(得分: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))

答案 1 :(得分:2)

1)假设DF如注释中的可重复显示,请使用sapply创建指标矩阵,然后cbind移至ID列。最后,使名称更好。不使用任何软件包。

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]))

前5列是:

> 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 :(得分:2)

@ G.Grothendieck提供了一个非常好的解决方案。这是该答案的一种变体,即使每个学生都正确回答了该问题,它也会为测试中的每个问题产生价值。诚然,它不那么优雅。另外请注意,我使用缺失值而不是空字符串来构造数据,因此过滤器略有不同

    x     y
0   A   NaN
1   A   NaN
2   A   NaN
3   B   5.0
4   B   NaN
5   B   NaN
6   B   NaN
7   C  10.0
8   C   NaN
9   C   NaN
10  C   NaN

答案 3 :(得分: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

答案 4 :(得分: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))]