панди отримують рядки, які НЕ є в інших даних


229

У мене є два кадри даних панд, які мають деякі спільні рядки.

Припустимо, dataframe2 - це підмножина dataframe1.

Як я можу отримати рядки dataframe1, які відсутні в 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 Я не бачу правильної відповіді, яку ви надали. Якщо у мене є два фрейми даних, один з яких є підмножиною іншого, мені потрібно видалити всі ті рядки, які знаходяться в підмножині. Я не хочу видаляти дублікати. Я повністю хочу видалити підмножину.
автомат

Відповіді:


172

Одним із методів було б збереження результату внутрішньої форми злиття обох dfs, тоді ми можемо просто вибрати рядки, коли значення одного стовпця не є цим загальним:

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

EDIT

Інший метод , як ви знайшли це використання , 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потрібно, щоб обидва dfs починалися з однакових значень рядків, наприклад, якщо df2 df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11,12, 13]})тоді ваш метод не працюватиме
EdChum

2
це перетворило всі ints в плаваючі!
Кріс Нільсен

3
@SergeyZakharov ця відповідь, опублікована майже 3 роки тому, була правильною, що стосується ОП, і щодо їхньої проблеми, інша відповідь - краща відповідь і стосується більш широкої проблеми, яка ніколи не була частиною оригінального питання, неправильно стверджувати, що це відповідь неправильна, вона правильна, враховуючи задану проблему. Крім того, хтось спростував це без пояснень, я мало що можу зробити, оскільки це прийнята відповідь, ОП не змінив свою думку, і я не збираюся канібалізувати іншу відповідь, щоб зробити це правильно .
EdChum

1
@Cecilia вам потрібно пройти keep=False: df0.append(df1).drop_duplicates(keep=False)за замовчуванням він зберігає перший дублікат, ви хочете відкинути всі дублікати
EdChum

189

Наразі вибране рішення дає невірні результати. Щоб правильно вирішити цю проблему, ми можемо виконати ліве з'єднання від 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

Виконайте з’єднання ліворуч, усуваючи дублікати df2так, щоб кожен ряд поєднувався df1з рівно 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 є унікальним індексом (не згадується у питанні, але очевидним). Отже, якщо ніколи не буває такого випадку, коли є два значення col2 для одного і того ж значення col1 (не може бути двох col1 = 3 рядків), відповіді вище є правильними.
пашуте

14
Це, звичайно, не очевидно, тому ваш пункт недійсний. Моє рішення узагальнює більше випадків.
Тед Петру

Запитання, чи не було б легше створити фрагмент, а не булівський масив? Оскільки мета - отримати рядки.
Матіас Ромо

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 (на диво, я не міг знайти жодних згадок про тильду в документах Pandas).
Денніс Голомазов

Здається, що dfs мають бути однакової довжини, ні? Я отримуюValueError: Item wrong length x instead of y.
словаззаду

@wordsforthewise ні, вони ні. Маска має довжину df1 і застосовується і до df1. Чи можете ви надати свій приклад?
Денніс Голомазов

Щоб виправити питання щодо довжини виробу, слід додати .loc
Морено

13

Як уже натякали, для isin потрібні стовпці та індекси однакові для відповідності. Якщо збіг повинен міститись лише для вмісту рядків, один із способів отримати маску для фільтрації наявних рядків - перетворити рядки в індекс (Multi):

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 аргумент ключового слова додає стовпці до існуючого індексу. Якщо стовпці не вирівнюються, список (df.колонки) можна замінити специфікаціями стовпців для вирівнювання даних.

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

Можна альтернативно використовувати для створення індексів, хоча я сумніваюся, що це більш ефективно.


@ Dev_123 Видаліть ~ на початку. Ядро полягає у створенні списку предикатів того, що рядки в df1 також зустрічаються у df2, тому рядки в df1 не властиві df1, ~ заперечує це у списку предикатів, чи не зустрічаються рядки в df1 у df2.
Rune Lyngsoe

11

Припустимо, у вас є два фрейми даних, df_1 та df_2, що мають кілька полів (імена стовпців), і ви хочете знайти лише ті записи в df_1, які не є в df_2 на основі деяких полів (наприклад, polja_x, поля_y), виконайте наступні кроки.

Крок 1.Додайте ключ стовпця1 та ключ2 до df_1 та df_2 відповідно.

Крок2.Змініть рамки даних, як показано нижче. field_x і field_y - наші бажані стовпці.

Виберіть лише ті рядки з df_1, де key1 не дорівнює key2.

Крок4.Запустіть клавіші1 та клавішу2.

Цей метод вирішить вашу проблему і працює швидко навіть при великих наборах даних. Я спробував це для фреймів даних з більш ніж 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 рядків, які були або унікальними для першого df, або другого df.
Legit Stack


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

Це дає неправильний результат. Дивіться моє пояснення нижче.
Тед Петру

2

Ви також можете Concat df1, df2:

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

а потім видаліть усі дублікати:

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

Ласкаво просимо до StackOverflow: якщо ви розміщуєте код, XML або зразки даних, будь ласка, виділіть ці рядки в текстовому редакторі та натисніть кнопку "зразки коду" ({}) на панелі інструментів редактора або використовуючи Ctrl + K на клавіатурі для гарного форматування і синтаксис виділити це!
WhatsThePoint

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

Мій спосіб зробити це передбачає додавання нового стовпця, унікального для одного фрейму даних, і використовувати його, щоб вибрати, чи потрібно зберігати запис

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

Це робить його таким, що кожен запис у df1 має код - 0, якщо він унікальний для df1, 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')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.