Равенство в Pandas DataFrames - вопросы порядка столбцов?
Как часть unit test, мне нужно проверить два DataFrames для равенства. Порядок столбцов в DataFrames для меня не важен. Однако, кажется, имеет значение Pandas:
import pandas
df1 = pandas.DataFrame(index = [1,2,3,4])
df2 = pandas.DataFrame(index = [1,2,3,4])
df1['A'] = [1,2,3,4]
df1['B'] = [2,3,4,5]
df2['B'] = [2,3,4,5]
df2['A'] = [1,2,3,4]
df1 == df2
Результаты в:
Exception: Can only compare identically-labeled DataFrame objects
Я считаю, что выражение df1 == df2
должно оцениваться в DataFrame, содержащем все значения True
. Очевидно, что это спорно, что правильная функциональность ==
должна быть в этом контексте. Мой вопрос: есть ли метод Pandas, который делает то, что я хочу? То есть, есть ли способ сделать сравнение равенства, игнорируя порядок столбцов?
Ответы
Ответ 1
Вы можете отсортировать столбцы, используя sort_index
:
df1.sort_index(axis=1) == df2.sort_index(axis=1)
Это приведет к оценке данных всех True
значений.
Поскольку @osa комментирует, что это невозможно для NaN и не особенно надежно, на практике, вероятно, рекомендуется использовать что-то похожее на @quant answer (Примечание: мы хотим использовать bool, а не рейз, если есть проблема):
def my_equal(df1, df2):
from pandas.util.testing import assert_frame_equal
try:
assert_frame_equal(df1.sort_index(axis=1), df2.sort_index(axis=1), check_names=True)
return True
except (AssertionError, ValueError, TypeError): perhaps something else?
return False
Ответ 2
Наиболее распространенное намерение выполняется следующим образом:
def assertFrameEqual(df1, df2, **kwds ):
""" Assert that two dataframes are equal, ignoring ordering of columns"""
from pandas.util.testing import assert_frame_equal
return assert_frame_equal(df1.sort_index(axis=1), df2.sort_index(axis=1), check_names=True, **kwds )
Конечно, см. pandas.util.testing.assert_frame_equal
для других параметров, которые вы можете передать
Ответ 3
def equal( df1, df2 ):
""" Check if two DataFrames are equal, ignoring nans """
return df1.fillna(1).sort_index(axis=1).eq(df2.fillna(1).sort_index(axis=1)).all().all()
Ответ 4
Обычно вам нужны быстрые тесты, и метод сортировки может быть жестоко неэффективен для больших индексов (например, если вы использовали строки вместо столбцов для этой проблемы). Метод сортировки также восприимчив к ложным негативам для неидеальных индексов.
К счастью, pandas.util.testing.assert_frame_equal
с тех пор обновляется с опцией check_like
. Установите для этого значение true, и порядок не будет рассмотрен в тесте.
С уникальными индексами вы получите критический ValueError: cannot reindex from a duplicate axis
. Это вызвано операцией reindex_like
под капотом, которая перестраивает один из DataFrames для соответствия другому порядку. Reindexing намного быстрее, чем сортировка, как показано ниже.
import pandas as pd
from pandas.util.testing import assert_frame_equal
df = pd.DataFrame(np.arange(1e6))
df1 = df.sample(frac=1, random_state=42)
df2 = df.sample(frac=1, random_state=43)
%timeit -n 1 -r 5 assert_frame_equal(df1.sort_index(), df2.sort_index())
## 5.73 s ± 329 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)
%timeit -n 1 -r 5 assert_frame_equal(df1, df2, check_like=True)
## 1.04 s ± 237 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)
Для тех, кто пользуется хорошим соотношением производительности:
Переиндексирование против сортировки по индексам int и str (str еще более резкое)
Ответ 5
Сортировка столбца работает только в том случае, если метки строк и столбцов совпадают между кадрами. Скажем, у вас есть 2 кадра данных с одинаковыми значениями в ячейках, но с разными метками, тогда решение сортировки не будет работать. Я столкнулся с этим сценарием при реализации кластеров k-режимов с помощью pandas.
Я обошел его с помощью простой эквивалентной функции для проверки равенства ячеек (код ниже)
def frames_equal(df1,df2) :
if not isinstance(df1,DataFrame) or not isinstance(df2,DataFrame) :
raise Exception(
"dataframes should be an instance of pandas.DataFrame")
if df1.shape != df2.shape:
return False
num_rows,num_cols = df1.shape
for i in range(num_rows):
match = sum(df1.iloc[i] == df2.iloc[i])
if match != num_cols :
return False
return True
Ответ 6
Вы пытались использовать df1.equals(df2)? я думаю, что более надежным является df1 == df2, хотя я не уверен, что он разрешит ваши проблемы с порядком столбцов.
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.equals.html
Ответ 7
При работе с фреймами данных, содержащими объекты Python, такие как кортежи и списки, df.eq(df2)
и df == df2
будет недостаточно. Даже если одинаковые ячейки в каждом кадре данных содержат один и тот же объект, например (0, 0)
, сравнение на равенство приведет к False
. Чтобы обойти это, преобразуйте все столбцы в строки перед сравнением:
df.apply(lambda x: x.astype(str)).eq(df2.apply(lambda x: x.astype(str)))
Ответ 8
Возможно, вам может понадобиться функция для сравнения DataFrames, игнорируя порядок строк и столбцов? Единственное требование - иметь уникальный столбец, чтобы использовать его в качестве индекса.
f1 = pd.DataFrame([
{"id": 1, "foo": "1", "bar": None},
{"id": 2, "foo": "2", "bar": 2},
{"id": 3, "foo": "3", "bar": 3},
{"id": 4, "foo": "4", "bar": 4}
])
f2 = pd.DataFrame([
{"id": 3, "foo": "3", "bar": 3},
{"id": 1, "bar": None, "foo": "1",},
{"id": 2, "foo": "2", "bar": 2},
{"id": 4, "foo": "4", "bar": 4}
])
def comparable(df, index_col='id'):
return df.fillna(value=0).set_index(index_col).to_dict('index')
comparable(f1) == comparable (f2) # returns True