You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

do.call(rbind, ...)在R语言循环中表现异常,求原因及解决方案

问题描述

尝试通过R代码批量下载自行车出行数据,代码如下:

library(RJSONIO)

first.day <- as.Date(paste0("202201","01"), "%Y%m%d")
today <- Sys.Date()
months <- format(seq(first.day, today,by="month"), "%Y%m")
length(months)

months.df.list <- vector("list", length = length(months))

for (m in months){
  print(m)
  filename <- paste0("./monthly_bike_data/", m, ".csv")
  query <- paste0("https://data.urbansharing.com/bergenbysykkel.no/trips/v1/", substr(m,0,4),"/", substr(m,5,6), ".json")
  month.df.raw <- fromJSON(query) #retrieve data
  month.df <- do.call(rbind,month.df.raw) # convert to a proper dataframe
  write.csv(month.df, filename)
  
}

运行class(month.df)得到结果:

[1] "list"

但在循环外单独运行以下代码时:

twentytwenty.raw <- fromJSON("https://data.urbansharing.com/bergenbysykkel.no/trips/v1/2020/01.json")
twentytwenty <- do.call(rbind, twentytwenty.raw)
class(twentytwenty)

得到的结果是:

[1] "matrix" "array"

仅有的差异是do.call(rbind, ...)运行在循环内还是外,但输出类型却不同,请问原因是什么,该如何修复?


原因分析

核心问题并非循环本身,而是不同月份的JSON返回结构存在差异:

  • 2020年1月的JSON数据返回的是包含多个长度一致子列表的列表,do.call(rbind, ...)可以将这些子列表按行绑定为矩阵。
  • 循环中部分月份的JSON数据结构不同:可能是该月份只有单条记录,或JSON返回的是单个嵌套列表而非列表的列表。此时do.call(rbind, 单个列表)不会执行行绑定操作,而是直接返回原列表。

另外,RJSONIO包对JSON的解析行为稳定性不足,面对不同结构的JSON时返回结果一致性较差,进一步放大了问题。


修复建议

1. 替换解析包为jsonlite(更稳定的JSON处理工具)

jsonlite对不同结构的JSON解析一致性更好,默认会将嵌套JSON转换为数据框:

library(jsonlite)

first.day <- as.Date(paste0("202201","01"), "%Y%m%d")
today <- Sys.Date()
months <- format(seq(first.day, today,by="month"), "%Y%m")

for (m in months){
  print(m)
  filename <- paste0("./monthly_bike_data/", m, ".csv")
  query <- paste0("https://data.urbansharing.com/bergenbysykkel.no/trips/v1/", substr(m,1,4),"/", substr(m,5,6), ".json")
  # 直接解析为数据框,无需手动rbind
  month.df <- fromJSON(query)
  # 确保最终为数据框类型
  if (!is.data.frame(month.df)) {
    month.df <- as.data.frame(month.df)
  }
  write.csv(month.df, filename, row.names = FALSE)
}

2. 对RJSONIO的兼容修复

如果必须使用RJSONIO,可先检查解析后的数据结构再做绑定处理:

library(RJSONIO)

first.day <- as.Date(paste0("202201","01"), "%Y%m%d")
today <- Sys.Date()
months <- format(seq(first.day, today,by="month"), "%Y%m")

for (m in months){
  print(m)
  filename <- paste0("./monthly_bike_data/", m, ".csv")
  query <- paste0("https://data.urbansharing.com/bergenbysykkel.no/trips/v1/", substr(m,1,4),"/", substr(m,5,6), ".json")
  month.df.raw <- fromJSON(query)
  
  # 判断是否为列表的列表,再选择绑定方式
  if (all(sapply(month.df.raw, is.list))) {
    month.df <- do.call(rbind, lapply(month.df.raw, as.data.frame))
  } else {
    month.df <- as.data.frame(month.df.raw)
  }
  
  write.csv(month.df, filename, row.names = FALSE)
}

3. 使用更鲁棒的绑定函数

用dplyr::bind_rows替代do.call(rbind),它能自动处理不同结构的输入,统一返回数据框:

library(RJSONIO)
library(dplyr)

first.day <- as.Date(paste0("202201","01"), "%Y%m%d")
today <- Sys.Date()
months <- format(seq(first.day, today,by="month"), "%Y%m")

for (m in months){
  print(m)
  filename <- paste0("./monthly_bike_data/", m, ".csv")
  query <- paste0("https://data.urbansharing.com/bergenbysykkel.no/trips/v1/", substr(m,1,4),"/", substr(m,5,6), ".json")
  month.df.raw <- fromJSON(query)
  month.df <- bind_rows(month.df.raw)
  write.csv(month.df, filename, row.names = FALSE)
}

内容的提问来源于stack exchange,提问作者Illimar Rekand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 15:40:33