首页 > 解决方案 > 在 R 中将大型 XML 解析为数据框

问题描述

我有大型 XML 文件,我想将其转换为数据帧,以便在 R 和其他程序中进行进一步处理。这一切都在 macOS 中完成。

每个月的 XML 大约 1gb,有 150k 条记录和 191 个不同的变量。最后,我可能不需要完整的 191 个变量,但我想保留它们并稍后再决定。

XML 文件可以在此处访问(滚动到底部以查看每月压缩包,未压缩时应查看“dming”XML)

我已经取得了一些进展,但是处理较大的文件需要很长时间(见下文)

XML 如下所示:

<ROOT>
 <ROWSET_DUASDIA>
  <ROW_DUASDIA NUM="1">
   <variable1>value</variable1>
   ...
   <variable191>value</variable191>
  </ROW_DUASDIA>
  ...
  <ROW_DUASDIA NUM="150236">
   <variable1>value</variable1>
   ...
   <variable191>value</variable191>
  </ROW_DUASDIA>
 </ROWSET_DUASDIA>
</ROOT>

我希望这已经足够清楚了。这是我第一次使用 XML。

我在这里查看了很多答案,实际上设法使用较小的样本(使用每日 XML 而不是每月的 XML)将数据放入数据框中,并且xml2. 这就是我所做的

library(xml2) 

raw <- read_xml(filename)

# Find all records
dua <- xml_find_all(raw,"//ROW_DUASDIA")

# Create empty dataframe
dualen <- length(dua)
varlen <- length(xml_children(dua[[1]]))
df <- data.frame(matrix(NA,nrow=dualen,ncol=varlen))

# For loop to enter the data for each record in each row
for (j in 1:dualen) {
  df[j, ] <- xml_text(xml_children(dua[[j]]),trim=TRUE)
}

# Name columns
colnames(df) <- c(names(as_list(dua[[1]])))

我想这是相当初级的,但我对 R 也很陌生。

无论如何,这适用于每日数据(4-5k 记录),但对于 150k 记录来说可能效率太低,事实上我等了几个小时还没有完成。当然,我只需要每月运行一次此代码,但我仍然想改进它。

as_list我尝试使用其中的函数将所有记录的元素转换为列表,xml2以便继续使用plyr,但这也花费了太长时间。

提前致谢。

标签: rxmlxml2

解决方案


虽然不能保证在较大的 XML 文件上有更好的性能,但(“老派”)包为像您这样的平面 XML 文件XML维护了一个紧凑的数据帧处理程序。xmlToDataFrame其他兄弟姐妹中可用的任何缺失节点都会导致NA相应的字段。

library(XML)

doc <- xmlParse("/path/to/file.xml")
df <- xmlToDataFrame(doc, nodes=getNodeSet(doc, "//ROW_DUASDIA"))

您甚至可以下载每日 zip,解压缩所需的 XML,并在每月的大型 XML 对内存造成挑战时将其解析为数据帧。例如,下面将 2018 年 12 月的每日数据提取到要在末尾绑定行的数据框列表中。Process 甚至添加了一个DDate字段。tryCatch由于缺少序列或其他 URL 或 zip 问题,方法被包装在 a中。

dec_urls <- paste0(1201:1231)
temp_zip <- "/path/to/temp.zip"
xml_folder <- "/path/to/xml/folder"

xml_process <- function(dt) {      
  tryCatch({
    # DOWNLOAD ZIP TO URL
    url <- paste0("ftp://ftp.aduanas.gub.uy/DUA%20Diarios%20XML/2018/dd2018", dt,".zip")
    file <- paste0(xml_folder, "/dding2018", dt, ".xml")

    download.file(url, temp_zip)
    unzip(temp_zip, files=paste0("dding2018", dt, ".xml"), exdir=xml_folder)
    unlink(temp_zip)           # DESTROY TEMP ZIP

    # PARSE XML TO DATA FRAME
    doc <- xmlParse(file)        
    df <- transform(xmlToDataFrame(doc, nodes=getNodeSet(doc, "//ROW_DUASDIA")),
                    DDate = as.Date(paste("2018", dt), format="%Y%m%d", origin="1970-01-01"))
    unlink(file)               # DESTROY TEMP XML

    # RETURN XML DF
    return(df)
  }, error = function(e) NA)      
}

# BUILD LIST OF DATA FRAMES
dec_df_list <- lapply(dec_urls, xml_process)

# FILTER OUT "NAs" CAUGHT IN tryCatch
dec_df_list <- Filter(NROW, dec_df_list)

# ROW BIND TO FINAL SINGLE DATA FRAME
dec_final_df <- do.call(rbind, dec_df_list)

推荐阅读