【问题标题】:R - Data frame append first rowR - 数据框追加第一行
【发布时间】:2015-07-28 15:53:03
【问题描述】:

很遗憾,我遇到了困难,需要您的帮助。

我正在初始化一个数据框并尝试在循环中用新行填充它。它几乎可以正常工作,只有第一行获得 row.names 值的“NA”。谁能为此提出解决方案和/或解释为什么会发生这种情况?

我正在使用这个问题的答案中的 f3 方法:How to append rows to an R data frame

例子:

df <- data.frame( "Type" = character(), 
                  "AvgError" = numeric(), 
                  "StandardDeviation"= numeric (), 
                  stringsAsFactors=FALSE)

for (i in 1:3){
  df[nrow(df) + 1, ]$Type           <- paste("Test", as.character(format(round(i, 2), nsmall = 2)))
  df[nrow(df), ]$AvgError           <- i/10
  df[nrow(df), ]$StandardDeviation  <- i/100
}

df
        Type AvgError StandardDeviation
NA Test 1.00      0.1              0.01
2  Test 2.00      0.2              0.02
3  Test 3.00      0.3              0.03

如果我能提供更多信息,请发表评论,我会尽力提供。感谢您的帮助。

编辑:好的,谢谢到目前为止的讨论。我了解(并且之前已经知道)这不是最好的方法,因为它比功能方法慢得多,但在这种情况下执行时间并不重要。 @MrFlick 在 cmets 中提供了一种解决方法,只需在末尾重命名 row.names (rownames(df)&lt;-1:nrow(df))。无论如何这有帮助,但它仍然让我感到不满意,因为它不治疗原因而只处理症状。

【问题讨论】:

  • 该问题并未解决逐行构建 data.frame 通常是个坏主意的问题。最好先构建数据列(向量),然后在完成后将它们组合成一个 data.frame。 i&lt;-1:3; df&lt;-data.frame(Type=paste("Test", as.character(format(round(i, 2), nsmall = 2))), AvegError=i/10, StandardDeviation=i/100)
  • 谢谢。好吧,我在那个循环中做了一大堆其他的计算。我输入的值是多行代码的结果。这只是一个简化的例子。它不会对运行时间产生太大影响,因为其余的运行时间要长得多。除了运行时间之外还有其他问题吗?或者为什么这是一个坏主意?
  • 几乎有更好、更“类似 R”的方法来做这样的事情。这看起来像是为过程语言编写的代码,而不是像 R 那样的函数式语言。但如果你不在乎,你可以在末尾用rownames(df)&lt;-1:nrow(df) 修复rownames()
  • @cowhi 在下面查看我的答案——我已经表明,当您追加到数据时,您可能会在小至 20,000 行的数据帧上浪费一分钟多的时间框架。除非您的数据框很短,否则一次追加一行可能会影响性能。
  • 您为什么在提供的选项中使用f3 而不是f4

标签: r dataframe


【解决方案1】:

通过一次追加一行来增加数据帧会使您的代码效率低下,因为您需要在每次迭代时继续为数据帧重新分配整个空间。尤其是当您增长到较大的对象大小时,这可能会导致您的代码非常慢。您可以在 R inferno 的 Circle 2 中阅读有关此问题的所有信息。

例如,考虑您的代码与单独计算数据帧的每一行然后在最后将它们与do.callrbind 组合在一起的类似代码:

OP <- function(vals) {
  df <- data.frame( "Type" = character(), 
                    "AvgError" = numeric(), 
                    "StandardDeviation"= numeric (), 
                    stringsAsFactors=FALSE)
  for (i in vals){
    df[nrow(df) + 1, ]$Type           <- paste("Test", as.character(format(round(i, 2), nsmall = 2)))
    df[nrow(df), ]$AvgError           <- i/10
    df[nrow(df), ]$StandardDeviation  <- i/100
  }
  row.names(df) <- vals
  df
}

josilber <- function(vals) {
  ret <- do.call(rbind, lapply(vals, function(x) {
    data.frame(Type=paste("Test", as.character(format(round(x, 2), nsmall = 2))),
               AvgError = x/10,
               StandardDeviation = x/100,
               stringsAsFactors=FALSE)
  }))
  ret
}

all.equal(OP(1:10000), josilber(1:10000))
# [1] TRUE
system.time(OP(1:10000))
#    user  system elapsed 
#  17.849   1.325  19.147 
system.time(josilber(1:10000))
#    user  system elapsed 
#   4.685   0.027   4.713 

对于长度为 10,000 的数据帧,等待到最后合并每一行的代码比连续附加到数据帧的代码快 4 倍。基本上,您已经为内存重新分配引入了 15 秒的延迟,这与每行计算无关,这仅适用于具有 10,000 行的数据帧。对于长度为 20,000 的数据帧,浪费的计算时间长达 64 秒:

system.time(OP(1:20000))
#    user  system elapsed 
#  70.755   7.065  77.717 
system.time(josilber(1:20000))
#    user  system elapsed 
#  12.502   0.968  13.470 

如 cmets 中所述,有更快的方法来构建这些特定数据帧(使用矢量化函数一次性计算每个变量),但我将函数 josilber 限制为计算每一行的代码 - by-one 以证明附加仍然会对性能产生重大影响。

【讨论】:

    猜你喜欢
    • 2014-05-09
    • 1970-01-01
    • 2014-06-16
    • 1970-01-01
    • 2017-01-14
    • 2021-04-21
    • 2018-05-14
    • 2016-07-21
    • 1970-01-01
    相关资源
    最近更新 更多