[英]R: Combine or compare several lists with different lengths
我正在處理幾個要比較的XML文件,每個文件包含大約200-300個不同的“ xml:ids”。 假設有三個文件包含以下xml:ids:
file1.xml
<?xml version="1.0" encoding="UTF-8"?>
<div>
<p xml:id= "F23_1b">1</p>
<p xml:id= "F54_34a">3</p>
</div>
file2.xml
<?xml version="1.0" encoding="UTF-8"?>
<div>
<p xml:id= "F23_1b">7</p>
<p xml:id= "F54_34a">8</p>
<p xml:id= "F54_63d">12</p>
</div>
file3.xml
<?xml version="1.0" encoding="UTF-8"?>
<div>
<p xml:id= "F143_32a">5</p>
<p xml:id= "F175_23c">6</p>
<p xml:id= "F95_1a">14</p>
<p xml:id= "F89_9d">15</p>
</div>
現在,我的目標是比較這些與a)當前xml:ids和b)它們各自值有關的不同文件(請參見下表)。 我首先使用R的XML包/ XPath為每個文件創建一個列表:
file1 <- xmlTreeParse("file1.xml", useInternalNodes = T)
a <- xpathSApply(file1, "//*[@xml:id]", xmlGetAttr, "xml:id")
file2 <- xmlTreeParse("file1.xml", useInternalNodes = T)
a <- xpathSApply(file1, "//*[@xml:id]", xmlGetAttr, "xml:id")
file3 <- xmlTreeParse("file1.xml", useInternalNodes = T)
a <- xpathSApply(file1, "//*[@xml:id]", xmlGetAttr, "xml:id")
現在,在第二步中,我想將結果合並到一個數據幀中,但是-這是我的主要問題-列表的長度不同。 剛開始我以為我可能會尋找最長的列表,並為其中存在的xml:id添加“空值”,而在較短的列表中卻沒有,但是我很快意識到這種方法會忽略僅存在於列表中的ID。較短的清單。
最后,我希望有一個可以輕松導出(到.csv)的數據框,看起來類似於此表:
|------------||-----------||-----------||-----------|
| xml:ids || file1 || file2 || file3 |
|------------||-----------||-----------||-----------|
|------------||-----------||-----------||-----------|
| F23_1b || 1 || 7 || NULL |
|------------||-----------||-----------||-----------|
| F54_34a || 3 || 8 || NULL |
|------------||-----------||-----------||-----------|
| F54_63d || NULL || 12 || NULL |
|------------||-----------||-----------||-----------|
| F143_32a || NULL || NULL || 5 |
|------------||-----------||-----------||-----------|
| F175_23c || NULL || NULL || 6 |
|------------||-----------||-----------||-----------|
| F95_1a || NULL || NULL || 14 |
|------------||-----------||-----------||-----------|
| F89_9d || NULL || NULL || 15 |
|------------||-----------||-----------||-----------|
您對我的問題有什么建議嗎?
如果您使用xml2和purrr,它可能看起來像
library(tidyverse)
library(xml2)
xml_data <- sprintf('file%s.xml', 1:3) %>% # make filepaths
map_df(~read_xml(.x) %>% # iterate over filenames; read xml
xml_find_all('//p') %>% # select p nodes
map_df(function(.y) { # iterate over nodes and combine to data frame of...
list(file = basename(.x), # the filename,
id = xml_attr(.y, 'id'), # the id attribute, and
value = as.integer(xml_text(.y))) # the node value.
}))
xml_data
#> # A tibble: 9 x 3
#> file id value
#> <chr> <chr> <int>
#> 1 file1.xml F23_1b 1
#> 2 file1.xml F54_34a 3
#> 3 file2.xml F23_1b 7
#> 4 file2.xml F54_34a 8
#> 5 file2.xml F54_63d 12
#> 6 file3.xml F143_32a 5
#> 7 file3.xml F175_23c 6
#> 8 file3.xml F95_1a 14
#> 9 file3.xml F89_9d 15
如果您真的想將其擴展為廣泛的形式,那么從這里開始很典型:
xml_data %>%
mutate(file = sub('.xml$', '', file)) %>%
spread(file, value)
#> # A tibble: 7 x 4
#> id file1 file2 file3
#> <chr> <int> <int> <int>
#> 1 F143_32a NA NA 5
#> 2 F175_23c NA NA 6
#> 3 F23_1b 1 7 NA
#> 4 F54_34a 3 8 NA
#> 5 F54_63d NA 12 NA
#> 6 F89_9d NA NA 15
#> 7 F95_1a NA NA 14
這是使用xml2
和dplyr::full_join
的解決方案:
# Read XML files
library(xml2);
fn <- paste0("file", 1:3, ".xml");
files <- lapply(fn, read_xml);
# Extract node attributes and values, store as data.frame
lst <- lapply(files, function(x)
cbind.data.frame(
id = xml_attr(xml_children(x), "id"),
val = as.numeric(xml_text(xml_children(x))),
stringsAsFactors = F))
# Outer full join on all data.frame's in list
df <- Reduce(function(x, y) dplyr::full_join(x, y, by = "id"), lst)
colnames(df)[2:ncol(df)] <- fn;
df;
# id file1.xml file2.xml file3.xml
#1 F23_1b 1 7 NA
#2 F54_34a 3 8 NA
#3 F54_63d NA 12 NA
#4 F143_32a NA NA 5
#5 F175_23c NA NA 6
#6 F95_1a NA NA 14
#7 F89_9d NA NA 15
說明:使用xml2::read_xml
讀取XML文件; 分別使用xml_attr
和xml_text
提取節點屬性和值,並存儲為data.frame
list
; 執行全外連接上data.frame
S中list
。
我不知道您在選擇技術方面有多靈活,但這是XSLT 3.0中的解決方案
<xsl:variable name="doc1" select="doc('file1.xml')"/>
<xsl:variable name="doc2" select="doc('file2.xml')"/>
<xsl:variable name="doc3" select="doc('file3.xml')"/>
<xsl:merge>
<xsl:merge-source for-each-source="($doc1, $doc2, doc3)" select=".//p[@xml:id]">
<xsl:merge-key select="@xml:id" sort-before-merge="yes"/>
</xsl:merge-source>
<xsl:merge-action>
<tr>
<td>{current-merge-key()}</td>
<xsl:for-each select="($doc1, $doc2, doc3)">
<td>{(current-merge-group()[(/) is current()], 'NA')[1]}</td>
</xsl:for-each>
</tr>
</xsl:merge-action>
</xsl:merge>
未經測試。 輕松歸納為N個輸入文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.