【问题标题】:how subset rows that have value larger than other values for multiple columns in R如何子集具有大于 R 中多列的其他值的值的行
【发布时间】:2023-04-08 21:48:01
【问题描述】:

我有以下data.table

library(data.table)
dt <- data.table(V1=c(1,3,1,0,NA,0),
                 V2=c(1,0,1,0,1,3),
                 Q1=c(3,5,10,14,0,3),
                 Q2=c(0,1,8,NA,0,NA))

我想添加一个新列,其值为1

如果V1,V2 中的任何列的值大于2, 如果 任何Q1,Q2 的值大于 0

所以最后我想要这样的东西:

> dt
   V1 V2 Q1 Q2 new
1:  1  1  3  0   0
2:  3  0  5  1   1
3:  1  1 10  8   0
4:  0  0 14 NA   0
5: NA  1  0  0   0
6:  0  3  3 NA   1

编辑 原则上,我希望有 2 个列名向量,例如 v_columms &lt;- names(dt)[names(dt) %like%"V"]q_columms &lt;- names(dt)[names(dt) %like%"q"] 并使用这些

【问题讨论】:

  • dt[(V1 &gt; 2 | V2 &gt; 2) &amp; (Q1 &gt; 0 | Q2 &gt; 0), new := 1] ?您是否需要对现实中的更多列执行此操作?也许更好:dt[, new := as.integer((V1 &gt; 2 | V2 &gt; 2) &amp; (Q1 &gt; 0 | Q2 &gt; 0))]
  • 是的,所以原则上我希望有 2 个向量,所以像 v_columms &lt;- names(dt)[names(dt) %like%"V"]q_columms &lt;- names(dt)[names(dt) %like%"q"] 并使用这些

标签: r datatable


【解决方案1】:

我们可以使用melt处理多个列,方法是在measure中指定patterns将其转换为'long'格式,然后应用条件

dt[, new := melt(dt, measure = patterns("V", "Q"))[,  
      +(any(value1 > 2) & any(value2 > 0)),rowid(variable)]$V1]
dt
#   V1 V2 Q1 Q2 new
#1:  1  1  3  0   0
#2:  3  0  5  1   1
#3:  1  1 10  8   0
#4:  0  0 14 NA   0
#5: NA  1  0  0   0
#6:  0  3  3 NA   1

或者没有melt,如果只有两组列,那么

vs <- grep("V", names(dt))
qs <- grep("Q", names(dt))
dt[, new := +(Reduce(`|`, lapply(.SD[, ..vs], `>`, 2)) &
              Reduce(`|`, lapply(.SD[, ..qs], `>`, 0)))]

【讨论】:

  • 看看我是否理解正确,如果我有3个模式V1V2Q,我的条件是V1&gt;1V2&gt;2Q&gt;0然后我会使用dt[, new := melt(dt, measure = patterns("V1","V2", "Q"))[, +(any(value1 &gt; 1 | value2 &gt; 2) &amp; any(value3 &gt; 0)),rowid(variable)]$V1] 吗?
  • @quant 是的,就是这样
  • @quant 对于多个变量,也可以使用Mapmelt(dt, measure = patterns("V", "Q"))[, +(Reduce('&amp;', Map(function(x, y) any(x &gt; y), .SD, c(2, 0)))), by = rowid(variable), .SDcols = value1:value2]
  • 在你的第二个解决方案中我得到Error in eval(expr, envir, enclos) : object '..vs' not found虽然
  • @quant 那有点老了。 ..是最近推出的
【解决方案2】:

使用dplyrcase_whenif_else

dt %>%
   mutate(new = case_when((V1 > 2 | V2 > 2) & (Q1 > 0 | Q2) > 0 ~ 1,
                     TRUE ~ 0))

dt %>%
   mutate(new = if_else((V1 > 2 | V2 > 2) & (Q1 > 0 | Q2 > 0), 1 , 0))

  V1 V2 Q1 Q2 new
1  1  1  3  0   0
2  3  0  5  1   1
3  1  1 10  8   0
4  0  0 14 NA   0
5 NA  1  0  0   0
6  0  3  3 NA   1

【讨论】:

    【解决方案3】:

    这是另一种带有一些辅助函数的方法:

    foo <- function(.dt, cols, vals, na.rm = TRUE) {
      rowSums(.dt[, cols, with=FALSE] > vals, na.rm = na.rm) > 0 
    }
    
    bar <- function(.dt, cols_list, vals_list) {
      as.integer(Reduce("&", Map(function(cols, vals) foo(.dt, cols, vals), cols_list, vals_list)))
    }
    
    dt[, new := bar(.SD, list(v_columms, q_columms), list(2, 0))]
    

    【讨论】:

      猜你喜欢
      • 2020-07-07
      • 2022-12-05
      • 1970-01-01
      • 1970-01-01
      • 2022-11-11
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多