使用R data.table行的排列填充"计数矩阵"

Sha*_*ang 5 r count matrix data.table

(对于以下内容,我可以使用R data.frame或R data.table.两者都可以.)

我有以下data.table:

library(data.table)

dt = data.table(V1=c("dog", "dog", "cat", "cat", "cat", "bird","bird","bird","bird"), 
                    V2=rep(42, 9), V3=c(1, 2, 4, 5, 7, 1, 2, 5, 8)) 

> print(dt)
     V1 V2 V3
1:  dog 42  1
2:  dog 42  2
3:  cat 42  4
4:  cat 42  5
5:  cat 42  7
6: bird 42  1
7: bird 42  2
8: bird 42  5
9: bird 42  8
Run Code Online (Sandbox Code Playgroud)

V3包含从1到8的整数.我的目标是在给定列中的唯一类别的情况下,使用每个组合"对"的计数填充8乘8的零矩阵V1

因此,对于组合对dog,catbird是:

dog: (1, 2)
cat: (4, 5), (4, 7), (5, 7)
bird: (1, 2), (1, 5), (1, 8), (2, 5), (2, 8), (5, 8)
Run Code Online (Sandbox Code Playgroud)

对于每一对,我添加+1到零矩阵中的相应条目.对于这个矩阵,(n, m) = (m, n).给出的矩阵dt是:

   1 2 3 4 5 6 7 8
1: 0 2 0 0 1 0 0 1
2: 2 0 0 0 1 0 0 1
3: 0 0 0 0 0 0 0 0
4: 0 0 0 0 1 0 1 0
5: 1 1 0 1 0 0 1 1
6: 0 0 0 0 0 0 0 0
7: 0 0 0 1 1 0 0 0
8: 1 1 0 0 1 0 0 0
Run Code Online (Sandbox Code Playgroud)

请注意,组合和组合(1,2)=(2,1)的计数为2 .dogbird

(1)在给定另一列中的唯一值的情况下,是否有一种计算R data.table/data.frame列中值组合的方法?

也许输出R列表是有意义的,例如,矢量"对"

list(c(1, 2), c(2, 1), c(4, 5), c(4, 7), c(5, 7), c(5, 4), c(7, 4), c(7, 5),
    c(1, 2), c(1, 5), c(1, 8), c(2, 5), c(2, 8), c(5, 8), c(2, 1), c(5, 1),
    c(8, 1), c(5, 2), c(8, 2), c(8, 5))
Run Code Online (Sandbox Code Playgroud)

但是,我不确定如何使用它来填充矩阵......

(2)给定输入data.table/data.frame,用于写出矩阵的最有效的数据结构是什么?

Dav*_*urg 6

这是一个看似高效的data.table解决方案.我们基本上做一个自我联接,以创建组合,然后计数.然后,类似于@coldspeed对Numpy的处理,我们将只按位数更新零矩阵.

# a self join
tmp <- dt[dt, 
             .(V1, id = x.V3, id2 = V3), 
             on = .(V1, V3 < V3), 
             nomatch = 0L,
             allow.cartesian = TRUE
          ][, .N, by = .(id, id2)]

## Create a zero matrix and update by locations
m <- array(0L, rep(max(dt$V3), 2L))
m[cbind(tmp$id, tmp$id2)] <- tmp$N
m + t(m)

#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
# [1,]    0    2    0    0    1    0    0    1
# [2,]    2    0    0    0    1    0    0    1
# [3,]    0    0    0    0    0    0    0    0
# [4,]    0    0    0    0    1    0    1    0
# [5,]    1    1    0    1    0    0    1    1
# [6,]    0    0    0    0    0    0    0    0
# [7,]    0    0    0    1    1    0    0    0
# [8,]    1    1    0    0    1    0    0    0
Run Code Online (Sandbox Code Playgroud)

或者,我们可以创建tmp使用,data.table::CJ但可能(可能 - 由于@Frank提示)更少的内存效率,因为它将首先创建所有可能的组合,例如

tmp <- dt[, CJ(V3, V3)[V1 < V2], by = .(g = V1)][, .N, by = .(V1, V2)]

## Then, as previously
m <- array(0L, rep(max(dt$V3), 2L))
m[cbind(tmp$V1, tmp$V2)] <- tmp$N
m + t(m)

#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
# [1,]    0    2    0    0    1    0    0    1
# [2,]    2    0    0    0    1    0    0    1
# [3,]    0    0    0    0    0    0    0    0
# [4,]    0    0    0    0    1    0    1    0
# [5,]    1    1    0    1    0    0    1    1
# [6,]    0    0    0    0    0    0    0    0
# [7,]    0    0    0    1    1    0    0    0
# [8,]    1    1    0    0    1    0    0    0
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚测试了一个大数据集,而'acast`部分需要很长时间,而零矩阵是即时的. (2认同)