アイソモカ

知の遊牧民の開発記録

開発記録 200311 Wed (データフレーム欠損位置を他のデータフレームにも反映)

自主課題(ということにしておきましょう。オシゴトでポチョポチョやっている開発で、どうするかなあと考えていて、家に帰って作った)。

課題

2つのデータフレームを比較したい。2つのデータフレームの行と列は共通だが、1つのデータフレームには欠損値が含まれている。欠損値があると比較できないので、前処理のひとつとして、片方の欠損値の位置をもう片方に反映する。

実行結果

raw_df1 は欠損のないデータフレームで、raw_df2 は欠損のあるデータフレーム。欠損値 Nan は、位置 (1, 2), (2, 1), (3, 0) にある。

df1 は欠損位置を反映した後のデータフレーム。位置 (1, 2), (2, 1), (3, 0) が Nan になっている。 なお、df2raw_df2 のまま。

$ python mask_nan_pandas.py 
raw_df1:
      0    1    2
0  1.0  2.0  3.0
1  1.0  2.0  3.0
2  1.0  2.0  3.0
3  1.0  2.0  3.0 

raw_df2:
      0    1    2
0  2.0  4.0  6.0
1  2.0  4.0  NaN
2  2.0  NaN  6.0
3  NaN  4.0  6.0 

--------------------
df1:
      0    1    2
0  1.0  2.0  3.0
1  1.0  2.0  NaN
2  1.0  NaN  3.0
3  NaN  2.0  3.0 

df2:
      0    1    2
0  2.0  4.0  6.0
1  2.0  4.0  NaN
2  2.0  NaN  6.0
3  NaN  4.0  6.0 

メモ:欠損値 np.nan かどうかを判定する

欠損値同士を == で比較しても False となってしまうため、欠損値かどうかを判定するには np.isnan()math.isnan() を使う。

👉 NumPyの配列ndarrayの欠損値np.nanを他の値に置換 | note.nkmk.me

メモ:df.itertuples() で行ごとに読む

参考 👉 pandas.DataFrameのforループ処理(イテレーション) | note.nkmk.me

for i, row in enumerate(df.itertuples()):
        print(row)

 ↓

Pandas(Index=0, _1=2.0, _2=4.0, _3=6.0)
Pandas(Index=1, _1=2.0, _2=4.0, _3=nan)
Pandas(Index=2, _1=2.0, _2=nan, _3=6.0)
Pandas(Index=3, _1=nan, _2=4.0, _3=6.0)

コード

もっとスマートでシンプルなやり方があるんじゃないかと思いつつ、分からなくてこうなった。

# mask_nan_pandas.py
import numpy as np
import pandas as pd


# 生のデータフレームを作る
def make_df():
    df1 = pd.DataFrame({\
        '0':[1, 1, 1, 1],\
        '1':[2, 2, 2, 2],\
        '2':[3, 3, 3, 3]\
    }, dtype=np.float32)

    df2 = pd.DataFrame({\
        '0':[2, 2, 2, np.nan],\
        '1':[4, 4, np.nan, 4],\
        '2':[6, np.nan, 6, 6]\
    }, dtype=np.float32)

    return df1, df2


# データフレーム内の欠損値を探し、リストで返す
def get_iloc_nan(df):
    iloc_nan_list = []
    for i, row in enumerate(df.itertuples()):
        for j, value in enumerate(row[1:]):
            if np.isnan(value):
                iloc_nan_list.append((i, j))
    return iloc_nan_list


# 位置を指定して NaN で置き換える
def mask(df1, df2):
    for locat in get_iloc_nan(df2):
        df1.iloc[locat[0], locat[1]] = np.nan
    return df1, df2


if __name__ == '__main__':
    raw_df1, raw_df2 = make_df()
    print('raw_df1:\n', raw_df1, '\n')
    print('raw_df2:\n', raw_df2, '\n')
    print('-'*20)
    df1, df2 = mask(raw_df1, raw_df2)
    print('df1:\n', df1, '\n')
    print('df2:\n', df2, '\n')