我正在尝试将美国联邦公报文件中的 xml 文件转换为数据框,其中每一行对应于一个特定操作(例如,通知、规则、建议规则),每列包含与该操作相关的属性(例如,机构类型、主题等)。我尝试过以下方法:
> setwd("C:/Users/mwilliamson/Desktop/FedReg/2000/01/")
> url = "FR-2000-01-18.xml"
> doc <- xmlInternalTreeParse("FR-2000-01-18.xml")
> doc_list <- xmlToList(doc)
> library(plyr)
> j <- ldply(doc_list, data.frame)
但是,它返回一个错误:
Error in data.frame(SECTNO = "§ 831.502", SUBJECT = "Automatic separation;
exemption.", :
arguments imply differing number of rows: 1, 0
似乎空白值的数量和变量长度的差异在 R 处理 XML 时产生了问题(我在这里可能是错的,对 xml 包没有太多经验)。我认为可以使用架构 (.xsd) 文件来避免这种情况,但不清楚如何将架构与 xmlToList 一起使用。本质上,我正在寻找“最佳”方法将 xml 处理到我描述的数据框中,并用 NA 填充任何空白单元格。我已将架构和示例文件上传到:
https://www.dropbox.com/sh/pluje12t185w1v2/ys1xHzilQO
您能提供的任何帮助都会很棒!!
更新:我也尝试过:
xmlToDataFrame(doc, colClasses = character, homogeneous = NA)
但收到以下信息:
Error: duplicate subscripts for columns
再次非常感谢您提供的任何帮助。
更新:看来/AGENCY 节点是数据开始真正符合我尝试创建的格式的地方;但是,我似乎无法提取所有其余数据(即,我可以获得包含标识该机构的 115 条记录的单列,但无法获取与这 115 条记录相关的其余信息)。我尝试过以下方法:
out <- getNodeSet(doc, "//*", fun=xmlToList)
df <- data.frame(do.call(rbind, out))
head(df)
但这似乎会导致 R 崩溃。我希望我的持续更新能够激励有人伸出援手。再次感谢您提供的任何帮助。
最佳答案
这个 XML 很困惑,我的猜测是您需要单独解析每个操作。
table(xpathSApply(doc, "//FEDREG/child::node()", xmlName))
DATE NEWPART NO NOTICES PRESDOCS PRORULES RULES UNITNAME VOL
12 6 12 1 3 1 1 12 12
table(xpathSApply(doc, "//NOTICES/child::node()", xmlName))
NOTICE
92
使用 getNodeSet 获取通知
z <- getNodeSet(doc, "//NOTICE")
z[[1]]
# check node names
sapply(z, xmlSApply, xmlName)
x <- xmlToDataFrame(z)
dim(x)
[1] 92 4
因此,这会混合来自 PREAMB 和 SUPLINFO 的大量详细信息,因此您可能需要单独解析这些节点。
如果你只拿PREAMB,那也是一团糟......
z2 <- getNodeSet(doc, "//NOTICE/PREAMB")
# check node names and notice different formats
sapply(z2, xmlSApply, xmlName)
## and count
sort( table(unlist(sapply(z2, xmlSApply, xmlName))) )
AUTH BILCOD NOTE GPOTABLE STARS PRTPAGE DATE FTNT GPH EFFDATE ADD DATES FP SIG DEPDOC EXTRACT SUM
2 3 3 5 5 8 15 15 15 16 19 24 32 37 45 47 52
AGY FURINF SUBAGY ACT AGENCY SUBJECT HD P
54 54 55 57 92 92 103 663
我在这里看到三种不同的格式,因此 xmlToDataFrame 将适用于某些节点,但不是所有节点
x <- xmlToDataFrame(z2[1:4])
将这 10 列与代码中 ldply 的结果进行比较
doc_list <- getNodeSet(doc, "//NOTICE/PREAMB", fun=xmlToList)
## this returns 31 columns since it grabs every child node...
j <- ldply(doc_list[1:4], data.frame)
names(j)
我认为有时最好循环遍历 getNodeSet 结果并解析您需要的内容,确保在节点不存在时添加 NA(此处使用 xp 函数)。请参阅 ?getNodeSet 创建子文档并使用 free 修复内存泄漏,但对于最常见的格式可能是这样的。您可以添加检查并获取带有大量 HD、EXTRACT 和 P 标签的通知的附加列。
xp <- function (doc, tag){
n <- xpathSApply(doc, tag, xmlValue)
if (length(n) > 0)
# paste multiple values? BILCOD and probably others..
paste0(n, collapse="; ")
else NA
}
z <- getNodeSet(doc, "//NOTICE")
n <-length(z)
notices <-vector("list",n)
for(i in 1:n)
{
z2<-xmlDoc(z[[i]])
notices[[i]] <- data.frame(
AGENCY = xp(z2, "//AGENCY"),
SUBAGY = xp(z2, "//SUBAGY"),
SUBJECT = xp(z2, "//PREAMB/SUBJECT"), ## SUBJECT node in SECTION too, so it helps to be as specific as possible
ACT= xp(z2, "//ACT"),
SUM = xp(z2, "//SUM"),
DATES = xp(z2, "//DATES"),
ADD = xp(z2, "//ADD"),
FURINF = xp(z2, "//FURINF"),
SIG = xp(z2, "//PREAMB/SIG"), ## SIG in SUPLINF too
SUPLINF = xp(z2, "//SUPLINF"),
FRDOC = xp(z2, "//FRDOC"),
BILCOD = xp(z2, "//BILCOD"),
DEPDOC = xp(z2, "//DEPDOC"),
PRTPAGE = xp(z2, "//PRTPAGE"),
stringsAsFactors=FALSE)
free(z2)
}
x <- do.call("rbind", notices)
head(x)
table(is.na(x$ACT) )
FALSE TRUE
57 35
您仍然拥有像 SUPLINF 这样的列,其中包含大量结构化数据 - 如果需要,您可以将其分解...
table(xpathSApply(doc, "//NOTICE/SUPLINF/child::node()", xmlName))
AMDPAR APPENDIX AUTH BILCOD DATE EXTRACT FP FTNT GPH GPOTABLE HD LSTSUB P PRTPAGE SIG text
1 1 10 1 4 10 23 31 10 12 186 1 783 4 52 1
xpathSApply(doc, "//NOTICE/SUPLINF/GPH", xmlValue)
[1] "EN18JA00.000" "EN18JA00.001" "EN18JA00.002" "EN18JA00.003" "EN18JA00.004" "EN18JA00.005" "EN18JA00.006" "EN18JA00.007" "EN18JA00.008" "EN18JA00.009"
## since SIG is in PREAMB and SUPLINF, you may want to parse that separately
xpathSApply(doc, "//NOTICE/SUPLINF/SIG", xmlValue)
关于xml - 将(可能格式错误的)xml 转换为 R 中的数据帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20527926/