web-dev-qa-db-ja.com

比較Python Pandas一致する行のDataFrames

私はこのDataFrame(_df1_)をパンダに持っています:

_df1 = pd.DataFrame(np.random.Rand(10,4),columns=list('ABCD'))
print df1

       A         B         C         D
0.860379  0.726956  0.394529  0.833217
0.014180  0.813828  0.559891  0.339647
0.782838  0.698993  0.551252  0.361034
0.833370  0.982056  0.741821  0.006864
0.855955  0.546562  0.270425  0.136006
0.491538  0.445024  0.971603  0.690001
0.911696  0.065338  0.796946  0.853456
0.744923  0.545661  0.492739  0.337628
0.576235  0.219831  0.946772  0.752403
0.164873  0.454862  0.745890  0.437729
_

別のデータフレーム(_df2_)の行(すべての列)が_df1_に存在するかどうかを確認したいと思います。ここに_df2_があります:

_df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
print df2

           A         B         C         D
    0.855955  0.546562  0.270425  0.136006
    0.491538  0.445024  0.971603  0.690001
    0.911696  0.065338  0.796946  0.853456
    0.744923  0.545661  0.492739  0.337628
    0.576235  0.219831  0.946772  0.752403
    2.000000  3.000000  4.000000  5.000000
   14.000000 15.000000 16.000000 17.000000
_

_df.lookup_を使用して、一度に1つの行を検索しようとしました。私はこのようにしました:

_list1 = df2.ix[0].tolist()
cols = df1.columns.tolist()
print df1.lookup(list1, cols)
_

しかし、私はこのエラーメッセージを受け取りました:

_  File "C:\Users\test.py", line 19, in <module>
    print df1.lookup(list1, cols)
  File "C:\python27\lib\site-packages\pandas\core\frame.py", line 2217, in lookup
    raise KeyError('One or more row labels was not found')
KeyError: 'One or more row labels was not found'
_

私は.all()も試しました:

_print (df2 == df1).all(1).any()
_

しかし、私はこのエラーメッセージを受け取りました:

_  File "C:\Users\test.py", line 12, in <module>
    print (df2 == df1).all(1).any()
  File "C:\python27\lib\site-packages\pandas\core\ops.py", line 884, in f
    return self._compare_frame(other, func, str_rep)
  File "C:\python27\lib\site-packages\pandas\core\frame.py", line 3010, in _compare_frame
    raise ValueError('Can only compare identically-labeled '
ValueError: Can only compare identically-labeled DataFrame objects
_

私もこのようにisin()を試しました:

_print df2.isin(df1)
_

しかし、どこでもFalseを得ましたが、これは正しくありません:

_    A      B      C      D
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
_

別のデータフレームの行と比較することにより、DataFrameの行セットを検索することは可能ですか?

編集:_df2_行が_df1_にも存在する場合、それらを削除することは可能ですか?

25
edesz

問題の解決策の1つは、 merge を使用することです。別のデータフレーム(df2)の行(すべての列)がdf1に存在するかどうかを確認することは、2つのデータフレームの交差を決定することと同等です。これは、次の機能を使用して実現できます。

_pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner')
_

たとえば、df1が

_    A           B            C          D
0   0.403846    0.312230    0.209882    0.397923
1   0.934957    0.731730    0.484712    0.734747
2   0.588245    0.961589    0.910292    0.382072
3   0.534226    0.276908    0.323282    0.629398
4   0.259533    0.277465    0.043652    0.925743
5   0.667415    0.051182    0.928655    0.737673
6   0.217923    0.665446    0.224268    0.772592
7   0.023578    0.561884    0.615515    0.362084
8   0.346373    0.375366    0.083003    0.663622
9   0.352584    0.103263    0.661686    0.246862
_

df2は次のように定義されました。

_     A          B            C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622
5   2.000000    3.000000    4.000000    5.000000
6   14.000000   15.000000   16.000000   17.000000
_

関数pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner')は以下を生成します。

_     A           B           C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622
_

結果は、df1とdf2の両方にあるすべての行(すべての列)です。

列がdf1とdf2で同じでない場合、この例を変更し、列のサブセットで同じ行の値を比較することもできます。元の例を変更する場合:

_df1 = pd.DataFrame(np.random.Rand(10,4),columns=list('ABCD'))
df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
df2 = df2[['A', 'B', 'C']] # df2 has only columns A B C
_

次に、2つのデータフレーム間でcommon_cols = list(set(df1.columns) & set(df2.columns))を使用して共通の列を確認し、マージします。

_pd.merge(df1, df2, on=common_cols, how='inner')
_

EDIT:新しい質問(コメント)は、最初のデータフレーム(df1)にも存在するdf2の行を識別したため、 pd.merge()の結果、df1にも存在するdf2から行をドロップする

私はdf1にも存在するdf2から行をドロップするタスクを達成するための簡単な方法を知りません。とはいえ、次のものを使用できます。

_ds1 = set(Tuple(line) for line in df1.values)
ds2 = set(Tuple(line) for line in df2.values)
df = pd.DataFrame(list(ds2.difference(ds1)), columns=df2.columns)
_

おそらくそのタスクを達成するためのより良い方法が存在しますが、私はそのようなメソッド/機能を知りません。

EDIT 2:@WR回答に示されているように、df1にも存在するdf2から行を削除する方法。

提供されるメソッドdf2[~df2['A'].isin(df12['A'])]は、すべてのタイプの状況を考慮していません。以下のデータフレームを考慮してください。

df1:

_   A  B  C  D
0  6  4  1  6
1  7  6  6  8
2  1  6  2  7
3  8  0  4  1
4  1  0  2  3
5  8  4  7  5
6  4  7  1  1
7  3  7  3  4
8  5  2  8  8
9  3  2  8  4
_

df2:

_   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8
5  1  1  1  1
6  2  2  2  2
_

df12:

_   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8
_

Df1にも存在するdf2から行を削除する目的で上記のDataFramesを使用すると、次のようになります。

_   A  B  C  D
0  1  1  1  1
1  2  2  2  2
_

行(1、1、1、1)および(2、2、2、2)はdf2にあり、df1にはありません。残念ながら、提供されたメソッド(df2[~df2['A'].isin(df12['A'])])を使用すると次の結果になります。

_   A  B  C  D
6  2  2  2  2
_

これは、列Aの値1が交差点DataFrame(つまり(1、0、2、3))とdf2の両方で検出され、したがって(1、0、2、3)と(1、1、 1、1)。これは、行(1、1、1、1)がdf1にないため、削除しないでください。

以下が解決策を提供すると思います。後でDataFrameをサブセットして目的の結果に使用するダミー列を作成します。

_df12['key'] = 'x'
temp_df = pd.merge(df2, df12, on=df2.columns.tolist(), how='left')
temp_df[temp_df['key'].isnull()].drop('key', axis=1)
_
37
Andrew

@Andrew:ループを使用せずに、別のデータフレームに既に存在する1つのデータフレームの行を削除する方法を見つけたと思います(つまり、編集に応答する)これを述べてください:

この作品

両方のデータフレームの列は常に同じです-ABCおよびD。これを念頭に置いて、Andrewのアプローチに大きく基づいて、_df2_に存在する行を_df1_から削除する方法を次に示します。

_common_cols = df1.columns.tolist()                         #generate list of column names
df12 = pd.merge(df1, df2, on=common_cols, how='inner')     #extract common rows with merge
df2 = df2[~df2['A'].isin(df12['A'])]
_

行3は次のことを行います。

  • _df2_の行と一致しない行のみを_df1_から抽出します。
  • 2つの行が異なるためには、1つの行の1つの列が
    別の行の対応する列とは必ずしも異なる必要があります。
  • ここでは、この比較を行うために列Aを選択しました。
    任意の列名を使用できますが、not ALL
    列名。

注:このメソッドは、本質的にSQL NOT IN()と同等です。

8
edesz