前提・実現したい内容
約1万ファイル、50万行ほどの膨大な量のJSONファイルを1つのCSVファイルにまとめたいです。
しかし複数のファイルにおいてデータが入っていない箇所があり、全ファイルを網羅的に変換してCSVファイルにまとめるとデータが入っていない部分が詰められてしまう状態です。
実現したい内容は以下の3点です。
- 複数JSONファイルを1行のCSVに変換して1つのCSVファイルに変換する
- 最初に変換したファイルのキーを参照して2つ目以降のファイルの変換を行う
- キーが一致する場所にデータを書き込み、キーが存在しなければ0を代入する
該当のソースコード
以下に記述しているのは実際に使用しているPythonコードです。現在の仕様では、「ネストされたJSONファイルをキーと共に出力する」となっています。
Python
1import sys 2import json 3import csv 4import io 5 6 7def to_string(s): 8 try: 9 return str(s) 10 except: 11 #Change the encoding type if needed 12 return s.encode('utf-8') 13 14def reduce_item(key, value): 15 global reduced_item 16 17 #Reduction Condition 1 18 if type(value) is list: 19 i=0 20 for sub_item in value: 21 reduce_item(key+'_'+to_string(i), sub_item) 22 i=i+1 23 24 #Reduction Condition 2 25 elif type(value) is dict: 26 sub_keys = value.keys() 27 for sub_key in sub_keys: 28 reduce_item(key+'_'+to_string(sub_key), value[sub_key]) 29 30 #Base Condition 31 else: 32 reduced_item[to_string(key)] = to_string(value) 33 34 35if __name__ == "__main__": 36 if len(sys.argv) != 4: 37 print ("\nUsage: python json_to_csv.py <node> <json_in_file_path> <csv_out_file_path>\n") 38 else: 39 #Reading arguments 40 node = sys.argv[1] 41 json_file_path = sys.argv[2] 42 csv_file_path = sys.argv[3] 43 44 with io.open(json_file_path, 'r', encoding='utf-8-sig') as fp: 45 json_value = fp.read() 46 raw_data = json.loads(json_value) 47 48 try: 49 data_to_be_processed = raw_data[node] 50 except: 51 data_to_be_processed = raw_data 52 53 processed_data = [] 54 header = [] 55 for item in data_to_be_processed: 56 reduced_item = {} 57 reduce_item(node, item) 58 59 header += reduced_item.keys() 60 61 processed_data.append(reduced_item) 62 63 header = list(set(header)) 64 header.sort() 65 66 with open(csv_file_path, 'a') as f: 67 writer = csv.DictWriter(f, header, quoting=csv.QUOTE_ALL) 68 writer.writeheader() 69 for row in processed_data: 70 writer.writerow(row) 71 print ("Just completed writing csv file with %d columns" % len(header))
###変換するファイルの例
行数が非常に多いため、適宜省略して載せています。...が省略箇所です。
{ "devices":[ { "name": "IntGW-01", "modules": { "openconfig-interfaces": { "interfaces": { "interface": [ { "name": "GigabitEthernet1", "config": { "name": "GigabitEthernet1", "type": "ianaift:ethernetCsmacd", "description": "ManagementIF", "enabled": true }, "state": { "name": "GigabitEthernet1", "type": "ianaift:ethernetCsmacd", "enabled": true, "ifindex": 1, "admin-status": "UP", "oper-status": "UP", "last-change": 1580105229000389000, "counters": { "in-octets": 17493436676, ... "out-errors": 0, "last-clear": 1580105112000091000 } }, "subinterfaces": { "subinterface": [ { "index": 0, "config": { "index": 0, "description": "ManagementIF", "enabled": true }, "state": { "enabled": true, "name": "GigabitEthernet1", "ifindex": 1, "admin-status": "UP", "oper-status": "UP", "last-change": 1580105229000389000, "counters": { "in-octets": 17493436676, ... "out-errors": 0, "last-clear": 1580105112000091000 } } } ] } }, { "name": "GigabitEthernet2", "config": { 〜上記GigabitEthernet1と同じ構成のものが8個〜 ] } }, "ietf-interfaces": { ... }, "Cisco-IOS-XE-interfaces-oper": { ... }, "Cisco-IOS-XE-bgp-oper": { ... }, "Cisco-IOS-XE-platform-software-oper": { ... }, "Cisco-IOS-XE-memory-oper": { ... } } }, { "name": "IntGW-02", "modules": { ... 〜以下同じ構造のものが4つ〜 } } ], "@log_name": "network-device-bgpnw2" }
このプログラムをシェルスクリプトを用いて網羅的な処理をしています。
sh
1#!/bin/bash 2 3dir_path="(フォルダパス)" 4dirs=`find $dir_path -maxdepth 0 -type f -name *.json` 5 6for dir in $dir_path; 7do 8 echo $dir 9 python json_to_csv.py(上記Pythonファイル) computes $dir physical-data.csv 10done
###その他
「キーを参照して一致すれば書き込み、なければ0」の処理方法が分からず苦戦しています。
拙い日本語ではありますが、教えて頂ければと思います。
###追記
編集願に「実行した時のCSVファイルと実現したいCSVファイルを載せてほしい」というものがありましたが、出力ファイルが非常に大きく、開こうとすると重すぎてまともに開けないという状況にあります。そのため実際の出力ファイルの中身は示すことができませんが、このような雰囲気の出力をしたいというものを書かせていただいております。
###実際の出力
2つのファイルを処理したとき、2つ目のJSONファイルにキーがCの要素がなかったとしています。スクリプトで処理しているため、キーが処理した回数分表示されます。
CSV
1"A","B","C","D" 21,2,3,4 3"A","B","D" 45,6,7
###実現したい出力
CSV
1"A","B","C","D" 21,2,3,4 35,6,0,7 4
あなたの回答
tips
プレビュー