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