it-swarm.com.ru

pandas получить строки, которых нет в другом фрейме данных

У меня есть два фрейма данных pandas, которые имеют несколько общих строк.

Предположим, 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]})
155
think nice things

Один из методов заключается в том, чтобы сохранить результат внутренней формы слияния обоих 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

ПРАВКА

Другой метод, который вы нашли, заключается в использовании 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
111
EdChum

Текущее выбранное решение дает неверные результаты. Чтобы правильно решить эту проблему, мы можем выполнить левое соединение от 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)
98
Ted Petrou

Предполагая, что индексы согласованы в кадрах данных (без учета фактических значений col):

df1[~df1.index.isin(df2.index)]
55
Dennis Golomazov

Как уже указывалось, isin требует, чтобы столбцы и индексы были одинаковыми для соответствия. Если соответствие должно быть только для содержимого строки, один из способов получить маску для фильтрации имеющихся строк - преобразовать строки в (мульти) индекс:

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.columns) можно заменить спецификациями столбцов для выравнивания данных.

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

в качестве альтернативы может быть использован для создания индексов, хотя я сомневаюсь, что это более эффективно.

11
Rune Lyngsoe

Предположим, у вас есть два кадра данных, df_1 и df_2, имеющие несколько полей (имена столбцов), и вы хотите найти только те записи в df_1, которые не находятся в df_2 на основе некоторых полей (например, fields_x, fields_y), выполните следующие шаги.

Шаг 1. Добавьте столбец key1 и key2 к df_1 и df_2 соответственно.

Step2.Merge фреймы данных, как показано ниже. field_x и field_y - наши желаемые столбцы.

Step3.Выберите только те строки из 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)
10
Pragalbh kulshrestha

Вы также можете указать df1, df2:

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

а затем удалите все дубликаты:

y = x.drop_duplicates(keep=False, inplace=False)
4
Semeon Balagula

немного поздно, но, возможно, стоит проверить параметр «индикатора» в pd.merge.

Посмотрите этот другой вопрос для примера: Сравнить PandaS DataFrames и вернуть строки, отсутствующие в первом -

3
jabellcu

вы можете сделать это, используя 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
3
MaxU

Вот еще один способ решения этой проблемы:

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)]
1
Sergey Zakharov

Как насчет этого:

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
adamwlev

Мой способ сделать это включает добавление нового столбца, уникального для одного кадра данных, и использование этого, чтобы выбрать, сохранять ли запись

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
r.rz