【问题标题】:R: losing column names when adding rows to an empty data frameR:向空数据框添加行时丢失列名
【发布时间】:2011-07-11 00:47:42
【问题描述】:

我刚从 R 开始,遇到了一个奇怪的行为:在空数据框中插入第一行时,原始列名会丢失。

示例:

a<-data.frame(one = numeric(0), two = numeric(0))
a
#[1] one two
#<0 rows> (or 0-length row.names)
names(a)
#[1] "one" "two"
a<-rbind(a, c(5,6))
a
#  X5 X6
#1  5  6
names(a)
#[1] "X5" "X6"

如您所见,列名 onetwo 已替换为 X5X6

有人可以告诉我为什么会发生这种情况吗?有没有正确的方法可以做到这一点而不会丢失列名?

shotgun 解决方案是将名称保存在辅助向量中,然后在完成对数据框的处理后将它们添加回来。

谢谢

上下文:

我创建了一个函数,它收集一些数据并将它们作为新行添加到作为参数接收的数据帧中。 我创建数据框,遍历我的数据源,将 data.frame 传递给每个函数调用以填充其结果。

【问题讨论】:

    标签: r dataframe names rbind


    【解决方案1】:

    解决方法是:

    a <- rbind(a, data.frame(one = 5, two = 6))
    

    ?rbind 声明合并对象需要匹配名称:

    然后它需要类 第一个数据框中的列,以及 按名称匹配列(而不是 按位置)

    【讨论】:

    • 我认为在您的代码中,rbind 中的a 被忽略,因此它实际上等同于a &lt;- data.frame(one = 5, two = 6)。但我可能错了。
    • +1 我通常使用这种方法 -- 请注意,您可以简单地将 a 初始化为空向量:a &lt;- c()
    • @juba,可能是这样,因为data.frame a 是空的。
    【解决方案2】:

    rbind 帮助页面指定:

    对于‘cbind’ (‘rbind’),向量为零 长度(包括“NULL”)被忽略 除非结果将有零行 (列),用于 S 兼容性。 (零范围矩阵不会出现在 S3 并且在 R 中不会被忽略。)

    所以,事实上,a 在您的 rbind 指令中被忽略了。似乎并没有完全忽略,因为它是一个数据框,rbind 函数被称为rbind.data.frame

    rbind.data.frame(c(5,6))
    #  X5 X6
    #1  5  6
    

    也许插入行的一种方法是:

    a[nrow(a)+1,] <- c(5,6)
    a
    #  one two
    #1   5   6
    

    但根据您的代码,可能有更好的方法。

    【讨论】:

    • 如果您有不同的数据类型(例如characternumeric),最好使用list 函数list("five",6)。否则它将把一切都理解为字符。
    【解决方案3】:

    FWIW,另一种设计可能会让您的函数为两列构建向量,而不是 rbinding 到数据框:

    ones <- c()
    twos <- c()
    

    修改函数中的向量:

    ones <- append(ones, 5)
    twos <- append(twos, 6)
    

    根据需要重复,然后一次性创建 data.frame:

    a <- data.frame(one=ones, two=twos)
    

    【讨论】:

    • 非常有帮助。也许没有那么简洁,但数据流不那么黑盒了。
    • 确实是一个不错的答案。但它似乎很“不R”。在构建 data.frame 时,您首先需要对所有内容进行 循环,而行运算符是 R 的主力。也许使用@juba 的答案,但在末尾设置 colnames:colnames(a) &lt;- c("one","two")?
    • 这种方法的问题是,您经常需要 colnames 来扩展数据框。为什么这么简单的事情在 r... 中这么复杂?
    【解决方案4】:

    几乎要屈服于这个问题。

    1) 创建数据框,将stringsAsFactor 设置为FALSE 或者直接进入下一个问题

    2) 不要使用rbind - 不知道为什么它会弄乱列名。只需这样做:

    df[nrow(df)+1,] &lt;- c("d","gsgsgd",4)

    df <- data.frame(a = character(0), b=character(0), c=numeric(0))
    
    df[nrow(df)+1,] <- c("d","gsgsgd",4)
    
    #Warnmeldungen:
    #1: In `[<-.factor`(`*tmp*`, iseq, value = "d") :
    #  invalid factor level, NAs generated
    #2: In `[<-.factor`(`*tmp*`, iseq, value = "gsgsgd") :
    #  invalid factor level, NAs generated
    
    df <- data.frame(a = character(0), b=character(0), c=numeric(0), stringsAsFactors=F)
    
    df[nrow(df)+1,] <- c("d","gsgsgd",4)
    
    df
    #  a      b c
    #1 d gsgsgd 4
    

    【讨论】:

    • 请注意,使用该方法,c 列不再是数字了! str(df) 说它是字符。
    【解决方案5】:

    你可以这样做:

    给初始数据框留一行

     df=data.frame(matrix(nrow=1,ncol=length(newrow))
    

    添加新行并取出 NAS

    newdf=na.omit(rbind(newrow,df))
    

    但要注意你的新行没有 NA,否则它也会被删除。

    干杯 阿古斯

    【讨论】:

      【解决方案6】:

      我没有使用numeric(0) 构造data.frame,而是使用as.numeric(0)

      a<-data.frame(one=as.numeric(0), two=as.numeric(0))
      

      这会创建一个额外的初始行

      a
      #    one two
      #1   0   0
      

      绑定额外的行

      a<-rbind(a,c(5,6))
      a
      #    one two
      #1   0   0
      #2   5   6
      

      然后使用负索引删除第一(假)行

      a<-a[-1,]
      a
      
      #    one two
      #2   5   6
      

      注意:它弄乱了索引(最左边)。我还没有弄清楚如何防止这种情况(其他人?),但大多数时候这可能无关紧要。

      【讨论】:

      • 大部分时间可能会这样。
      【解决方案7】:

      一种使这项工作通用且最少重新键入列名的方法如下。此方法不需要破解 NA 或 0。

      rs <- data.frame(i=numeric(), square=numeric(), cube=numeric())
      for (i in 1:4) {
          calc <- c(i, i^2, i^3)
          # append calc to rs
          names(calc) <- names(rs)
          rs <- rbind(rs, as.list(calc))
      }
      

      rs 将具有正确的名称

      > rs
          i square cube
      1   1      1    1
      2   2      4    8
      3   3      9   27
      4   4     16   64
      > 
      

      另一种更干净的方法是使用 data.table:

      > df <- data.frame(a=numeric(0), b=numeric(0))
      > rbind(df, list(1,2)) # column names are messed up
      >   X1 X2
      > 1  1  2
      
      > df <- data.table(a=numeric(0), b=numeric(0))
      > rbind(df, list(1,2)) # column names are preserved
         a b
      1: 1 2
      

      请注意,data.table 也是 data.frame。

      > class(df)
      "data.table" "data.frame"
      

      【讨论】:

        【解决方案8】:

        我使用以下解决方案向空数据框添加一行:

        d_dataset <- 
          data.frame(
            variable = character(),
            before = numeric(),
            after = numeric(),
            stringsAsFactors = FALSE)
        
        d_dataset <- 
          rbind(
            d_dataset,
              data.frame(
                variable = "test",
                before = 9,
                after = 12,
                stringsAsFactors = FALSE))  
        
        print(d_dataset)
        
        variable before after  
        1     test      9    12
        

        HTH。

        亲切的问候

        乔治

        【讨论】:

          【解决方案9】:

          研究这个古老的 R 烦恼将我带到了这个页面。我想为 Georg 的出色答案 (https://stackoverflow.com/a/41609844/2757825) 添加更多解释,这不仅解决了 OP 引发的问题(丢失字段名称),而且还防止了所有字段到因子的不必要转换。对我来说,这两个问题是一起出现的。我想要一个不涉及编写额外代码但保留两个不同操作的基本 R 解决方案:定义数据框,附加行 - 这是 Georg 的答案提供的。

          下面的前两个例子说明了问题,第三和第四个例子显示了 Georg 的解决方案。

          示例 1:将新行作为向量附加到 rbind

          • 结果:丢失列名并将所有变量转换为因子
          my.df <- data.frame(
              table = character(0),
              score = numeric(0),
              stringsAsFactors=FALSE
              )
          my.df <- rbind(
              my.df, 
              c("Bob", 250) 
              )
              
          my.df
            X.Bob. X.250.
          1    Bob    250
          
          str(my.df)
          'data.frame':   1 obs. of  2 variables:
           $ X.Bob.: Factor w/ 1 level "Bob": 1
           $ X.250.: Factor w/ 1 level "250": 1
          
          

          示例 2:将新行作为数据框附加到 rbind 中

          • 结果:保留列名,但仍将字符变量转换为因子。
          my.df <- data.frame(
              table = character(0),
              score = numeric(0),
              stringsAsFactors=FALSE
              )
          my.df <- rbind(
              my.df, 
              data.frame(name="Bob", score=250) 
              )
              
          my.df
                name score
          1 Bob  250
          
          str(my.df)
          'data.frame':   1 obs. of  2 variables:
           $ name : Factor w/ 1 level "Bob": 1
           $ score: num 250
          

          示例 3:将 rbind 中的新行作为数据框附加,其中 stringsAsFactors=FALSE

          • 结果:问题解决了。
          my.df <- data.frame(
              table = character(0),
              score = numeric(0),
              stringsAsFactors=FALSE
              )
          my.df <- rbind(
              my.df, 
              data.frame(name="Bob", score=250, stringsAsFactors=FALSE) 
              )
              
          my.df
                name score
          1 Bob  250
          
          str(my.df)
          'data.frame':   1 obs. of  2 variables:
           $ name : chr "Bob"
           $ score: num 250
          

          示例 4:与示例 3 类似,但一次添加多行。

          my.df <- data.frame(
              table = character(0),
              score = numeric(0),
              stringsAsFactors=FALSE
              )
          my.df <- rbind(
              my.df, 
              data.frame(
                  name=c("Bob", "Carol", "Ted"), 
                  score=c(250, 124, 95), 
                  stringsAsFactors=FALSE) 
              )
          
          str(my.df)
          'data.frame':   3 obs. of  2 variables:
           $ name : chr  "Bob" "Carol" "Ted"
           $ score: num  250 124 95
          
          my.df
             name score
          1   Bob   250
          2 Carol   124
          3   Ted    95
          
          

          【讨论】:

            猜你喜欢
            • 2022-11-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-03-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-09-15
            相关资源
            最近更新 更多