【问题标题】:Update data.table based on multiple columns and conditions根据多列和条件更新 data.table
【发布时间】:2019-07-24 05:23:27
【问题描述】:

这是来自Efficient way to subset data.table based on value in any of selected columns 的后续问题。

样本数据
我有一个带有 5 个 p 列的 data.table,表示一个类型(type1 或 type2 或NA)。 我还有 5 个 r 列,表示分数(1-10,或NA)。

library(data.table)
set.seed(123)
v  <- c( "type1", "type2", NA_character_ )
v2 <- c( 1:10, rep( NA_integer_, 10 ) )
DT <- data.table( id = 1:100,
                  p1 = sample(v, 100, replace = TRUE ),
                  p2 = sample(v, 100, replace = TRUE ),
                  p3 = sample(v, 100, replace = TRUE ),
                  p4 = sample(v, 100, replace = TRUE ),
                  p5 = sample(v, 100, replace = TRUE ),
                  r1 = sample(v2, 100, replace = TRUE ),
                  r2 = sample(v2, 100, replace = TRUE ),
                  r3 = sample(v2, 100, replace = TRUE ),
                  r4 = sample(v2, 100, replace = TRUE ),
                  r5 = sample(v2, 100, replace = TRUE ))

期望的输出
我想创建两个新列(一个用于 type1,一个用于 type2),我逐行检查一个或多个 p 列中是否出现 type1/type2,以及是否至少有一个对应的 r 列(p1 - > check r1, p2 -> check r2, etc.) 包含一个值。

“手动”解决方案
这可以像下面这样使用大量的 AND 和 OR 语句来完成:

manual_solution <- DT[ ( p1 == "type1" & !is.na( r1 ) ) |
                         ( p2 == "type1" & !is.na( r2 ) ) |
                         ( p3 == "type1" & !is.na( r3 ) ) |
                         ( p4 == "type1" & !is.na( r4 ) ) |
                         ( p5 == "type1" & !is.na( r5 ) ), 
                       type1_present := "yes"]
manual_solution <- DT[ ( p1 == "type2" & !is.na( r1 ) ) |
                         ( p2 == "type2" & !is.na( r2 ) ) |
                         ( p3 == "type2" & !is.na( r3 ) ) |
                         ( p4 == "type2" & !is.na( r4 ) ) |
                         ( p5 == "type2" & !is.na( r5 ) ), 
                       type2_present := "yes"]
manual_solution[ is.na( type1_present ), type1_present := "no" ]
manual_solution[ is.na( type2_present ), type2_present := "no" ]

问题:数十个 p 和 r 列的自动化
但看看Efficient way to subset data.table based on value in any of selected columns 的答案,我相信有更好的方法。特别是因为我的生产数据包含更多的 p 列和 r 列。

所以我开始玩,但很快就卡住了......

#build vectors p-columns and r-columns
p_cols <- grep( "^p", names( DT ), value = TRUE )
r_cols <- grep( "^r", names( DT ), value = TRUE )

#create logical vectors to test for NA
logi_p <- as.data.table( sapply( DT[, ..p_cols ], function(x) !is.na(x) ) )
logi_r <- as.data.table( sapply( DT[, ..r_cols ], function(x) !is.na(x) ) )

#which non-NA p-values also have a non-NA r-value?
logi <- as.data.table( sapply( logi_p * logi_r, as.logical ) )

现在我没有任何关于如何继续的灵感。
有什么想法/建议吗?

奖金
我的主要关注点是上面的问题。但是我的生产数据还包含更多类型(在 p 列中)......因此,按类型添加列(或可以对该结果进行 dcast)的解决方案将“用一块石头杀死两只鸟”。

【问题讨论】:

  • 我确信有一些技巧可以在宽格式中执行此操作,但我倾向于认为处理长格式数据更容易。像这样的东西似乎有效:d &lt;- melt(DT, id = "id", measure = patterns("^p", "^r"), value.name = c("p", "r")); d[ , `:=`(t1 = p == "type1" &amp; !is.na(r), t2 = p == "type2" &amp; !is.na(r))]; d[ , .(type1_present = c("no", "yes")[any(t1, na.rm = TRUE) + 1], type2_present = c("no", "yes")[any(t2, na.rm = TRUE) + 1]), by = .(id)].
  • 你可能会发现this question很有用

标签: r data.table


【解决方案1】:

这是一个解决方案,我将类型列转换为矩阵,使用 r 列中的信息更新它们,然后在它上面应用搜索相关类型的次数与要查找的类型一样多。

# Convert to a matrix
pMAT <- DT[, as.matrix(.SD), .SDcols = patterns("^p")]
# Subset a matrix with another matrix of the r columns
pMAT[] <- pMAT[DT[, as.logical(as.matrix(.SD)), .SDcols = patterns("^r")]]

types2check <- c("type1", "type2")
for (t in types2check) {
  set(
    x = DT, 
    j = paste0(t, "_present"), 
    value = ifelse(apply(pMAT, 1, function(x) any(x == t, na.rm = TRUE)), "yes", "no")
  )
}

额外

使用dcast(),您可以执行以下操作。管道只是为了便于阅读,其中一些步骤可能可以简化。

result <- data.table(id = DT[["id"]], stack(DT[, ..pcols]), stack(DT[, ..rcols])) %>%
  setnames(c("id", "type", "pind", "rval", "rind")) %>% 
  .[, .(type = type[as.logical(rval)], id)] %>% 
  dcast(id ~ type, value.var = "id", fill = "no", fun.aggregate = function(x) if (length(x)) "yes") %>% 
  .[, `NA` := NULL]

> head(result)
   id type1 type2
1:  1   yes   yes
2:  2   yes    no
3:  3    no   yes
4:  4    no    no
5:  5    no   yes
6:  6   yes    no

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多