R:如何根据xml文件中的嵌套结果展开data.frame

R: How to expand a data.frame based on nested results in xml file

提问人:Adrian 提问时间:9/11/2022 最后编辑:Adrian 更新时间:9/11/2022 访问量:65

问:

<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>

我有上面的文件。我按如下方式解析它,以提取 、 和 记录。.xmlidGendercode

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 包含每个唯一 、 和 组合的唯一行?GendercodeidGendercode

> 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
R 数据帧 XML 解析

评论

0赞 Parfait 9/11/2022
奇怪的XML ...如果第二性别集包含男性和女性怎么办?你会为两性重复四个代码吗?
0赞 Adrian 9/11/2022
@Parfait 是的,没错

答:

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>

Online Demo

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 文件未存储在我的计算机上本地,而是存储在名为 ?在这种情况下,我应该如何使用?我试过了,但没有用。请注意,这是inputsystem("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 传递。saveXMLXML
0赞 Adrian 9/12/2022
我有 50k+ 个这些对象,所以我想避免将它们保存到磁盘上。有没有其他选择?
0赞 Parfait 9/12/2022
在您的原始帖子中了解而不是使用 .您可以将此解决方案集成到定义的函数中,并以迭代方式调用它,例如在末尾构建数据帧列表。将每个都另存为 xsltproc 的 temp.xml。XSLT 对于中小型文件非常快。或者将此步骤添加到这些 50k 对象的采购中。example.xmllapplyrbind