在向空数据框添加行时丢失列名。

90

我刚开始学习 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

请问有人能告诉我为什么会这样,是否有正确的方法在不丢失列名的情况下进行替换?

一个解决方案是将列名保存到辅助向量中,然后在完成对数据框的操作后再将其添加回去。

谢谢

上下文:

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

10个回答

44
rbind 的帮助页面指出:
“对于‘cbind’(‘rbind’),零长度的向量(包括‘NULL’)将被忽略,除非结果为零行(列),以实现 S 的兼容性。(零范围矩阵不会出现在 S3 中,在 R 中也不会被忽略。)”
因此,实际上,在您的rbind指令中忽略了a。似乎并不是完全忽略了它,因为它是一个数据框,所以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

但根据您的代码,可能有更好的方法来解决这个问题。


3
如果你有不同的数据类型(例如字符数字),最好使用list函数list("five",6)。否则,它会将所有东西都解释为字符。 - Untitpoi

18

我几乎要向这个问题投降了。

1)创建数据框时,将 stringsAsFactor 设置为 FALSE,否则您会遇到下一个问题。

2)不要使用 rbind - 不知道它为什么会搞乱列名。只需按此方式执行:

df[nrow(df)+1,] <- 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) 显示它是字符。 - Untitpoi

9

一个解决方法是:

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

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

然后,它从第一个数据框中获取列的类,并通过名称(而不是位置)匹配列。


1
我认为在你的代码中,rbind 函数内的 a 被忽略了,因此实际上等同于 a <- data.frame(one = 5, two = 6)。但是我可能是错的。 - juba
+1 我通常使用这种方法——请注意,您可以将a初始化为空向量:a <- c() - Prasad Chalasani
@juba,可能是这样,因为数据框a是空的。 - Roman Luštrik

9

顺便说一句,另一种设计可能是让您的函数构建两列的向量,而不是将它们连接到数据框中:

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

修改函数中的向量:

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

根据需要重复操作,然后一次性创建您的数据框:

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

1
非常有帮助。也许不够简洁,但数据流程不那么黑盒子化。 - Andrew
确实是一个不错的答案。但它似乎非常“不像R”。在构建data.frame时,您首先需要循环遍历所有内容,而行运算符是R的工作马。也许可以使用@juba的答案,但在最后设置列名:colnames(a) <- c("one","two") - user989762
这种方法的问题在于,通常需要使用列名来扩展数据框。为什么在R中如此简单的事情变得如此复杂呢...? - TMOTTM

2

以下是一种通用且最少需要重新键入列名称的方法。该方法不需要操作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"

1
你可以这样做:
给初始数据框添加一行。
 df=data.frame(matrix(nrow=1,ncol=length(newrow))

添加您的新行并移除NAS。
newdf=na.omit(rbind(newrow,df))

但要注意,您的新行中不要有NA,否则它也将被擦除。

干杯 Agus


1
我使用以下方法向空数据框添加行:
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.

敬礼

Georg


0

研究这个可敬的 R 烦恼带我来到了这个页面。我想对 Georg 的优秀答案(https://dev59.com/Am435IYBdhLWcg3wvy-_#41609844)进行更多的解释,它不仅解决了 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


0

我使用 as.numeric(0) 构建数据框,而不是使用 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

注意:它会破坏索引(最左边)。我还没有找到如何防止这种情况的方法(有其他人知道吗?),但大多数情况下可能并不重要。

2
大多数情况下,它可能会这样做。 - TMOTTM

0
你可以使用 tibble 包中的 add_row 函数:
tibble::add_row(a, one = c(5, 10), two = c(6, 8))

输出

  one two
1   5   6
2  10   8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接