使用与purrr :: :: map等效的方法来迭代数据表

5
我想遍历data.table,就像purrr::map一样。虽然我能够通过将data.frame转换为data.table并在purrr::map内部应用data.table函数,但我想知道是否data.table内置了某些东西,可以避免使用purrr::map。我之所以问这个问题,是因为我不确定purrr::map的性能在速度和所需内存方面如何。当处理大型数据集时,与dplyr相比,我对dplyr的速度和内存利用率感到失望。

我在stackoverflow上进行了研究,并发现Iterate through data tables线程中的被接受的答案使用了for循环。出于性能原因,我不是for循环的忠实粉丝。

以下是示例数据文件:

dput(Input_File)
structure(list(Zone = c("East", "East", "East", "East", "East", 
"East", "East", "West", "West", "West", "West", "West", "West", 
"West"), Fiscal.Year = c(2016, 2016, 2016, 2016, 2016, 2016, 
2017, 2016, 2016, 2016, 2017, 2017, 2018, 2018), Transaction.ID = c(132, 
133, 134, 135, 136, 137, 171, 171, 172, 173, 175, 176, 177, 178
), L.Rev = c(3, 0, 0, 1, 0, 0, 2, 1, 1, 2, 2, 1, 2, 1), L.Qty = c(3, 
0, 0, 1, 0, 0, 1, 1, 1, 2, 2, 1, 2, 1), A.Rev = c(0, 0, 0, 1, 
1, 1, 0, 0, 0, 0, 0, 1, 0, 0), A.Qty = c(0, 0, 0, 2, 2, 3, 0, 
0, 0, 0, 0, 3, 0, 0), I.Rev = c(4, 4, 4, 0, 1, 0, 3, 0, 0, 0, 
1, 0, 1, 1), I.Qty = c(2, 2, 2, 0, 1, 0, 3, 0, 0, 0, 1, 0, 1, 
1)), .Names = c("Zone", "Fiscal.Year", "Transaction.ID", "L.Rev", 
"L.Qty", "A.Rev", "A.Qty", "I.Rev", "I.Qty"), row.names = c(NA, 
14L), class = "data.frame")

以下是使用purrr::mapdata.table的示例代码:

UZone <- unique(Input_File$Zone)
FYear <- unique(Input_File$Fiscal.Year)
a<-purrr::map(UZone, ~ dplyr::filter(Input_File, Zone == .)) %>%
   purrr::map(~ data.table::as.data.table(.)) %>%
   purrr::map(~ .[,.(sum = sum(L.Rev)),by=Fiscal.Year])

我对输出结果并不是太关心,但我想知道有哪些替代方法可以基于特定列迭代 data.table。非常感谢任何意见。


2
也许我过于简化了,但这不只是:b <- Input_File[, .(sum=sum(L.Rev)), by=.(Zone,Fiscal.Year)]吗?如果你真的想要分开部分,你可以使用split - split(b[,-"Zone"], b$Zone) - thelatemail
@Thelatemail - 那确实有帮助。有时候简单的解决方案比复杂的更好。如果您能发布一个答案,我可以接受它。非常感谢您的帮助。我想我陷入了兔子洞。否则,我将保持这个问题开放,以防我们得到其他的解决方案。 - watchtower
2个回答

2

通过重复使用[],例如DT[][][],可以很好地管道化数据表。对于列表,我认为没有替代magrittr的方法。其余部分可以通过链接lapply来完成。

library(data.table)
library(magrittr)

Input_File <- data.table(Input_File)

UZone <- unique(Input_File$Zone)
FYear <- unique(Input_File$Fiscal.Year)

lapply(UZone, function(x) Input_File[Zone==x]) %>% 
  lapply(function(x) x[,.(sum=sum(L.Rev)), by=Fiscal.Year])

如果您想迭代列,您可能需要查看此解决方案

更新:我想可能会有一种更干净的解决方案,不需要导入magrittr并且不需要使用$子集。

library(data.table)

Input_File <- data.table(Input_File)

by_zone_lst <- lapply(Input_File[,unique(Zone)], function(x) Input_File[Zone==x])
summary_lst <- lapply(by_zone_lst, function(y) y[,.(sum=sum(L.Rev)), by=Fiscal.Year])

summary_lst

%>%更有效率吗?我很好奇。我在一个帖子中看到,lapplypurrr::map更有效率。 - watchtower
1
它只是更易读一些。你可以简化它,只使用lapply(lapply())即可。 - dmi3kno
谢谢你的帮助。你觉得你能不能发一下 lapply(lappy()) 会是什么样子?这将有助于像我这样的新手和其他阅读该线程的人。 - watchtower
1
编辑了答案以删除 magrittr 管道。嵌套的 lapply() 完全无法阅读。 - dmi3kno

1
我不确定问题背后的原因,但我更喜欢。
library(data.table)
setDT(Input_File)[, .(sum = sum(L.Rev)), by = .(Zone, Fiscal.Year)]
   Zone Fiscal.Year sum
1: East        2016   4
2: East        2017   2
3: West        2016   4
4: West        2017   3
5: West        2018   3
OP的方法返回a
[[1]]
   Fiscal.Year sum
1:        2016   4
2:        2017   2

[[2]]
   Fiscal.Year sum
1:        2016   4
2:        2017   3
3:        2018   3

谢谢您的帮助。我想要显示列表的唯一原因是可以使用write.xlsx函数将输出分割到不同的Excel选项卡中。 - watchtower
你可以在 data.table 汇总后面直接链接 split(dt_df, by = "Fiscal.Year") 来生成一个列表。setDT(Input_File)[, .(sum = sum(L.Rev)), by = .(Zone, Fiscal.Year)] %>% split(., by = 'Fiscal.Year') - Fred

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接