提问人:Adrian 提问时间:9/11/2022 最后编辑:Adrian 更新时间:9/11/2022 访问量:65
R:如何根据xml文件中的嵌套结果展开data.frame
R: How to expand a data.frame based on nested results in xml file
问:
<StudyFieldsResponse>
<StudyFieldsList>
<StudyFields Rank="2">
<FieldValues Field="id">
<FieldValue>327635</FieldValue>
</FieldValues>
<FieldValues Field="Gender">
<FieldValue>Male</FieldValue>
<FieldValue>Female</FieldValue>
</FieldValues>
<FieldValues Field="code">
<FieldValue>55905</FieldValue>
</FieldValues>
</StudyFields>
<StudyFields Rank="3">
<FieldValues Field="id">
<FieldValue>555828</FieldValue>
</FieldValues>
<FieldValues Field="Gender">
<FieldValue>Male</FieldValue>
</FieldValues>
<FieldValues Field="code">
<FieldValue>55407-1139</FieldValue>
<FieldValue>77030</FieldValue>
<FieldValue>90901</FieldValue>
<FieldValue>23144</FieldValue>
</FieldValues>
</StudyFields>
</StudyFieldsList>
</StudyFieldsResponse>
我有上面的文件。我按如下方式解析它,以提取 、 和 记录。.xml
id
Gender
code
library(XML)
dat <- xmlParse(file = "example.xml")
final_dat <- xmlToDataFrame(nodes = xmlChildren(xmlRoot(dat)[["StudyFieldsList"]]))
names(final_dat) <- c("id", "Gender", "code")
> final_dat
id Gender code
1 327635 MaleFemale 55905
2 555828 Male 55407-1139770309090123144
但是,请注意,对于第一行,有 2 个 s,男性和女性。同样,对于第二个,有超过 1 秒。如何扩展我的 data.frame,以便 data.frame 包含每个唯一 、 和 组合的唯一行?Gender
code
id
Gender
code
> final_dat_expanded
id Gender code
1 327635 Male 55905
2 327635 Female 55905
3 555828 Male 55407-1139
4 555828 Male 77030
5 555828 Male 90901
6 555828 Male 23144
答:
1赞
Parfait
9/11/2022
#1
对于复杂的嵌套 XML,请考虑 XSLT,这是一种用于转换 XML 文件的特殊用途语言。在 Unix (Mac/Linux) 上,R 可以通过调用开源实用工具 xsltproc
来运行 XSLT 1.0 脚本。在 Windows 上,R 可以运行参数化的 PowerShell 脚本。system
具体而言,下面的 XSLT 以代码下的最低级别 FieldValue 为目标,然后有条件地检索 id 和 gender 的祖先节点,并使用 映射到 gender 的所有节点。for-each
XSLT(另存为 .xsl,一个特殊的 .xml 文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/StudyFieldsResponse">
<xsl:copy>
<xsl:apply-templates select="descendant::FieldValues[@Field='code']/FieldValue"/>
</xsl:copy>
</xsl:template>
<xsl:template match="FieldValues[@Field='code']/FieldValue">
<xsl:variable name="curr_code"><xsl:value-of select="text()"/></xsl:variable>
<xsl:for-each select="ancestor::StudyFields/FieldValues[@Field='Gender']/FieldValue">
<xsl:copy>
<id><xsl:value-of select="ancestor::StudyFields/FieldValues[@Field='id']/FieldValue"/></id>
<gender><xsl:copy-of select="text()"/></gender>
<code><xsl:copy-of select="$curr_code"/></code>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
R
library(XML)
# TRANSFORM INPUT TO FLATTER OUTPUT AND SAVE TO DISK
system("xsltproc style.xsl input.xml -o output.xml")
doc <- xmlParse("output.xml")
# BIND TO DATA FRAME
field_values_df <- xmlToDataFrame(doc)
field_values_df
# id gender code
# 1 327635 Male 55905
# 2 327635 Female 55905
# 3 555828 Male 55407-1139
# 4 555828 Male 77030
# 5 555828 Male 90901
# 6 555828 Male 23144
如果使用 Windows,请考虑 PowerShell 脚本
PowerShell(另存为 .ps1)PowerShell (save as .ps1)
param ($xml, $xsl, $output)
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform
$xslt.Load($xsl)
$xslt.Transform($xml, $output)
R
library(XML)
# TRANSFORM INPUT TO FLATTER OUTPUT AND SAVE TO DISK
system("PowerShell -ExecutionPolicy bypass -File transform.ps1 input.xml style.xsl output.xml")
doc <- xmlParse("output.xml")
# BIND TO DATA FRAME
field_values_df <- xmlToDataFrame(doc)
评论
0赞
Adrian
9/12/2022
谢谢。如果 .xml 文件未存储在我的计算机上本地,而是存储在名为 ?在这种情况下,我应该如何使用?我试过了,但没有用。请注意,这是input
system("xsltproc style.xsl input.xml -o output.xml")
system(paste("xsltproc style.xsl", input, "-o output.xml")
class(input)
'XMLInternalDocument', 'XMLAbstractDocument'
0赞
Parfait
9/12/2022
尝试将该对象以 .xml 格式保存到磁盘(请参阅 )。对象似乎是使用库解析的。或者检查 R 对象的原始来源。将其作为 xsltproc 的输入 .xml 传递。saveXML
XML
0赞
Adrian
9/12/2022
我有 50k+ 个这些对象,所以我想避免将它们保存到磁盘上。有没有其他选择?
0赞
Parfait
9/12/2022
在您的原始帖子中了解而不是使用 .您可以将此解决方案集成到定义的函数中,并以迭代方式调用它,例如在末尾构建数据帧列表。将每个都另存为 xsltproc 的 temp.xml。XSLT 对于中小型文件非常快。或者将此步骤添加到这些 50k 对象的采购中。example.xml
lapply
rbind
评论