too late amatuar programmer

[python] テーブルデータを扱う方法を考える

2010-07-12 by ebon | Lavel:

unixやlinuxで作業していると、あるコマンドで得た、テーブルデータ式の出力結果を、
管理する良い方法がないか考えるシーンがたまにだが、ある.
e.g) df や mount の出力結果を管理したい時 etc..

pythonの学習を兼ねて、テーブルデータ形式の出力結果を扱う方法を考えてみる.
簡単な例を元に考える.
ファイルや、標準出力で何らか以下のようなデーブルデータ形式のデータを得たとする.

X Y Z
a 1 2
b 3 4
c 5 6

多次元リストにして扱う という方法が通常の?やり方だと思う.
その場合、リストに対して該当列を行番号で指定する方法か、awkのように行ごと処理する
というように、欲しいデータが得られるがやり方がやや気に入らない.
やはりここは、行×列 という形式で各項目を指定することで値を得るようにしたい.

そこで,このテーブル形式のデータについて、

hogehogeのfuga

というより自然な形でアクセスできるようにしたい.


そのためには、 多次元連想配列にしておくと良さそう.
# pythonでは連想配列はディクショナリという.(ハッシュの方が呼びやすいな..辞書と呼ぶものなんだか..)



目的のデータ構造;ディクショナリのディクショナリ

を得るために、分解していく.
以下の処理が必要と考えられる.

  1. 行ごとに読み込み、改行ごとに行のリストを得る -> readlinesメソッド
  2. 各行について、特定の文字列で分割し、列ごとのリストを得る -> splitメソッド
  3. 各行について、特定の列の値をkeyとした、残りの列をvalueとするディクショナリを作る 

    1,2は普通によくやる事で、この時点で、デーブル形式のデータが得られる.
    その先でやりたいことは、特定のkeyで呼び出すようにしたいという事だ.
    そのため、単純な"多次元リスト"から、"多次元辞書"の形式にしたい.

    3が厄介だ..
    少し考えると、行処理×列処理の2重ループなど、面倒な事になると分かる.

    目的のデータ構造を得るため、3について、以下のステップに分解して考える.
    やり方は煩雑かもしれない.

    ⅰ. 列項目となるレコード、実際のデータレコードを決定する
    ⅱ. 特定の列の値をkeyとした、残りの列をvalueとするディクショナリ
      というデータ構造を得る.そのために以下ステップを踏む
       a) "行×列"のディクショナリにする
       b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする
       c) dict() によりディクショナリにする
    

    以下、サンプル.

    1. # テーブルデータを、リストのリストから、 特定の列の値をkeyとした、残りの列をvalueとするディクショナリに変換する  
    2. # table_dict['a']['Y'] ==> 1 という形で、値を取得する方法を実現する.  
    3.   
    4. table_list = [  
    5.     ['X','Y','Z'],  
    6.     ['a',1,2],  
    7.     ['b',3,4],  
    8.     ['c',5,6],  
    9. ]  
    10.   
    11. # ⅰ. 列項目となるレコード、実際のデータレコードを決定する  
    12. termrecord,valuerecords = table_list[0],table_list[1:]  
    13.   
    14. # ⅱ. 特定の列の値をkeyとした、残りの列をvalueとするディクショナリ  
    15. # というデータ構造を得る.そのために以下ステップを踏む  
    16. # a) "行×列"のディクショナリにする  
    17. # b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする  
    18. # c) dict() によりディクショナリにする  
    19.   
    20. # ⅱ-a)  
    21. table_dict_tmp   
    22.     = [ dict(zip(termrecord,valuerecord)) for valuerecord in valuerecords ]  
    23.   
    24. # ⅱ-b)  
    25. table_dict_pair   
    26.     = [ devide_dict_to_pair(dc,'X'for dc in table_dict_tmp ]  
    27.   
    28. # ⅱ-c)  
    29. table_dict = dict(table_dict_pair)  
    30.   
    31. # 各行を、("特定の列の値",{項目:その他の列の値})というペアにする関数  
    32. def devide_dict_to_pair(dc,targetKey):  
    33.     ''''' 
    34.     porpose : 辞書を、特定のkeyのvalueを全体のkeyとし、それ以外のkey:valueの辞書を要素としたペアに変換する 
    35.     contract: devide_dict :: {a} -> a ==> (a,{}) 
    36.     example : devide_dict({'X':'a','Y':'b'},'X') ==> ('a',{'Y':b}) 
    37.     '''  
    38.     rest_dc = {}  
    39.     mainkey = dc[targetKey]  
    40.     for key,value in dc.items():  
    41.         if key != targetKey:  
    42.             rest_dc[key] = value  
    43.     return (mainkey,rest_dc)  
    44.   
    45. # test : devide_dict_to_pair()  
    46. testdc = {'Y'1'X''a''Z'2}  
    47. test_devide_dict = devide_dict_to_pair(testdc,'X')  

    小生の説明、コードは分かりにくい.
    流れを自分で理解するため、いちいち変数にあてている.
    本当はわざわざ変数化しないで、続きで処理を記述するのが良いんだろうか..
    # ちなみに、内包表記は便利すぎて、ついネストさせまくって記述したくなってしまう

    データ構造の変遷を図示しておく.
    少しは意図が分かりやすくなることを期待する..

    1. ・デーブルデータ  
    2. [  
    3.     [X,Y,Z],  
    4.     [a,1,2],  
    5.     [b,3,4],  
    6.     [c,5,6],  
    7. ]  
    8.   
    9. # ⅰ. 列項目となるレコード、実際のデータレコードを決定する  
    10. ↓ keyrecord,valuerecords = records[0],records[1:]  
    11.   
    12. keyrecord    = [X,Y,Z]  
    13. valuerecords = [[a,1,2],[b,3,4],[c,5,6]]  
    14.   
    15. # ⅱ. "特定の列の値をkeyとした、残りの列をvalueとするディクショナリ"というデータ構造を得る  
    16.   
    17. # a) "行×列"のディクショナリにする  
    18. ↓ [ dict(zip(keyrecord,x)) for x in valuerecords ]   
    19.   
    20. [  
    21.     {'Y'1'X''a''Z'2},  
    22.     {'Y'3'X''b''Z'4},  
    23.     {'Y'5'X''c''Z'6},  
    24. ]  
    25.   
    26. # b) a) の各行を、("特定の列の値",{項目:その他の列の値})というペアにする  
    27. ↓ devide_dict_to_pair([{a}],'key'for each record  
    28.   
    29. [  
    30.     ('a', {'Y'1'Z'2}),  
    31.     ('b', {'Y'3'Z'4}),  
    32.     ('c', {'Y'5'Z'6}),  
    33. ]  
    34.   
    35. # c) dict() によりディクショナリにする  
    36. ↓ dict()  
    37.   
    38. {  
    39.     'a': {'Y'1'Z'2},  
    40.     'c': {'Y'5'Z'6},  
    41.     'b': {'Y'3'Z'4},  
    42. }  

    目的の、"特定の列の値をkeyとした、残りの列をvalueとするディクショナリ"
    というデータ構造が得られた.

    これで、"xx"の"xx" といった自然な形で値を取ることができる.
    上記データを、tabel_dictとしたとき、
    "aのYの値" は以下のように取得する.

    table_dict['a']['Y'] ==> 1
    

    ここまでやってきたものの、意味があったのか甚だ疑問..
    わざわざディクショナリにしなくとも、textによるfladDBのような形にしておき、
    呼び出すquery関数側で請け負わせる方が、柔軟性があって、使い勝手も良さそうな気がする.
    しかも、データが大量になったときに遅いだろうから、実質あまり使えなそう...
    良いプラクティスを考えるにはスキルも経験も足りないかな。。

    0 comment:

    Post a Comment