Как я могу преобразовать этот XML-файл по этому адресу в фрейм данных pandas?
Я загрузил XML в виде файла, назвал его '058com.xml'
и запустил приведенный ниже код, хотя последний столбец результирующего фрейма данных представляет собой беспорядок данных, организованных как несколько OrderedDict. Структура XML кажется сложной и находится за пределами моего понимания.
json_normalize
документация меня смутила.
Как улучшить код, чтобы полностью сгладить XML?
import pandas as pd
import xmltodict
rawdata = '058com.xml'
with open(rawdata) as fd:
doc = xmltodict.parse(fd.read(), encoding='ISO-8859-1', process_namespaces=False)
pd.json_normalize(doc['Election']['Departement']['Communes']['Commune'])
В идеале фрейм данных должен выглядеть как идентификаторы, имена географических объектов, результаты голосования и имена кандидатов на выборах.
Окончательный фрейм данных должен содержать много столбцов при полном сведении и, как ожидается, будет очень близок к приведенному ниже CSV. Я вставил заголовки и первую строку в виде .csv
(разделенные точкой с запятой) в качестве репрезентативного образца того, как должен выглядеть фрейм данных.
Code du département;Libellé du département;Code de la commune;Libellé de la commune;Etat saisie;Inscrits;Abstentions;% Abs/Ins;Votants;% Vot/Ins;Blancs;% Blancs/Ins;% Blancs/Vot;Nuls;% Nuls/Ins;% Nuls/Vot;Exprimés;% Exp/Ins;% Exp/Vot;N°Panneau;Sexe;Nom;Prénom;Voix;% Voix/Ins;% Voix/Exp
01;Ain;001;L'Abergement-Clémenciat;Complet;645;108;16,74;537;83,26;16;2,48;2,98;1;0,16;0,19;520;80,62;96,83;1;F;ARTHAUD;Nathalie;3;0,47;0,58;2;M;ROUSSEL;Fabien;6;0,93;1,15;3;M;MACRON;Emmanuel;150;23,26;28,85;4;M;LASSALLE;Jean;18;2,79;3,46;5;F;LE PEN;Marine;149;23,10;28,65;6;M;ZEMMOUR;Éric;43;6,67;8,27;7;M;MÉLENCHON;Jean-Luc;66;10,23;12,69;8;F;HIDALGO;Anne;5;0,78;0,96;9;M;JADOT;Yannick;30;4,65;5,77;10;F;PÉCRESSE;Valérie;26;4,03;5,00;11;M;POUTOU;Philippe;3;0,47;0,58;12;M;DUPONT-AIGNAN;Nicolas;21;3,26;4,04
Понятно! Должно ли быть всего 313 строк в выходном CSV, так как CodSubCom
принимает значение от 1 до 313? Я бы предложил преобразовать XML в JSON и использовать JSON для визуализации данных с помощью онлайн-инструмента форматирования цвета JSON.
309 строк, 313 - это идентификатор последнего локального объекта, хотя в регионе всего 309 локальных объектов, четырех идентификаторов просто не существует, например 022, но да, я думаю, вы поняли.
Я пробовал это:
import pandas as pd
import xmltodict
rawdata = '058com.xml'
with open(rawdata) as fd:
doc = xmltodict.parse(fd.read(), encoding='ISO-8859-1', process_namespaces=False)
df = pd.json_normalize(doc['Election']['Departement']['Communes']['Commune'])
col_length_df = len(df.columns)
all_columns = list(df.columns[:-1]) + list(df.iloc[0, len(df.columns)-1][0].keys())
new_df = df.reindex(columns = all_columns)
new_df.astype({"RapportExprime": str, "RapportInscrit": str}).dtypes
for index, rows in new_df.iterrows():
new_df.iloc[index, col_length_df-1:] = list(df.iloc[index, len(df.columns)-1][0].values())
Поскольку последняя строка df
является упорядоченным словарем, код использует свои ключи для добавления пустых столбцов вместе с исходными столбцами df
в new_df
. Наконец, он перебирает строки df
и new_df
, чтобы заполнить пустые столбцы new_df
.
Приведенный выше код дает нам:
CodSubCom LibSubCom Tours.Tour.NumTour Tours.Tour.Mentions.Inscrits.Nombre Tours.Tour.Mentions.Abstentions.Nombre ... PrenomPsn CivilitePsn NbVoix RapportExprime RapportInscrit
0 001 Achun 1 105 24 ... Nathalie Mme 0 0,00 0,00
1 002 Alligny-Cosne 1 696 133 ... Nathalie Mme 3 0,54 0,43
2 003 Alligny-en-Morvan 1 533 123 ... Nathalie Mme 5 1,25 0,94
3 004 Alluy 1 263 48 ... Nathalie Mme 1 0,48 0,38
4 005 Amazy 1 188 51 ... Nathalie Mme 2 1,53 1,06
.. ... ... ... ... ... ... ... ... ... ... ...
304 309 Villapourçon 1 327 70 ... Nathalie Mme 1 0,40 0,31
305 310 Villiers-le-Sec 1 34 4 ... Nathalie Mme 0 0,00 0,00
306 311 Ville-Langy 1 203 46 ... Nathalie Mme 1 0,64 0,49
307 312 Villiers-sur-Yonne 1 263 60 ... Nathalie Mme 0 0,00 0,00
308 313 Vitry-Laché 1 87 13 ... Nathalie Mme 1 1,37 1,15
Наконец, new_df.columns
это:
Index(['CodSubCom', 'LibSubCom', 'Tours.Tour.NumTour',
'Tours.Tour.Mentions.Inscrits.Nombre',
'Tours.Tour.Mentions.Abstentions.Nombre',
'Tours.Tour.Mentions.Abstentions.RapportInscrit',
'Tours.Tour.Mentions.Votants.Nombre',
'Tours.Tour.Mentions.Votants.RapportInscrit',
'Tours.Tour.Mentions.Blancs.Nombre',
'Tours.Tour.Mentions.Blancs.RapportInscrit',
'Tours.Tour.Mentions.Blancs.RapportVotant',
'Tours.Tour.Mentions.Nuls.Nombre',
'Tours.Tour.Mentions.Nuls.RapportInscrit',
'Tours.Tour.Mentions.Nuls.RapportVotant',
'Tours.Tour.Mentions.Exprimes.Nombre',
'Tours.Tour.Mentions.Exprimes.RapportInscrit',
'Tours.Tour.Mentions.Exprimes.RapportVotant', 'NumPanneauCand',
'NomPsn', 'PrenomPsn', 'CivilitePsn', 'NbVoix', 'RapportExprime',
'RapportInscrit'],
dtype='object')
Общее количество столбцов в new_df
: 24
Великолепно! Это очень близко, но код возвращает результат для одного единственного кандидата. В .csv в моем вопросе вы можете заметить, что есть (на удивление) дополнительные столбцы без заголовков для других кандидатов.
Поскольку URL-адрес действительно содержит два раздела данных под каждым <Tour>
, в частности <Mentions>
(которые представляют собой сводные данные о голосовании) и <Candidats>
(которые представляют собой детализированные данные на уровне человека) (простите мой французский), рассмотрите возможность создания двух отдельных фреймов данных с использованием нового метода ввода-вывода, pandas.read_xml
, который поддерживает XSLT 1.0 (через сторонний пакет lxml
). Нет перехода на словари для обработки JSON.
Как язык специального назначения, написанный на XML, XSLT может преобразовать вашу вложенную структуру в более плоский формат для миграции во фрейм данных. В частности, каждая таблица стилей переходит к наиболее детализированному узлу, а затем по оси ancestor
извлекает информацию более высокого уровня в виде родственных столбцов.
Упоминания(сохранить как .xsl, специальный файл .xml или встроить как строку в Python)
<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output indent = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:template match = "/">
<Tours>
<xsl:apply-templates select = "descendant::Tour/Mentions"/>
</Tours>
</xsl:template>
<xsl:template match = "Mentions/*">
<Mention>
<xsl:copy-of select = "ancestor::Election/Scrutin/*"/>
<xsl:copy-of select = "ancestor::Departement/*[name()!='Communes']"/>
<xsl:copy-of select = "ancestor::Commune/*[name()!='Tours']"/>
<xsl:copy-of select = "ancestor::Tour/NumTour"/>
<Mention><xsl:value-of select = "name()"/></Mention>
<xsl:copy-of select = "*"/>
</Mention>
</xsl:template>
</xsl:stylesheet>
Питон(читать прямо из URL)
url = (
"https://www.resultats-elections.interieur.gouv.fr/telechargements/"
"PR2022/resultatsT1/027/058/058com.xml"
)
mentions_df = pd.read_xml(url, stylesheet=mentions_xsl)
Выход
Type Annee CodReg CodReg3Car LibReg CodDpt CodMinDpt CodDpt3Car LibDpt CodSubCom LibSubCom NumTour Mention Nombre RapportInscrit RapportVotant
0 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 Inscrits 105 None None
1 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 Abstentions 24 22,86 None
2 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 Votants 81 77,14 None
3 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 Blancs 2 1,90 2,47
4 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 Nuls 0 0,00 0,00
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1849 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 Abstentions 13 14,94 None
1850 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 Votants 74 85,06 None
1851 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 Blancs 1 1,15 1,35
1852 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 Nuls 0 0,00 0,00
1853 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 Exprimes 73 83,91 98,65
[1854 rows x 16 columns]
Кандидаты(сохранить как .xsl, специальный файл .xml или встроить как строку в Python)
<?xml version = "1.0" encoding = "UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output indent = "yes"/>
<xsl:strip-space elements = "*"/>
<xsl:template match = "/">
<Candidats>
<xsl:apply-templates select = "descendant::Tour/Resultats/Candidats"/>
</Candidats>
</xsl:template>
<xsl:template match = "Candidat">
<xsl:copy>
<xsl:copy-of select = "ancestor::Election/Scrutin/*"/>
<xsl:copy-of select = "ancestor::Departement/*[name()!='Communes']"/>
<xsl:copy-of select = "ancestor::Commune/*[name()!='Tours']"/>
<xsl:copy-of select = "ancestor::Tour/NumTour"/>
<xsl:copy-of select = "*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Питон(читать прямо из URL)
url = (
"https://www.resultats-elections.interieur.gouv.fr/telechargements/"
"PR2022/resultatsT1/027/058/058com.xml"
)
candidats_df = pd.read_xml(url, stylesheet=candidats_xsl)
Выход
Type Annee CodReg CodReg3Car LibReg CodDpt CodMinDpt CodDpt3Car LibDpt CodSubCom LibSubCom NumTour NumPanneauCand NomPsn PrenomPsn CivilitePsn NbVoix RapportExprime RapportInscrit
0 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 1 ARTHAUD Nathalie Mme 0 0,00 0,00
1 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 2 ROUSSEL Fabien M. 3 3,80 2,86
2 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 3 MACRON Emmanuel M. 14 17,72 13,33
3 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 4 LASSALLE Jean M. 2 2,53 1,90
4 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 1 Achun 1 5 LE PEN Marine Mme 28 35,44 26,67
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3703 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 8 HIDALGO Anne Mme 0 0,00 0,00
3704 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 9 JADOT Yannick M. 4 5,48 4,60
3705 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 10 PÉCRESSE Valérie Mme 6 8,22 6,90
3706 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 11 POUTOU Philippe M. 1 1,37 1,15
3707 Présidentielle 2022 27 27 Bourgogne-Franche-Comté 58 58 58 Nièvre 313 Vitry-Laché 1 12 DUPONT-AIGNAN Nicolas M. 4 5,48 4,60
[3708 rows x 19 columns]
Вы можете объединить полученные фреймы данных, используя их общие Communes
узлы: <CodSubCom>
и <LibSubCom>
, но, возможно, придется pivot_table
объединять данные для слияния «один ко многим». Ниже показано с агрегатом Номбре:
mentions_candidats_df = (
candidats_df.merge(
mentions_df.pivot_table(
index=["CodSubCom", "LibSubCom"],
columns = "Mention",
values = "Nombre",
aggfunc = "max"
).reset_index(),
on=["CodSubCom", "LibSubCom"]
)
)
mentions_candidats_df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3708 entries, 0 to 3707
Data columns (total 25 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Type 3708 non-null object
1 Annee 3708 non-null int64
2 CodReg 3708 non-null int64
3 CodReg3Car 3708 non-null int64
4 LibReg 3708 non-null object
5 CodDpt 3708 non-null int64
6 CodMinDpt 3708 non-null int64
7 CodDpt3Car 3708 non-null int64
8 LibDpt 3708 non-null object
9 CodSubCom 3708 non-null int64
10 LibSubCom 3708 non-null object
11 NumTour 3708 non-null int64
12 NumPanneauCand 3708 non-null int64
13 NomPsn 3708 non-null object
14 PrenomPsn 3708 non-null object
15 CivilitePsn 3708 non-null object
16 NbVoix 3708 non-null int64
17 RapportExprime 3708 non-null object
18 RapportInscrit 3708 non-null object
19 Abstentions 3708 non-null int64
20 Blancs 3708 non-null int64
21 Exprimes 3708 non-null int64
22 Inscrits 3708 non-null int64
23 Nuls 3708 non-null int64
24 Votants 3708 non-null int64
dtypes: int64(16), object(9)
memory usage: 753.2+ KB
В будущих пандах 1.5 read_xml
поддержит dtypes
, чтобы разрешить преобразование после XSLT в этом случае.
Я попытался визуализировать его с помощью Excel, который обрабатывает файл XML, но таким образом, что добавляет много строк. На самом деле этот файл представляет результаты голосования, Excel добавляет строки для кандидатов, где мне нужны столбцы для кандидатов, как показано в CSV в вопросе.