Подмножество data.table на основе всех возможных комбинаций двух или более переменных

Я хочу подмножить data.frame на основе того, являются ли некоторые переменные положительными, отрицательными или какой-то промежуточной комбинацией. Для n переменных это должно привести к 2^n возможным комбинациям.

Я думаю, что combn можно использовать для достижения этого, но я изо всех сил пытаюсь сделать это правильно.

Образец данных:

library(data.table)
dt <- data.table(x = runif(100, -1, 1), y = runif(100, -1, 1), z = runif(100, -1, 1))

Что я хочу:

dt[x < 0 & y < 0 z < 0, ]
dt[x < 0 & y < 0 z > 0, ]
dt[x < 0 & y > 0 z < 0, ]
dt[x < 0 & y > 0 z > 0, ]
dt[x > 0 & y < 0 z < 0, ]
dt[x > 0 & y < 0 z > 0, ]
dt[x > 0 & y > 0 z < 0, ]
dt[x > 0 & y > 0 z > 0, ]

Что я пробовал до сих пор:

combinator <- function(z){
  cnames <- colnames(z)
  combinations <- t(combn(c(rep("<", ncol(z)), rep(">", ncol(z))),ncol(z)))

  retval <- t(sapply(1:nrow(combinations), function(p){
    sapply(1:ncol(z), function(q) paste(cnames[q], combinations[p,q], 0))
  }))

  return(apply(retval, 1, paste, collapse = " & "))
}

Выход:

> l <- combinator(dt)
> l
 [1] "x < 0 & y < 0 & z < 0" "x < 0 & y < 0 & z > 0" "x < 0 & y < 0 & z > 0" "x < 0 & y < 0 & z > 0"
 [5] "x < 0 & y < 0 & z > 0" "x < 0 & y < 0 & z > 0" "x < 0 & y < 0 & z > 0" "x < 0 & y > 0 & z > 0"
 [9] "x < 0 & y > 0 & z > 0" "x < 0 & y > 0 & z > 0" "x < 0 & y < 0 & z > 0" "x < 0 & y < 0 & z > 0"
[13] "x < 0 & y < 0 & z > 0" "x < 0 & y > 0 & z > 0" "x < 0 & y > 0 & z > 0" "x < 0 & y > 0 & z > 0"
[17] "x < 0 & y > 0 & z > 0" "x < 0 & y > 0 & z > 0" "x < 0 & y > 0 & z > 0" "x > 0 & y > 0 & z > 0"

> l[1]
[1] "x < 0 & y < 0 & z < 0"

> subset(dt, eval(l[1]))
Error in subset.data.table(dt, eval(l[1])) : 
  'subset' must evaluate to logical

Также, если следующее показывает, что я не перечисляю все желаемые комбинации:

> unique(l)
[1] "x < 0 & y < 0 & z < 0" "x < 0 & y < 0 & z > 0" 
[3] "x < 0 & y > 0 & z > 0" "x > 0 & y > 0 & z > 0"

вывод должен иметь 8 уникальных результатов вместо 4, показанных выше.


person Gautam    schedule 29.01.2018    source источник


Ответы (1)


Просто выполните dt[, sign_combi := do.call(paste, lapply(dt, sign))], и вы можете split или by = этот столбец по мере необходимости, например, split(dt, dt$sign_combi). Попытка склеить код — плохая идея.

Например:

set.seed(47) # setting seed for reproducibility
dt <- data.table(x = runif(100, -1, 1), y = runif(100, -1, 1), z = runif(100, -1, 1))

# create combination column (you could keep it separate if you prefer)
dt[, sign_combi := do.call(paste, lapply(dt, sign))]

# split original data by sign combinations
result = split(dt, dt$sign_combi)

# list of 8 resulting data tables
length(result)
# [1] 8

# peaking at the first three rows of the first three tables:
lapply(head(result, 3), head, 3)
# $`-1 -1 -1`
#             x          y          z sign_combi
# 1: -0.5713038 -0.7103555 -0.6873705   -1 -1 -1
# 2: -0.1407803 -0.8371153 -0.3686299   -1 -1 -1
# 3: -0.6478446 -0.7629461 -0.7458949   -1 -1 -1
# 
# $`-1 -1 1`
#             x          y         z sign_combi
# 1: -0.8070969 -0.3952283 0.9212030    -1 -1 1
# 2: -0.1190934 -0.4969318 0.8082232    -1 -1 1
# 3: -0.6536104 -0.3280965 0.6880454    -1 -1 1
# 
# $`-1 1 -1`
#              x         y          z sign_combi
# 1: -0.78789241 0.8577848 -0.7586369    -1 1 -1
# 2: -0.04442825 0.4736388 -0.3354734    -1 1 -1
# 3: -0.22105744 0.3012645 -0.4160631    -1 1 -1
person Gregor Thomas    schedule 29.01.2018
comment
Отлично, работает для меня! Не знал о функции знаков. - person Gautam; 30.01.2018
comment
К вашему сведению, в data.table добавлена ​​собственная функция разделения, которая позволяет, например, split(dt, by="sign_combi", keep.by=FALSE) удалить столбец, используемый для разделения. - person Frank; 30.01.2018