Установить разницу для pandas
Простой вопрос панд:
Есть ли drop_duplicates()
чтобы удалить каждую строку, участвующую в дублировании?
Эквивалентный вопрос заключается в следующем: имеют ли панды разницу в наборах для данных?
Например:
In [5]: df1 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
In [6]: df2 = pd.DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})
In [7]: df1
Out[7]:
col1 col2
0 1 2
1 2 3
2 3 4
In [8]: df2
Out[8]:
col1 col2
0 4 6
1 2 3
2 5 5
так что, возможно, что-то вроде df2.set_diff(df1)
создаст это:
col1 col2
0 4 6
2 5 5
Тем не менее, я не хочу полагаться на индексы, потому что в моем случае мне приходится иметь дело с dataframes, которые имеют разные индексы.
Кстати, я изначально думал о расширении текущего drop_duplicates()
, но теперь я понимаю, что второй подход, использующий свойства теории множеств, был бы гораздо полезнее в целом. Однако оба подхода решают мою текущую проблему.
Спасибо!
Ответы
Ответ 1
from pandas import DataFrame
df1 = DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
df2 = DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})
print df2[~df2.isin(df1).all(1)]
print df2[(df2!=df1)].dropna(how='all')
print df2[~(df2==df1)].dropna(how='all')
Ответ 2
Бит свернут, но если вы хотите полностью игнорировать данные индекса. Преобразуйте содержимое файлов данных в набор кортежей, содержащих столбцы:
ds1 = set([tuple(line) for line in df1.values])
ds2 = set([tuple(line) for line in df2.values])
Этот шаг также избавится от любых дубликатов в кадрах данных (индекс игнорируется)
set([(1, 2), (3, 4), (2, 3)]) # ds1
затем можно использовать методы набора, чтобы найти что-либо. Например, чтобы найти различия:
ds1.difference(ds2)
дает: множество ([(1, 2), (3, 4)])
может потребоваться обратно в dataframe, если это необходимо. Обратите внимание, что необходимо преобразовать set в список 1st, поскольку set не может использоваться для создания dataframe:
pd.DataFrame(list(ds1.difference(ds2)))
Ответ 3
Здесь другой ответ, который хранит индекс и не требует идентичных индексов в двух фреймах данных.
pd.concat([df2, df1, df1]).drop_duplicates(keep=False)
Это быстро и результат
col1 col2
0 4 6
2 5 5
Ответ 4
Применить по столбцам объекта, который вы хотите сопоставить (df2); найдите строки, которые не находятся в наборе (isin
, как оператор набора)
In [32]: df2.apply(lambda x: df2.loc[~x.isin(df1[x.name]),x.name])
Out[32]:
col1 col2
0 4 6
2 5 5
То же самое, но включать все значения в df1, но все же за столбец в df2
In [33]: df2.apply(lambda x: df2.loc[~x.isin(df1.values.ravel()),x.name])
Out[33]:
col1 col2
0 NaN 6
2 5 5
Второй пример
In [34]: g = pd.DataFrame({'x': [1.2,1.5,1.3], 'y': [4,4,4]})
In [35]: g.columns=df1.columns
In [36]: g
Out[36]:
col1 col2
0 1.2 4
1 1.5 4
2 1.3 4
In [32]: g.apply(lambda x: g.loc[~x.isin(df1[x.name]),x.name])
Out[32]:
col1 col2
0 1.2 NaN
1 1.5 NaN
2 1.3 NaN
Обратите внимание, что в 0.13 на уровне кадра будет isin
, поэтому возможно следующее: df2.isin(df1)
Ответ 5
Получите индексы пересечения с слиянием, затем оставьте их:
>>> df_all = pd.DataFrame(np.arange(8).reshape((4,2)), columns=['A','B']); df_all
A B
0 0 1
1 2 3
2 4 5
3 6 7
>>> df_completed = df_all.iloc[::2]; df_completed
A B
0 0 1
2 4 5
>>> merged = pd.merge(df_all.reset_index(), df_completed); merged
index A B
0 0 0 1
1 2 4 5
>>> df_pending = df_all.drop(merged['index']); df_pending
A B
1 2 3
3 6 7
Ответ 6
Существует 3 метода, но два из них имеют некоторые недостатки.
Метод 1 (метод Хэш):
Он работал во всех случаях, которые я тестировал.
df1.loc[:, "hash"] = df1.apply(lambda x: hash(tuple(x)), axis = 1)
df2.loc[:, "hash"] = df2.apply(lambda x: hash(tuple(x)), axis = 1)
df1 = df1.loc[~df1["hash"].isin(df2["hash"]), :]
Метод 2 (метод Дикта):
Это не удается, если в DataFrames содержатся столбцы datetime.
df1 = df1.loc[~df1.isin(df2.to_dict(orient="list")).all(axis=1), :]
Метод 3 (метод MultiIndex):
Я столкнулся с случаями, когда он потерпел неудачу в столбцах с None или NaN.
df1 = df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)
Ответ 7
Предположение:
- df1 и df2 имеют одинаковые столбцы
- это заданная операция, поэтому дубликаты игнорируются
- не очень большие, поэтому вы не беспокоитесь о памяти
union = pd.concat([df1,df2])
sym_diff = union[~union.duplicated(keep=False)]
union_of_df1_and_sym_diff = pd.concat([df1, sym_diff])
diff = union_of_df1_and_sym_diff[union_of_df1_and_sym_diff.duplicated()]
Ответ 8
Я не уверен, что pd.concat()
неявно соединяется с перекрывающимися столбцами, но мне пришлось немного подправить ответ @radream.
Концептуально, разность в наборе (symmetric) в нескольких столбцах - это объединение соединений (внешнее соединение) минус набор пересечений (или внутреннее соединение):
df1 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
df2 = pd.DataFrame({'col1':[4,2,5], 'col2':[6,3,5]})
o = pd.merge(df1, df2, how='outer')
i = pd.merge(df1, df2)
set_diff = pd.concat([o, i]).drop_duplicates(keep=False)
Это дает:
col1 col2
0 1 2
2 3 4
3 4 6
4 5 5
Ответ 9
Объекты Pandas MultiIndex имеют операции быстрого набора, реализованные как методы, поэтому вы можете преобразовать DataFrames в MultiIndexes, использовать метод difference()
, а затем преобразовать результат обратно в DataFrame. Это решение должно быть намного быстрее (на 100% или более от моего краткого тестирования), чем предлагаемые здесь решения, и это не будет зависеть от индексации строк исходных кадров. Как отметил Петр для ответа, это приведет к ошибкам с нулевыми значениями, поскольку np.nan! = Np.nan. Любая строка в df2 с нулевым значением всегда будет отображаться в разнице. Кроме того, столбцы должны быть в одном порядке для обоих DataFrames.
df1mi = pd.MultiIndex.from_arrays(df1.values.transpose(), names=df1.columns)
df2mi = pd.MultiIndex.from_arrays(df2.values.transpose(), names=df2.columns)
dfdiff = df2mi.difference(df1mi).to_frame().reset_index(drop=True)
Ответ 10
это должно работать, даже если у вас есть несколько столбцов в обоих фреймах данных. Но убедитесь, что имена столбцов обоих информационных фреймов совпадают.
set_difference = pd.concat([df2, df1, df1]).drop_duplicates(keep=False)
С несколькими столбцами вы также можете использовать:
col_names=['col_1','col_2']
set_difference = pd.concat([df2[col_names], df1[col_names],
df1[col_names]]).drop_duplicates(keep=False)