min*_*nem 1 xml performance r xml-parsing xml2
所以,我有大量的XML文件,包含大量报告.我在下面创建了数据示例,以大致显示xml的大小及其结构:
x <- "<Report><Agreements><AgreementList /></Agreements><CIP><RecordList><Record><Date>2017-05-26T00:00:00</Date><Grade>2</Grade><ReasonsList><Reason><Code>R</Code><Description>local</Description></Reason></ReasonsList><Score>xxx</Score></Record><Record><Date>2017-04-30T00:00:00</Date><Grade>2</Grade><ReasonsList><Reason><Code>R</Code><Description/></Reason></ReasonsList><Score>xyx</Score></Record></RecordList></CIP><Individual><Contact><Email/></Contact><General><FirstName>MM</FirstName></General></Individual><Inquiries><InquiryList><Inquiry><DateOfInquiry>2017-03-19</DateOfInquiry><Reason>cc</Reason></Inquiry><Inquiry><DateOfInquiry>2016-10-14</DateOfInquiry><Reason>er</Reason></Inquiry></InquiryList><Summary><NumberOfInquiries>2</NumberOfInquiries></Summary></Inquiries></Report>"
x <- paste(rep(x, 1.5e+5), collapse = "")
x <- paste0("<R>", x, "</R>")
require(XML)
p <- xmlParse(x)
p <- xmlRoot(p)
p[[1]]
Run Code Online (Sandbox Code Playgroud)
我想将这些数据转换为data.frame,但XML的结构并不简单.以前使用XML我创建了循环,每个报告将其子节点转换为data.frame,但是这里(在此数据中)子节点数大于30(并未将所有这些都放在示例中),并且结构不同(列表节点在XML中甚至可以出现2级深度).
所以我几乎没有问题:
1)我确信循环报告不是处理此问题的最佳方法.我该如何处理这个问题?
2)我可以以某种方式提取一个报告的所有数据两行一行data.frame(递归可能)?
3)或者我可以为XML的每个列表对象自动创建单独的data.frames吗?
任何帮助将非常感激.
结果示例可能如下所示:
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 1 obs. of 17 variables:
$ Record.1.Date : chr "2017-05-26T00:00:00"
$ Record.1.Grade : num 2
$ Record.1.Reason.1.Code : chr "R"
$ Record.1.Reason.1.Description: chr "local"
$ Record.1.Score : chr "xxx"
$ Record.2.Date : chr "2017-05-26T00:00:00"
$ Record.2.Grade : num 2
$ Record.2.Reason.1.Code : chr "R"
$ Record.2.Reason.1.Description: chr "NA"
$ Record.2.Score : chr "xyx"
$ Email.1 : chr "NA"
$ FirstName : chr "MM"
$ Inquiry.1.DateOfInquiry : POSIXct, format: "2017-03-19"
$ Inquiry.1.Reason : chr "cc"
$ Inquiry.2.DateOfInquiry : POSIXct, format: "2016-10-14"
$ Inquiry.2.Reason : chr "er"
$ NumberOfInquiries : num 2
Run Code Online (Sandbox Code Playgroud)
,但正如我之前提到的,子列表也可以在单独的表中.
L=xmlToList(x)
str(data.frame(t(unlist(L)), stringsAsFactors=FALSE))
# 'data.frame': 1 obs. of 15 variables:
# $ CIP.RecordList.Record.Date : chr "2017-05-26T00:00:00"
# $ CIP.RecordList.Record.Grade : chr "2"
# $ CIP.RecordList.Record.ReasonsList.Reason.Code : chr "R"
# $ CIP.RecordList.Record.ReasonsList.Reason.Description: chr "local"
# $ CIP.RecordList.Record.Score : chr "xxx"
# $ CIP.RecordList.Record.Date.1 : chr "2017-04-30T00:00:00"
# $ CIP.RecordList.Record.Grade.1 : chr "2"
# $ CIP.RecordList.Record.ReasonsList.Reason.Code.1 : chr "R"
# $ CIP.RecordList.Record.Score.1 : chr "xyx"
# $ Individual.General.FirstName : chr "MM"
# $ Inquiries.InquiryList.Inquiry.DateOfInquiry : chr "2017-03-19"
# $ Inquiries.InquiryList.Inquiry.Reason : chr "cc"
# $ Inquiries.InquiryList.Inquiry.DateOfInquiry.1 : chr "2016-10-14"
# $ Inquiries.InquiryList.Inquiry.Reason.1 : chr "er"
# $ Inquiries.Summary.NumberOfInquiries : chr "2"
Run Code Online (Sandbox Code Playgroud)
如果要将具有合适表示形式的字符串转换为数字,假设这df是上面的数据框:
data.frame(t(lapply(df, function(x)
ifelse(is.na(y<-suppressWarnings(as.numeric(x))), x, y))))
Run Code Online (Sandbox Code Playgroud)
没有数字表示的字符串将不会被转换.
动机
A)在一些评论中,OP增加了对执行速度的进一步请求,这对于一次性任务(例如数据导入)通常不是问题.上面的解决方案基于递归,正如问题中明确要求的那样.当然,遍历节点会增加很多开销.
B)最近的一个答案提出了一种基于外部工具集合的复杂方法.当然可能有不同的实用程序来管理XML文件,但是恕我直言,许多XPATH工作可以在R本身中舒适有效地完成.
C)OP想知道是否可以"为XML的每个列表对象创建单独的data.frames".
D)我注意到在问号标签中,OP(似乎)需要更新的xml2包.
我直接从R使用XPATH解决了上述问题.
XPATH方法
下面我在一个单独的数据框中提取Record节点.也可以对其他(子)节点使用相同的方法.
library(xml2)
xx=read_xml(x)
xx=(xml_find_all(xx, "//Record"))
system.time(
xx <- xml_find_all(xx, ".//descendant::*[not(*)]"))
# user system elapsed
# 38.00 0.36 38.35
system.time(xx <- xml_text(xx))
# user system elapsed
# 68.39 0.05 68.53
head(data.frame(t(matrix(xx, 5))))
# X1 X2 X3 X4 X5
# 1 2017-05-26T00:00:00 2 R local xxx
# 2 2017-04-30T00:00:00 2 R xyx
# 3 2017-05-26T00:00:00 2 R local xxx
# 4 2017-04-30T00:00:00 2 R xyx
# 5 2017-05-26T00:00:00 2 R local xxx
# 6 2017-04-30T00:00:00 2 R xyx
Run Code Online (Sandbox Code Playgroud)
(您可能希望在名称数据框列中添加更多代码)
时间是指我的普通笔记本电脑.
说明
解决方案的核心在于XPATH .//descendant::*[not(*)].
.//descendant::提取当前上下文的所有后代(Record节点); 添加 [not(*)]进一步平整布局.这允许线性化树结构,使其更适合于数据科学建模.
灵活性* 在计算方面是有代价的.然而,计算负担并不依赖于R,这是一种解释型语言,但需要高效的外部C库libxml2.结果应该等于或优于其他实用程序和库.
| 归档时间: |
|
| 查看次数: |
2073 次 |
| 最近记录: |