将大 XML 解析为 R 中的数据帧
Parsing large XML to dataframe in R
我有很大的 XML 文件,我想将其转换为数据帧以便在 R 和其他程序中进一步处理。这一切都在 macOS 中完成。
每个月 XML 大约 1gb,有 150k 条记录和 191 个不同的变量。最后我可能不需要完整的 191 个变量,但我想保留它们并稍后再决定。
The XML files can be accessed here(滚动到每月 zip 的底部,未压缩时应该查看 "dming" XMLs)
我取得了一些进展,但处理较大文件的时间太长(见下文)
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 而不是每月)和 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 条记录来说效率可能太低了,事实上我等了几个小时还没有完成。当然,我只需要每月 运行 一次此代码,但我还是想改进它。
我尝试使用 xml2
中的 as_list
函数将所有记录的元素转换为列表,以便我可以继续 plyr
,但这也花费了太长时间。
提前致谢。
这是一个一次性处理整个文档的解决方案,而不是在循环中逐条读取 150,000 条记录。这应该会显着提高性能。
此版本还可以处理每条记录的变量数量不同的情况。
library(xml2)
doc<-read_xml('<ROOT>
<ROWSET_DUASDIA>
<ROW_DUASDIA NUM="1">
<variable1>value1</variable1>
<variable191>value2</variable191>
</ROW_DUASDIA>
<ROW_DUASDIA NUM="150236">
<variable1>value3</variable1>
<variable2>value_new</variable2>
<variable191>value4</variable191>
</ROW_DUASDIA>
</ROWSET_DUASDIA>
</ROOT>')
#find all of the nodes/records
nodes<-xml_find_all(doc, ".//ROW_DUASDIA")
#find the record NUM and the number of variables under each record
nodenum<-xml_attr(nodes, "NUM")
nodeslength<-xml_length(nodes)
#find the variable names and values
nodenames<-xml_name(xml_children(nodes))
nodevalues<-trimws(xml_text(xml_children(nodes)))
#create dataframe
df<-data.frame(NUM=rep(nodenum, times=nodeslength),
variable=nodenames, values=nodevalues, stringsAsFactors = FALSE)
#dataframe is in a long format.
#Use the function cast, or spread from the tidyr to convert wide format
# NUM variable values
# 1 1 variable1 value1
# 2 1 variable191 value2
# 3 150236 variable1 value3
# 4 150236 variable2 value_new
# 5 150236 variable191 value4
#Convert to wide format
library(tidyr)
spread(df, variable, values)
虽然不能保证在较大的 XML 文件上有更好的性能,但 ("old school") XML
包维护了一个紧凑的数据帧处理程序,xmlToDataFrame
,用于像你这样的扁平 XML 文件。其他兄弟中可用的任何缺失节点都会导致相应字段的 NA
。
library(XML)
doc <- xmlParse("/path/to/file.xml")
df <- xmlToDataFrame(doc, nodes=getNodeSet(doc, "//ROW_DUASDIA"))
您甚至可以想像地下载每日 zip,解压缩需要 XML,并将其解析为数据帧,以防每月 XML 的大数据造成内存挑战。例如,下面将 2018 年 12 月的每日数据提取到要在最后行绑定的数据框列表中。 Process 甚至添加了一个 DDate 字段。由于缺少天数或其他 URL 或 zip 问题,方法被包装在 tryCatch
中。
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)
我有很大的 XML 文件,我想将其转换为数据帧以便在 R 和其他程序中进一步处理。这一切都在 macOS 中完成。
每个月 XML 大约 1gb,有 150k 条记录和 191 个不同的变量。最后我可能不需要完整的 191 个变量,但我想保留它们并稍后再决定。
The XML files can be accessed here(滚动到每月 zip 的底部,未压缩时应该查看 "dming" XMLs)
我取得了一些进展,但处理较大文件的时间太长(见下文)
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 而不是每月)和 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 条记录来说效率可能太低了,事实上我等了几个小时还没有完成。当然,我只需要每月 运行 一次此代码,但我还是想改进它。
我尝试使用 xml2
中的 as_list
函数将所有记录的元素转换为列表,以便我可以继续 plyr
,但这也花费了太长时间。
提前致谢。
这是一个一次性处理整个文档的解决方案,而不是在循环中逐条读取 150,000 条记录。这应该会显着提高性能。
此版本还可以处理每条记录的变量数量不同的情况。
library(xml2)
doc<-read_xml('<ROOT>
<ROWSET_DUASDIA>
<ROW_DUASDIA NUM="1">
<variable1>value1</variable1>
<variable191>value2</variable191>
</ROW_DUASDIA>
<ROW_DUASDIA NUM="150236">
<variable1>value3</variable1>
<variable2>value_new</variable2>
<variable191>value4</variable191>
</ROW_DUASDIA>
</ROWSET_DUASDIA>
</ROOT>')
#find all of the nodes/records
nodes<-xml_find_all(doc, ".//ROW_DUASDIA")
#find the record NUM and the number of variables under each record
nodenum<-xml_attr(nodes, "NUM")
nodeslength<-xml_length(nodes)
#find the variable names and values
nodenames<-xml_name(xml_children(nodes))
nodevalues<-trimws(xml_text(xml_children(nodes)))
#create dataframe
df<-data.frame(NUM=rep(nodenum, times=nodeslength),
variable=nodenames, values=nodevalues, stringsAsFactors = FALSE)
#dataframe is in a long format.
#Use the function cast, or spread from the tidyr to convert wide format
# NUM variable values
# 1 1 variable1 value1
# 2 1 variable191 value2
# 3 150236 variable1 value3
# 4 150236 variable2 value_new
# 5 150236 variable191 value4
#Convert to wide format
library(tidyr)
spread(df, variable, values)
虽然不能保证在较大的 XML 文件上有更好的性能,但 ("old school") XML
包维护了一个紧凑的数据帧处理程序,xmlToDataFrame
,用于像你这样的扁平 XML 文件。其他兄弟中可用的任何缺失节点都会导致相应字段的 NA
。
library(XML)
doc <- xmlParse("/path/to/file.xml")
df <- xmlToDataFrame(doc, nodes=getNodeSet(doc, "//ROW_DUASDIA"))
您甚至可以想像地下载每日 zip,解压缩需要 XML,并将其解析为数据帧,以防每月 XML 的大数据造成内存挑战。例如,下面将 2018 年 12 月的每日数据提取到要在最后行绑定的数据框列表中。 Process 甚至添加了一个 DDate 字段。由于缺少天数或其他 URL 或 zip 问题,方法被包装在 tryCatch
中。
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)