パンダは他のデータフレームにない行を取得します


229

いくつかの行が共通する2つのパンダデータフレームがあります。

dataframe2がdataframe1のサブセットであるとします。

dataframe2にないdataframe1の行を取得するにはどうすればよいですか?

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})

1
@TedPetrou私はあなたが提供した答えがどのように正しいものであるかを確認できません。2つのデータフレームのうち、一方が他方のサブセットである場合、サブセット内にあるそれらの行をすべて削除する必要があります。重複を削除したくない。サブセットを完全に削除したい。
ジュークボックス

回答:


172

1つの方法は、両方のdfの内部マージフォームの結果を格納することです。1つの列の値がこの共通でない場合は、行を単に選択できます。

In [119]:

common = df1.merge(df2,on=['col1','col2'])
print(common)
df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))]
   col1  col2
0     1    10
1     2    11
2     3    12
Out[119]:
   col1  col2
3     4    13
4     5    14

編集

あなたが見つけた別の方法は、あなたがドロップできる行をisin生成NaNすることを使うことです:

In [138]:

df1[~df1.isin(df2)].dropna()
Out[138]:
   col1  col2
3     4    13
4     5    14

ただし、df2が同じ方法で行を開始しない場合、これは機能しません。

df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})

df全体を生成します。

In [140]:

df1[~df1.isin(df2)].dropna()
Out[140]:
   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14

13
df1[~df1.isin(df2)].dropna(how = 'all')トリックを行うようです。とにかくありがとう-あなたの答えは私が解決策を見つけるのに役立ちました。
いいことを考える

5
使用isinするには、両方のdfが同じ行の値で始まる必要があることに注意してください。たとえば、df2がそうだったdf2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11,12, 13]})場合、メソッドは機能しません
EdChum

2
これはすべてのintをfloatに変換しました!
Chris Nielsen 2017年

3
@SergeyZakharov約3年前に投稿されたこの回答は、OPに関する限り正しいものであり、その問題については、他の回答がより適切な回答であり、元の質問の一部ではなかった幅広い問題を処理します。これを述べるのは誤りです答えは間違っています。設定された問題を考えると正しいです。さらに、誰かが説明なしでこれに反対票を投じました。これは受け入れられた回答であるため、私ができることはほとんどありません。OPは彼らの考えを変えていません。それを正しくするために別の回答を共食いするつもりはありません。
EdChum

1
@Cecilia渡す必要がありますkeep=Falsedf0.append(df1).drop_duplicates(keep=False)デフォルトでは、最初の重複を保持し、すべての重複を削除します
EdChum

189

現在選択されているソリューションでは、正しくない結果が生成されます。この問題を正しく解決するには、左結合from df1を実行してdf2、最初にの一意の行だけを取得するようにしdf2ます。

最初に、元のDataFrameを変更して、データを含む行を追加する必要があります[3、10]。

df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 
                           'col2' : [10, 11, 12, 13, 14, 10]}) 
df2 = pd.DataFrame(data = {'col1' : [1, 2, 3],
                           'col2' : [10, 11, 12]})

df1

   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14
5     3    10

df2

   col1  col2
0     1    10
1     2    11
2     3    12

左結合を実行して重複を排除しdf2df1結合の各行がの1行だけになるようにしdf2ます。パラメータindicatorを使用して、行の元のテーブルを示す追加の列を返します。

df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'], 
                   how='left', indicator=True)
df_all

   col1  col2     _merge
0     1    10       both
1     2    11       both
2     3    12       both
3     4    13  left_only
4     5    14  left_only
5     3    10  left_only

ブール条件を作成します。

df_all['_merge'] == 'left_only'

0    False
1    False
2    False
3     True
4     True
5     True
Name: _merge, dtype: bool

他の解決策が間違っている理由

いくつかの解決策は同じ間違いを犯します-それらは、各値が同じ行に一緒にではなく、各列に独立していることのみをチェックします。一意ですが、両方の列の値を持つ最後の行を追加df2すると、間違いが明らかになります。

common = df1.merge(df2,on=['col1','col2'])
(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))
0    False
1    False
2    False
3     True
4     True
5    False
dtype: bool

このソリューションは同じ間違った結果を取得します:

df1.isin(df2.to_dict('l')).all(1)

2
しかし、おそらく、彼らはcol1がインデックスであることを一意であると想定していました(質問には記載されていませんが、明白です)。したがって、col1の同じ値に対して2つのcol2の値が存在するようなケースが決してない場合(2つのcol1 = 3行はあり得ない)、上記の答えは正しいです。
pashute

14
それは確かに明白ではないので、あなたの主張は無効です。私の解決策は、より多くのケースに一般化します。
Ted Petrou 2017年

質問、ブール配列ではなくスライスを作成する方が簡単ではないでしょうか?行を取得することが目的なので、
マティアスロモ

5
結果を使用df_all[df_all['_merge'] == 'left_only']してdfを作成するために使用
gies0r

77

インデックスがデータフレームで一貫していると仮定します(実際のcol値は考慮していません):

df1[~df1.index.isin(df2.index)]

1
@ChrisNielsen条件の否定。したがって、この例では、「df1インデックスが含まれていない行を取得する」という意味df2.indexです。否定の詳細:stackoverflow.com/q/19960077/304209(驚いたことに、パンダのドキュメントでチルドの言及が見つかりませんでした)。
Dennis Golomazov 2017年

dfは同じ長さである必要があるようですが、違いますか?私は得ていますValueError: Item wrong length x instead of y.
言葉の言い方

@wordsforthewiseいいえ、彼らはしません。マスクの長さはdf1で、df1にも適用されます。あなたの例を提供できますか?
Dennis Golomazov 2017

アイテムの長さの問題を修正するには、.loc
Moreno

13

すでに示唆したように、isinは列とインデックスが一致するために同じである必要があります。一致が行の内容のみに当てはまる場合、存在する行をフィルタリングするためのマスクを取得する1つの方法は、行を(マルチ)インデックスに変換することです。

In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]})
In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]})
In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)]
Out[79]:
   col1  col2
1     2    11
4     5    14
5     3    10

インデックスを考慮する必要がある場合、set_indexには、既存のインデックスに列を追加するキーワード引数appendがあります。列が整列しない場合は、list(df.columns)を列指定で置き換えて、データを整列できます。

pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())

代わりにインデックスを作成するために使用することもできますが、これがより効率的であるとは思えません。


@ Dev_123最初の〜を削除します。コアは、df1の行がdf2でも発生するかどうかの述語リストを作成するため、df1の行はdf1に一意ではないため、これをdf1の行がdf2で発生しないかどうかの述語リストに否定します。
Rune Lyngsoe

11

2つのデータフレーム、df_1とdf_2に複数のフィールド(column_names)があり、df_2にないdf_1内のエントリのみをいくつかのフィールド(たとえば、fields_x、fields_y)に基づいて検索する場合は、次の手順に従います。

Step1。列key1とkey2をそれぞれdf_1とdf_2に追加します。

Step2。以下に示すようにデータフレームをマージします。field_xとfield_yが目的の列です。

Step3。df_1から、key1がkey2と等しくない行のみを選択します。

Step4。key1とkey2をドロップします。

この方法は問題を解決し、ビッグデータセットでも高速に動作します。1,000,000行を超えるデータフレームで試してみました。

df_1['key1'] = 1
df_2['key2'] = 1
df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left')
df_1 = df_1[~(df_1.key2 == df_1.key1)]
df_1 = df_1.drop(['key1','key2'], axis=1)

これは技術的に彼が望んでいることだとは思いません-彼はどの行がどのdfに一意であるかを知りたいのです。しかし、この解決策は、最初のdfまたは2番目のdfに一意の行のdfを返すと思います。
正当なスタック


3

isin(dict)メソッドを使用してそれを行うことができます:

In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)]
Out[74]:
   col1  col2
3     4    13
4     5    14

説明:

In [75]: df2.to_dict('l')
Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]}

In [76]: df1.isin(df2.to_dict('l'))
Out[76]:
    col1   col2
0   True   True
1   True   True
2   True   True
3  False  False
4  False  False

In [77]: df1.isin(df2.to_dict('l')).all(1)
Out[77]:
0     True
1     True
2     True
3    False
4    False
dtype: bool

これは間違った結果をもたらします。以下の私の説明を参照してください。
Ted Petrou 2017年

2

またCONCATことができdf1df2

x = pd.concat([df1, df2])

そして、すべての重複を削除します。

y = x.drop_duplicates(keep=False, inplace=False)

StackOverflowへようこそ:コード、XML、またはデータサンプルを投稿する場合は、テキストエディターでそれらの行を強調表示し、エディターのツールバーの[コードサンプル]ボタン({})をクリックするか、キーボードでCtrl + Kを使用して適切にフォーマットしてくださいそして構文はそれを強調します!
WhatsThePoint 2018

4
これは、df1にのみ存在するデータだけでなく、いずれかのセットに存在するすべてのデータを返します。
ジェイミーマーシャル

1

これはどう:

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 
                               'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 
                               'col2' : [10, 11, 12]})
records_df2 = set([tuple(row) for row in df2.values])
in_df2_mask = np.array([tuple(row) in records_df2 for row in df1.values])
result = df1[~in_df2_mask]

1

これを解決する別の方法を次に示します。

df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

または:

df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

0

これを行う私の方法には、1つのデータフレームに固有の新しい列を追加し、これを使用してエントリを保持するかどうかを選択することが含まれます

df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)

これにより、df1のすべてのエントリにコードがあります。df1に一意の場合は0、両方のデータフレームにある場合は1になります。次に、これを使用して必要なものに制限します

answer = nonuni[nonuni['Empt'] == 0]

0
マージ機能を使用して異なる行を抽出する
df = df.merge(same.drop_duplicates(), on=['col1','col2'], 
               how='left', indicator=True)
異なる行をCSVに保存する
df[df['_merge'] == 'left_only'].to_csv('output.csv')
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.