web-dev-qa-db-ja.com

Pandasデータフレームを共有列とマージし、左から右にフィルナ

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)
9
Kenan

マージを混乱させる問題は、両方のデータフレームに「b」列があることですが、左と右のバージョンでは、一致しない場所にNaNがあります。最初にmergeから不要な複数の「b」列「b_x」、「b_y」を取得しないようにします

  • 非共有列「a」、「e」をdf1からスライスする
  • do 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)
_
4
piRSquared

短縮版

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
5
YOBEN_S

インデックスが揃っている場合のみ(重要な注意)、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 を参照してください。

2
Erfan

データをマスクできます。

元のデータ:

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
2
krewsayder