我正在尝试使用R将具有复杂节点结构的XML转换为数据帧。这是XML文件的简短示例:
<products>
<product>
<id>1</id>
<data>
<data_value>
<number>12345</number>
<city>London</city>
</data_value>
</data>
<attributes>
<p_attribute>
<name>Name_1</name>
<value>Value_1</value>
</p_attribute>
<p_attribute>
<name>Name_2</name>
<value>Value_2</value>
</p_attribute>
</attributes>
</product>
<product>
<id>2</id>
<data>
<data_value>
<number>98765</number>
<city>London</city>
</data_value>
</data>
<attributes>
<p_attribute>
<name>Name_9</name>
<value>Value_9</value>
</p_attribute>
<p_attribute>
<name>Name_8</name>
<value>Value_8</value>
</p_attribute>
</attributes>
</product>
</products>
当我尝试将此文件转换为数据框时,我使用以下代码(XML库)
library(XML)
doc=xmlParse("file.xml")
xmldf=xmlToDataFrame(nodes = getNodeSet(doc, "//product"))
然后,最终结果是可以在下面看到的数据框:
id data attributes
1 1 12345London Name_1Value_1Name_2Value_2
2 2 98765London Name_9Value_9Name_8Value_8
如何获得不同的数据框架,从而消除XML文件的复杂结构,以获得类似的结果?
id number city name.1 value.1 name.2 value.2
1 1 12345 London Name_1 Value_1 Name_2 Vlaue_2
2 2 98765 London Name_9 Value_9 Name_8 Value_8
答案 0 :(得分:5)
我不太熟悉XML
软件包,但更多地使用了xml2
软件包。它适合tidyverse软件包,因此可以与我将在此处使用的基于purrr
的方法配合使用。对于每个<product>
节点,我正在调用一个函数,该函数提取其所有子ID,数字,城市,名称和值节点,并提取其文本。我按产品进行操作是因为我想为每个对象获取一个小的数据框,以确保所有ID与名称和值节点保持在一起,从而允许它们具有不同的长度。最后,map_dfr
按行绑定数据帧列表。
library(tidyr)
library(purrr)
library(xml2)
products <- read_xml("text.xml") %>%
xml_find_all("//product")
prod_df <- map_dfr(products, function(p_node) {
list(".//id", ".//number", ".//city", ".//name", ".//value") %>%
set_names(stringr::str_extract, "\\w+") %>%
map(~xml_find_all(p_node, .)) %>%
map(xml_text) %>%
as_tibble()
})
prod_df
#> # A tibble: 4 x 5
#> id number city name value
#> <chr> <chr> <chr> <chr> <chr>
#> 1 1 12345 London Name_1 Value_1
#> 2 1 12345 London Name_2 Value_2
#> 3 2 98765 London Name_9 Value_9
#> 4 2 98765 London Name_8 Value_8
我个人建议使用这种格式,尤其是因为您可能为不同的产品使用不同数量的名称/值对。但是,如果您确实需要宽格式,则可以为每个产品的子级标记一个观察号,然后重塑形状。
prod_df %>%
dplyr::group_by(id, number, city) %>%
dplyr::mutate(obs = dplyr::row_number()) %>%
pivot_wider(names_from = obs, values_from = c(name, value), names_sep = ".")
#> # A tibble: 2 x 7
#> # Groups: id, number, city [2]
#> id number city name.1 name.2 value.1 value.2
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 1 12345 London Name_1 Name_2 Value_1 Value_2
#> 2 2 98765 London Name_9 Name_8 Value_9 Value_8
答案 1 :(得分:0)
您好,JCMendes您可以使用tidyverse解决该问题
不幸的是,这不是很可扩展,我也建议您使用长数据
x <- xmldf %>%
mutate(number = data %>% str_extract("[:digit:]{1,}"),
city = data %>% str_extract("[:alpha:]{1,}"),
characterss = str_split(attributes,"(?=[[:upper:]])"),
name = characterss %>% map(keep,str_detect,"Name"),
value= characterss %>% map(keep,str_detect,"Value")) %>%
select(-attributes,-data,-characterss) %>%
unnest(name) %>%
unnest(value) %>%
group_by(id, number, city) %>%
dplyr::mutate(obs = dplyr::row_number()) %>%
pivot_wider(names_from = obs, values_from = c(name, value), names_sep = ".")