【问题标题】:XML Data in R different FilestructureR不同文件结构中的XML数据
【发布时间】:2019-01-20 08:19:05
【问题描述】:

我需要解析 2000 个 XML 文件。我管理设置,我可以自动从文件中获取我的数据。由于我是一个完整的初学者,它可能看起来很乱,这里是一个例子:

filenames <- list.files("C:/...", recursive=TRUE, full.names=TRUE, pattern=".xml")

name <- unlist(lapply(filenames, function(f) {  
  xml <- xmlParse(f)  
  xpathSApply(xml, "//...", xmlValue)
}))
data <- data.frame(name)

这适用于我需要的大部分数据,但我目前的问题是某些文件丢失了某些数据,因此由于行数不同,我无法包含它们。 文件的示例如下: 文件 1:

<Kontaktdaten>
   <Name> Name </Name>
   <ID>12345678</ID>
   <Kontakt_Zugang>
       <Strasse>ABC-Strasse</Strasse>
       <Hausnummer>1</Hausnummer>
       <Postleitzahl>12345</Postleitzahl>
       <Ort>ABC</Ort>
   </Kontakt_Zugang> 
</Kontaktdaten>

文件 2(例如缺少“Hausnummer”):

<Kontaktdaten>
   <Name> Name2 </Name>
   <ID>8765321</ID>
   <Kontakt_Zugang>
       <Strasse>CBA-Strasse</Strasse>
       <Postleitzahl>54321</Postleitzahl>
       <Ort>CBA</Ort>
   </Kontakt_Zugang> 
</Kontaktdaten>

有什么方法可以将它们组合在一个 data.frame 中,或者仅使用“Hausnummer”和 ID 创建第二个 data.frame?

编辑:这只是说明我的问题的一个例子。原始文件长达 500 个节点,其中一些节点加倍。

【问题讨论】:

标签: r xml


【解决方案1】:

这里有一个解决方案,解析每个 xml 文件,创建单个文件中的子节点列表,然后组合所有列表,然后转换为所需的格式。

请参阅代码 cmets 了解分步说明。

library(xml2)

#list of files to process
fnames<-c("xml1.xml", "xml2.xml")

dfs<-lapply(fnames, function(fname) {
  doc<-read_xml(fname)


  #find Name and ID
  Name<-trimws(xml_text(xml_find_all(doc, ".//Name")))
  ID<-trimws(xml_text(xml_find_all(doc, ".//ID")))

  #find all of the nodes/records under the Kontakt_Zugang node
  nodes<-xml_children(xml_find_all(doc, ".//Kontakt_Zugang"))

  #find the sub nodes names and values
  nodenames<-xml_name(nodes)
  nodevalues<-trimws(xml_text(nodes))

  #make data frame of all the values
  df<-data.frame(file=fname, Name=Name, ID=ID, node.names=nodenames, 
             values=nodevalues, stringsAsFactors = FALSE)

})

#Make one long df
longdf<-do.call(rbind, dfs)

#make into a wide format
library(tidyr)
finalanswer<-spread(longdf, key=node.names, value=values)

这是最终结果:

#     file  Name       ID Hausnummer Ort Postleitzahl     Strasse
# xml1.xml  Name 12345678          1 ABC        12345 ABC-Strasse
# xml2.xml Name2  8765321       <NA> CBA        54321 CBA-Strasse

【讨论】:

  • 尝试这个时,我得到行数不同的错误,但我找不到我疯狂调整这个的错误
  • 最好从这里复制粘贴到您的编辑器中。我不确定是什么可能导致该错误。 lapply 功能完成了吗?如果没有,请尝试在 lapply 函数的第一行下添加print(fname),以识别导致问题的文件。
  • 我只更改了 .// 因为我正在使用的文件有多达 500 个节点,有时会加倍,所以我插入了确切的“路径”
  • 听起来您的实际文件比您发布的示例更复杂。如果您的文件有多个 ID 和名称,那么上面的代码很可能不起作用。我对 df
  • 对不起,我的回复晚了,我测试了你的方法,它适用于一些数据。这是我目前的问题:imgur.com/a/D3a0v4E
【解决方案2】:

考虑专用语言XSLT,它旨在转换 XML 文件以用于最终用途解决方案,例如展平嵌套节点 Kontakt_Zugang 以导入 R 并迁移到数据框。

XSLT (保存为 .xsl 文件,以便像任何 .xml 文件一样解析到 R 中)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Kontakt_Zugang">
    <xsl:apply-templates select="@*|node()"/>
</xsl:template>

</xsl:stylesheet>

Online Demo

R

library(xml2)
library(xslt)

# RETRIEVE XML FILE NAMES
filenames <- list.files("C:/...", recursive=TRUE, full.names=TRUE, pattern=".xml")
all_cols <- c("Name", "ID", "Strasse", "Hausnummer", "Postleitzahl", "Ort")

# PARSE XSLT
style <- read_xml("/path/to/xslt_script.xsl", package = "xslt")

df_list <- lapply(filenames, function(f) {  
  # PARSE XML
  xml <- xml2::read_xml(f)    
  # TRANSFORM INPUT INTO OUTPUT
  new_xml <- xslt::xml_xslt(xml, style)

  # BUILD DATA FRAME
  vals <- xml_children(xml_find_all(new_xml, "//Kontaktdaten"))
  df <- setNames(data.frame(t(trimws(xml_text(vals)))), xml_name(vals))

  # FILL IN MISSING COLUMNS
  df[all_cols[!(all_cols %in% colnames(df))]] <- NA

  return(df[all_cols])
})

final_df <- do.call(rbind, df_list)
final_df
#    Name       ID     Strasse Hausnummer Postleitzahl Ort
# 1  Name 12345678 ABC-Strasse          1        12345 ABC
# 2 Name2  8765321 CBA-Strasse       <NA>        54321 CBA

顺便说一句,由于 XSLT 是一种特殊用途的语言,它不仅限于 R,而是任何语言,例如支持它的 Java、PHP、Python 甚至是 R 可以的 external processors进行命令行调用以运行。例如,下面使用 Unix 的(即 Mac 和 Linux)xsltproc

# COMMAND LINE CALL TO UNIX'S XSLTPROC (ALTERNATIVE TO xslt PACKAGE)
system("xsltproc -o /path/to/input.xml /path/to/xslt_script.xsl /path/to/output.xml")
doc <- xmlParse("/path/to/output.xml")

【讨论】:

    【解决方案3】:

    我创建了一个 XML 文件,我认为它与您可能使用的文件相似。我解析这个文件并使用函数get_xml_dat 提取数据。

    file <- list.files('~/R/', pattern = '.xml', full.names = TRUE)
    
    
    get_xml_dat <- function(xml_file, main_node, values) {
      require(tidyverse)
      require(rvest)
      require(xml2)
    
    
      nodeset <- xml_file %>% read_html() %>% html_nodes(tolower(main_node)) # pull out node with data from XML
    
      node_values <- paste0("<", tolower(values), ">.*</", tolower(values), ">") # create pattern to extract values
    
      dat <-  str_extract_all(as.character(nodeset), paste0(node_values, collapse = "|")) # extract values from nodeset
    
      lapply(dat, function(foo) {
        foo %>% gsub("</.*>|<", "", .) %>% # clean up data values
          tibble(V1 = .) %>% # transform string of values to long data_frames
          separate(., # separate node name from node value
                   col = V1,
                   into = c("cols", "vals"),
                   sep = ">"
          ) %>%
          spread(cols, vals) # long data_frame to wide data_frame
      }) %>%
        bind_rows() %>% # bind list of data_frames
        select(tolower(values)) # orders columns
    }
    

    输出

    > get_xml_dat(
    +   xml_file = file, # XML file you want to get data from
    +   main_node = 'Kontaktdaten', # node where the data is located
    +   values = c('Name', 'ID', 'Hausnummer', 'Postleitzahl', 'Ort', 'Strasse') # values you want to get from the main_node
    + )
    # A tibble: 3 x 6
      name         id     hausnummer postleitzahl ort   strasse    
      <chr>        <chr>  <chr>      <chr>        <chr> <chr>      
    1 " Name_ABC " 912283 1          12345        ABC   ABC-Strasse
    2 " Name_DEF " 123456 NA         12345        DEF   DEF-Strasse
    3 " Name_XYZ " 123456 3          12345        XYZ   XYZ-Strasse
    

    数据

    加载到 R 中的 XML 文件。

    <Qualitaetsbericht>
        <Krankenhaus>
            <Kontaktdaten>
                <Name> Name_ABC </Name>
                <ID>912283</ID>
                <Kontakt_Zugang>
                    <Strasse>ABC-Strasse</Strasse>
                    <Hausnummer>1</Hausnummer>
                    <Postleitzahl>12345</Postleitzahl>
                    <Ort>ABC</Ort>
                </Kontakt_Zugang>
            </Kontaktdaten>
        </Krankenhaus>
        <Klinik> 
            <Kontaktdaten>
                <Name> Name_DEF </Name>
                <ID>123456</ID>
                <Kontakt_Zugang>
                    <Strasse>DEF-Strasse</Strasse>
                    <Postleitzahl>12345</Postleitzahl>
                    <Ort>DEF</Ort>
                </Kontakt_Zugang>
            </Kontaktdaten>
        </Klinik>
        <Universitaet>
            <Kontaktdaten>
                <Name> Name_XYZ </Name>
                <ID>123456</ID>
                <Kontakt_Zugang>
                    <Strasse>XYZ-Strasse</Strasse>
                    <Hausnummer>3</Hausnummer>
                    <Postleitzahl>12345</Postleitzahl>
                    <Ort>XYZ</Ort>
                </Kontakt_Zugang>
            </Kontaktdaten>
        </Universitaet>
        <Other_DATA>
            <Some_Var0>
                <X>100</X>
                <Y>100</Y>
                <Z>100</Z>
            </Some_Var0>
        </Other_DATA>
    </Qualitaetsbericht>
    

    【讨论】:

    • 没有理由,我实际上使用更直接的方法修复了我的答案。
    • 您能解释一下要填写什么吗?我正在努力理解它
    • @Tamy 我在代码中添加了一些 cmets。如果您还有其他问题,请告诉我。
    • 我正在使用的文件有 50-500 个节点,有时会加倍,所以我想我不能使用这个?抱歉,这只是文件的一个示例,因为我虽然没有它我无法解释我的问题,而且很遗憾我不能不发布我的数据
    • 你现在应该试试上面的代码。在不查看您的实际数据的情况下,我认为它与您想要的一样接近。
    猜你喜欢
    • 1970-01-01
    • 2020-09-16
    • 2017-06-28
    • 1970-01-01
    • 2021-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多