在 R 中读取 XML 文件并在节点具有不同长度和内容时创建数据框
Read an XML file in R and create a dataframe when nodes have different lengths and content
我正在尝试从 XML 文件创建数据框对象。任务看似简单,但经过多次尝试,我无法准确提取出我想要的东西。
基本上,该文档有超过17K个同名节点,即“FICHES”。其中一个“子节点”是“BLOCS_COMPETENCES”。那个“子节点”还有一个叫做“BLOC_COMPETENCES”(区别在于 BLOC 中的 S),这个有 2 个我感兴趣的节点:CODE 和 LIBELLE。
但是,并非所有“FICHES”都具有相同的属性 and/or 子节点。此外,其中一些有 1 个“BLOC_COMPETENCES”,有 1、2、3 个或根本没有元素。
在 this document 之后(在其他帖子中)我尝试提取根节点并使用 xpath 访问元素:
# Import the file after zip extraction
RNCP_aout2020 <- xmlParse("U:/RNCP_2020/Fiches-rncp-2020-08-03/export_fiches_RNCP_2020-08-03.xml", encoding = "UTF-8")
# Finding root node
rootNode <- xmlRoot(RNCP_aout2020)
BLOCS_COMPETENCES <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/BLOCS_COMPETENCES/BLOC_COMPETENCES/CODE', xmlValue))
这适用于提取所有存在的节点和属性。例如,以下行在提取每个“FICHES”的 ID 时有效:
# Extract ID
NUMERO_FICHE <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/NUMERO_FICHE', xmlValue))
但是当我为“BLOC_COMPETENCES”尝试使用它时,长度不同,因此无法与其他列合并。
我可以提供我尝试过的所有代码,但不能提供 CSS 格式的 reprex(目前),因为我不知道如何对大文件进行子集化。
任何帮助将不胜感激!
“BLOC_COMPETENCES”的可变数量确实使问题复杂化。在下面的脚本中,解析出所有的 fiche 节点,然后循环遍历每个节点以检索所需的代码和脚本。还需要检查零长度代码和诽谤。由于 fiche 列表很长,lapply 语句将需要一段时间才能完成。
library(xml2)
library(dplyr)
#read the document
page <- read_xml("export_fiches_RNCP_2020-08-03.xml")
#read all fiches nodes
fiches <- xml_find_all(page, "//FICHE")
#parse each fiches
dfs <-lapply(fiches, function(node){
id <- node %>% xml_find_first(".//ID_FICHE") %>% xml_text()
codes <- node %>% xml_find_all(".//BLOC_COMPETENCES/CODE") %>% xml_text()
libelles <- node %>% xml_find_all(".//BLOC_COMPETENCES/LIBELLE")%>% xml_text()
#correct for codes which don't exist
if (length(codes) <1 ) {codes = NA}
if (length(libelles) <1 ) {libelles = NA}
df<- data.frame(id, codes, libelles, stringsAsFactors = FALSE)
})
#merge all of the data frames
answer <- bind_rows(dfs)
考虑 XSLT 转换您的原始 XML 以提取所需的节点。然后,使用便利处理程序 xmlToDataFrame
,避免 for
或 apply
系列循环或 if
逻辑。
作为一种独立的、可移植的行业语言,运行 XSLT 1.0 包括 processors like xsltproc、通过其他语言(Java、Python、PHP) 或使用 R 包 xslt
.
XSLT (另存为.xsl文件,下面用到的特殊.xml文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- EXTRACT ONLY FICHE NODES -->
<xsl:template match="/FICHES">
<xsl:copy>
<xsl:apply-templates select="FICHE"/>
</xsl:copy>
</xsl:template>
<!-- WITHIN EACH FICHE NODE, EXTRACT ONLY ITS DESCENDANT, BLOC_COMPETENCES NODES-->
<xsl:template match="FICHE">
<xsl:apply-templates select="descendant::BLOC_COMPETENCES"/>
</xsl:template>
<!-- WITHIN EACH BLOC_COMPETENCE, DO TWO THINGS: -->
<!-- 1. RETRIEVE UPPER LEVEL ANCESTOR NODE, ID_FICHE -->
<!-- 2. RETRIEVE CODE AND LIBELLE CHILD NODES -->
<xsl:template match="BLOC_COMPETENCES">
<xsl:copy>
<xsl:copy-of select="ancestor::FICHE/ID_FICHE"/>
<xsl:copy-of select="CODE | LIBELLE"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
方法 1 (在名为 style.xsl 的文件中使用上述 XSLT)
R + xsltproc
library(XML)
setwd("...")
system("xsltproc -o transformed.xml style.xsl export_fiches_RNCP_2020-08-03.xml")
doc <- xmlParse("transformed.xml")
doc
# <FICHES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC01</CODE>
# <LIBELLE>Du dérushage au montage</LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC02</CODE>
# <LIBELLE>Analyser un projet cinématographique</LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC03</CODE>
# <LIBELLE>Tourner sur fond d'incrustation </LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# ...
df <- xmlToDataFrame(doc)
方法 2 (在名为 style.xsl 的文件中使用以上 XSLT)
R + xslt
library(xml2)
library(xslt)
library(XML)
# PARSE XML AND XSLT
doc <- read_xml('export_fiches_RNCP_2020-08-03.xml')
style <- read_xml('style.xsl', package = "xslt")
# TRANSFORM NESTED INPUT INTO FLATTER OUTPUT
new_xml <- as.character(xslt::xml_xslt(doc, style))
# PARSE FLATTER XML
flat_xml <- XML::xmlParse(new_xml, asText=TRUE)
# BUILD DATA FRAME
df <- xmlToDataFrame(doc)
我正在尝试从 XML 文件创建数据框对象。任务看似简单,但经过多次尝试,我无法准确提取出我想要的东西。
基本上,该文档有超过17K个同名节点,即“FICHES”。其中一个“子节点”是“BLOCS_COMPETENCES”。那个“子节点”还有一个叫做“BLOC_COMPETENCES”(区别在于 BLOC 中的 S),这个有 2 个我感兴趣的节点:CODE 和 LIBELLE。
但是,并非所有“FICHES”都具有相同的属性 and/or 子节点。此外,其中一些有 1 个“BLOC_COMPETENCES”,有 1、2、3 个或根本没有元素。
在 this document 之后(在其他帖子中)我尝试提取根节点并使用 xpath 访问元素:
# Import the file after zip extraction
RNCP_aout2020 <- xmlParse("U:/RNCP_2020/Fiches-rncp-2020-08-03/export_fiches_RNCP_2020-08-03.xml", encoding = "UTF-8")
# Finding root node
rootNode <- xmlRoot(RNCP_aout2020)
BLOCS_COMPETENCES <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/BLOCS_COMPETENCES/BLOC_COMPETENCES/CODE', xmlValue))
这适用于提取所有存在的节点和属性。例如,以下行在提取每个“FICHES”的 ID 时有效:
# Extract ID
NUMERO_FICHE <- as.data.frame(xpathSApply(rootNode, '/FICHES/FICHE/NUMERO_FICHE', xmlValue))
但是当我为“BLOC_COMPETENCES”尝试使用它时,长度不同,因此无法与其他列合并。
我可以提供我尝试过的所有代码,但不能提供 CSS 格式的 reprex(目前),因为我不知道如何对大文件进行子集化。
任何帮助将不胜感激!
“BLOC_COMPETENCES”的可变数量确实使问题复杂化。在下面的脚本中,解析出所有的 fiche 节点,然后循环遍历每个节点以检索所需的代码和脚本。还需要检查零长度代码和诽谤。由于 fiche 列表很长,lapply 语句将需要一段时间才能完成。
library(xml2)
library(dplyr)
#read the document
page <- read_xml("export_fiches_RNCP_2020-08-03.xml")
#read all fiches nodes
fiches <- xml_find_all(page, "//FICHE")
#parse each fiches
dfs <-lapply(fiches, function(node){
id <- node %>% xml_find_first(".//ID_FICHE") %>% xml_text()
codes <- node %>% xml_find_all(".//BLOC_COMPETENCES/CODE") %>% xml_text()
libelles <- node %>% xml_find_all(".//BLOC_COMPETENCES/LIBELLE")%>% xml_text()
#correct for codes which don't exist
if (length(codes) <1 ) {codes = NA}
if (length(libelles) <1 ) {libelles = NA}
df<- data.frame(id, codes, libelles, stringsAsFactors = FALSE)
})
#merge all of the data frames
answer <- bind_rows(dfs)
考虑 XSLT 转换您的原始 XML 以提取所需的节点。然后,使用便利处理程序 xmlToDataFrame
,避免 for
或 apply
系列循环或 if
逻辑。
作为一种独立的、可移植的行业语言,运行 XSLT 1.0 包括 processors like xsltproc、通过其他语言(Java、Python、PHP) 或使用 R 包 xslt
.
XSLT (另存为.xsl文件,下面用到的特殊.xml文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- EXTRACT ONLY FICHE NODES -->
<xsl:template match="/FICHES">
<xsl:copy>
<xsl:apply-templates select="FICHE"/>
</xsl:copy>
</xsl:template>
<!-- WITHIN EACH FICHE NODE, EXTRACT ONLY ITS DESCENDANT, BLOC_COMPETENCES NODES-->
<xsl:template match="FICHE">
<xsl:apply-templates select="descendant::BLOC_COMPETENCES"/>
</xsl:template>
<!-- WITHIN EACH BLOC_COMPETENCE, DO TWO THINGS: -->
<!-- 1. RETRIEVE UPPER LEVEL ANCESTOR NODE, ID_FICHE -->
<!-- 2. RETRIEVE CODE AND LIBELLE CHILD NODES -->
<xsl:template match="BLOC_COMPETENCES">
<xsl:copy>
<xsl:copy-of select="ancestor::FICHE/ID_FICHE"/>
<xsl:copy-of select="CODE | LIBELLE"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
方法 1 (在名为 style.xsl 的文件中使用上述 XSLT)
R + xsltproc
library(XML)
setwd("...")
system("xsltproc -o transformed.xml style.xsl export_fiches_RNCP_2020-08-03.xml")
doc <- xmlParse("transformed.xml")
doc
# <FICHES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC01</CODE>
# <LIBELLE>Du dérushage au montage</LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC02</CODE>
# <LIBELLE>Analyser un projet cinématographique</LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# <ID_FICHE>3614</ID_FICHE>
# <CODE>RNCP13004BC03</CODE>
# <LIBELLE>Tourner sur fond d'incrustation </LIBELLE>
# </BLOC_COMPETENCES>
# <BLOC_COMPETENCES>
# ...
df <- xmlToDataFrame(doc)
方法 2 (在名为 style.xsl 的文件中使用以上 XSLT)
R + xslt
library(xml2)
library(xslt)
library(XML)
# PARSE XML AND XSLT
doc <- read_xml('export_fiches_RNCP_2020-08-03.xml')
style <- read_xml('style.xsl', package = "xslt")
# TRANSFORM NESTED INPUT INTO FLATTER OUTPUT
new_xml <- as.character(xslt::xml_xslt(doc, style))
# PARSE FLATTER XML
flat_xml <- XML::xmlParse(new_xml, asText=TRUE)
# BUILD DATA FRAME
df <- xmlToDataFrame(doc)