【问题标题】:Using a for loop to apply a function to a list in R使用 for 循环将函数应用于 R 中的列表
【发布时间】:2022-01-03 23:40:18
【问题描述】:

我有四个数据框,每个都有两列,一列用于日期,另一列用于值。我想在每个数据框中生成 24 个新列,12 个用于滞后指标,12 个用于领先指标。我已经设法使用以下代码一次完成一个数据帧:

df[paste0("lag", 1:12)] = lapply(1:12, lag, x=df[,2])
df[paste0("lead", 1:12)] = lapply(1:12, lead, x=df[,2])

但是,我想使用遍历数据框列表的 for 循环来自动执行此操作。到目前为止,我已经尝试了以下方法:

dataframes = list(df1,df2,df3,df4)

for (df in dataframes){
        df[paste0("lag", 1:12)] = lapply(1:12, lag, x=df[,2])
        df[paste0("lead", 1:12)] = lapply(1:12, lead, x=df[,2])
}

遗憾的是,这不起作用,因为数据帧在 for 循环之后保持不变。关于如何完成这项工作的任何建议?

【问题讨论】:

    标签: r dataframe for-loop


    【解决方案1】:

    编写一个更明确的函数(如果需要),会给你更多的灵活性。使用您的示例,但简化表格

    library(dplyr)
    library(purrr)
    
    a <- tibble(x = 1:50)
    b <- tibble(x = 51:75)
    dflist <- list(a, b)
    
    # quick function using single lag on single column, but easily extendible
    cv <- function(a)
    {
      nca <- ncol(a)
      for(i in seq(from = 1, to = 23, by = 2))
      {  
       a[nca+i] = lag(a$x)
       a[nca+i+1] = lead(a$x)
      }
      return(a)
    }
    
    # simple to apply to create your new columns (or put in loop)
    na <- cv(a)
    
    # or simple to do all df at once and concatenate the results
    f <- dflist %>% map_dfr(cv)
    

    【讨论】:

      【解决方案2】:

      df 不会更新全局环境中的原始对象“df1”、“df2”。如果我们愿意,请使用assign(或者更好的方法是将其保存在list 中)

      # // create a named `list`
      dataframes = list(df1,df2,df3,df4)
      names(dataframes) <- c("df1", "df2", "df3", "df4")
      # // loop over the names of the list
      for(nm in names(dataframes)) {
          # // get the value of the object from the names
          df <- get(nm)
          # // create the new columns
          df[paste0("lag", 1:12)] <- lapply(1:12, lag, x=df[,2])
          df[paste0("lead", 1:12)] <- lapply(1:12, lead, x=df[,2])
          # // assign to update the original object 
          assign(nm, df)
      }
      

      最好把它保存在list

      dataframes2 <- lapply(dataframes, function(df) {
            df[paste0("lag", 1:12)] <- lapply(1:12, lag, x=df[,2])
           df[paste0("lead", 1:12)] <- lapply(1:12, lead, x=df[,2])
           df 
          })
      

      list 输出可用于更新带有list2env 的原始对象,但不推荐

      list2env(dataframe2, .GlobalEnv)
      

      【讨论】:

        【解决方案3】:

        澄清问题

        请阅读 标记页面顶部的信息,特别是示例应该是独立的、完整的,包括所有输入和库语句、可重现以便其他任何人都可以轻松地运行它们并且最小化。

        1. 缺少库语句。 R 中没有 lead 函数,因此我们假设正在使用 dplyr。
        2. 数据框本身缺失,因此我们根据 R 中包含的 BOD 数据框构建示例数据框。
        3. 为了使这个最小化,我们使用 2 个滞后和 2 个超前,而不是 12 和 12。

        以上是在下面的替代部分中完成的。

        代码问题

        问题中的代码的问题在于它会复制列表的每个组件,修改副本但不更改列表本身。如果我们像这样将修改后的数据框重新插入到列表中,它将起作用。 ## 结尾的行来自问题,其他行是新的或修改的。

        dataframes = list(df1,df2,df3,df4) ##
        
        for (i in seq_along(dataframes)) {
                df <- dataframes[[i]]
                df[paste0("lag", 1:12)] = lapply(1:12, lag, x=df[,2]) ##
                df[paste0("lead", 1:12)] = lapply(1:12, lead, x=df[,2]) ##
                dataframes[[i]] <- df
        }
        

        替代方案

        1) 定义一些测试数据,然后使用 mget 创建一个数据帧的命名列表 L,然后遍历名称,在 L 中创建新数据帧,覆盖 L 中的旧数据帧。

        虽然不推荐,除非有充分的理由这样做,我们可以使用 listenv(L, .GlobalEnv) 将 L 中的数据帧写回全局环境。

        library(dplyr)
        
        # test data
        for(i in 1:4) assign(paste0("df", i), i * BOD)
        nms <- paste0("df", 1:4)  
        L <- mget(nms)
        
        for (nm in names(L)) {
          L[[nm]][paste0("lag", 1:2)] = lapply(1:2, lag, x=L[[nm]][,2])
          L[[nm]][paste0("lead", 1:2)] = lapply(1:2, lead, x=L[[nm]][,2])
        }
        

        给予:

        str(L)
        ## List of 4
        ##  $ df1:'data.frame':    6 obs. of  6 variables:
        ##   ..$ Time  : num [1:6] 1 2 3 4 5 7
        ##   ..$ demand: num [1:6] 8.3 10.3 19 16 15.6 19.8
        ##   ..$ lag1  : num [1:6] NA 8.3 10.3 19 16 15.6
        ##   ..$ lag2  : num [1:6] NA NA 8.3 10.3 19 16
        ##   ..$ lead1 : num [1:6] 10.3 19 16 15.6 19.8 NA
        ##   ..$ lead2 : num [1:6] 19 16 15.6 19.8 NA NA
        ##  $ df2:'data.frame':    6 obs. of  6 variables:
        ## ...snip...
        

        2) 我们可以在折叠包中使用flag 来简化此操作。它接受一个领先/落后的向量。创建测试数据后,只需一行代码。它创建一个新列表 L2。原始列表 L 没有被修改。

        library(collapse)
        
        # test data
        for(i in 1:4) assign(paste0("df", i), i * BOD)
        nms <- paste0("df", 1:4)  
        L <- mget(nms)
        
        L2 <- lapply(L, function(x) cbind(x[1], flag(x[2], seq(-2, 2))))
        
        str(L2)
        ## List of 4
        ##  $ df1:'data.frame':    6 obs. of  6 variables:
        ##   ..$ Time     : num [1:6] 1 2 3 4 5 7
        ##   ..$ F2.demand: num [1:6] 19 16 15.6 19.8 NA NA
        ##   ..$ F1.demand: num [1:6] 10.3 19 16 15.6 19.8 NA
        ##   ..$ demand   : num [1:6] 8.3 10.3 19 16 15.6 19.8
        ##   ..$ L1.demand: num [1:6] NA 8.3 10.3 19 16 15.6
        ##   ..$ L2.demand: num [1:6] NA NA 8.3 10.3 19 16
        ##  $ df2:'data.frame':    6 obs. of  6 variables:
        ## ...snip...
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-21
          • 2015-02-06
          • 2023-02-02
          • 1970-01-01
          • 1970-01-01
          • 2019-10-18
          • 2020-06-03
          • 2019-11-09
          相关资源
          最近更新 更多