【问题标题】:Convert data.table with one id and one variable column to presence matrix将具有一个 id 和一个变量列的 data.table 转换为存在矩阵
【发布时间】:2020-06-18 04:10:41
【问题描述】:

我有一个具有以下结构的 data.table:

num_id  value
1000    A1
1001    A1
1000    A2
1000    A3
1001    A54
1002    A55
1001    A100

想把它变成一个dt的形式

num_id A1        A2       A3       A54       A55       A100
1000   1         1        1        0         0         0
1001   1         0        0        1         0         1
1002   0         0        0        0         1         0

我认为使用dcast 会很容易。想到的公式是dcast(dt, numid~value) 但是抱怨Cross product of elements provided to CJ() would result in 4850158203 rows which exceeds .Machine$integer.max == 2147483647。这超过了预期的行数,因为我有大约 500,000 个唯一 ID。在较小的数据表上运行测试后,似乎对 dcast 的调用将 ID 保持原样,将 value 列替换为只有 1 个元素不为空的列向量。这没有多大帮助,因为缺少必要的聚合/分组步骤。

我编写了以下代码,该代码有效,但速度慢且令人费解。有没有办法在单个 dcast 调用中做到这一点?

futurecolumns=unique(dt$value)
aggregated=dt[,list(list(value)), by=num_id]
out=t(sapply(aggregated$V1, function(x){futurecolumns %in% x}))
out=as.data.table(out*1)
out$num_id=aggregated$num_id
setnames(out, c(futurecolumns, "num_id"))

【问题讨论】:

  • dcast(unique(dt), num_id ~ value, fun.aggregate=length) 呢?
  • 您的建议中的unique 似乎可以删除dt 中的重复行?那不是问题,没有重复的AFAIK。这给了我完全相同的错误和行为,即生成的 df 对于num_idvalue 的每个组合都有一行,而我想要一个 df 的行数与num_id 中的唯一值一样多。
  • ~500,000 个唯一 num_ids * 9700 个唯一 values = 网格中的 4,850,000,000 个值。这对任何人都不会奏效。也许是一个稀疏矩阵,因为你会有这么多的 0?
  • 只有大约 500 个唯一的 values,所以最终的 df 尺寸应该是 500,000x500。

标签: r data.table reshape2


【解决方案1】:

一种方法是 count num_idvalue 的行数并使用 pivot_wider

library(dplyr)

dt %>%
  count(num_id, value) %>%
  tidyr::pivot_wider(names_from = value, values_from = n, 
                     values_fill = list(n = 0))

# A tibble: 3 x 7
#  num_id    A1    A2    A3   A54   A55  A100
#   <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1   1000     1     1     1     0     0     0
#2   1001     1     0     0     1     0     1
#3   1002     0     0     0     0     1     0

在基础 R 中,您可以使用 aggregate

futurecolumns=unique(dt$value)
aggregate(value~num_id, dt, function(x) table(factor(x, levels = futurecolumns)))

【讨论】:

  • 哦,对了!我对 reshape 和 DT 方法如此深入,以至于我没有想到聚合!谢谢。
  • 当我尝试运行dplyr 版本时,我也收到一个错误:Error: Can't assign to elements that don't exist. ✖ Locations 413590, 827179, 1240768, 1654357, 2067946, etc. don't exist. ℹ There are only 1 element. 此外,还有一些警告:Warning message in nrow * ncol: “NAs produced by integer overflow”Warning message in nrow * (val_id$col - 1L): “NAs produced by integer overflow”Warning message in val_id$row + nrow * (val_id$col - 1L):“NAs produced by integer overflow”。好像 df 太大了?
  • @ArthurGilly base R 方法工作正常吗?您还有其他未显示的列吗?如果是这种情况,您可以在使用pivot_wider 之前select 所需的列。
  • 不,它似乎创建了一个包含 2 列的数据框,第二列包含嵌套列表/表。 @hello_friend 的方法也使用 aggregate 有效。我在 df 中没有其他列。
  • @ArthurGilly 如果您将aggregate 的输出存储在变量temp &lt;- aggregate(value~num_id, dt, function(x) table(factor(x, levels = futurecolumns))) 中,您可以使用do.call(data.frame, temp) 作为单独的列。
【解决方案2】:

Base R 一混淆表达式:

aggregate(. ~ num_id,
          data.frame(num_id = df$num_id,
                     +sapply(unique(df$value), `==`, df$value)), sum)

【讨论】:

  • 谢谢!这可行,但比我上面的 sn-p 和 @Ronak 的基本 R 解决方案都慢(microbenchmark,100 次重复)。
  • @ArthurGilly 不用担心,Ronak 是一把枪,也是世界上最好的 R 程序员之一,所以他的解决方案总是非常好。对于我自己的实践,我尝试提供额外的解决方案来展示不同的(通常是基础 R)解决问题的方法。不幸的是,这是我能做的最好的了。希望 sapply 函数中的相等性测试以及从布尔矩阵到整数矩阵的转换为您和其他人提供了一些有用的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
  • 2018-12-11
  • 2016-11-27
  • 2016-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多