前回の投稿では『Pythonでpandasを使ってExcelファイルを読み込んでcsvファイルを出力するまで』ということで、Pythonの環境構築からCSV出力までの段取りを書いてみました。
CSV出力ができればデータ分析の現場としてはどうにかなってしまうことが多いといえば多いですが、JSON形式やXML形式でも出力できると色々とやれることの幅が広がります。なので今回の投稿では各データ形式のメリット・デメリットを簡単に比較しつつ、実際にコードを書いて実行するところまでを書いてみたいと思います。
どのデータ形式もデータ活用の現場でバリバリの現役ではありますが、だからこそ、それぞれのデータ形式に適した活用シーンがあります。そのあたりを考慮しつつ、CSV, XML, JSONの順に特徴を見ていきます。(※なお下記特徴は、PythonだけでなくVBAでも処理することを念頭に書いています。)
特徴 | |
---|---|
CSV |
|
XML |
|
JSON |
|
やはりPythonで使うならCSV or JSONの2択ですね。わざわざJSONではなくXMLを使うべきシーンはなかなか思いつきません。とはいえ両方変換できるに越したことはないので、タイトル通りJSONとXMLの両方に変換してみたいと思います。
ということで前回の続きで変換スクリプトを早速書いてみます。
(※ちなみにCSVの読み込みとJSON処理に使っている『pandas』ライブラリについて知りたい方は Python Data Analysis Library - pandas を、XML処理に使っている『xml.etree.ElementTree』ライブラリについて知りたい方は Python Software Foundationをご覧ください。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import pandas as pd import xml.etree.ElementTree as et #縦持ちのcsvの読み込み inputFileName = "output_unpivotted.csv" df = pd.read_csv(inputFileName) #JSONのレコード形式にて出力 outputFileName = "output_records.json" df.to_json(outputFileName, orient="records") #JSONのスプリット形式にて出力 outputFileName = "output_split.json" df.to_json(outputFileName, orient="split") #XMLデータを生成 rootXml = et.Element('root') recordsXml = et.SubElement(rootXml, 'records') for iRow, row in df.iterrows(): recordXml = et.SubElement(recordsXml, 'record') for iCol, column in df.iteritems(): cellXml = et.SubElement(recordXml, iCol) cellXml.text = str(row[iCol]) #XMLとして出力 outputFileName = "output.xml" et.ElementTree(rootXml).write(outputFileName,encoding="unicode") |
順番としては、最初にJSON形式、次にXML形式にて出力しました。
JSON出力はpandasの基本機能として提供されているので、圧倒的に楽ですね。一般的なフォーマットはorient="records"だと思いますが、大規模なCSV的データの場合はorient="split"にすることでデータをより軽快に扱うことができると思います。
ちなみに出力されたデータを実際に見てみると、
JSON形式, orient="records"
[{"age":"0~4\u6b73","sex":"total","yyyymm":201905,"value":4810000},{"age":"0~4\u6b73","sex":"male","yyyymm":201905,"value":2470000},{"age":"0~4\u6b73","sex":"female","yyyymm":201905,"value":2350000},{"age":"0~4\u6b73","sex":"total","yyyymm":201812,"value":4827000},{"age":"0~4\u6b73","sex":"male","yyyymm":201812,"value":2473000},{"age":"0~4\u6b73","sex":"female","yyyymm":201812,"value":2354000},{"age":"0~4\u6b73","sex":"total-ja","yyyymm":201812,"value":4750000},{"age":"0~4\u6b73","sex":"male-ja","yyyymm":201812,"value":2433000},{"age":"0~4\u6b73","sex":"female-ja","yyyymm":201812,"value":2317000},{"age":"5~9\u6b73","sex":"total","yyyymm":201905,"value":5130000},{"age":"5~9\u6b73","sex":"male","yyyymm":201905,"value":2630000},{"age":"5~9\u6b73","sex":"female","yyyymm":201905,"value":2500000},{"age":"5~9\u6b73","sex":"total","yyyymm":201812,"value":5172000},{"age":"5~9\u6b73","sex":"male","yyyymm":201812,"value":2648000},{"age":"5~9\u6b73","sex":"female","yyyymm":201812,"value":2524000}, ...中略...}
JSON形式, orient="split"
{"columns":["age","sex","yyyymm","value"],"index":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188],"data":[["0~4\u6b73","total",201905,4810000],["0~4\u6b73","male",201905,2470000],["0~4\u6b73","female",201905,2350000],["0~4\u6b73","total",201812,4827000],["0~4\u6b73","male",201812,2473000],["0~4\u6b73","female",201812,2354000],["0~4\u6b73","total-ja",201812,4750000],["0~4\u6b73","male-ja",201812,2433000],["0~4\u6b73","female-ja",201812,2317000],["5~9\u6b73","total",201905,5130000],["5~9\u6b73","male",201905,2630000],["5~9\u6b73","female",201905,2500000],["5~9\u6b73","total",201812,5172000],["5~9\u6b73","male",201812,2648000],["5~9\u6b73","female",201812,2524000], ...中略...}
という構造の違いがあります。なおファイル容量はrecordsが12.7KB、splitが8.09KBとなり、splitにすることで約3分の2に減りました。もちろんフォーマットが異なるので処理する際のコードも異なるわけですが、データ容量が大きくなればなるほどこの恩恵は大きくなりますね。
さて、続いてXML出力です。私が知る限り、pandasのDataFrameを即座にXML出力する関数は存在しないので、実際にXMLのツリー構造を作ってからファイルとして出力する必要があります。ElementTreeを使えば直感的にツリー構造を作ることはできますが、まあ何も考えずにできるJSONと比べれば少し面倒な気はします。
なお上のコードでは、JSON形式で言うところのrecordsフォーマットをイメージしてツリー構造を作りました。出力時にはunicode指定して、日本語文字を日本語に変換しての出力なので、少し読みやすいです。ちなみに出力された実際のデータを見ると、
XML形式
<root><records><record><age>0~4歳</age><sex>total</sex><yyyymm>201905</yyyymm><value>4810000</value></record><record><age>0~4歳</age><sex>male</sex><yyyymm>201905</yyyymm><value>2470000</value></record><record><age>0~4歳</age><sex>female</sex><yyyymm>201905</yyyymm><value>2350000</value></record><record><age>0~4歳</age><sex>total</sex><yyyymm>201812</yyyymm><value>4827000</value></record><record><age>0~4歳</age><sex>male</sex><yyyymm>201812</yyyymm><value>2473000</value></record><record><age>0~4歳</age><sex>female</sex><yyyymm>201812</yyyymm><value>2354000</value></record><record><age>0~4歳</age><sex>total-ja</sex><yyyymm>201812</yyyymm><value>4750000</value></record><record><age>0~4歳</age><sex>male-ja</sex><yyyymm>201812</yyyymm><value>2433000</value></record><record><age>0~4歳</age><sex>female-ja</sex><yyyymm>201812</yyyymm><value>2317000</value></record><record><age>5~9歳</age><sex>total</sex><yyyymm>201905</yyyymm><value>5130000</value></record><record><age>5~9歳</age><sex>male</sex><yyyymm>201905</yyyymm><value>2630000</value></record><record><age>5~9歳</age><sex>female</sex><yyyymm>201905</yyyymm><value>2500000</value></record><record><age>5~9歳</age><sex>total</sex><yyyymm>201812</yyyymm><value>5172000</value></record><record><age>5~9歳</age><sex>male</sex><yyyymm>201812</yyyymm><value>2648000</value></record><record><age>5~9歳</age><sex>female</sex><yyyymm>201812</yyyymm><value>2524000</value></record> ...中略...</root>
こんな感じに並びます。JSONと違ってデータ型が区別されないので、全て文字列扱いです。今回は使っていないですが、これに属性を付与してよりリッチなデータ表現にすることも可能です。(データが散らばるので私はあまり使いませんが・・・)
ということで、『【Pythonデータ活用】CSVファイルをJSONファイル、XMLファイルに変換して出力してみる』と題して各形式を比較しつつ、実際に変換するコードを書いて試してみました。
各形式にメリット・デメリットがあるのでどれが正義というわけではありませんが、現場に合った最適な形式を選定できること、その形式のもとで最適なデータ構造を設計できること、そして万一の際に変換ができることが、データ活用において大切なことかなと思いました。
とりわけJSONやXMLは、後から色々とデータ構造を追加/変更しやすい点がメリットであるとともに、日々の運用によってデータ構造が滅茶苦茶になりやすいという裏の意味がデメリットにもなりえます。その意味では初期にどこまで先を見通したデータ構造を設計できるかが重要だと思いつつも・・・このあたり、実務書としてあまり良い教科書的なものに巡り合えていないので、
データベース設計のベストプラクティスを把握しつつ、オントロジーを参考に業務要件を再整理して、あとはどこまで精緻・厳密なデータであるべきか実効性とコストパフォーマンスを鑑みながら調整して落としどころを探すくらいしか思い付かないので、何か良書があればご指南くださいませ。。