LoginSignup
0
0

More than 1 year has passed since last update.

[pandas]辞書<->pd.DataFrame変換をていねいに書いた@値の区切り文字処理

Last updated at Posted at 2023-01-28

要約

区切り文字を抜いて全紐づけパターンを出力する処理を書いた!動いた!
とは言え、辞書型変換を雑スクラッチしてしまった感が強く残った。
その後にもっと良いやり方(諸説あり)を知ったので修正。

学びの備忘録として残す。
一応公開しています。
https://github.com/gmoriki/rm_delimiter

実装の背景

Scivalから提供されるExcelデータに区切り文字が使用されていることが多い。
例えば一つの論文に紐づく著者3名の固有IDが、以下のような形式の値で格納されている。

例:"10000 | 20000 | 300000"

この状態で固有IDと別の情報をマージすることは困難であり、部署内の利用者はうまいこと区切り文字を処理してきた。今後同じような事態(車輪の再生産)を避けるために、今回は区切り文字を排除した上でExcelデータに再出力する、比較的汎用的なプログラムをPythonで書こう、と思い至った。

読み込みテーブル

key,targetという列名を含むExcelデータを用意する。

key target
ronbun1 10000 | 20000 | 300000
ronbun2 6543 | 53 | 45323

出力テーブル

全パターン、冗長な形で出力したい。

key target
ronbun1 10000
ronbun1 20000
ronbun1 300000
ronbun2 6543
ronbun2 53
ronbun2 45323

最初に書いたコード

最初にとりあえず書いてみたもの

rm_delimiter.py

# データの読み込み
df = pd.read_excel(excelpath,dtype=str,usecols=['key','target'])

datalist = []
# dfのレコードに対する繰り返し処理:区切り文字を取ってdatalistに格納する
for record in df.to_dict(orient='records'):

    # 区切り文字の処理
    target_list = [_.strip() for _ in record['target'].split(delimiter)]

    # DataFarmeの元データを作成
    for target in target_list:
        datalist.append({'key':record['key'],'target':target})

# 出力
pd.DataFrame(datalist).to_excel(output_filename,index=False)

1. 読み込んだDataFrameを行単位で辞書データに変換
2. 区切り文字を無くしたリストから要素を取り出し、辞書としてdatalistに追加
3. できあがった辞書をDataFrameに突っ込んで出力

動く!けどなんだか無駄が多い(特に# DataFarmeの元データを作成)。
冗長な形式の縦持ちテーブルを作るには辞書を逐次appendするしかないと考えていた。

当初は辞書の値をリスト型にした出力を考えていたが、
想定と違うDataFrameを返したので非採用。

d = {'key1': [1,2,3], 'key2': [4,5,6,7], 'key3': [8,9]}
pd.DataFrame([d])

        key1          key2    key3
0  [1, 2, 3]  [4, 5, 6, 7]  [8, 9]

うーむ知識の抜け感がぬぐえない。

修正の軌跡

pd.DataFrame.from_dict

辞書の操作を調べてたところ、いくつか参考になる記事を見つけた。

d = {'key1': [1,2,3], 'key2': [4,5,6,7], 'key3': [8,9]}

df = pd.DataFrame.from_dict(d, orient='index').T
# もしくは
df = pd.DataFrame(d.values(), index=d.keys()).T

print(df)
   key1  key2  key3
0   1.0   4.0   8.0
1   2.0   5.0   9.0
2   3.0   6.0   NaN
3   NaN   7.0   NaN

ちゃんとfrom_dictを使えば要素がテーブルの値に乗ってくれることを知る。

df.unstack()

from_dictから作ったDataFrameをunstackすれば、欲しい形が手に入りそう。
unstackはcolumnsをmulit indexに持たせるメソッド。
https://note.nkmk.me/python-pandas-stack-unstack-pivot/

print(df.unstack())

key1  0    1.0
      1    2.0
      2    3.0
      3    NaN
key2  0    4.0
      1    5.0
      2    6.0
      3    7.0
key3  0    8.0
      1    9.0
      2    NaN
      3    NaN
dtype: float64

あとは不要なindex,columnを落とせばOK。

# columnsをmulti indexに持たせる
# リストの長さは可変なのでNanを落としている
df_unstack = df.unstack() \
                .dropna() \
                .reset_index()
                
# 列名の定義・整理
df_unstack.drop(df_unstack = df_.columns[1],inplace=True)
df_unstack.columns=['key','target']

print(df_unstack)

    key  target
0  key1     1.0
1  key1     2.0
2  key1     3.0
3  key2     4.0
4  key2     5.0
5  key2     6.0
6  key2     7.0
7  key3     8.0
8  key3     9.0

欲しい形までたどり着いた。
実際に実装してみる。ついでに可能な限りapplyで対応。

修正後のコード

rm_delimiter_fixed.py
# update対応の箱
tmp_dict = {}
# 区切り文字を排除したリストを辞書に格納する関数
def format_dict_data(row):
    target_list = [_.strip() for _ in row['target'].split(delimiter)]
    tmp_dict.update({row['key']:target_list})
df.apply(format_dict_data,axis=1) # dfにformat_dict_data適用

# 得られた辞書をDataFrameとして再定義
df_from_dict = pd.DataFrame.from_dict(tmp_dict, orient='index').T

# DataFrameを取り回す関数
def preprocess_df(df_):
    df_ = df_.unstack() \
        .dropna() \
        .reset_index()
    df_.drop(columns = df_.columns[1],inplace=True)
    df_.columns=['key','target']
    return df_
df_key_target = df_from_dict.pipe(preprocess_df) # df_from_dictにpreprocess_df適用

1. 区切り文字の排除し辞書データを作成(formmat_dict_data)
2. 得られた辞書データをDataFrameとして再定義
3. DataFrameに対する整形処理(preprocess_df)

結論

思ったより長いコードになった!が、似た情報を何度もappendするよりかは幾分マシだと思う。
改良されたのか諸説あるが道草食って納得感のある実装ができたので及第点。

参考文献

https://qiita.com/ShoheiKojima/items/30ee0925472b7b3e5d5c
https://note.nkmk.me/python-pandas-stack-unstack-pivot/

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0