【问题标题】:How do I make a list of data frames?如何制作数据框列表?
【发布时间】:2013-07-04 03:42:54
【问题描述】:

如何制作数据框列表以及如何访问列表中的每个数据框?

例如,如何将这些数据框放入列表中?

d1 <- data.frame(y1 = c(1, 2, 3),
                 y2 = c(4, 5, 6))
d2 <- data.frame(y1 = c(3, 2, 1),
                 y2 = c(6, 5, 4))

【问题讨论】:

  • 这是在几个答案中,但也值得在这里有一个可见的评论:在data.frame() 中使用= 而不是&lt;-。通过使用&lt;-,您可以在全局环境中创建y1y2,而您的数据框不是您想要的。
  • 看看 data.frame() 中没有空格和&lt;-s 的那乱七八糟的代码。我真是个新手。
  • 不再。我刚刚编辑了您的问题以修复代码格式。如果您感到怀旧,请随时恢复。

标签: r list dataframe r-faq


【解决方案1】:

for 循环模拟

如果我有一个 for 循环生成数据帧,我会从一个空的 list() 开始,并在生成数据帧时附加它们。

# Empty list
dat_list <- list()

for(i in 1:5){
    # Generate dataframe
    dat <- data.frame(x=rnorm(10), y=rnorm(10))
    # Add to list
    dat_list <- append(dat_list, list(dat))
}

请注意,在我们的 append() 调用中是 list(dat)

访问数据

然后从我们使用dat_list[[n]] 的列表中获取nth 数据帧。您可以以正常方式访问此数据框中的数据,例如dat_list[[2]]$x.

或者,如果您想从所有数据框中获取相同的部分 sapply(dat_list, "[", "x")

请参阅@Gregor Thomas 的答案,了解如何在没有for 循环的情况下执行此操作。

【讨论】:

    【解决方案2】:

    我认为自己是一个完全的新手,但我认为对于此处未说明的原始子问题之一,我有一个非常简单的答案:访问数据帧或其中的一部分。

    让我们从创建带有数据框的列表开始,如上所述:

    d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
    
    d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
    
    my.list <- list(d1, d2)
    

    然后,如果您想访问其中一个数据框中的特定值,您可以按顺序使用双括号来实现。第一组带您进入数据框,第二组带您进入特定坐标:

    my.list[[1]][[3,2]]
    
    [1] 6
    

    【讨论】:

      【解决方案3】:

      其他答案向您展示如何在您已经一堆 data.frames 时制作 data.frames 列表,例如,d1、@987654325 @, .... 按顺序命名的数据帧是个问题,将它们放在一个列表中是一个很好的解决方法,但最佳做法是避免将一堆 data.frames 不在列表中首先。

      其他答案提供了大量关于如何将数据框分配给列表元素、访问它们等的详细信息。我们也会在这里稍微介绍一下,但要点是说不要等到你有一堆data.frames 才将它们添加到列表中。从列表开始。

      此答案的其余部分将涵盖一些您可能会想创建顺序变量的常见情况,并向您展示如何直接进入列表。如果您不熟悉 R 中的列表,您可能还想阅读 What's the difference between [[ and [ in accessing elements of a list?


      从头开始列出

      首先不要创建d1 d2 d3, ..., dn。使用 n 元素创建一个列表 d

      将多个文件读入数据框列表

      这在读入文件时很容易完成。也许您的目录中有文件data1.csv, data2.csv, ...。您的目标是一个名为mydata 的data.frames 列表。您需要的第一件事是包含所有文件名的向量。您可以使用粘贴(例如,my_files = paste0("data", 1:5, ".csv"))来构建它,但使用list.files 获取所有适当的文件可能更容易:my_files &lt;- list.files(pattern = "\\.csv$")。您可以使用正则表达式来匹配文件,如果您需要帮助,请在其他问题中阅读有关正则表达式的更多信息。通过这种方式,您可以获取所有 CSV 文件,即使它们没有遵循良好的命名方案。或者,如果您需要从一堆 CSV 文件中挑选出某些 CSV 文件,您可以使用更高级的正则表达式模式。

      此时,大多数 R 初学者都会使用 for 循环,这并没有什么问题,它工作得很好。

      my_data <- list()
      for (i in seq_along(my_files)) {
          my_data[[i]] <- read.csv(file = my_files[i])
      }
      

      更类似于 R 的方式是使用 lapply,这是上述方法的快捷方式

      my_data <- lapply(my_files, read.csv)
      

      当然,可以酌情用其他数据导入功能代替read.csvreadr::read_csvdata.table::fread 会更快,或者您可能还需要针对不同文件类型的不同函数。

      无论哪种方式,命名列表元素以匹配文件都很方便

      names(my_data) <- gsub("\\.csv$", "", my_files)
      # or, if you prefer the consistent syntax of stringr
      names(my_data) <- stringr::str_replace(my_files, pattern = ".csv", replacement = "")
      

      将数据框拆分为数据框列表

      这非常简单,基本函数split() 为您完成。您可以按一列(或多列)数据进行拆分,也可以按您想要的任何其他内容进行拆分

      mt_list = split(mtcars, f = mtcars$cyl)
      # This gives a list of three data frames, one for each value of cyl
      

      这也是一种将数据框分解为多个片段以进行交叉验证的好方法。也许您想将 mtcars 拆分为训练、测试和验证部分。

      groups = sample(c("train", "test", "validate"),
                      size = nrow(mtcars), replace = TRUE)
      mt_split = split(mtcars, f = groups)
      # and mt_split has appropriate names already!
      

      模拟数据框列表

      也许你在模拟数据,像这样:

      my_sim_data = data.frame(x = rnorm(50), y = rnorm(50))
      

      但是谁只做一次模拟呢?你想这样做 100 次,1000 次,更多!但是您希望工作区中有 10,000 个数据框。使用replicate 并将它们放在一个列表中:

      sim_list = replicate(n = 10,
                           expr = {data.frame(x = rnorm(50), y = rnorm(50))},
                           simplify = F)
      

      尤其是在这种情况下,您还应该考虑是否真的需要单独的数据框,或者带有“组”列的单个数据框也可以工作?使用 data.tabledplyr 可以很容易地“按组”对数据框执行操作。

      我没有把我的数据放在一个列表中 :( 下次我会的,但我现在能做什么?

      如果它们是奇怪的分类(这是不寻常的),您可以简单地分配它们:

      mylist <- list()
      mylist[[1]] <- mtcars
      mylist[[2]] <- data.frame(a = rnorm(50), b = runif(50))
      ...
      

      如果您有以模式命名的数据框,例如df1df2df3,并且您希望将它们放在一个列表中,您可以get它们,如果您可以编写一个正则表达式来匹配名字。类似的东西

      df_list = mget(ls(pattern = "df[0-9]"))
      # this would match any object with "df" followed by a digit in its name
      # you can test what objects will be got by just running the
      ls(pattern = "df[0-9]")
      # part and adjusting the pattern until it gets the right objects.
      

      通常,mget 用于获取多个对象并在命名列表中返回它们。其对应的get 用于获取单个对象并将其返回(不在列表中)。

      将数据框列表组合成单个数据框

      一个常见的任务是将一系列数据框组合成一个大数据框。如果您想将它们堆叠在一起,您可以使用rbind 来表示它们,但是对于数据框列表,这里有三个不错的选择:

      # base option - slower but not extra dependencies
      big_data = do.call(what = rbind, args = df_list)
      
      # data table and dplyr have nice functions for this that
      #  - are much faster
      #  - add id columns to identify the source
      #  - fill in missing values if some data frames have more columns than others
      # see their help pages for details
      big_data = data.table::rbindlist(df_list)
      big_data = dplyr::bind_rows(df_list)
      

      (类似地使用cbinddplyr::bind_cols 表示列。)

      要合并(加入)数据框列表,您可以查看these answers。通常,这个想法是使用 Reducemerge(或其他一些连接函数)将它们组合在一起。

      为什么要把数据放在一个列表中?

      将相似的数据放在列表中,因为您想对每个数据框执行相似的操作,而 lapplysapplydo.callthe purrr package 和旧的 plyrl*ply 等函数可以实现很容易做到这一点。用列表轻松做事的例子比比皆是。

      即使您使用低级的 for 循环,循环列表的元素也比使用 paste 构造变量名并使用 get 访问对象要容易得多。也更容易调试。

      考虑可扩展性。如果你真的只需要三个变量,使用d1d2d3就可以了。但是如果事实证明你真的需要 6 个,那就需要更多的输入。下一次,当您需要 10 或 20 行代码时,您会发现自己在复制和粘贴代码行,可能使用 find/replace 将 d14 更改为 d15,而您认为 这不是编程方式应该是。如果使用列表,则 3 例、30 例和 300 例之间的差异最多只有一行代码——如果您的案例数量是自动检测的,例如有多少 .csv 文件,则根本没有变化在您的目录中。

      您可以命名列表的元素,以防您想使用数字索引以外的其他内容来访问您的数据框(您可以同时使用这两种方法,这不是 XOR 选择)。

      总体而言,使用列表将导致您编写更清晰、更易于阅读的代码,从而减少错误和混乱。

      【讨论】:

      • 你推荐哪本书涉及使用列表?
      • 我建议阅读 Stack Overflow 上标有 rlist 的问题和答案。
      • @Gregor 我想补充一点,我们可以避免命名列表元素来匹配文件,只需分配my_data &lt;- NULL 而不是`my_data
      • 有可能,但my_data &lt;- list() 明确表示您正在创建一个列表,这很好!清晰的代码是一件好事。我认为使用 my_data &lt;- NULL 没有任何优势。
      • 我同意你所说的,但就像我说的那样,这样做你可以逃避命名文件的阶段。 names(my_data) &lt;- gsub("\\.csv$", "", my_files) ;)
        但我尊重你的建议,因为我作为新手从他们那里学到了很多东西,我真的很感激 :)
      【解决方案4】:

      假设您有“大量”具有相似名称的 data.frames(此处为 d#,其中 # 是某个正整数),以下是@mark-miller 方法的轻微改进。它更简洁,并返回一个 named data.frames 列表,其中列表中的每个名称都是对应的原始 data.frame 的名称。

      关键是使用mgetls。如果问题中提供的数据框 d1 和 d2 是环境中唯一名称为 d# 的对象,那么

      my.list <- mget(ls(pattern="^d[0-9]+"))
      

      会返回

      my.list
      $d1
        y1 y2
      1  1  4
      2  2  5
      3  3  6
      
      $d2
        y1 y2
      1  3  6
      2  2  5
      3  1  4
      

      此方法利用ls 中的模式参数,它允许我们使用正则表达式对环境中的对象名称进行更精细的解析。正则表达式"^d[0-9]+$" 的替代方法是"^d\\d+$"

      作为@gregor points out,总体而言,最好设置您的数据构造过程,以便在开始时将data.frames放入命名列表中。

      数据

      d1 <- data.frame(y1 = c(1,2,3),y2 = c(4,5,6))
      d2 <- data.frame(y1 = c(3,2,1),y2 = c(6,5,4))
      

      【讨论】:

        【解决方案5】:

        非常简单!这是我的建议:

        如果您想在工作区中选择数据框,请尝试以下操作:

        Filter(function(x) is.data.frame(get(x)) , ls())
        

        ls()[sapply(ls(), function(x) is.data.frame(get(x)))]
        

        所有这些都会产生相同的结果。

        您可以更改is.data.frame 以检查其他类型的变量,例如is.function

        【讨论】:

          【解决方案6】:

          这与您的问题无关,但您想在函数调用中使用 = 而不是 &lt;-。如果您使用&lt;-,您最终会在您工作的任何环境中创建变量y1y2

          d1 <- data.frame(y1 <- c(1, 2, 3), y2 <- c(4, 5, 6))
          y1
          # [1] 1 2 3
          y2
          # [1] 4 5 6
          

          这不会产生在数据框中创建列名的看似想要的效果:

          d1
          #   y1....c.1..2..3. y2....c.4..5..6.
          # 1                1                4
          # 2                2                5
          # 3                3                6
          

          另一方面,= 运算符会将您的向量与 data.frame 的参数相关联。

          至于您的问题,制作数据框列表很容易:

          d1 <- data.frame(y1 = c(1, 2, 3), y2 = c(4, 5, 6))
          d2 <- data.frame(y1 = c(3, 2, 1), y2 = c(6, 5, 4))
          my.list <- list(d1, d2)
          

          您可以像访问任何其他列表元素一样访问数据框:

          my.list[[1]]
          #   y1 y2
          # 1  1  4
          # 2  2  5
          # 3  3  6
          

          【讨论】:

            【解决方案7】:

            您还可以使用[[[ 访问每个列表元素中的特定列和值。这里有几个例子。首先,我们可以使用lapply(ldf, "[", 1) 访问列表中每个数据框的第一列,其中1 表示列号。

            ldf <- list(d1 = d1, d2 = d2)  ## create a named list of your data frames
            lapply(ldf, "[", 1)
            # $d1
            #   y1
            # 1  1
            # 2  2
            # 3  3
            #
            # $d2
            #   y1
            # 1  3
            # 2  2
            # 3  1
            

            同样,我们可以使用

            访问第二列中的第一个值
            lapply(ldf, "[", 1, 2)
            # $d1
            # [1] 4
            # 
            # $d2
            # [1] 6
            

            然后我们也可以直接访问列值,作为向量,[[

            lapply(ldf, "[[", 1)
            # $d1
            # [1] 1 2 3
            #
            # $d2
            # [1] 3 2 1
            

            【讨论】:

              【解决方案8】:

              这可能有点晚了,但回到你的例子,我想我会稍微扩展一下答案。

               D1 <- data.frame(Y1=c(1,2,3), Y2=c(4,5,6))
               D2 <- data.frame(Y1=c(3,2,1), Y2=c(6,5,4))
               D3 <- data.frame(Y1=c(6,5,4), Y2=c(3,2,1))
               D4 <- data.frame(Y1=c(9,9,9), Y2=c(8,8,8))
              

              然后你就可以轻松地列出你的清单了:

              mylist <- list(D1,D2,D3,D4)
              

              现在您有一个列表,但不是以旧方式访问列表,例如

              mylist[[1]] # to access 'd1'
              

              您可以使用此功能来获取和分配您选择的数据框。

              GETDF_FROMLIST <- function(DF_LIST, ITEM_LOC){
                 DF_SELECTED <- DF_LIST[[ITEM_LOC]]
                 return(DF_SELECTED)
              }
              

              现在得到你想要的。

              D1 <- GETDF_FROMLIST(mylist, 1)
              D2 <- GETDF_FROMLIST(mylist, 2)
              D3 <- GETDF_FROMLIST(mylist, 3)
              D4 <- GETDF_FROMLIST(mylist, 4)
              

              希望额外的一点帮助。

              干杯!

              【讨论】:

              • 是的,我知道,但由于某种原因,当我复制和粘贴时,一切都大写了。 :( 无论如何,小写的代码都有效。
              • 我很好奇为什么你更喜欢GETDF_FROMLIST(mylist, 1) 而不是mylist[[1]]?如果您更喜欢函数语法,您甚至可以在不定义自定义函数的情况下使用 "[["(mylist, 1)
              • 你也可以简化你的函数定义,整个函数体可以是return(DF_LIST[[ITEM_LOC]]),不需要分配中间变量。
              【解决方案9】:

              如果您有大量按顺序命名的数据框,您可以创建所需数据框子集的列表,如下所示:

              d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
              d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
              d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
              d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
              
              my.list <- list(d1, d2, d3, d4)
              my.list
              
              my.list2 <- lapply(paste('d', seq(2,4,1), sep=''), get)
              my.list2
              

              其中my.list2 返回一个包含第二、第三和第四个数据帧的列表。

              [[1]]
                y1 y2
              1  3  6
              2  2  5
              3  1  4
              
              [[2]]
                y1 y2
              1  6  3
              2  5  2
              3  4  1
              
              [[3]]
                y1 y2
              1  9  8
              2  9  8
              3  9  8
              

              但请注意,上述列表中的数据框不再命名。如果您想创建一个包含数据框子集的列表并希望保留它们的名称,您可以尝试以下操作:

              list.function <-  function() { 
              
                   d1 <- data.frame(y1=c(1,2,3), y2=c(4,5,6))
                   d2 <- data.frame(y1=c(3,2,1), y2=c(6,5,4))
                   d3 <- data.frame(y1=c(6,5,4), y2=c(3,2,1))
                   d4 <- data.frame(y1=c(9,9,9), y2=c(8,8,8))
              
                   sapply(paste('d', seq(2,4,1), sep=''), get, environment(), simplify = FALSE) 
              } 
              
              my.list3 <- list.function()
              my.list3
              

              返回:

              > my.list3
              $d2
                y1 y2
              1  3  6
              2  2  5
              3  1  4
              
              $d3
                y1 y2
              1  6  3
              2  5  2
              3  4  1
              
              $d4
                y1 y2
              1  9  8
              2  9  8
              3  9  8
              
              > str(my.list3)
              List of 3
               $ d2:'data.frame':     3 obs. of  2 variables:
                ..$ y1: num [1:3] 3 2 1
                ..$ y2: num [1:3] 6 5 4
               $ d3:'data.frame':     3 obs. of  2 variables:
                ..$ y1: num [1:3] 6 5 4
                ..$ y2: num [1:3] 3 2 1
               $ d4:'data.frame':     3 obs. of  2 variables:
                ..$ y1: num [1:3] 9 9 9
                ..$ y2: num [1:3] 8 8 8
              
              > my.list3[[1]]
                y1 y2
              1  3  6
              2  2  5
              3  1  4
              
              > my.list3$d4
                y1 y2
              1  9  8
              2  9  8
              3  9  8
              

              【讨论】:

              • lapply(foo, get)代替mget(foo)
              猜你喜欢
              • 1970-01-01
              • 2018-03-11
              相关资源
              最近更新 更多