为什么 rbind() 在 for 循环中不起作用?

Why doesn't rbind() work inside a for loop?

我有一个日期范围:

date_rng <- seq( as.Date("2008-01-01"), as.Date("2008-12-31"), by="+1 day")

我有一些与问题必然相关的辅助函数,我会尽量将它们排除在外。

我从第一个约会开始调用这个函数:

# Function for getting weather table by airport code and date and return dataframe
get_table <- function(code, date){
  adv <- sprintf(
    "https://www.wunderground.com/history/airport/K%s/2008/%s/%s/DailyHistory.html", 
    code, month(date), day(date)
  )
  h <- adv %>% read_html()
  t <- h%>% 
  html_nodes(xpath = '//*[@id="obsTable"]') %>%
  html_table()
  df <- data.frame(t)
  return(df)
}
atl_weather <- get_table("ATL", date_rng[1])

现在我遍历其余日期,为每个日期创建一个新的 df,然后我尝试将其附加到原始日期:

# Loop through remaining dates and bind dfs
for(d in as.list(date_rng[2:4])){
  rbind(atl_weather, get_table("ATL", d), d)
}

但是绑定没有发生,我只剩下范围内第一个日期的原始数据框,它是在 for 循环之前创建的。

这虽然有效:

atl_weather <- get_table("ATL", date_rng[1])
new_df <- get_table("ATL", date_rng[2])
new_df <- scraped_data_formatter(new_df, date_rng[2])
rbind(atl_weather, new_df)

如何让 rbind() 在 for 循环中工作(以便我迭代构建数据框以包含完整日期范围内的所有数据)?

确实有效。问题是您丢弃了结果,因为您没有将 rbind() 的输出分配给任何东西。

改变

rbind(atl_weather, get_table("ATL", d), d)

至此

atl_weather <- rbind(atl_weather, get_table("ATL", d), d)

假设 atl_weather 是您要增量添加的数据框。

就是说,您不想在 R 中这样做;每次向对象添加 column/row 时,R 都需要进行大量数据复制。基本上,以这种方式递增增长的对象会产生大量开销,这样做肯定会让您的代码陷入困境。

理想情况下,您首先分配足够的 space(即足够的行以便您可以在分配时索引第 i 行:new_atl_weather[i, ] <- c(....)。)

我会冒着跑题的风险(因为这个问题已经得到正确回答),只要我被迫在 for 循环中构建数据框,我就会为您提供我最喜欢的编程模式:

for (d in as.list(date_rng[2:4])){
    if (exists("atl_weather")) {
        atl_weather = rbind(atl_weather, get_table("ATL", d), d)
    } else {
        atl_weather = get_table("ATL", d)
    }
}

当然,如果我有 get_table 中包含的功能,我会改用某种 apply 语句。但是当现实生活妨碍并且 for 循环的内部太复杂时,我通常会使用类似的模式将某种 temp.data.frame 对象分配给或绑定到 atl_weather以上:

if (exists("atl_weather")) rm(atl_weather) # in case I'm dynamically running code in chunks

for (d in as.list(date_rng[2:4])){
    temp.df = ... # complicated stuff

    if (exists("atl_weather")) {
        atl_weather = rbind(atl_weather, temp.df)
    } else {
        atl_weather = temp.df
    }
}