从数据帧形成共现矩阵

Forming a co-occurence matrix from a data frame

我有一个看起来像这样的数据框:

id  val
1    a
1    b
2    a
2    c
2    d
3    a
3    a

将每一行想象成一个标签,val,它被赋予了一些带有 id 的观察。

我最终想要得到的是一个 "co-occurence" 矩阵,它看起来像这样,我计算每个字母与其他字母在同一 ID 中出现的次数:

    a  b  c  d
a   1  1  1  1
b   1  0  0  0
c   1  0  0  1
d   1  0  1  0

我一直在绞尽脑汁寻找方法,但到目前为止一无所获。有什么提示吗?最好使用 tidyverse 工具,但此时也可以使用其他选项。

编辑:在这种情况下,链接为可能重复的问题的解决方案不起作用。我不确定为什么,但我怀疑这与具有 3 列数据框的问题有关。

这是一个基于 R 的解决方案。不太优雅但似乎有效

temp = data.frame(do.call(cbind, lapply(split(df, df$id), function(a)
    combn(a$val, 2))), stringsAsFactors = FALSE)
sapply(sort(unique(df$val)), function(rows)
    sapply(sort(unique(df$val)), function(cols)
        sum(sapply(temp, function(x)
            identical(sort(x), sort(c(rows, cols)))))))
#  a b c d
#a 1 1 1 1
#b 1 0 0 0
#c 1 0 0 1
#d 1 0 1 0

igraph

temp = t(do.call(cbind, lapply(split(df, df$id), function(a) combn(a$val, 2))))
library(igraph)
as.matrix(get.adjacency(graph(temp, directed = FALSE)))
#  a c b d
#a 1 1 1 1
#c 1 0 0 1
#b 1 0 0 0
#d 1 1 0 0

数据

df = structure(list(id = c(1L, 1L, 2L, 2L, 2L, 3L, 3L),
                    val = c("a", "b", "a", "c", "d", "a", "a")),
               .Names = c("id", "val"),
               class = "data.frame",
               row.names = c(NA, -7L))

dplyr+purrr的解决方案:

library(dplyr)
library(purrr)
df %>%
  split(.$id) %>%
  map_dfr(function(x){
    t(combn(x$val, 2)) %>% 
      data.frame(stringsAsFactors = FALSE)
  }) %>%
  mutate_all(funs(factor(., levels = c("a", "b", "c", "d")))) %>%
  table() %>%
  pmax(., t(.))

结果:

   X2
X1  a b c d
  a 1 1 1 1
  b 1 0 0 0
  c 1 0 0 1
  d 1 0 1 0

备注:

  1. 我首先将 df 拆分为 id,然后使用 purrr 中的 map_dfrcombn 函数映射到每个 id 组。
  2. combn 查找向量(length(vec) 选择 2)和 returns 矩阵中元素的所有组合。
  3. _dfrmap_dfr 的末尾意味着结果将是一个按行绑定列表中每个元素的数据框。所以这实际上是 do.call(rbind, lapply())
  4. mutate_all 确保 table 保留所需的所有级别,即使列中不存在字母。
  5. 最后,由于在 table 步骤之后,生成了一个上三角矩阵,我将该矩阵及其转置输入 pmax
  6. pmax 从两个输入中找到平行最大值,并根据需要 returns 一个对称矩阵。

数据:

df = read.table(text=  "id  val
                1    a
                1    b
                2    a
                2    c
                2    d
                3    a
                3    a", header = TRUE, stringsAsFactors = FALSE)