根据多个列上的条件创建新列

时间:2018-09-21 03:31:25

标签: r data.table

我下面有这个数据集:

library(data.table)
set.seed(123)
dt <- data.table(x_1 = c(3,2,2,1,3,2,1,2,3,3),
                 x_2 = c(2,1,1,3,2,3,3,1,2,3),
                 x_3 = c(2,3,3,2,1,2,3,3,1,1),
                 y_1 = sample(2, 10, replace = T),
                 y_2 = sample(2, 10, replace = T),
                 y_3 = sample(2, 10, replace = T))

我想对x列执行if else操作,如果x列中的任何一个等于1,它将创建一个具有相应y列值的新列。例如在第五行x_3 = 1中,因此新列应返回与y_3相对应的值。

我的想法是返回与条件匹配的x列的名称作为中间列,然后使用值后缀(1,2,3)从相应的y列中提取值。

但是创建中间列的第一步是创建一个列表,不符合条件的行将返回character(0)

cols <- c("x_1", "x_2", "x_3")
dt$int <- apply(dt[,..cols], 1, function(x) names(which(x == 1)))

我想要的输出:

x_1 x_2 x_3 y_1 y_2 y_3 new
3   2   2   1   2   2   NA
2   1   3   2   1   2   1
2   1   3   1   2   2   2
1   3   2   2   2   2   2
3   2   1   2   1   2   2
2   3   2   1   2   2   NA
1   3   3   2   1   2   2
2   1   3   2   1   2   1
3   2   1   2   1   1   1
3   3   1   1   2   1   1

关于如何实现这一目标的任何想法?基于数据表的解决方案将是更可取的。

3 个答案:

答案 0 :(得分:2)

不确定在同一行的x_ *中如何处理没有任何1或多个1的情况。

这是一种可能的方法,将sparsehash/internal/libc_allocator_with_realloc.h:68:40: warning: ‘void* realloc(void*, size_t)’ moving an object of non-trivially copyable type ‘struct std::pair<const std::__cxx11::basic_string<char>, int>’; use ‘new’ and ‘delete’ instead [-Wclass-memaccess] return static_cast<pointer>(realloc(p, n * sizeof(value_type))); ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ 转换为长格式,然后在x_ 中找到第一个位置1,然后访问y _

data.table::melt

输出:

dt[, rn:=.I]
dt[melt(dt, id.vars="rn", meas=list(c("x_1", "x_2", "x_3"), c("y_1", "y_2", "y_3")))[,
    value2[which(value1==1L)[1L]], by=.(rn)], yval := V1, on=.(rn)]

编辑:合并了latemail的简洁版本,并且还可以处理多个版本

    x_1 x_2 x_3 y_1 y_2 y_3 rn yval
 1:   3   2   2   1   2   2  1   NA
 2:   2   1   3   2   1   2  2    1
 3:   2   1   3   1   2   2  3    2
 4:   1   3   2   2   2   2  4    2
 5:   3   2   1   2   1   2  5    2
 6:   2   3   2   1   2   2  6   NA
 7:   1   3   3   2   1   2  7    2
 8:   2   1   3   2   1   2  8    1
 9:   3   2   1   2   1   1  9    1
10:   3   3   1   1   2   1 10    1

答案 1 :(得分:2)

另一种可能的解决方案:

ix <- dt[, max.col(.SD == 1) * NA^(!rowSums(.SD == 1)), .SDcols = 1:3]

dt[, newcol := as.matrix(.SD)[cbind(.I, ix)]
   , .SDcols = 4:6][]

给出:

    x_1 x_2 x_3 y_1 y_2 y_3 newcol
 1:   3   2   2   1   2   2     NA
 2:   2   1   3   2   1   2      1
 3:   2   1   3   1   2   2      2
 4:   1   3   2   2   2   2      2
 5:   3   2   1   2   1   2      2
 6:   2   3   2   1   2   2     NA
 7:   1   3   3   2   1   2      2
 8:   2   1   3   2   1   2      1
 9:   3   2   1   2   1   1      1
10:   3   3   1   1   2   1      1

注释:

  • 您也可以使用as.matrix代替as.data.frame
  • 如果有多个x列等于1,则需要使用ties.method的{​​{1}}参数。您可以在max.col"random""first"之间进行选择。

如果您事先不知道列的位置,则可以将以上解决方案归纳为:

"last"

答案 2 :(得分:0)

这是Map的一个选项。为“ x”和“ y”列子集data.table(.SD)的子集,创建“ x”列的逻辑向量,并获得相应的“ y”值,其中“ x”为1,并使用pmin将其折叠为单个元素(假设'x列每行的数量不超过1个)

dt[, new := do.call(pmin, c(Map(function(x, y) y * NA^(x != 1),
      .SD[, 1:3, with = FALSE], .SD[, 4:6, with = FALSE]), na.rm = TRUE)), ]
dt
#    x_1 x_2 x_3 y_1 y_2 y_3 new
# 1:   3   2   2   1   2   2  NA
# 2:   2   1   3   2   1   2   1
# 3:   2   1   3   1   2   2   2
# 4:   1   3   2   2   2   2   2
# 5:   3   2   1   2   1   2   2
# 6:   2   3   2   1   2   2  NA
# 7:   1   3   3   2   1   2   2
# 8:   2   1   3   2   1   2   1
# 9:   3   2   1   2   1   1   1
#10:   3   3   1   1   2   1   1