XML (Extensible Markup Language) は構造化されたデータを保存するために使用されるマークアップ言語です。Pandasのデータ解析ライブラリは、ほとんどのファイルタイプに対してデータの読み書きを行う関数を提供しています。
例えば、CSVファイルを扱うための read_csv()
と to_csv()
が含まれています。しかし、PandasはXMLファイルを読み書きするためのメソッドを一切含んでいません。
この記事では、他のモジュールを使ってXMLファイルからデータを読み込み、Pandas DataFrameにロードする方法を見ていきます。また、Pandas DataFrame からデータを取り出し、XML ファイルに書き込む方法も紹介します。
PandasによるXMLの読み込み
XMLデータを読み込んでPandas DataFrameに格納する方法をいくつか見てみましょう。
このセクションでは、各スクリプトに1セットの入力データを使用します。以下のXMLを properties.xml
という名前のファイルに保存します。
<?xml version="1.0" encoding="UTF-8"?
<root
<bathrooms
<n35237 type="number"1.0</n35237
<n32238 type="number"3.0</n32238
<n44699 type="number"nan</n44699
</bathrooms
<price
<n35237 type="number"7020000.0</n35237
<n32238 type="number"10000000.0</n32238
<n44699 type="number"4128000.0</n44699
</price
<property_id
<n35237 type="number"35237.0</n35237
<n32238 type="number"32238.0</n32238
<n44699 type="number"44699.0</n44699
</property_id
</root
xml.etree.ElementTree を用いた読み込み
Python には xml.etree.ElementTree
モジュールが組み込まれています。このモジュールはXML文書を解析したり、作成したりするための機能を提供する。ElementTree` は XML ドキュメントをツリーとして表現する。XMLファイルの要素や子要素をノードとし、ドキュメントを横断することができる。
この手法では、ファイルの内容を変数に読み込んで ET.XML()
を用いて文字列定数から XML 文書をパースする。各子要素およびサブ子要素に渡ってループし、それらが含むデータのリストを維持します。一方、DataFrameカラムの子タグを書き込みます。そして、このデータをDataFrameに書き込みます。
注:XMLからデータを読み込む場合、データリストの子要素はカラムで書かれているので、DataFrameをトランスポーズする必要があります。
それでは、xml.etree.ElementTree
の使い方を説明します。
import xml.etree.ElementTree as ET
import pandas as pd
xml_data = open('properties.xml', 'r').read() # Read file
root = ET.XML(xml_data) # Parse XML
data = []
cols = []
for i, child in enumerate(root):
data.append([subchild.text for subchild in child])
cols.append(child.tag)
df = pd.DataFrame(data).T # Write in DF and transpose it
df.columns = cols # Update column names
print(df)
上記のコードは、以下のような出力を生成します(使用する入力ファイルに依存します)。
bathrooms price property_id
0 1.0 7020000.0 35237.0
1 3.0 10000000.0 32238.0
2 nan 4128000.0 44699.0
lxmlで読み込む
lxmlライブラリは C ライブラリである
libxml2と
libxsltを Python でバインディングしたものである。また、ネイティブの
ElementTreeモジュールを拡張しています。このモジュールはサードパーティ製なので、以下のように
pip` と一緒にインストールする必要がある。
$ pip install lxml
ElementTreeとは異なり、ファイルのデータを読み込んでパースすることはない。Objectify.parse()
を直接使って、XMLファイルのパスを指定することができる。ルート要素を取得するために、パースされたXMLデータに対して getroot()
を使用します。
これで、ルートノードの子要素をループして、Pythonのリストに書き込むことができます。先ほどと同じように、データリストを使ってDataFrameを作成し、DataFrameをトランスポーズします。
それでは、lxml
を使用してPandasのDataFrameを作成するコードを見てみましょう。
from lxml import objectify
import pandas as pd
xml_data = objectify.parse('properties.xml') # Parse XML data
root = xml_data.getroot() # Root element
data = []
cols = []
for i in range(len(root.getchildren())):
child = root.getchildren()[i]
data.append([subchild.text for subchild in child.getchildren()])
cols.append(child.tag)
df = pd.DataFrame(data).T # Create DataFrame and transpose it
df.columns = cols # Update column names
print(df)
これをPythonインタープリターで実行すると、以下のような出力が得られます。
bathrooms price property_id
0 1.0 7020000.0 35237.0
1 3.0 10000000.0 32238.0
2 nan 4128000.0 44699.0
xmltodictによる読み込み
xmltodictモジュールは、その名の通り XML データを Python の辞書に変換します。lxml
と同様に、このモジュールも pip
と一緒にインストールする必要がある。
$ pip install xmltodict
先ほどと同じように、XMLの内容を変数に読み込んでいきます。このデータを parse()
メソッドに渡すと、XML データの辞書が返されます。これは、XMLファイルの要素とサブ要素を持つ、ネストされた辞書になります。これらの要素をループして、データリストに書き込み、DataFrameを作成します。
それでは、xmltodictを使ってXMLデータを解析し、DataFrameを作成するコードを見てみましょう。
import xmltodict
import pandas as pd
xml_data = open('properties.xml', 'r').read() # Read data
xmlDict = xmltodict.parse(xml_data) # Parse XML
cols = xmlDict['root'].keys()
data = []
for i in xmlDict['root']:
child = xmlDict['root'][i]
data.append([child[subchild]['#text'] for subchild in child])
df = pd.DataFrame(data).T # Create DataFrame and transpose it.
df.columns = cols
print(df)
上記のコードを実行すると、次のように出力されます。
bathrooms price property_id
0 1.0 7020000.0 35237.0
1 3.0 10000000.0 32238.0
2 nan 4128000.0 44699.0
注意: xmltodict
ライブラリは、多くの開発者がパフォーマンスの低下を経験しているため、巨大な XML ファイルを扱う場合には推奨されません。lxmlライブラリは XML を扱うのに最も高速であると考えられており、同梱されている
xml.etree.ElementTree` よりも高速に動作します。
もし、パフォーマンスが重要であれば、各ライブラリでテストを行う必要があります。
PandasでXMLを書く
PandasのDataFrameをXMLファイルに書き出す方法をいろいろと見てみましょう。以下のスクリプトは、いずれも coordinates.xml というファイルを新規に作成し、以下の内容を記述します。
<root
<a
<x1.3</x
<y2.6</y
<z2.1</z
</a
<b
<x1.4</x
<y1.4</y
<z5.6</z
</b
<c
<x5.2</x
<y4.6</y
<z4.6</z
</c
</root
内蔵のwrite()関数による書き込み
DataFrameをXMLファイルとして書き出すには、ファイルに含まれる write()
関数を使用します。これを実現するために、各項目がXMLの1行を表すように、XMLデータのリストを保持しておきます。そして、DataFrameを繰り返し処理し、データリストにXMLの適切な開始タグと終了タグを付けてデータを書き込みます。
それが完了したら、もう一度リストに対して繰り返し処理を行い、XMLファイルにデータを書き込んでいきます。以下は、write()
の使い方を示したコードです。
import pandas as pd
df = pd.DataFrame([[1.3, 1.4, 5.2],
[2.6, 1.4, 4.6],
[2.1, 5.6, 4.6]],
columns=['A', 'B', 'C'],
index=['X', 'Y', 'Z'])
xml_data = ['<root']
for column in df.columns:
xml_data.append('<{}>'.format(column)) # Opening element tag
for field in df.index:
# writing sub-elements
xml_data.append('<{0}>{1}<!--{0}--'.format(field, df[field]))
xml_data.append('<!--{}--'.format(column)) # Closing element tag
xml_data.append('</root')
with open('coordinates.xml', 'w') as f: # Writing in XML file
for line in xml_data:
f.write(line)
このコードを実行すると、カレントディレクトリに coordinates.xml という名前のファイルが生成されます。
xml.etree.ElementTree で XML ファイルを書き込む
デフォルトの xml.etree.ElementTree
モジュールを使用すると、データを XML として保存し、それを文字列に変換してファイルに書き出すことができます。
最初のステップは、ルート要素を作成することです。次に、DataFrame の列と行を繰り返し、それらを ElementTree の要素や副要素として追加します。次に、ElementTree
オブジェクトのデータを tostring()
メソッドでバイナリ文字列に変換します。
XMLデータはバイナリ文字列なので、ファイルに書き込む前にUTF-8にデコードする。
次のコードは xml.etree.ElementTree
を用いて、DataFrame を XML ファイルとして書き出すものである。
import xml.etree.ElementTree as ET
import pandas as pd
df = pd.DataFrame([[1.3, 1.4, 5.2],
[2.6, 1.4, 4.6],
[2.1, 5.6, 4.6]],
columns=['A', 'B', 'C'],
index=['X', 'Y', 'Z'])
header = df.columns
root = ET.Element('root') # Root element
for column in df.columns:
entry = ET.SubElement(root, column) # Adding element
for row in df.index:
schild = row
child = ET.SubElement(entry, schild) # Adding sub-element
child.text = str(df[schild])
xml_data = ET.tostring(root) # binary string
with open('coordinates.xml', 'w') as f: # Write in file as utf-8
f.write(xml_data.decode('utf-8'))
前回と同様に、このスクリプトを実行すると、期待通りの出力を持つ coordinates.xml ファイルが作成されます。
lxml による XML ファイルの作成
lxmlの使い方は、
xml.etree.ElementTreeの使い方と似ています。まず、作成するファイルのルート要素で
etreeオブジェクトを作成します。次に、DataFrame を繰り返し処理し、列や行をツリーの要素やサブ要素として追加していきます。最後に、
tostring()メソッドを使用して、
etree` をバイナリ文字列として取得します。バイナリ文字列をUTF-8にデコードしてから、ファイルを書き込む。
以下は、lxmlを使用してDataFrameをXMLとして書き出すコードです。
from lxml import etree as et
import pandas as pd
root = et.Element('root') # Create root element
df = pd.DataFrame([[1.3, 1.4, 5.2],
[2.6, 1.4, 4.6],
[2.1, 5.6, 4.6]],
columns=['A', 'B', 'C'],
index=['X', 'Y', 'Z'])
for column in df.columns:
entry = et.SubElement(root, column) # Writing element
for row in df.index:
schild = row
child = et.SubElement(entry, schild) # Writing sub-elements
child.text = str(df[schild])
xml_data = et.tostring(root) # binary string
with open('coordinates.xml', 'w') as f: # Write in XML file as utf-8
f.write(xml_data.decode('utf-8'))
正常に終了すると、XML座標を含む座標.xmlが表示されます。
結論
このチュートリアルでは、Pandas DataFrame を使って XML データを読み書きする様々な方法を紹介しました。ビルトインの xml.etree.ElementTree
モジュールと、2つのサードパーティモジュールを使用してデータを読み込むことができます。lxmlと
xmltodict` です。
Pandas DataFrameからXMLファイルへの書き込みには、従来のリスト付きファイル write()
と xml.etree.ElementTree
モジュール、および lxml
を使用しました。XMLの文字列を直接操作してファイルを書き込むのはヒューマンエラーが起こりやすいので、データフレームをXMLに書き出すには xml.etree.ElementTree
と lxml
が望ましい解決策と言えます。