2つのデータフレームをマージして、左側のdfのナンを右側のdfに置き換えようとしています。以下のように3行のコードでそれを行うことができますが、より良い/短い方法があるかどうか知りたいですか?
# Example data (my actual df is ~500k rows x 11 cols)
df1 = pd.DataFrame({'a': [1,2,3,4], 'b': [0,1,np.nan, 1], 'e': ['a', 1, 2,'b']})
df2 = pd.DataFrame({'a': [1,2,3,4], 'b': [np.nan, 1, 0, 1]})
# Merge the dataframes...
df = df1.merge(df2, on='a', how='left')
# Fillna in 'b' column of left df with right df...
df['b'] = df['b_x'].fillna(df['b_y'])
# Drop the columns no longer needed
df = df.drop(['b_x', 'b_y'], axis=1)
マージを混乱させる問題は、両方のデータフレームに「b」列があることですが、左と右のバージョンでは、一致しない場所にNaNがあります。最初にmerge
から不要な複数の「b」列「b_x」、「b_y」を取得しないようにします:
merge(df2, 'left')
、これは右のデータフレームから 'b'を取得します(右のdfにのみ存在するため)df1.update(...)
を実行します。これにより、df2から取得した列 'b'のNaNが_df1['b']
_で更新されます解決:
_df1.update(df1[['a', 'e']].merge(df2, 'left'))
df1
a b e
0 1 0.0 a
1 2 1.0 1
2 3 0.0 2
3 4 1.0 b
_
注:merge(..., how='left')
を使用したため、呼び出し側のデータフレームの行の順序を保持しています。私の_df1
_にa
の値があり、順序が正しくない場合
_ a b e
0 1 0.0 a
1 2 1.0 1
2 4 1.0 b
3 3 NaN 2
_
結果は
_df1.update(df1[['a', 'e']].merge(df2, 'left'))
df1
a b e
0 1 0.0 a
1 2 1.0 1
2 4 1.0 b
3 3 0.0 2
_
予想通りです。
より多くの列が含まれる可能性がある場合に、より明確にしたい場合
_df1.update(df1.drop('b', 1).merge(df2, 'left', 'a'))
_
データフレームをupdate
したくない場合は、_combine_first
_を使用できます
クイック
_df1.combine_first(df1[['a', 'e']].merge(df2, 'left'))
_
明示的
_df1.combine_first(df1.drop('b', 1).merge(df2, 'left', 'a'))
_
_'left'
_ merge
は順序を維持できますが、インデックスは[〜#〜]しない[〜#〜]。これは非常に保守的なアプローチです。
_df3 = df1.drop('b', 1).merge(df2, 'left', on='a').set_index(df1.index)
df1.combine_first(df3)
_
短縮版
df1.b.fillna(df1.a.map(df2.set_index('a').b),inplace=True)
df1
Out[173]:
a b e
0 1 0.0 a
1 2 1.0 1
2 3 0.0 2
3 4 1.0 b
あなたが言及したので、複数の列があるでしょう
df = df1.combine_first(df1[['a']].merge(df2, on='a', how='left'))
df
Out[184]:
a b e
0 1 0.0 a
1 2 1.0 1
2 3 0.0 2
3 4 1.0 b
また、dfを使用してfillna
に渡すこともできます
df1.fillna(df1[['a']].merge(df2, on='a', how='left'))
Out[185]:
a b e
0 1 0.0 a
1 2 1.0 1
2 3 0.0 2
3 4 1.0 b
インデックスが揃っている場合のみ(重要な注意)、update
を使用できます。
df1['b'].update(df2['b'])
a b e
0 1 0.0 a
1 2 1.0 1
2 3 0.0 2
3 4 1.0 b
または単にfillna
:
df1['b'].fillna(df2['b'], inplace=True)
インデックスが揃っていない場合は、 WenNYoBen's answerまたは comment を参照してください。
データをマスクできます。
元のデータ:
print(df)
one two three
0 1 1.0 1.0
1 2 NaN 2.0
2 3 3.0 NaN
print(df2)
one two three
0 4 4 4
1 4 2 4
2 4 4 3
以下を参照してください。条件に基づいて塗りつぶしだけをマスクします。
# mask values where isna()
df1[['two','three']] = df1[['two','three']]\
.mask(df1[['two','three']].isna(),df2[['two','three']])
出力:
one two three
0 1 1.0 1.0
1 2 2.0 2.0
2 3 3.0 3.0