Избегайте df.iterrow для удаления строк данных в определенных условиях

У меня есть фрейм данных, подобный этому:

import pandas as pd

colA = ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
colB = [(21,1,2), (0,1,21), (2,1,21), (1,12,5), (21,1,0), (12,5,6), (18,7,14), (7,5,12), (14,7,18), (12,7,11), (11,7,12), (3,5,7)]
df = pd.DataFrame(list(zip(colA, colB)), columns = ['colA', 'colB'])
display(df)

выход:

    colA    colB
0   a   (21, 1, 2)
1   a   (0, 1, 21)
2   a   (2, 1, 21)
3   a   (1, 12, 5)
4   b   (21, 1, 0)
5   b   (12, 5, 6)
6   b   (18, 7, 14)
7   b   (7, 5, 12)
8   c   (14, 7, 18)
9   c   (12, 7, 11)
10  c   (11, 7, 12)
11  c   (3, 5, 7)

Мне нужно было бы удалить (или отфильтровать) все строки, в которых при одном и том же значении colA значение colB в строке равно обратному значению colB в другой строке.
В представленном примере:

  • внутри colA='a' строки 2 есть colB=(2,1,21), противоположная строке 0 colB=(21,1,2), поэтому ее следует опустить

  • colA='b' в 4-м ряду есть colB=(21,1,0), противоположный 1-му ряду colB=(0,1,21), но это colA='a', поэтому здесь нечего опускать

  • в colA='c' строке 10 есть colB=(11,7,12), противоположная строке 9 colB=(12,7,11), поэтому ее следует опустить

Окончательные результаты будут примерно такими:

    colA    colB
0   a   (21, 1, 2)
1   a   (0, 1, 21)
2   a   (1, 12, 5)
3   b   (21, 1, 0)
4   b   (12, 5, 6)
5   b   (18, 7, 14)
6   b   (7, 5, 12)
7   c   (14, 7, 18)
8   c   (12, 7, 11)
9   c   (3, 5, 7)

Наблюдения:

  1. Предпочтительно удалить строку в дублированном фрейме данных и сохранить оригинал

  2. Очень важно: мой реальный фрейм данных имеет форму (3 миллиона, 11), поэтому я ищу эффективный способ сделать это, например .apply, lambda и т. д. Я делал это в прошлом с df.iterrows, это уже не было лучший способ, мой плохой .. сейчас это совершенно невозможно

Текущее решение df.iterrows:

unique_df = df.copy()
seen_a_b = set()
for i, row in df.iterrows():
    val_a = row['colA']
    val_b = row['colB']
    a_b = (val_a, val_b)
    a_revb = (val_a, val_b[::-1])
    if a_b in seen_a_b:
        unique_df.drop(i, inplace=True)
        continue
    seen_a_b.add(a_b)
    seen_a_b.add(a_revb)

Как насчет строк, где colB совпадает? Например. скажем, у вас есть строка a (21, 1, 2), за которой следует вторая строка с тем же содержанием. Это тоже надо отбросить? Вам все равно, как это передано?

Nick ODell 25.11.2022 18:01

Привет @NickODell, спасибо за ответ. В принципе, в моем реальном df этой ситуации не происходит, если обе строки равны colA='a', тогда как могут быть два одинаковых значения colB, если в одной строке colA равно a, а в другой — b. В этом случае не следует сбрасывать

Lowrenzo 25.11.2022 19:12

Можете ли вы включить текущее решение iterrows(), даже если оно неосуществимо? Полезно иметь эталонную реализацию для сравнения.

Nick ODell 25.11.2022 19:27

Решение @NickODell df.iterrows теперь добавлено к вопросу

Lowrenzo 26.11.2022 16:20
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
4
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Попробуй это:

df.groupby(['colA',df['colB'].map(lambda x: frozenset((x[0],x[-1])))],as_index=False).first()

Это решение создает замороженный набор или неизменяемый набор, который можно использовать в качестве группового ключа. Это вместе с colA используется для получения первого значения каждой группы. Мы используем только первое и последнее значение в colB, так как среднее значение одинаково вперед и назад.

или

df.loc[df['colB'].map(lambda x: frozenset((x[0],x[-1]))).groupby(df['colA']).transform(lambda x: ~x.duplicated())]

Выход:

  colA         colB
0    a   (21, 1, 2)
1    a   (0, 1, 21)
2    a   (1, 12, 5)
3    b   (21, 1, 0)
4    b   (12, 5, 6)
5    b  (18, 7, 14)
6    b   (7, 5, 12)
7    c  (14, 7, 18)
8    c  (12, 7, 11)
9    c    (3, 5, 7)

Спасибо @rhug123, это работает! Не могли бы вы поделиться дополнительной информацией о том, что делает ваш хороший код? Я считаю, что это будет полезно и для всех людей, которые увидят предложенное вами решение :)

Lowrenzo 26.11.2022 16:22

хорошо, я добавил краткое объяснение. Если это помогло, отметьте это как принятый ответ, чтобы закрыть вопрос!

rhug123 26.11.2022 19:17

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