在大型数据集上生成 R 中的共现矩阵

Generating a co-occurrance matrix in R on a LARGE dataset

我试图在一个非常大的数据集(2600 万行)上用 R 创建一个共现矩阵,它看起来基本上是这样的:

ID 观察

11000 榕树
11112 樱桃
11112 榕树
12223 杜松
12223 橄榄
12223 杜松
12223 榕树
12334 橄榄
12334 樱桃
12334 橄榄
... ...

而且持续了很长时间。我想通过 ID 合并观察结果,并生成观察者 ID 观察到的观察结果的共现矩阵。我对数据的一个子集进行了管理,但我所做的一些事情 "manually" 对于整个数据集来说是不切实际的。 (见下面的代码)我抵制使用循环的冲动,因为每个人都告诉我如果你在 R 中使用循环你做错了,但我不确定如何实现这一点。

以下是适用于较小集合的方法:

tfmat = data.frame(cmsclip$ID[1:100],
              cmsclip$tree[1:100] %in% c(cmsclip$tree[1]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[2]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[3]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[4]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[5]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[6]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[7]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[9]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[10]),
               cmsclip$tree[1:100] %in% c(cmsclip$tree[11]))
colnames(tfmat) <- c('ID', cmsclip$tree[1:7], cmsclip$tree[9:11])

我刚刚选择了前 10 个唯一的树名,它们在整个数据块中恰好是 cmsclip$tree[1:7]、cmsclip$tree[9:11],并且只看了那些,在前 100 个观察值,为每行上的每棵树生成一个 TRUE/FALSE 矩阵。然后我使用 split() 按 ID 号拆分合并数据并对每一列(现在代表一棵特定的树)求和以查看每个观察者记录了多少棵树。

testsplit = split(tfmat, tfmat[1])
summed1 <-colSums(testsplit$`ficus`)
summed2 <-colSums(testsplit$`cherry`)
summed3 <-colSums(testsplit$`juniper`)
summed4 <-colSums(testsplit$`pine`)
summed5 <-colSums(testsplit$`olive`)
summed6 <-colSums(testsplit$`elm`)
summed7 <-colSums(testsplit$`rain`)
summed8 <-colSums(testsplit$`redwood`)
summed9 <-colSums(testsplit$`shimpaku`)
summed10 <-colSums(testsplit$`maple`)

问题在于我手动输入了每个名称,但我无法对整个数据框执行此操作。这是我正在寻找更好的东西的地方。我认为将每一行组合成一个最终矩阵,该矩阵只有给定列中每棵树的观察次数,其中一行代表一个 ID 号。

finmat = data.frame(summed1[2:11],summed2[2:11],summed3[2:11],
                summed4[2:11],summed5[2:11],summed6[2:11],
                summed7[2:11],summed8[2:11],summed9[2:11],
                summed10[2:11],summed11[2:11])

然后我就这样做了...

finmat <- t(finmat)
treenames <- c(cmsclip$tree[1:7], cmsclip$tree[9:11])
colnames(finmat) <- treenames

total_occurrences <- colSums(finmat)
data_matrix <- as.matrix(finmat)
co_occurrence <- t(data_matrix) %*% data_matrix
library(igraph)
graph <- graph.adjacency(co_occurrence,
                                                weighted=TRUE,
                                                mode="undirected",
                                                diag=FALSE)

plot(graph,
      vertex.label=names(data),
      vertex.size=total_occurrences*5,
      edge.width=E(graph)$weight*8)

它运行良好,只是无法扩展到更大的数据集(太多手动操作)。任何关于更快方法的建议将不胜感激。谢谢!

编辑:理想情况下,我想创建一个矩阵,其中行对应于 ID 号,列对应于每棵树,它会显示观察到的每棵树的数量。例如:

ID 榕 樱桃 杜松 橄榄 ...
11000 1 0 0 0
11112 1 1 0 0
12223 1 0 2 1
12334 0 1 0 2
...

我应该补充一点,我实际上最终想使用不同树的观察值的共现对数据进行层次聚类,以此作为计算树之间 "distance" 的一种方式。因此,关于如何实现这一目标的任何建议都会很棒。如果有一种简单的方法可以从我所拥有的内容跳转到距离矩阵,我可能会放弃生成上述矩阵。

试试这个:

tapply(cmsclips$ID, cmsclips$Observation, FUN = c)

这里有一个例子:

test = data.frame(id = c(11,12,13,14), obs=c("cat", "dog", "cat", "cat"))
#  id obs
#1 11 cat
#2 12 dog
#3 13 cat
#4 14 cat
tapply(test$id, test$obs, FUN = c)
#$cat
#[1] 11 13 14
#
#$dog
#[1] 12

好的,我刚刚使用了 table(),它几乎完全符合我的要求。太棒了,学习 R。现在开始研究距离矩阵和聚类...

(谢谢@mts 的帮助!)