gấu trúc có được các hàng KHÔNG trong khung dữ liệu khác


229

Tôi có hai khung dữ liệu gấu trúc có một số hàng chung.

Giả sử dataframe2 là tập con của dataframe1.

Làm cách nào tôi có thể nhận được các hàng của dataframe1 không có trong dataframe2?

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 Tôi không biết câu trả lời bạn cung cấp là câu trả lời đúng như thế nào. Nếu tôi có hai datafram trong đó một tập con là tập con khác, tôi cần xóa tất cả các hàng nằm trong tập hợp con. Tôi không muốn loại bỏ trùng lặp. Tôi hoàn toàn muốn loại bỏ tập hợp con.
máy hát tự động

Câu trả lời:


172

Một phương pháp sẽ là lưu trữ kết quả của một hình thức hợp nhất bên trong cả hai dfs, sau đó chúng ta có thể chỉ cần chọn các hàng khi các giá trị của một cột không phổ biến:

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

BIÊN TẬP

Một phương pháp khác mà bạn đã tìm thấy là sử dụng isinsẽ tạo ra NaNcác hàng mà bạn có thể thả:

In [138]:

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

Tuy nhiên, nếu df2 không bắt đầu các hàng theo cách tương tự thì điều này sẽ không hoạt động:

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

sẽ sản xuất toàn bộ 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')dường như để làm các mẹo. Dù sao cũng cảm ơn - câu trả lời của bạn đã giúp tôi tìm ra giải pháp.
nghĩ những điều tốt đẹp

5
Lưu ý rằng việc sử dụng isinyêu cầu cả hai dfs đều bắt đầu với cùng một giá trị hàng, vì vậy, nếu df2 df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11,12, 13]})thì phương thức của bạn sẽ không hoạt động
EdChum

2
Điều này đã chuyển đổi tất cả các ints để nổi!
Chris Nielsen

3
@SergeyZakharov câu trả lời này được đăng gần 3 năm trước là đúng theo quan điểm của OP và đối với vấn đề của họ, câu trả lời khác là câu trả lời tốt hơn và xử lý một vấn đề rộng hơn không bao giờ là một phần của câu hỏi ban đầu, không đúng khi nói rằng Trả lời là sai, nó là chính xác cho vấn đề như đặt ra. Ngoài ra, ai đó đã đánh giá thấp điều này mà không cần giải thích, tôi có thể làm rất ít vì đây là câu trả lời được chấp nhận, OP đã không thay đổi ý định và tôi sẽ không thể thay đổi câu trả lời khác để làm cho đúng .
EdChum

1
@Cecilia bạn cần vượt qua keep=False: df0.append(df1).drop_duplicates(keep=False), theo mặc định, nó giữ bản sao đầu tiên, bạn muốn loại bỏ tất cả các bản sao
EdChum

189

Các giải pháp hiện đang được lựa chọn tạo ra kết quả không chính xác. Để giải quyết chính xác vấn đề này, chúng tôi có thể thực hiện nối từ trái df1sang df2, trước tiên hãy đảm bảo chỉ nhận các hàng duy nhất chodf2 .

Trước tiên, chúng ta cần sửa đổi DataFrame gốc để thêm hàng với dữ liệu [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

Thực hiện nối trái, loại bỏ trùng lặp sao df2cho mỗi hàng df1nối với đúng 1 hàng df2. Sử dụng tham số indicatorđể trả về một cột phụ cho biết hàng đó được lấy từ bảng nào.

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

Tạo một điều kiện boolean:

df_all['_merge'] == 'left_only'

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

Tại sao các giải pháp khác là sai

Một vài giải pháp mắc cùng một lỗi - họ chỉ kiểm tra xem mỗi giá trị độc lập trong mỗi cột chứ không phải cùng một hàng. Thêm hàng cuối cùng, là duy nhất nhưng có các giá trị từ cả hai cột df2để lộ lỗi:

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

Giải pháp này nhận được kết quả sai tương tự:

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

2
nhưng, tôi cho rằng, họ đã cho rằng col1 là duy nhất là một chỉ mục (không được đề cập trong câu hỏi, nhưng rõ ràng). Vì vậy, nếu không bao giờ có trường hợp như vậy khi có hai giá trị col2 cho cùng một giá trị của col1 (không thể có hai col1 = 3 hàng) thì các câu trả lời ở trên là chính xác.
pashute

14
Nó chắc chắn không rõ ràng, vì vậy quan điểm của bạn là không hợp lệ. Giải pháp của tôi khái quát cho nhiều trường hợp.
Ted Petrou

Câu hỏi, sẽ dễ dàng hơn để tạo ra một lát cắt hơn là một mảng boolean? Vì mục tiêu là để có được các hàng.
Matías Romo

5
Sử dụng df_all[df_all['_merge'] == 'left_only']để có một df với kết quả
gies0r

77

Giả sử rằng các chỉ mục phù hợp trong các dataframes (không tính đến các giá trị col thực tế):

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

1
@ChrisNielsen phủ định điều kiện. Vì vậy, trong ví dụ này có nghĩa là "lấy các hàng df1mà chỉ mục KHÔNG ở trong df2.index". Thông tin thêm về phủ định: stackoverflow.com/q/19960077/304209 (đáng ngạc nhiên, tôi không thể tìm thấy bất kỳ đề cập nào về dấu ngã trong tài liệu gấu trúc).
Dennis Golomazov

Có vẻ như các dfs phải có cùng chiều dài, phải không? Tôi đang nhận đượcValueError: Item wrong length x instead of y.
wordsforthewise

@wordsforthewise không, họ không. Mặt nạ có chiều dài của df1 và cũng được áp dụng cho df1. Bạn có thể cung cấp ví dụ của bạn?
Dennis Golomazov

Để sửa vấn đề về độ dài vật phẩm, bạn nên thêm .loc
Moreno

13

Như đã được gợi ý, isin yêu cầu các cột và chỉ số giống nhau cho một trận đấu. Nếu chỉ khớp với nội dung hàng, một cách để lấy mặt nạ để lọc các hàng hiện tại là chuyển đổi các hàng thành Chỉ mục (Đa):

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

Nếu chỉ mục cần được tính đến, set_index có thêm đối số từ khóa để nối thêm các cột vào chỉ mục hiện có. Nếu các cột không xếp hàng, danh sách (df.columns) có thể được thay thế bằng thông số kỹ thuật của cột để căn chỉnh dữ liệu.

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

cách khác có thể được sử dụng để tạo các chỉ số, mặc dù tôi nghi ngờ điều này là hiệu quả hơn.


@ Dev_123 Xóa ~ lúc đầu. Cốt lõi là tạo một danh sách vị ngữ cho dù các hàng trong df1 cũng xảy ra trong df2, vì vậy các hàng trong df1 không phải là duy nhất cho df1, ~ phủ nhận điều này vào danh sách vị ngữ về việc các hàng trong df1 không xảy ra trong df2.
Rune Lyngsoe

11

Giả sử bạn có hai datafram, df_1 và df_2 có nhiều trường (cột_names) và bạn muốn tìm các mục duy nhất trong df_1 không có trong df_2 trên cơ sở một số trường (ví dụ: field_x, Field_y), hãy làm theo các bước sau.

Bước 1. Thêm một cột key1 và key2 tương ứng với df_1 và df_2.

Step2.Merge các dataframes như dưới đây. field_x và field_y là các cột mong muốn của chúng tôi.

Bước 3. Chỉ chọn những hàng đó từ df_1 trong đó key1 không bằng key2.

Step4.Drop key1 và key2.

Phương pháp này sẽ giải quyết vấn đề của bạn và hoạt động nhanh ngay cả với các tập dữ liệu lớn. Tôi đã thử nó cho dataframes với hơn 1.000.000 hàng.

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)

Tôi không nghĩ rằng đây là kỹ thuật những gì anh ấy muốn - anh ấy muốn biết hàng nào là duy nhất với df. nhưng, tôi nghĩ giải pháp này trả về một df của các hàng là duy nhất cho df đầu tiên hoặc df thứ hai.
Ngăn xếp hợp pháp


3

bạn có thể làm điều đó bằng phương pháp isin (dict) :

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

Giải trình:

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

Điều này tạo ra kết quả sai. Xem giải thích của tôi dưới đây.
Ted Petrou

2

Bạn cũng có thể concat df1, df2:

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

và sau đó loại bỏ tất cả các bản sao:

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

Chào mừng bạn đến với StackOverflow: nếu bạn đăng các mẫu mã, XML hoặc dữ liệu, vui lòng tô sáng các dòng đó trong trình soạn thảo văn bản và nhấp vào nút "mẫu mã" ({}) trên thanh công cụ của trình soạn thảo hoặc sử dụng Ctrl + K trên bàn phím để định dạng độc đáo và cú pháp làm nổi bật nó!
WhatsThePoint

4
Điều này sẽ trả về tất cả dữ liệu trong một trong hai tập hợp, không chỉ dữ liệu chỉ có trong df1.
Jamie Marshall

1

Còn cái này thì sao:

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

Đây là một cách khác để giải quyết điều này:

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

Hoặc là:

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

0

Cách làm của tôi liên quan đến việc thêm một cột mới duy nhất cho một khung dữ liệu và sử dụng điều này để chọn có giữ một mục nhập không

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

Điều này làm cho nó sao cho mọi mục trong df1 đều có mã - 0 nếu nó là duy nhất với df1, 1 nếu nó nằm trong cả hai dataFrames. Sau đó, bạn sử dụng điều này để hạn chế những gì bạn muốn

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

0
trích xuất các hàng khác nhau bằng cách sử dụng hàm hợp nhất
df = df.merge(same.drop_duplicates(), on=['col1','col2'], 
               how='left', indicator=True)
lưu các hàng khác nhau trong CSV
df[df['_merge'] == 'left_only'].to_csv('output.csv')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.