← Все курсы · Курс Python · Все руководства

pandas.drop_duplicates: Держать='первый' и строка, которую он выбрал для вас

Два разных вопроса и тот, который отвечает на один из них · Часть Analyst Prep Kit

К , аналитик данных · Опубликовано

К концу этой страницы вы сможете отличить повторяющуюся строку от повторяющегося ключа, посчитать каждую из них, ничего не удаляя, выбрать, какая из двух конфликтующих строк сохранится, и узнать, почему одна и та же таблица по-разному дедуплицируется в pandas и в электронной таблице. Прошло около двадцати пяти минут, и каждое число ниже получено в результате выполнения кода.

Вот что нужно сделать сегодня, прежде чем что-либо drop_duplicates. Запустите эти две строки и сравните их:

df.duplicated().sum()                       # whole rows repeated
df.duplicated(subset=["order_id"]).sum()    # keys repeated

Если второй больше первого, у вас есть строки, которые имеют общий ключ и в чем-то не согласны, и эти строки представляют собой интересную часть ваших данных, а не помеху, которую нужно удалить.

Краткая версия: keep="first" сохраняет ту строку, которая оказалась выше, поэтому выжившая выбирается по сортировке, а не по данным.

В этом заключается идея, поэтому она и представляет картину.

The sort chooses the survivor Two small stacks stand side by side, and each holds the same pair of rows: both rows carry the order number 1003, one of them with a quantity of 3 and the other with a quantity of 5. In the left stack the row holding 3 is on top and the row holding 5 is underneath. In the right stack the order is reversed, so the row holding 5 is on top. In both stacks the top row is drawn in full strength with a heavy blue outline, and the bottom row is drawn pale grey with a line struck through it. The result is that the left stack keeps the 3 and the right stack keeps the 5, from identical data, because the only thing that differs between the two pictures is which row was placed first. 1003 3 1003 5 1003 5 1003 3
Те же два ряда, в двух порядках. Оба раза верхний живет, а нижний уходит, поэтому в одном изображении сохраняется количество 3, а в другом — 5. Ничто в данных не решило этого.
Что вы узнаете
  1. Два разных вопроса
  2. дублируется, а тройка сохраняется
  3. drop_duplications и строки, которые он возвращает.
  4. Выживший выбирается по сортировке
  5. Отметить вместо удаления
  6. pandas чувствителен к регистру, а электронная таблица — нет.
  7. Индекс после этого
  8. Считать вместо того, чтобы отбрасывать
  9. Полное до и после
  10. Пограничные случаи, которые стоит знать
  11. Почему это работает
  12. Использование этого в вашем собственном проекте
  13. Все на одном экране
Каждое число на этой странице является реальным. Таблица из восьми строк, запущенная в pandas 3.0.2. Это намеренно те же восемь рядов, которые используются в электронная версия этой проблемы, потому что два инструмента дают разные ответы на один и тот же вопрос, а шестой раздел посвящен тому, почему.

Вот таблица. Восемь рядов, 56 единиц, три ряда повторяют номер заказа.

Строкаидентификатор заказаПредставительРегионЕдиницыЧто это такое
01001Дана РейесСевер4
11002Оуэн ПаркЮг10
21002Оуэн ПаркЮг10одна и та же строка дважды
31003Прия ШахВосток3
41003Прия ШахВосток5тот же ключ, разные единицы измерения
51004Дана РейесСевер6
61004Дана РейесСевер6отличается на одну заглавную букву
71005Сэм ОкафорЗапад12

1. Два разных вопроса

Перед пояснением: восемь рядов, три повторяющихся порядковых номера. Скажите, сколько строк повторяются.

На вопрос есть два ответа, и какой из них вы хотите, решает все остальное.

df.duplicated().sum()                      1     whole rows repeated
df.duplicated(subset=["order_id"]).sum()   3     keys repeated

Одна строка является точной копией другой. Три строки имеют общий номер заказа со строкой над ними. Разрыв между единицей и тремя и есть вся тема: две из этих строк повторяют ключ, неся при этом разную информацию, и они не являются копиями чего-либо.

«Дубликат» не является свойством данных, это свойство сравниваемых столбцов. При сравнении каждого столбца возникает вопрос: «Является ли эта строка копией?». При сравнении ключей возникает вопрос: «Должна ли эта вещь появиться только один раз». Это разные вопросы и на них разные правильные ответы. drop_duplicates отвечу на любой ваш subset аргумент запрошен без комментариев по поводу выбора.

2. дублируется, а тройка сохраняется

duplicated() возвращает логическое значение для каждой строки, а keep аргумент решает, какой член каждой группы является нет помечен.

df.duplicated()                 flags 1 row     keep='first', the default
df.duplicated(keep='last')      flags 1 row
df.duplicated(keep=False)       flags 2 rows

keep='first' помечает все, кроме первого из каждой группы, что вам и нужно, когда вы собираетесь его удалить. keep=False флаги Каждый член любой группы, которая имеет более одной, и это то, что вам нужно, когда вы собираетесь искать, потому что это дает вам все задействованные строки, а не только дополнительные.

Второй из двух более полезен при расследовании:

df[df.duplicated(subset=["order_id"], keep=False)]

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

3. drop_duplications и возвращаемые им строки

Один и тот же стол, четыре звонка и четыре разных ответа.

drop_duplicates()                                rows 7   units 46
drop_duplicates(subset=["order_id"])             rows 5   units 35
drop_duplicates(subset=["order_id"], keep="last") rows 5   units 37
drop_duplicates(subset=["order_id"], keep=False)  rows 2   units 16

От 8 рядов и 56 единиц до 7–2. Последнее является сюрпризом для большинства людей: keep=False здесь не означает «не оставлять дубликатов», это означает удалить каждую строку, входящую в любую группу из более чем одной, поэтому сохраняются только два порядковых номера, которые появляются ровно один раз.

Два средних отличаются только тем, какой член каждой группы жил, и это изменило общее количество единиц с 35 до 37. Две единицы в восьми строках из одного ключевого слова. На реальном столе это не две единицы.

Скажите вслух, как будут выглядеть 35 или 37 в отчете. Оба варианта правдоподобны, ни один из них не указывает на то, что выбор был сделан, и человек, читающий его, не может сказать, какое ключевое слово было введено.

4. Выживший выбирается по сорту

С keep="first" означает «в зависимости от того, что выше», способ сознательного выбора состоит в том, чтобы поместить нужную строку сверху, прежде чем ее удалять.

df.sort_values(["order_id", "units"]).drop_duplicates(subset=["order_id"])
     1003 keeps 3        total units 35

df.sort_values(["order_id", "units"], ascending=[True, False]) \
  .drop_duplicates(subset=["order_id"])
     1003 keeps 5        total units 37

Идентичные данные, одинаковое падение, два разных результата, полностью определяемые сортом перед ним. Это не недостаток; это единственное место, где можно выразить выбор, поэтому его следует записать, а не оставлять в том порядке, в котором поступил файл.

Три вида, которые появляются постоянно:

Сортировать подержит
Временная метка, по убываниюСамая последняя версия каждой записи.
Сумма по убываниюСамый большой, который часто является настоящим, а повтор - частичным.
Оценка полноты по убываниюСтрока с наименьшим количеством пробелов: df["filled"] = df.notna().sum(axis=1).

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

5. Отметьте вместо удаления

Две дополнительные колонки превращают деструктивную операцию в обратимую и отвечают сразу на оба вопроса из первого раздела.

df["seen"]     = df.groupby("order_id").cumcount() + 1
df["row_seen"] = df.groupby(list(df.columns)).cumcount() + 1

cumcount нумерует строки внутри каждой группы, начиная с нуля, поэтому добавление единицы дает удобочитаемый текст: «Я это вижу уже в н-й раз». Бегите по столу:

row  order_id  region  units  seen  row_seen
  0      1001   North      4     1         1
  1      1002   South     10     1         1
  2      1002   South     10     2         2
  3      1003   East       3     1         1
  4      1003   East       5     2         1
  5      1004   North      6     1         1
  6      1004   north      6     2         1
  7      1005   West      12     1         1

rows where the key repeats:  3
rows where the row repeats:  1
the rows worth looking at:   1003 and 1004

Строки 4 и 6 являются ключевыми. У обоих есть seen = 2 и row_seen = 1: номер заказа уже был виден, а эта точная строка — нет. Это две строки, в которых данные не согласуются сами с собой, и дедупликация одного столбца уничтожает обе без комментариев.

Сохраняйте отмеченный кадр и фильтруйте его, а не отбрасывайте и надейтесь. Стоит две колонки и значит на каждый последующий вопрос, сколько, какие, в чем не согласились, можно ответить ничего не перезагружая.

6. В pandas учитывается регистр, а в электронной таблице нет.

Перед пояснением: строки 5 и 6 идентичны, за исключением того, что одна область North а другой north. Скажите, есть ли drop_duplicates() рассматривает их как одну и ту же строку.

"North" == "north"                False
df["region"].nunique()            5    East, North, South, West, north
df["region"].str.lower().nunique() 4

Это не так. pandas точно сравнивает строки, так что это две разные строки, и обе выживают, вот почему drop_duplicates() по всем возвращенным столбцам Семь строки здесь.

Те же восемь строк в Excel возвращаются как шесть, поскольку сравнение «Удалить дубликаты» игнорирует заглавные буквы. Те же данные, одно и то же намерение, два инструмента, два ответа, и ни один из них не говорит вам, что он сделал выбор в отношении дела.

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

key = df["region"].str.strip().str.casefold()

casefold скорее, чем lower, потому что он обрабатывает больше мировых алфавитов, и strip во-первых, потому что пробелы — это еще одно невидимое отличие. Сделайте то же самое с любым текстовым столбцом, который вы сравниваете, и оба инструмента снова согласуются.

7. Индекс после

Удаление строк не приводит к перенумерации оставшихся.

df.drop_duplicates(subset=["order_id"]).index      [0, 1, 3, 5, 7]

В индексе есть пробелы там, где были пропущенные строки, что правильно, но иногда неудобно. Это означает .iloc[2] и .loc[2] теперь ссылайтесь на разные строки, и все, что предполагает непрерывный индекс, включая некоторые построения графиков и конкатенацию, будет вести себя странно.

df.drop_duplicates(subset=["order_id"]).reset_index(drop=True).index   [0, 1, 2, 3, 4]

drop=True имеет значение: без него старый индекс сохраняется как новый столбец с именем index, что иногда полезно для отслеживания строки до исходного файла и обычно просто загромождает.

8. Считаем, а не опускаем

Часто честный результат — это не дедуплицированная таблица, а подсчет.

df["order_id"].value_counts().sort_index()

1001  1
1002  2
1003  2
1004  2
1005  1

Три номера заказа появляются дважды. Это факт о данных, и если номер заказа должен быть уникальным, то это факт о системе, которая где-то нуждается в исправлении, и сообщать об этом более полезно, чем о таблице, которую вы привели в порядок по собственной инициативе.

Два взаимосвязанных пункта, которые стоит иметь рядом. df["order_id"].nunique() против len(df) сообщает вам размер проблемы в одном сравнении, здесь 5 против 8. И value_counts()[lambda s: s > 1] фильтрует прямо к нарушителям.

Представьте себе наиболее часто используемый экстракт. Если в следующем месяце его столбец идентификатора перестанет быть уникальным, есть ли что-нибудь в вашем коде, что это заметит, или итоговые значения просто вырастут?

Полное до и после

Те же восемь рядов, та же работа: по одному ряду на заказ.

До

df = df.drop_duplicates(subset=["order_id"])

rows 8 -> 5, units 56 -> 35

Двадцать одна единица исчезла. Пять из них относились к приказу 1003, не являвшемуся копией чего-либо, а шесть — к ряду, отличавшемуся лишь заглавной буквой. Никаких записей об этом не было, и в каждой паре сохранилась та строка, которая была указана в файле первой.

после

# 1. normalise the text you will compare on
df["region_key"] = df["region"].str.strip().str.casefold()

# 2. mark rather than delete
df["seen"]     = df.groupby("order_id").cumcount() + 1
df["row_seen"] = df.groupby(["order_id","rep","region_key","units"]).cumcount() + 1

# 3. read the two counts and the disagreement
(df.seen > 1).sum()                       3
(df.row_seen > 1).sum()                   2
df[(df.seen > 1) & (df.row_seen == 1)]    order 1003, the real conflict

# 4. choose the survivor on purpose, and say why
clean = (df.sort_values(["order_id", "units"], ascending=[True, False])
           .drop_duplicates(subset=["order_id"])      # keep the larger quantity
           .reset_index(drop=True))

rows 5, units 37, and the choice is in the code

Претензия, и именно поэтому первый раздел идет первым: те же восемь строк возвращают 7, 5 или 2 строки в зависимости от двух аргументов ключевого слова, а общее количество единиц перемещается с 35 до 37 в зависимости от выбора keep один, без ничего в используемой выходной записи.

Пограничные случаи, которые стоит знать

Шесть, которые пройдут.

Пробелы. "North " и "North" разные, поэтому дубликаты прячутся за конечным пробелом и drop_duplicates сообщает, что ничего не нашел. Прежде чем сравнивать, разденьтесь.

Отсутствующие значения в ключе. панда угощает NaN как равный NaN для этой цели каждая строка с отсутствующим ключом является дубликатом каждой другой, и все строки, кроме одной, удаляются. На частично заполненном ключе, который может занимать большую часть таблицы.

Плавает. 10.0 и 10.000000001 — это разные строки и отображаются одинаково. Явно округляйте перед сравнением числового столбца.

Даты со временем. Две записи в один и тот же день имеют разные значения, если одна содержит 09:14. Нормализовать с помощью .dt.normalize() если день - это то, что вы имеете в виду.

Порядок столбцов в subset. Это не имеет значения для правильности, но имеет значение для чтения: перечислите столбцы в том порядке, в котором кто-то их произнесет, потому что этот список является вашим определением идентичности.

Дубликаты, созданные в результате слияния. Если количество строк увеличилось до того, как вы сюда попали, дубликатов нет в данных, они были созданы путем объединения с таблицей, ключ которой не был уникальным. Последующая дедупликация скрывает причину; руководство по слиянию имеет проверку, которая перехватывает его в источнике.

Почему это работает

Причина, по которой ни одно значение по умолчанию не является правильным, заключается в том, что идентичность — это утверждение о мире, а не свойство таблицы. В реляционных терминах объявление ключа означает объявление того, что некоторый набор столбцов однозначно идентифицирует строку, и все последующие действия зависят от истинности этого объявления (Кодд, 1970, Коммуникации ACM, 13(6), 377–387). Прохождение subset=["order_id"] делает это заявление мимоходом, и две строки с seen = 2 и row_seen = 1 являются ли данные, говорящие вам, что декларация в настоящее время является ложной? Их удаление устраняет доказательства, а не проблему.

Случай точного совпадения — это лишь легкая половина работы. Обнаружение записей, которые относятся к одному и тому же реальному объекту, но не являются побайтно-идентичными, является областью исследований со своими собственными мерами сходства, пороговыми значениями и методами оценки, поскольку, как правило, не существует точного правила, и решение включает в себя суждение о том, насколько разница по-прежнему считается одной и той же сущностью (Эльмагармид, Ипейротис и Верикиос, 2007, Транзакции IEEE по знаниям и инженерии данных, 19(1), 1–16). North против north Это самая мягкая из возможных версий, и тот факт, что два основных инструмента решают ее по-разному, является хорошим показателем того, что это суждение, а не факт.

Прежде чем что-либо делать, одно замечание о том, почему на этой странице эти два вопроса разделены. Удержание нескольких незнакомых различий одновременно делает тему сложной, а в учебных исследованиях нагрузка, налагаемая тем, как представлен материал, рассматривается как нечто, что дизайнер контролирует независимо от сложности материала (Свеллер, 1988, Когнитивная наука, 12(2), 257–285). Повторяющаяся строка и повторяющийся ключ — это две идеи; пытаюсь научиться subset, keep и, хотя их все еще смешивают, это три идеи, основанные на путанице.

Использование этого в вашем собственном проекте

Дедупликация всего конвейера — это не задача. Вместо этого сделайте это по порядку.

  1. сравнивать nunique() с len() в каждом столбце идентификатора. Одна строка, и она определяет проблему.
  2. Запустите два duplicated имеет значение, целый ряд и ключ, и посмотри на пробел.
  3. Посмотрите на преступников с keep=False прежде чем что-либо решить.
  4. Нормализовать текст Вы сравниваете по: полоска, потом футляр.
  5. Сортируйте сознательно перед каждой каплей и прокомментируйте выбранное вами правило.
  6. Сообщить о количестве а также доставку чистого стола. Неуникальный ключ — это чья-то ошибка, а не ваша хозяйственная работа.

Если у вас под рукой есть бумага, один дополнительный эскиз стоит пяти минут. Напишите столбцы в ряд и обведите рамкой те, которые вместе обозначают одну реальную вещь. Затем напишите рядом с полем, что вы будете делать, если две строки внутри него не согласятся. Эта коробка твоя subset и это предложение вашего типа, и оба решения вы в противном случае приняли бы случайно.

Подробнее об этом и еще что-нибудь подобное. Все инструкции находятся в одном месте на индекс направляющих. То же самое задание в базе данных, где ничего не удалено, а вместо этого перечислены дубликаты, поиск повторяющихся строк в SQL.

Все на одном экране

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

вещьЧто он делает
Два вопросаЯвляется ли эта строка копией? Должен ли этот ключ появиться один раз? Разные ответы.
duplicated()Логическое значение для каждой строки. Помечает все, кроме первого, в каждой группе.
keep='last'Флаги все, кроме последнего.
keep=FalseОтмечает каждую строку в любой группе, состоящей из более чем одной. Используйте его, чтобы посмотреть.
Первоначальный запросdf[df.duplicated(subset=key, keep=False)]
drop_duplicates()Все столбцы. Только точные копии. 8 рядов стало 7.
subset=["order_id"]Только ключ. 8 рядов стало 5, единиц 56 стало 35.
keep='last' тамОдни и те же 5 рядов, разные элементы, единиц 37.
keep=False тамОтбрасывает каждую задействованную строку. Осталось 2 ряда.
Какая строка выживетТот, что выше. Сначала сортируйте, сознательно.
Общие сортаСамая новая временная метка, самая большая сумма, наименьшее количество пробелов.
Отметьте вместо этогоgroupby(key).cumcount() + 1и еще раз по всем столбцам.
Интересные строкиКлюч виден дважды, ряд — один раз. Тот же ключ, разные данные.
Случайpandas чувствителен, а Excel нет. Та же таблица, 7 строк против 6.
Нормализация.str.strip().str.casefold() прежде чем сравнивать.
Отсутствующие ключиСчитайте равными друг другу, чтобы они слились в одно.
ИндексСохраняет пробелы. reset_index(drop=True) перенумеровать.
Вместо этого считатьvalue_counts(), и nunique() против len().
Единственная привычка, которую следует сохранить. Прежде чем что-либо удалять, сравните количество дубликатов во всей строке с количеством дубликатов ключа. Когда они различаются, разрыв образуется из строк, имеющих общий идентификатор и несогласных в отношении данных, и эти строки являются выводом о системе, а не неряшливостью в файле. Если дубликаты ведут себя не так, как описано на этой странице, существует общая шлейф диагностики на предмет застревания.

Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. У меня был ночной экстракт, который иногда запускался дважды, причем во втором выпуске были частичные количества, а drop_duplicates в заказе сохранялся тот, который поступил первым, поэтому примерно в один вечер в месяц сообщалось о меньшем количестве, чем фактически было выполнено предприятием. Что для вас незаметно выбрала дедупликация?

Ссылки

Количество строк слишком велико, и при печати заголовка каждая строка выглядит уникально.

Python для аналитиков составляет 203 страницы, дублированные и drop_duplications читаются аргумент за аргументом, включая то, какую копию вы собираетесь сохранить.

Python для аналитиков, 19 $ →
Потренируйтесь на столе с установленными на нем дубликатами.

тот Курс Python дает вам повторы: точные копии, общие ключи и варианты корпуса в одном кадре, а также карточки с информацией о том, что каждый keep возвраты и пробный экзамен, на котором запрашивается количество строк, прежде чем запускать удаление. Теперь вы владеете методом; комплект - это практика. Ничего устанавливать, учетная запись не требуется.

Откройте комплект Python →