如何在 df 中创建以列为条件的新列并将它们合计为 R 中的一个

How to create new columns conditional of columns in a df and sum them together to one in R

我是 R 的新手并且有一个 df,我在其中通过使用 sqldf 创建了一些标准(a1、b1、c1、d1.. 等等)(在这个例子中我只显示 a1 到c1)

df <- data.frame('var1' = c('x','1', 'X', '', 'X'), "var2" = c('y','3', '', 'X', ''), "var3" = c('y','7', '', 'X', 'X'))

library(sqldf)

testcases_sql <- 
("

CASE WHEN var1 = 1  THEN 1 ELSE 0 END AS a1, 

CASE WHEN var1 = 1  AND var2 = 'y' THEN 1 ELSE 0 END AS b1,

CASE WHEN var1= 1 AND var2= 3 THEN 1 ELSE 0 END AS b1,

CASE WHEN var1= 1 AND var2= 3 THEN 1 ELSE 0 END AS b1,

CASE WHEN var1= 1 AND var2= 'X' THEN 1 ELSE 0 END AS b1,

CASE WHEN var1= 1 AND var2= 'X' AND var3=7 THEN 1 ELSE 0 END AS c1,

CASE WHEN var1= 'X' AND var3='X' THEN 1 ELSE 0 END AS c1")



sql_string = paste("SELECT *" , ",", testcases_sql, " FROM ", "df", sep=" ") 

#create criteria
data = sqldf(sql_string)
head(data)

SQLDF 为每个条件创建一个新列

head(data)

# var1 var2 var3 a1 b1 b1 b1 b1 c1 c1
# 1    x    y    y  0  0  0  0  0  0  0
# 2    1    3    7  1  0  1  1  0  0  0
# 3    X            0  0  0  0  0  0  0
# 4         X    X  0  0  0  0  0  0  0
# 5    X         X  0  0  0  0  0  0  1

但我需要一个变量中的所有条件,以便所有 b1 都在一列中,所有 c1 都在一列中,依此类推。每行满足标准的次数无关紧要,我只需要在每一列中有一个“1”。在我原来的df中,一个条件可以重复多少次是没有系统的,完全是随机的。

我的预期结果是:

wished_df <- data.frame('var1' = c('x','1', 'X', '', 'X'), "var2" = c('y','3', '', 'X', ''), "var3" = c('y','7', '', 'X', 'X'), "a1" = c('0','1', '0', '0', '0'), "b1=" =c('0','1', '0', '0','0'), "c1=" =c('0','0', '0', '0','1') )

head(wished_df)
#  var1 var2 var3 a1 b1 c1
#1    x    y    y  0   0   0
#2    1    3    7  1   1   0
#3    X            0   0   0
#4         X    X  0   0   0
#5    X         X  0   0   1

sqldf 可能不是最好的函数。我最好的方法是通过将 variabels

相加来更改 df
#sum the variable

data$newb1 <- data$b1 + data$b1 + data$b1 + data$b1

#error in `$<-.data.frame`(`*tmp*`, newb1, value = numeric(0)) : replacement has 0 rows, data has 5

#delete the old variable
data$b1 <- data$b1 <-data$b1 <- data$b1 <- NULL

#rename the variable
data$b1 <- data$newb1

#delete old variable
data$newb1 <- NULL

#repeat for c1, d1, e1 and so on...

data$newc1 <- data$c1 + data$c1

data$c1 <- data$c1 <- NULL

data$c1 <- data$newc1

data$newc1 <- NULL

这是行不通的,而且非常难看,需要大量代码(我有 80 个测试用例)。

有更简单的方法吗?

非常感谢

我只想使用 R 的内置布尔运算符来完成这项任务。请注意,我已从您的 SQL 选择中删除了一些逻辑冗余:

df <- data.frame('var1' = c('x','1', 'X', '', 'X'), 
                 "var2" = c('y','3', '', 'X', ''), 
                 "var3" = c('y','7', '', 'X', 'X'))

df$a1 <- 1 *  (df$var1 == "1")
df$b1 <- 1 * ((df$var1 == "1") & (df$var2 == "y" | df$var2 == "3"  | df$var2 == "X"))
df$c1 <- 1 * ((df$var1 == "1"  &  df$var2 == "X" & df$var3 == "7") | 
              (df$var1 == "X"  &  df$var3 == "X"))

df
#>   var1 var2 var3 a1 b1 c1
#> 1    x    y    y  0  0  0
#> 2    1    3    7  1  1  0
#> 3    X            0  0  0
#> 4         X    X  0  0  0
#> 5    X         X  0  0  1

reprex package (v0.3.0)

于 2020-05-14 创建

在SQL中我们可以OR条件来简化代码。每个 true 条件将评估为 1,每个 false 条件评估为 0。我们已将 SQL 字符串的名称更改为 testcasesSQL 因为 $ 字符串插值需要变量名称的单词字符 -- 非单词字符终止变量名,不被视为变量名的一部分。

如果测试用例有某种模式,那么我们可以使用 R 代码生成测试用例SQL 字符串,但不清楚是否存在,我们只是修复问题中的代码并将其翻译成更多紧凑 SQL。

请注意,逻辑条件 (var1 = 1) 或 (var1 = 1 AND var2 = 'y') 可以简化为 (var1 = 1) 。下面我们没有应用这个或其他潜在的逻辑简化来阐明问题中的代码如何直接转换为更简单的 SQL。此外,如果这些是自动生成的,它可能不是最简单的形式,从答案的角度来看,它没有区别。

library(sqldf)

testcasesSQL <- "(var1 = 1) or (var1 = 1  AND var2 = 'y') as a1,
  (var1 = 1 AND var2 = 'y') or (var1 = 1 AND var2 = 3) or (var1 = 1 AND var2 = 'X') AS b1,
  (var1 = 1 AND var2 = 'X' AND var3 = 7) or (var1 = 'X' AND var3 ='X') AS c1"

dfname <- "df"

fn$sqldf("select *, $testcasesSQL from $dfname")

给予:

  var1 var2 var3 a1 b1 c1
1    x    y    y  0  0  0
2    1    3    7  1  1  0
3    X            0  0  0
4         X    X  0  0  0
5    X         X  0  0  1

生成条件

我们可以定义一个矩阵,其条件名称为第 1 列,其中一列用于 var1、var2 和 var3,这样一行上的条件是 AND,多行上的条件具有相同的条件名称 OR 'd。从问题中的示例来看,似乎 var1 始终存在,我们在 gsub 行中使用了该事实。

condmat <- matrix(c('c1', 1, NA, NA,
'c1', 1, 'y', NA,
'c2', 1, 'y', NA,
'c2', 1, 3, NA,
'c2', 1, 'X', NA,
'c3', 1, 'X', 7,
'c3', 'X', NA, 'X'),, 4, byrow = TRUE)
colnames(condmat) <- c("cond", "var1", "var2", "var3")

s <- sprintf("(%s = '%s' AND %s = '%s' AND %s = '%s')", 
  colnames(condmat)[2], condmat[, 2], 
  colnames(condmat)[3], condmat[, 3], 
  colnames(condmat)[4], condmat[, 4])

s2 <- gsub("AND \w+ = 'NA'", "", s)
s3 <- tapply(s2, condmat[, 1], paste, collapse = " OR ")
cond <- paste(paste(s3, 'as', names(s3)), collapse = ",\n")

dfname <- "df"

fn$sqldf("select *, $cond from $dfname")

注意上面生成的cond变量是:

cat(cond)
## (var1 = '1'  ) OR (var1 = '1' AND var2 = 'y' ) as c1,
## (var1 = '1' AND var2 = 'y' ) OR (var1 = '1' AND var2 = '3' ) OR (var1 = '1' AND var2 = 'X' ) as c2,
## (var1 = '1' AND var2 = 'X' AND var3 = '7') OR (var1 = 'X'  AND var3 = 'X') as c3