Р: данные.таблица сравнение наборов строк


Я работаю в R с данными.таблицы. У меня есть следующие данные.таблица, кодирующая набор точек с координатами A,B,C, D и индексом, кодирующим набор, к которому принадлежит точка.

library(data.table)

      A B C D set
   1: 0 0 0 0   1
   2: 1 0 1 0   2
   3: 1 1 1 0   2
   4: 0 1 0 0   2
   5: 1 0 1 1   2
   6: 0 1 0 0   3
   7: 1 1 0 0   3
   8: 0 0 1 0   4
   9: 1 0 1 0   4
  10: 0 1 0 1   4
  11: 0 0 0 0   5
  12: 1 0 0 0   5
  13: 1 1 1 0   5
  14: 1 1 1 1   5

dt = setDT(structure(list(A = c(0L, 1L, 1L, 0L, 1L, 0L, 1L, 0L, 1L, 0L, 
0L, 1L, 1L, 1L), B = c(0L, 0L, 1L, 1L, 0L, 1L, 1L, 0L, 0L, 1L, 
0L, 0L, 1L, 1L), C = c(0L, 1L, 1L, 0L, 1L, 0L, 0L, 1L, 1L, 0L, 
0L, 0L, 1L, 1L), D = c(0L, 0L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 1L, 
0L, 0L, 0L, 1L), set = c(1L, 2L, 2L, 2L, 2L, 3L, 3L, 4L, 4L, 
4L, 5L, 5L, 5L, 5L)), .Names = c("A", "B", "C", "D", "set"), row.names = c(NA, 
-14L), class = "data.frame"))

У меня есть другая таблица, кодирующая, например, вероятность каждого набора.

   set       mass
1:   1 0.27809187
2:   2 0.02614841
3:   3 0.36890459
4:   4 0.28975265
5:   5 0.03710247

wt = setDT(structure(list(set = 1:5, mass = c(0.27809187, 0.02614841, 0.36890459, 
0.28975265, 0.03710247)), .Names = c("set", "mass"), row.names = c(NA, 
-5L), class = "data.frame"))

Я хотел бы иметь процедуру для создания проекции на подпространство, например, C, D. (обратите внимание, что исходные точки 1,4,6,7,11,12 совпадают в этом случае, наборы 1 и 3 одинаковы в этом подпространстве, а также наборы 2 и 5.

unique(dt[,c("C","D", "set")])
>   C D set
 1: 0 0   1
 2: 1 0   2
 3: 0 0   2
 4: 1 1   2
 5: 0 0   3
 6: 1 0   4
 7: 0 1   4
 8: 0 0   5
 9: 1 0   5
10: 1 1   5

А чтобы идентифицировать одни и те же множества, оставьте только уникальные и суммируйте соответствующие массы. То есть в данном случае:

>   C D set
 1: 0 0   1
 2: 1 0   2
 3: 0 0   2
 4: 1 1   2
 5: 1 0   4
 6: 0 1   4

   set       mass
1:   1 0.6469965 % set 1 + set 3
2:   2 0.06325088 % set 2 + set 5
3:   4 0.36890459

Спасибо за ваши идеи.

2 2

2 ответа:

Аналогично концепции Фрэнка, мы можем сопоставить двоичные значения каждого набора с десятичными с помощью x * 2 ^ ((length(x) - 1):0). Подставляя также для "C" и "D", мы получаем:

coords = c("C", "D")
d = data.frame(set = dt$set, 
           val = Reduce("+", Map("*", list(dt$C, dt$D), 2 ^ ((length(coords) - 1):0))))
d

Тогда мы можем группировать идентичные множества, следуя одной и той же идее:

tab = table(d$val, d$set) > 0L ## `table(d) > 0` to ignore the duplicates
gr = colSums(tab * (2 ^ ((nrow(tab) - 1):0)))
gr
# 1  2  3  4  5 
# 8 11  8  6 11

## another (pre-edit) alternative with unnecessary overhead
#gr = cutree(hclust(dist(table(d) > 0L)), h = 0)
#gr                        
#1 2 3 4 5 
#1 2 1 3 2

И агрегат на основе этой группы:

rowsum(wt$mass[match(names(gr), wt$set)], gr, reorder = FALSE)
#         [,1]
#8  0.64699646
#11 0.06325088
#6  0.28975265

Несколько неуклюжий вариант: сделайте уникальную строку для каждого набора,а затем сгруппируйте ее.

coords = c("C", "D")
gDT = setorder(unique(dt[,c(coords, "set"), with=FALSE]))[,
  .(s = paste(do.call(paste, c(.SD, .(sep="_"))), collapse="."))
, by=set, .SDcols = coords][, 
  g := .GRP
, by=s][]

#    set           s g
# 1:   1         0_0 1
# 2:   2 0_0.1_0.1_1 2
# 3:   3         0_0 1
# 4:   5 0_0.1_0.1_1 2
# 5:   4     0_1.1_0 3

gDT[wt, on=.(set), mass := i.mass ]
gDT[, .(set = first(set), mass = sum(mass)), by=g]

#    g set       mass
# 1: 1   1 0.64699646
# 2: 2   2 0.06325088
# 3: 3   4 0.28975265

Комментарии

  • Вы можете избавиться от g, цепляясь за [, g := NULL][] в последней строке.

  • setorder это просто сортировка данных так, чтобы уникальная строка получилась одинаковой в множествах наборов, которые одинаковы.

  • Сгруппированные first и sum операции оптимизируются, как вы можете видеть, если вы добавляете verbose = TRUE в последнюю строку, например gDT[, .(set = first(set), mass = sum(mass)), by=g, verbose=TRUE].