Панды: удалите обратные дубликаты из кадра данных

У меня есть фрейм данных с двумя столбцами, A а также B, Получатель чего-то A а также B неважно в этом контексте; например, я бы рассмотрел (0,50) а также (50,0) быть дубликатами. Что такое эффективный способ удаления этих дубликатов из кадра данных в пандах?

import pandas as pd

# Initial data frame.
data = pd.DataFrame({'A': [0, 10, 11, 21, 22, 35, 5, 50], 
                     'B': [50, 22, 35, 5, 10, 11, 21, 0]})
data
    A   B
0   0  50
1  10  22
2  11  35
3  21   5
4  22  10
5  35  11
6   5  21
7  50   0

# Desired output with "duplicates" removed. 
data2 = pd.DataFrame({'A': [0, 5, 10, 11], 
                      'B': [50, 21, 22, 35]})
data2
    A   B
0   0  50
1   5  21
2  10  22
3  11  35

В идеале вывод должен быть отсортирован по значениям столбца A,

6 ответов

Решение

Вы можете отсортировать каждую строку фрейма данных перед удалением дубликатов:

data.apply(lambda r: sorted(r), axis = 1).drop_duplicates()

#   A    B
#0  0   50
#1  10  22
#2  11  35
#3  5   21

Если вы предпочитаете, чтобы результат сортировался по столбцу A:

data.apply(lambda r: sorted(r), axis = 1).drop_duplicates().sort_values('A')

#   A    B
#0  0   50
#3  5   21
#1  10  22
#2  11  35

Вот немного более уродливое, но более быстрое решение:

In [44]: pd.DataFrame(np.sort(data.values, axis=1), columns=data.columns).drop_duplicates()
Out[44]:
    A   B
0   0  50
1  10  22
2  11  35
3   5  21

Сроки: для 8K рядов DF

In [50]: big = pd.concat([data] * 10**3, ignore_index=True)

In [51]: big.shape
Out[51]: (8000, 2)

In [52]: %timeit big.apply(lambda r: sorted(r), axis = 1).drop_duplicates()
1 loop, best of 3: 3.04 s per loop

In [53]: %timeit pd.DataFrame(np.sort(big.values, axis=1), columns=big.columns).drop_duplicates()
100 loops, best of 3: 3.96 ms per loop

In [59]: %timeit big.apply(np.sort, axis = 1).drop_duplicates()
1 loop, best of 3: 2.69 s per loop

df.T.apply(отсортировано).T.drop_duplicates()

Другой классический вариант — агрегировать значения как замороженный набор и использовать логическое индексирование.

      out = data[~data[['A', 'B']].agg(frozenset, axis=1).duplicated()]

Выход:

          A   B
0   0  50
1  10  22
2  11  35
3  21   5

Он также довольно эффективен, хотя и не так сильно, как очень оптимизированныйnp.sortподход:

      %timeit big.apply(lambda r: sorted(r), axis = 1).drop_duplicates()
27.2 ms ± 914 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit pd.DataFrame(np.sort(big.values, axis=1), columns=big.columns).drop_duplicates()
733 µs ± 20.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit big.apply(np.sort, axis = 1).drop_duplicates()
12 s ± 403 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit big[~big[['A', 'B']].agg(frozenset, axis=1).duplicated()]
25 ms ± 657 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Теперь это решение работает,

data.set_index(['A','B']).stack().drop_duplicates().unstack().reset_index()

При необходимости можно добавить больше столбцов. например

data.set_index(['A','B', 'C']).stack().drop_duplicates().unstack().reset_index()

Вот немного длинное решение, но может быть полезно для новичков -

Создание новых столбцов для сортировки значений из столбца A и B по строке -

      data['C'] = np.where(data['A']<data['B'] , data['A'], data['B'])
data['D'] = np.where(data['A']>data['B'] , data['A'], data['B'])

Удаление дубликатов и сортировка по столбцу 'C' согласно запросу и переименование столбцов

      data2 = data[['C', 'D']].drop_duplicates().sort_values('C')
data2.columns = ['A', 'B']   
data2

PS - функция "np.where" работает аналогично формуле If в Excel (логическое условие, значение, если ИСТИНА, значение, если ЛОЖЬ)

Другие вопросы по тегам