从许多 XML 文件在 R 中创建数据框



我正在尝试使用瑞典选举统计数据检索一些xml数据,并从中创建R中的数据框,但我不太熟悉xml文件无法获取我想要的信息。我看到了一些关于如何从许多 XML 文件创建数据框的其他问题,但它们的结构比我正在使用的数据更简单。

数据作为包含许多 XML 文件的压缩文件夹发布。可以通过以下 R 代码读取它:

library(xml2)
library(tidyverse)
tf <- tempfile(tmpdir = tdir <- tempdir())
download.file("https://data.val.se/val/val2014/valnatt/valnatt.zip", tf)
xml_files <- unzip(tf, exdir = tdir)

该文件夹包含 290 个城市中每个城市的文件(具有 4 位代码的文件(和每种选举类型,其中文件名中的最后一个字母表示选举类型(R=国家议会,L=县议会,K=市议会(。它还包含 3 个 XML 文件,用于三种选举类型中每种类型的总结果。包含市政数据的 XML 文件具有以下结构(为清楚起见,删除了行(:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/html"?>
<!DOCTYPE VAL PUBLIC "-//Valmyndigheten//DTD Valresultat parti kommun 1.5//SV" "http://www.val.se/dtd/resultat/parti_kommun_1_5.dtd">
<VAL TILLFÄLLE="Allmänna val 14 september 2014" FILNAMN="valnatt_0114R.xml" RAPPORTERING="VALNATTSRAPPORTERING" VALTYP="Riksdagsval" VALDAG="20140914" VALDAG_FGVAL="20100919" TID_RAPPORT="20140916105203">
<PARTI FÖRKORTNING="M" BETECKNING="Moderaterna" FÄRG="#66BEE6" />
<KOMMUN KOD="0114" NAMN="Upplands Väsby" TYP="Summering" KLARA_VALDISTRIKT="22" ALLA_VALDISTRIKT="22" RÖSTER="23638" RÖSTER_FGVAL="22215" TID_RAPPORT="20140914230336" MODNR="117144935">
<GILTIGA PARTI="M" RÖSTER="6748" RÖSTER_FGVAL="8201" PROCENT="28,5" PROCENT_FGVAL="36,9" PROCENT_ÄNDRING="-8,4"/>
<GILTIGA PARTI="C" RÖSTER="901" RÖSTER_FGVAL="891" PROCENT="3,8" PROCENT_FGVAL="4,0" PROCENT_ÄNDRING="-0,2"/>
<KRETS_KOMMUN KOD="011401" NAMN="Norra valkretsen" TYP="Summering" KLARA_VALDISTRIKT="12" ALLA_VALDISTRIKT="12" RÖSTER="11907" RÖSTER_FGVAL="11202" TID_RAPPORT="20140914222651" MODNR="117118974">
<GILTIGA PARTI="M" RÖSTER="3083" RÖSTER_FGVAL="3860" PROCENT="25,9" PROCENT_FGVAL="34,5" PROCENT_ÄNDRING="-8,6"/>
<GILTIGA PARTI="C" RÖSTER="440" RÖSTER_FGVAL="431" PROCENT="3,7" PROCENT_FGVAL="3,8" PROCENT_ÄNDRING="-0,2"/>
<VALDISTRIKT KOD="01140212" NAMN="Smedby Södra" RÖSTER="1201" RÖSTER_FGVAL="1186" TID_RAPPORT="20140914230336" MODNR="117144935">
<GILTIGA PARTI="M" RÖSTER="227" RÖSTER_FGVAL="336" PROCENT="18,9" PROCENT_FGVAL="28,3" PROCENT_ÄNDRING="-9,4"/>
<GILTIGA PARTI="C" RÖSTER="35" RÖSTER_FGVAL="17" PROCENT="2,9" PROCENT_FGVAL="1,4" PROCENT_ÄNDRING="+1,5"/>
<GILTIGA PARTI="FP" RÖSTER="43" RÖSTER_FGVAL="61" PROCENT="3,6" PROCENT_FGVAL="5,1" PROCENT_ÄNDRING="-1,6"/>
<ÖVRIGA_GILTIGA RÖSTER="20" RÖSTER_FGVAL="10" PROCENT="1,7" PROCENT_FGVAL="0,8" PROCENT_ÄNDRING="+0,8"/>
<OGILTIGA TEXT="BLANK" RÖSTER="12" RÖSTER_FGVAL="13" PROCENT="1,0" PROCENT_FGVAL="1,1" PROCENT_ÄNDRING="-0,1"/>
<OGILTIGA TEXT="OG" RÖSTER="13" RÖSTER_FGVAL="1" PROCENT="1,1" PROCENT_FGVAL="0,1" PROCENT_ÄNDRING="+1,0"/>
<VALDELTAGANDE RÖSTBERÄTTIGADE="1551" RÖSTBERÄTTIGADE_KLARA_VALDISTRIKT_FGVAL="1546" SUMMA_RÖSTER="1226" SUMMA_RÖSTER_FGVAL="1200" PROCENT="79,0" PROCENT_FGVAL="77,6" PROCENT_ÄNDRING="+1,4"/>
</VALDISTRIKT>
</KRETS_KOMMUN>
</KOMMUN>
</VAL>

现在,我希望每个文件获取所有VALDISTRIKT节点及以下节点中的数据并创建一个数据框。我不确定如何最好地构建这样的数据框,但以下结构就足够了,其中GROUP包含 GILTIGA 中的 PARTI,OGILTIGA 中的文本以及ÖVRIGA_GILTIGA中的 ÖVRIGA_GILTIGA。如果可能的话,我还想添加 VALDELTAGANDE 中的 PROCENT 和 PROCENT_FG_VAL 作为变量(一个 VALDISTRIKT 中的每一行都有相同的信息(。

KOD      NAMN             GROUP        RÖSTER RÖSTER_FG_VAL PROCENT PROCENT_FG_VAL PROCENT_FÖRÄNDRING
01140212 "Smedby Södra"   M              227   336            18,9   18,3           -9,4
01140212 "Smedby Södra"   C              35    17             2,9     1,4           +1,5
01140212 "Smedby Södra"   FP             43    61             3,6     5,1           -1,6
01140212 "Smedby Södra"   ÖVRIGA_GILTIGA 20    10             1,7     0,8           +0,8       
01140212 "Smedby Södra"   BLANK          12    13             1,0     1,1           -0,1
01140212 "Smedby Södra"   OG             13    1              1,1     0,1           +1,0

此信息应从 290 个文件中的每个文件中检索,每个文件中的名称为 4 位数字,并以 R 结尾。我想我应该遍历这些文件,或者更确切地说是使用map_df

我知道这个问题有很多要问的问题,如果我没有对 XML 文件的某些部分使用正确的术语,我很抱歉,但如果你能给我一些关于如何将信息从 xml 文件获取到数据框中的指示,或者我可以在哪里阅读更多关于如何做到这一点, 将不胜感激。

更新

我已经设法向前迈出了几步。对于一个文件,我可以使用以下代码将所有信息放入两个单独的数据框中,其中顶部包含有关选区的数据,下方包含选举结果。我现在只需要找到一种方法将两者结合起来并调整代码以读取所有文件。

top <- xml_find_all(t, "//VALDISTRIKT")
top <- top %>% 
map(xml_attrs) %>% 
map_df(~as.list(.))
below <- xml_find_all(t, "//VALDISTRIKT/*")
below <- p2 %>% 
map(xml_attrs) %>% 
map_df(~as.list(.))

万事如意,R

我在RStudio社区中得到了答案,我想我也可以在这里添加它,以防它对其他人有任何帮助。

library(xml2)
library(tidyverse)
# Make a temporary file (tf) and a temporary folder (tdir)
tf <- tempfile(tmpdir = tdir <- tempdir())
# Download the zip file 
download.file("https://data.val.se/val/val2014/valnatt/valnatt.zip", tf)
# Unzip it in the temp folder
xml_files <- unzip(tf, exdir = tdir)
# Get the filenames of the files to import
# They have 4 digits in the file name, and ends with the letter K
files_to_import <- fs::dir_ls(tdir) %>%
str_subset(pattern = "valnatt_\d{4}K.xml$")
# Create a function to read a file and get the information wanted
read_dist <- . %>% 
read_xml() %>% 
xml_find_all(., "//VALDISTRIKT") %>% 
map_dfr(~ {
# extract the attributes from the parent tag as a data.frame
parent <- xml_attrs(.x) %>% enframe() %>% spread(name, value)
# make a data.frame out of the attributes of the kids
kids <- xml_children(.x) %>% map_dfr(~ as.list(xml_attrs(.x)))
# combine them (bind_cols does not repeat parent rows)
cbind.data.frame(parent, kids) %>% set_tidy_names() %>% as_tibble() 
})
# Map over all the files
df <- map_df(files_to_import, read_dist)

最新更新