将XML转换为带有缺失节点的数据框架

3
此问题的版本已经被问过,如这里这里。但是,我仍然无法让它工作。我正在尝试将XML文档解析为数据框架。问题在于某些观测值中没有出现某些变量,因此我会因为行数不同而出现错误。我的数据看起来像这样:
library("xml2")
library("dplyr")

example <- read_xml(
'
<particDesc>
<person role="participant" sameAs="#P484" xml:id="EDcon250_S1">
<age value="3">35-49</age>
<sex value="1">male</sex>
<occupation>waiter</occupation>
<langKnowledge>
<langKnown level="L1" tag="ita"/>
</langKnowledge>
</person>
<person role="participant" sameAs="#P485" xml:id="EDcon250_S7">
<age value="0">unknown</age>
<sex value="2">female</sex>
<occupation>waitress</occupation>
<langKnowledge>
<langKnown level="L1" tag="ger-AT"/>
</langKnowledge>
</person>
<person role="participant" sameAs="#P465" xml:id="EDcon250_S2">
<age value="2">25-34</age>
<sex value="2">female</sex>
<langKnowledge>
<langKnown level="L1" tag="ger-AT"/>
<langKnown level="L1" tag="eng-US"/>
</langKnowledge>
</person>
</particDesc>
')

我正在使用Wickham的xml2包来读取xml。我更喜欢使用这个包,但如果这是解决问题的最佳(或唯一)方法,我也可以使用XML。无论如何,我的代码如下:

participants <- xml_find_all(example, './/person[@role = "participant"]')

extract_participants <- function(div){
id <- xml_attr(div, "id")
same_as <- xml_attr(div, "sameAs")
role <- xml_attr(div, "role")
age <- xml_find_all(div, ".//age") %>% xml_text()
sex <- xml_find_all(div, ".//sex") %>% xml_text()
occupation <- xml_find_all(div, ".//occupation") %>% xml_text()

data_frame(id, same_as,role, age, sex, occupation)
}


parts_ls <- lapply(participants, extract_participants)

participants_df <- do.call(rbind, parts_ls)

这个问题特别关注occupation变量(第三个人没有),但在我的实际数据中,也可能是其他变量之一。正如我所说,我看到这个问题之前已经被问过了,但我无法让任何建议起作用(可能是因为我没有完全理解解决方案)。最终,我希望在特定节点缺失时返回NAs(因此第三个人的occupation变量将为NA)。
编辑: 这是备选的XML版本。
library("XML")
library("magrittr")

example2 <- xmlParse(
'
 <particDesc>
 <person role="participant" sameAs="#P484" xml:id="EDcon250_S1">
 <age value="3">35-49</age>
 <sex value="1">male</sex>
 <occupation>waiter</occupation>
 <langKnowledge>
 <langKnown level="L1" tag="ita"/>
 </langKnowledge>
 </person>
 <person role="participant" sameAs="#P485" xml:id="EDcon250_S7">
 <age value="0">unknown</age>
 <sex value="2">female</sex>
 <occupation>waitress</occupation>
 <langKnowledge>
 <langKnown level="L1" tag="ger-AT"/>
 </langKnowledge>
 </person>
 <person role="participant" sameAs="#P465" xml:id="EDcon250_S2">
 <age value="2">25-34</age>
 <sex value="2">female</sex>
 <langKnowledge>
 <langKnown level="L1" tag="ger-AT"/>
 <langKnown level="L1" tag="eng-US"/>
 </langKnowledge>
 </person>
 </particDesc>
')

example_root <- xmlRoot(example2) 

process <- function(x){
id <- xmlGetAttr(x, "id")
role <- xmlGetAttr(x, "role")
age <- getNodeSet(x, ".//age") %>% xmlSApply(xmlValue)
sex <- getNodeSet(x, ".//sex") %>% xmlSApply(xmlValue)
#occupation <- getNodeSet(x, ".//occupation") %>% xmlSApply(xmlValue)
data.frame(id = id,
           role = role,
           #occupation = occupation,
           age = age,
           sex = sex,
           stringsAsFactors = FALSE)
}


 ls <- xpathApply(example_root, "//person", process)
 df <- do.call(rbind, ls)

只需取消注释occupation即可查看问题。

1个回答

2

我找到了一种方法使其工作,但不确定它是否是最佳解决方案(我认为这种方法比较啰嗦)。目前这是我拥有的。欢迎提出改进建议。

library("XML")
library("magrittr")

example2 <- xmlParse(
'
<particDesc>
<person role="participant" sameAs="#P484" xml:id="EDcon250_S1">
<age value="3">35-49</age>
<sex value="1">male</sex>
<occupation>waiter</occupation>
<langKnowledge>
<langKnown level="L1" tag="ita"/>
</langKnowledge>
</person>
<person role="participant" sameAs="#P485" xml:id="EDcon250_S7">
<age value="0">unknown</age>
<sex value="2">female</sex>
<occupation>waitress</occupation>
<langKnowledge>
<langKnown level="L1" tag="ger-AT"/>
</langKnowledge>
</person>
<person role="participant" sameAs="#P465" xml:id="EDcon250_S2">
<age value="2">25-34</age>
<sex value="2">female</sex>
<langKnowledge>
<langKnown level="L1" tag="ger-AT"/>
<langKnown level="L1" tag="eng-US"/>
</langKnowledge>
</person>
</particDesc>
')

example_root <- xmlRoot(example2) 
person <- getNodeSet(example_root, "//person")

id <- lapply(person, xmlGetAttr, "id") %>% unlist()
role <- lapply(person, xmlGetAttr, "role") %>% unlist()
age <- lapply(person, xpathSApply, ".//age", xmlValue) %>% unlist()
sex <- lapply(person, xpathSApply, ".//sex", xmlValue) %>% unlist()
occupation <- lapply(person, xpathSApply, ".//occupation", xmlValue)
occupation[sapply(occupation, is.list)] <- NA 
occupation <- unlist(occupation)

df <- data.frame(
   id = id,
   role = role,
   age = age,
   sex = sex,
   occupation = occupation)

编辑说明: 为了完整起见,这里提供了相应的xml2版本(节选)

example <- read_xml(
'
 <particDesc>
 <person role="participant" sameAs="#P484" xml:id="EDcon250_S1">
 <age value="3">35-49</age>
 <sex value="1">male</sex>
 <occupation>waiter</occupation>
 <langKnowledge>
 <langKnown level="L1" tag="ita"/>
 </langKnowledge>
 </person>
 <person role="participant" sameAs="#P485" xml:id="EDcon250_S7">
 <age value="0">unknown</age>
 <sex value="2">female</sex>
 <occupation>waitress</occupation>
 <langKnowledge>
 <langKnown level="L1" tag="ger-AT"/>
 </langKnowledge>
 </person>
 <person role="participant" sameAs="#P465" xml:id="EDcon250_S2">
 <age value="2">25-34</age>
 <sex value="2">female</sex>
 <langKnowledge>
 <langKnown level="L1" tag="ger-AT"/>
 <langKnown level="L1" tag="eng-US"/>
 </langKnowledge>
 </person>
 </particDesc>
')

participants <- xml_find_all(example, './/person[@role = "participant"]')

id <- lapply(participants, xml_attr, "id")
occupation <- lapply(participants, xml_find_all, ".//occupation")
occupation <- lapply(occupation, xml_text)
occupation[!sapply(occupation, function(y) length(y == 0))] <- NA

occupation <- unlist(occupation)
id <- unlist(id)

data_frame(
  id = id, 
  occupation = occupation)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接