用lapply在R中替换嵌套的for循环

huangapple go评论96阅读模式
英文:

Replacing nested for loops with lapply in R

问题

我有一个庞大的数据集,使用for循环获取结果需要很长时间。似乎我可以使用lapply来代替,但我在分析中使用它时遇到了问题。

以下是一个示例代码。我使用data.table而不是dataframe。

  1. library(data.table)
  2. allCountries = rep(rep(LETTERS[1:3],3),3)
  3. allYears = rep(rep(1991:1993, each=3),3)
  4. myData = data.table(allCountries,allYears)
  5. myData[,variable1 := rnorm(nrow(myData))]
  6. myData[,variable2 := rnorm(nrow(myData))]
  7. myData2 = myData[,.(variable3=mean(variable1)),by=.(allCountries,allYears)]
  8. myData2[,variable4:=rnorm(nrow(myData2))]
  9. myFunction = function(x,y){summary(lm(y~x))}
  10. for(ii in unique(myData$allCountries)){
  11. for(jj in unique(myData$allYears)){
  12. xx=myData[allCountries==ii&allYears==jj,variable1]
  13. yy=myData[allCountries==ii&allYears==jj,variable2]
  14. test = myFunction(xx,yy)
  15. a=test$coefficients[2]
  16. myData2[allCountries==ii&allYears==jj,result:=a]
  17. }
  18. }

我尝试将模型拟合到数据子集,并将结果记录在另一个数据集中。我理解lapply的逻辑,但在实施时遇到困难。任何帮助将不胜感激!

英文:

I have a large dataset, and it takes forever to get the results using for loops. It seems I can use lapply instead, but I'm having trouble using it for my analysis.

A sample code is below. I am using a data.table instead of dataframe.

  1. library(data.table)
  2. allCountries = rep(rep(LETTERS[1:3],3),3)
  3. allYears = rep(rep(1991:1993, each=3),3)
  4. myData = data.table(allCountries,allYears)
  5. myData[,variable1 := rnorm(nrow(myData))]
  6. myData[,variable2 := rnorm(nrow(myData))]
  7. myData2 = myData[,.(variable3=mean(variable1)),by=.(allCountries,allYears)]
  8. myData2[,variable4:=rnorm(nrow(myData2))]
  9. myFunction = function(x,y){summary(lm(y~x))}
  10. for(ii in unique(myData$allCountries)){
  11. for(jj in unique(myData$allYears)){
  12. xx=myData[allCountries==ii&allYears==jj,variable1]
  13. yy=myData[allCountries==ii&allYears==jj,variable2]
  14. test = myFunction(xx,yy)
  15. a=test$coefficients[2]
  16. myData2[allCountries==ii&allYears==jj,result:=a]
  17. }
  18. }

I'm trying to fit the model to the subset of the data and record the result in another dataset. I understand the logic of lapply, but struggling to implement it. Any help would be much appreciated!

答案1

得分: 0

  1. # 技巧在于将数据按`allCountries`和`allYears`拆分。这将创建一个data.table列表,`lapply`可以对它们进行操作。
  2. 库(data.table)
  3. # 原始代码
  4. 所有国家= rep(rep(LETTERS[1:3],3),3)
  5. 所有年份= rep(rep(1991:1993, each=3),3)
  6. # 使结果可重现
  7. set.seed(2023)
  8. 我的数据= data.table(所有国家,所有年份)
  9. 我的数据[,变量1 := rnorm(nrow(我的数据))]
  10. 我的数据[,变量2 := rnorm(nrow(我的数据))]
  11. 我的数据2 = 我的数据[,.(变量3=mean(变量1)),by=.(所有国家,所有年份)]
  12. 我的数据2[,变量4:=rnorm(nrow(我的数据2))]
  13. 我的函数 = function(x,y){summary(lm(y~x))}
  14. for(ii in unique(我的数据$所有国家)){
  15. for(jj in unique(我的数据$所有年份)){
  16. xx=我的数据[所有国家==ii & 所有年份==jj,变量1]
  17. yy=我的数据[所有国家==ii & 所有年份==jj,变量2]
  18. 测试 = 我的函数(xx, yy)
  19. a = 测试$coefficients[2]
  20. 我的数据2[所有国家==ii & 所有年份==jj, 结果 := a]
  21. }
  22. }
  23. # 保存以便后续比较
  24. md2 <- 我的数据2
  25. # lapply代码从这里开始
  26. rm(list = ls(pattern = "^我的数据"))
  27. # 重新启动伪随机数生成器并重新生成数据
  28. set.seed(2023)
  29. 我的数据 = data.table(所有国家,所有年份)
  30. 我的数据[,变量1 := rnorm(nrow(我的数据))]
  31. 我的数据[,变量2 := rnorm(nrow(我的数据))]
  32. #
  33. 我的数据2 = 我的数据[,.(变量3=mean(变量1)),by=.(所有国家,所有年份)]
  34. 我的数据2[,变量4:=rnorm(nrow(我的数据2))]
  35. sp <- split(我的数据, list(我的数据$所有国家, 我的数据$所有年份))
  36. # data.table会在原地转换数据,所以‘res’并不是严格需要的,但它避免了打印lapply的输出
  37. res <- lapply(sp, \(X) {
  38. xx <- X[, 变量1]
  39. yy <- X[, 变量2]
  40. 测试 <- 我的函数(xx, yy)
  41. a <- 测试$coefficients[2]
  42. 我的数据2[所有国家 == X$所有国家[1] & 所有年份 == X$所有年份[1], 结果 := a]
  43. })
  44. identical(md2, 我的数据2)
  45. #> [1] TRUE
  46. rm(sp, res) # 最终清理

编辑

以下是上面的lapply循环的简化版本。

  1. # 这段代码比上面的lapply代码简单,它们的结果(我的数据2)是相同的()
  2. res <- lapply(sp, \(X) {
  3. 测试 <- with(X, 我的函数(变量1,变量2))
  4. a <- 测试$coefficients[2]
  5. 我的数据2[所有国家 == X$所有国家[1] & 所有年份 == X$所有年份[1], 结果 := a]
  6. })
英文:

The trick is to split the data by allCountries and allYears. This creates a list of data.tables and lapply can operate on them.

  1. library(data.table)
  2. # original code
  3. allCountries = rep(rep(LETTERS[1:3],3),3)
  4. allYears = rep(rep(1991:1993, each=3),3)
  5. # make the results reproducible
  6. set.seed(2023)
  7. myData = data.table(allCountries,allYears)
  8. myData[,variable1 := rnorm(nrow(myData))]
  9. myData[,variable2 := rnorm(nrow(myData))]
  10. myData2 = myData[,.(variable3=mean(variable1)),by=.(allCountries,allYears)]
  11. myData2[,variable4:=rnorm(nrow(myData2))]
  12. myFunction = function(x,y){summary(lm(y~x))}
  13. for(ii in unique(myData$allCountries)){
  14. for(jj in unique(myData$allYears)){
  15. xx=myData[allCountries==ii&amp;allYears==jj,variable1]
  16. yy=myData[allCountries==ii&amp;allYears==jj,variable2]
  17. test = myFunction(xx, yy)
  18. a = test$coefficients[2]
  19. myData2[allCountries==ii &amp; allYears==jj, result := a]
  20. }
  21. }
  22. # save to compare later
  23. md2 &lt;- myData2
  1. # lapply code starts here
  2. rm(list = ls(pattern = &quot;^myData&quot;))
  3. # restart the pseudo-RNG and reproduce the data
  4. set.seed(2023)
  5. myData = data.table(allCountries,allYears)
  6. myData[,variable1 := rnorm(nrow(myData))]
  7. myData[,variable2 := rnorm(nrow(myData))]
  8. #
  9. myData2 = myData[,.(variable3=mean(variable1)),by=.(allCountries,allYears)]
  10. myData2[,variable4:=rnorm(nrow(myData2))]
  11. sp &lt;- split(myData, list(myData$allCountries, myData$allYears))
  12. # data.table transforms the data in place so &#39;res&#39; is
  13. # not stricktly needed but it avoids printing lapply&#39;s output
  14. res &lt;- lapply(sp, \(X) {
  15. xx &lt;- X[, variable1]
  16. yy &lt;- X[, variable2]
  17. test &lt;- myFunction(xx, yy)
  18. a &lt;- test$coefficients[2]
  19. myData2[allCountries == X$allCountries[1] &amp; allYears == X$allYears[1], result := a]
  20. })
  21. identical(md2, myData2)
  22. #&gt; [1] TRUE
  23. rm(sp, res) # final clean-up

<sup>Created on 2023-02-24 with reprex v2.0.2</sup>


Edit

Here is a simplification of the lapply loop above.

  1. # this code is simpler than the lapply code above
  2. # and their results (myData2) are identical()
  3. res &lt;- lapply(sp, \(X) {
  4. test &lt;- with(X, myFunction(variable1,variable2))
  5. a &lt;- test$coefficients[2]
  6. myData2[allCountries == X$allCountries[1] &amp; allYears == X$allYears[1], result := a]
  7. })

huangapple
  • 本文由 发表于 2023年2月24日 14:03:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75553101.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定