R(数据表):以最有效的方式计算连接后的平均值

rta7y2nd  于 2022-12-20  发布在  其他
关注(0)|答案(1)|浏览(110)

我有两个massive数据集,我创建了一个简单的例子(如下)来复制我的数据结构:DT_ade包含变量ade,而DT_abce包含变量abce。我想用a-b-c计算d的平均值。在下面的代码中,我根据a-e的值合并了两个DT,因为我对变量e不感兴趣,所以我只选择了连接后的其余变量(abc、然后我计算了d乘以a-b-c的平均值。

library(data.table)

DT_ade = data.table(a = c("a1", "a1", "a2", "a3", "a3"),
                    d = c(10, 20, 30, 40, 80) ,
                    e = c("e1", "e2", "e2", "e2", "e3"))

DT_abce = data.table(a = c("a1", "a1", "a1", "a1", "a2", "a3", "a3"),
                     b = c("b1", "b1", "b2", "b2", "b2", "b2", "b2"),
                     c = c("c1", "c1", "c2", "c2", "c2", "c2", "c2"),
                     e = c("e1", "e2", "e1", "e2", "e2", "e2", "e3"))
  

DT_ade[
  DT_abce, on=.(a, e), .(a, b, c, d)
  ][, .(mean_d = mean(d, na.rm=TRUE)),
    .(a, b, c)]

代码在以下简单示例中工作:它给出以下输出:

a  b  c mean_d
1: a1 b1 c1     15
2: a1 b2 c2     15
3: a2 b2 c2     30
4: a3 b2 c2     60

但是,当将此代码应用于我的真实的数据时,由于数据的大小,我得到了以下错误消息:

Error in gforce(thisEnv, jsub, o__, f__, len__, irows) : 
  Internal error: Failed to allocate counts or TMP when assigning g in gforce

我想知道是否有更简单的方法来完成这个任务,例如,我可以用a-b-c来计算d的均值,而不生成完整的a-b-c-d列表吗?

aiqt4smr

aiqt4smr1#

我将通过一个示例来扩展Waldi对arrow的推荐。

  • 前面 *:使用arrow的好处是在处理大量数据时,以及它对数据的惰性检索/处理。虽然我在这里使用arrow::arrow_table,这是因为它对这个示例很方便;在您的情况下,它们应该是文件系统上的parquet文件(最好是本地的)。

也就是说,理想情况下,您可以将这些对象创建为:

ds_ade <- arrow::open_dataset("path/to/ade.parquet")
ds_abce <- arrow::open_dataset("path/to/abcd.parquet")

但现在,我将用途:

ds_ade <- arrow::arrow_table(DT_ade)
ds_abce <- arrow::arrow_table(DT_abce)

其中每个对象都表示箭头对象,如下所示:没有提取数据,只是看起来像这样:

ds_ade
# Table
# 5 rows x 3 columns
# $a <string>
# $d <double>
# $e <string>

从这里开始,可以使用一个相对简单的dplyr管道,它还不拉取数据或执行计算:

library(dplyr)
left_join(ds_ade, ds_abce, on = c("a", "e")) %>%
  group_by(a, b, c) %>%
  summarize(mean_d = mean(d, na.rm=TRUE)) %>%
  ungroup()
# Table (query)
# a: string
# b: string
# c: string
# mean_d: double
# See $.data for the source Arrow object

我们只需要将%>% collect()加到它上面,数据就实体化了(即,计算后拉入R):

left_join(ds_ade, ds_abce, on = c("a", "e")) %>%
  group_by(a, b, c) %>%
  summarize(mean_d = mean(d, na.rm=TRUE)) %>%
  ungroup() %>%
  collect()
# # A tibble: 4 × 4
#   a     b     c     mean_d
#   <chr> <chr> <chr>  <dbl>
# 1 a1    b2    c2        15
# 2 a1    b1    c1        15
# 3 a2    b2    c2        30
# 4 a3    b2    c2        60

不要将 data.table-vs-dplyr的速度“ 与此处发生的情况混淆:你没有损失速度,因为计算是在箭头上完成的,而不是在data.tabledplyr中。直到collect(),计算才真正完成(在箭头上),然后R才第一次看到数据。如果你选择as.data.table这个数据(对于你的处理的其余部分),这是很好的。
这种方法的“痛苦”在于首先要将数据放入Parquet文件中,因为数据相当大,所以当它加载到R中时,你似乎处于一种脆弱的境地:如果您的Windows计算机决定必须立即更新并重新启动,或者如果您断电(UPS/电池故障),或发生其他事情,* 您沉没 *,需要再次创建或加载所有数据。
根据我的经验,根据您的真实的数据,您可能希望先将CSV加载到R中,然后进行一些简单的处理(即转换为POSIXt,设置factor s和级别等,但不整形/聚合),然后保存到parquet。保存时,底层的数据类和属性被保留。实际上,由于您正在保存一个data.table,你可以很快地运行ds_ade %>% head() %>% collect()并看到(1)它几乎是瞬时的,并且(2)它是一个data.table。(POSIXtfactor),则它们将保留那些类。filter ing是高效的,特别是当变量是数值的时候(但是我不是arrow-维护者/设计者/工程师,所以我总是可能误解了,因此在这里错误地描述了懒惰的一部分)。

相关问题