unixやlinuxで作業していると、あるコマンドで得た、テーブルデータ式の出力結果を、
管理する良い方法がないか考えるシーンがたまにだが、ある.
e.g) df や mount の出力結果を管理したい時 etc..
pythonの学習を兼ねて、テーブルデータ形式の出力結果を扱う方法を考えてみる.
簡単な例を元に考える.
ファイルや、標準出力で何らか以下のようなデーブルデータ形式のデータを得たとする.
X Y Z a 1 2 b 3 4 c 5 6
多次元リストにして扱う という方法が通常の?やり方だと思う.
その場合、リストに対して該当列を行番号で指定する方法か、awkのように行ごと処理する
というように、欲しいデータが得られるがやり方がやや気に入らない.
やはりここは、行×列 という形式で各項目を指定することで値を得るようにしたい.
そこで,このテーブル形式のデータについて、
hogehogeのfuga
というより自然な形でアクセスできるようにしたい.
そのためには、 多次元連想配列にしておくと良さそう.
# pythonでは連想配列はディクショナリという.(ハッシュの方が呼びやすいな..辞書と呼ぶものなんだか..)
目的のデータ構造;ディクショナリのディクショナリ
を得るために、分解していく.
以下の処理が必要と考えられる.
- 行ごとに読み込み、改行ごとに行のリストを得る -> readlinesメソッド
- 各行について、特定の文字列で分割し、列ごとのリストを得る -> splitメソッド
- 各行について、特定の列の値をkeyとした、残りの列をvalueとするディクショナリを作る
その先でやりたいことは、特定のkeyで呼び出すようにしたいという事だ.
そのため、単純な"多次元リスト"から、"多次元辞書"の形式にしたい.
3が厄介だ..
少し考えると、行処理×列処理の2重ループなど、面倒な事になると分かる.
目的のデータ構造を得るため、3について、以下のステップに分解して考える.
やり方は煩雑かもしれない.
ⅰ. 列項目となるレコード、実際のデータレコードを決定する ⅱ. 特定の列の値をkeyとした、残りの列をvalueとするディクショナリ というデータ構造を得る.そのために以下ステップを踏む a) "行×列"のディクショナリにする b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする c) dict() によりディクショナリにする
以下、サンプル.
- # テーブルデータを、リストのリストから、 特定の列の値をkeyとした、残りの列をvalueとするディクショナリに変換する
- # table_dict['a']['Y'] ==> 1 という形で、値を取得する方法を実現する.
- table_list = [
- ['X','Y','Z'],
- ['a',1,2],
- ['b',3,4],
- ['c',5,6],
- ]
- # ⅰ. 列項目となるレコード、実際のデータレコードを決定する
- termrecord,valuerecords = table_list[0],table_list[1:]
- # ⅱ. 特定の列の値をkeyとした、残りの列をvalueとするディクショナリ
- # というデータ構造を得る.そのために以下ステップを踏む
- # a) "行×列"のディクショナリにする
- # b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする
- # c) dict() によりディクショナリにする
- # ⅱ-a)
- table_dict_tmp
- = [ dict(zip(termrecord,valuerecord)) for valuerecord in valuerecords ]
- # ⅱ-b)
- table_dict_pair
- = [ devide_dict_to_pair(dc,'X') for dc in table_dict_tmp ]
- # ⅱ-c)
- table_dict = dict(table_dict_pair)
- # 各行を、("特定の列の値",{項目:その他の列の値})というペアにする関数
- def devide_dict_to_pair(dc,targetKey):
- '''''
- porpose : 辞書を、特定のkeyのvalueを全体のkeyとし、それ以外のkey:valueの辞書を要素としたペアに変換する
- contract: devide_dict :: {a} -> a ==> (a,{})
- example : devide_dict({'X':'a','Y':'b'},'X') ==> ('a',{'Y':b})
- '''
- rest_dc = {}
- mainkey = dc[targetKey]
- for key,value in dc.items():
- if key != targetKey:
- rest_dc[key] = value
- return (mainkey,rest_dc)
- # test : devide_dict_to_pair()
- testdc = {'Y': 1, 'X': 'a', 'Z': 2}
- test_devide_dict = devide_dict_to_pair(testdc,'X')
小生の説明、コードは分かりにくい.
流れを自分で理解するため、いちいち変数にあてている.
本当はわざわざ変数化しないで、続きで処理を記述するのが良いんだろうか..
# ちなみに、内包表記は便利すぎて、ついネストさせまくって記述したくなってしまう
データ構造の変遷を図示しておく.
少しは意図が分かりやすくなることを期待する..
- ・デーブルデータ
- [
- [X,Y,Z],
- [a,1,2],
- [b,3,4],
- [c,5,6],
- ]
- # ⅰ. 列項目となるレコード、実際のデータレコードを決定する
- ↓ keyrecord,valuerecords = records[0],records[1:]
- keyrecord = [X,Y,Z]
- valuerecords = [[a,1,2],[b,3,4],[c,5,6]]
- # ⅱ. "特定の列の値をkeyとした、残りの列をvalueとするディクショナリ"というデータ構造を得る
- # a) "行×列"のディクショナリにする
- ↓ [ dict(zip(keyrecord,x)) for x in valuerecords ]
- [
- {'Y': 1, 'X': 'a', 'Z': 2},
- {'Y': 3, 'X': 'b', 'Z': 4},
- {'Y': 5, 'X': 'c', 'Z': 6},
- ]
- # b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする
- ↓ devide_dict_to_pair([{a}],'key') for each record
- [
- ('a', {'Y': 1, 'Z': 2}),
- ('b', {'Y': 3, 'Z': 4}),
- ('c', {'Y': 5, 'Z': 6}),
- ]
- # c) dict() によりディクショナリにする
- ↓ dict()
- {
- 'a': {'Y': 1, 'Z': 2},
- 'c': {'Y': 5, 'Z': 6},
- 'b': {'Y': 3, 'Z': 4},
- }
目的の、"特定の列の値をkeyとした、残りの列をvalueとするディクショナリ"
というデータ構造が得られた.
これで、"xx"の"xx" といった自然な形で値を取ることができる.
上記データを、tabel_dictとしたとき、
"aのYの値" は以下のように取得する.
table_dict['a']['Y'] ==> 1
ここまでやってきたものの、意味があったのか甚だ疑問..
わざわざディクショナリにしなくとも、textによるfladDBのような形にしておき、
呼び出すquery関数側で請け負わせる方が、柔軟性があって、使い勝手も良さそうな気がする.
しかも、データが大量になったときに遅いだろうから、実質あまり使えなそう...
良いプラクティスを考えるにはスキルも経験も足りないかな。。
0 comment:
Post a Comment