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