У меня есть фрейм данных, подобный этому:
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)
Наблюдения:
Предпочтительно удалить строку в дублированном фрейме данных и сохранить оригинал
Очень важно: мой реальный фрейм данных имеет форму (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)
Привет @NickODell, спасибо за ответ. В принципе, в моем реальном df этой ситуации не происходит, если обе строки равны colA='a'
, тогда как могут быть два одинаковых значения colB
, если в одной строке colA
равно a
, а в другой — b
. В этом случае не следует сбрасывать
Можете ли вы включить текущее решение iterrows(), даже если оно неосуществимо? Полезно иметь эталонную реализацию для сравнения.
Решение @NickODell df.iterrows теперь добавлено к вопросу
Попробуй это:
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, это работает! Не могли бы вы поделиться дополнительной информацией о том, что делает ваш хороший код? Я считаю, что это будет полезно и для всех людей, которые увидят предложенное вами решение :)
хорошо, я добавил краткое объяснение. Если это помогло, отметьте это как принятый ответ, чтобы закрыть вопрос!
Как насчет строк, где
colB
совпадает? Например. скажем, у вас есть строкаa (21, 1, 2)
, за которой следует вторая строка с тем же содержанием. Это тоже надо отбросить? Вам все равно, как это передано?