【问题标题】:Use equivalent of purrr:::map to iterate through data.table使用等价的 purrr:::map 遍历 data.table
【发布时间】:2017-12-21 03:41:35
【问题描述】:

我想遍历data.table,就像purrr::map 一样。虽然我可以通过在purrr::map 中将data.frame 转换为data.table 来应用data.table 函数,但我想知道data.table 是否有内置的东西可以使用purrr::map 来消除。我问这个是因为我不确定purrr::map 在速度和所需内存方面的性能。与 data.table 在处理大型数据集时相比,我对 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。如有任何想法,我将不胜感激。

【问题讨论】:

  • 也许我过于简单化了,但这不只是:b &lt;- Input_File[, .(sum=sum(L.Rev)), by=.(Zone,Fiscal.Year)] 吗?如果你真的想要,你可以split 分开部分 - split(b[,-"Zone"], b$Zone)
  • @Thelatemail - 这确实有帮助。有时更简单的解决方案比复杂的解决方案更好。如果您可以发布答案,我可以接受。非常感谢你的帮助。我想我掉进了兔子洞。否则,我会保留这个问题,以防万一我们得到任何其他解决方案。

标签: r dplyr data.table purrr


【解决方案1】:

重复[]可以很好地完成管道数据表,例如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])

如果您想迭代 列,您可能需要查看this solution

更新:我想可能有一个更清洁的解决方案,而不导入 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

【讨论】:

  • 谢谢。 %&gt;% 效率更高吗?我很好奇。我在其中一个线程中看到lapplypurrr::map 更有效
  • 它只是更具可读性。你可以把它剪掉,然后用 lapply(lapply())
  • 感谢您的帮助。你认为你可以发布lapply(lappy()) 的样子吗?它将帮助像我这样的新手和其他正在阅读该主题的人。
  • 编辑了删除magrittr 管道的答案。嵌套的 lapply() 绝对不可读。
【解决方案2】:

我不确定问题的背后是什么,但我确实更喜欢

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 选项卡中输出输出。
  • 您可以在data.table 总结之后将split(dt_df, by = "Fiscal.Year") 链接起来以创建一个列表。 setDT(Input_File)[, .(sum = sum(L.Rev)), by = .(Zone, Fiscal.Year)] %&gt;% split(., by = 'Fiscal.Year')
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-18
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多