← Все курсы · Курс Python · Все руководства
pandas.drop_duplicates: Держать='первый' и строка, которую он выбрал для вас
К концу этой страницы вы сможете отличить повторяющуюся строку от повторяющегося ключа, посчитать каждую из них, ничего не удаляя, выбрать, какая из двух конфликтующих строк сохранится, и узнать, почему одна и та же таблица по-разному дедуплицируется в pandas и в электронной таблице. Прошло около двадцати пяти минут, и каждое число ниже получено в результате выполнения кода.
Вот что нужно сделать сегодня, прежде чем что-либо drop_duplicates. Запустите эти две строки и сравните их:
df.duplicated().sum() # whole rows repeated
df.duplicated(subset=["order_id"]).sum() # keys repeated
Если второй больше первого, у вас есть строки, которые имеют общий ключ и в чем-то не согласны, и эти строки представляют собой интересную часть ваших данных, а не помеху, которую нужно удалить.
Краткая версия: keep="first" сохраняет ту строку, которая оказалась выше, поэтому выжившая выбирается по сортировке, а не по данным.
В этом заключается идея, поэтому она и представляет картину.
- Два разных вопроса
- дублируется, а тройка сохраняется
- drop_duplications и строки, которые он возвращает.
- Выживший выбирается по сортировке
- Отметить вместо удаления
- pandas чувствителен к регистру, а электронная таблица — нет.
- Индекс после этого
- Считать вместо того, чтобы отбрасывать
- Полное до и после
- Пограничные случаи, которые стоит знать
- Почему это работает
- Использование этого в вашем собственном проекте
- Все на одном экране
Вот таблица. Восемь рядов, 56 единиц, три ряда повторяют номер заказа.
| Строка | идентификатор заказа | Представитель | Регион | Единицы | Что это такое |
|---|---|---|---|---|---|
| 0 | 1001 | Дана Рейес | Север | 4 | |
| 1 | 1002 | Оуэн Парк | Юг | 10 | |
| 2 | 1002 | Оуэн Парк | Юг | 10 | одна и та же строка дважды |
| 3 | 1003 | Прия Шах | Восток | 3 | |
| 4 | 1003 | Прия Шах | Восток | 5 | тот же ключ, разные единицы измерения |
| 5 | 1004 | Дана Рейес | Север | 6 | |
| 6 | 1004 | Дана Рейес | Север | 6 | отличается на одну заглавную букву |
| 7 | 1005 | Сэм Окафор | Запад | 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 и, хотя их все еще смешивают, это три идеи, основанные на путанице.
Использование этого в вашем собственном проекте
Дедупликация всего конвейера — это не задача. Вместо этого сделайте это по порядку.
- сравнивать
nunique()сlen()в каждом столбце идентификатора. Одна строка, и она определяет проблему. - Запустите два
duplicatedимеет значение, целый ряд и ключ, и посмотри на пробел. - Посмотрите на преступников с
keep=Falseпрежде чем что-либо решить. - Нормализовать текст Вы сравниваете по: полоска, потом футляр.
- Сортируйте сознательно перед каждой каплей и прокомментируйте выбранное вами правило.
- Сообщить о количестве а также доставку чистого стола. Неуникальный ключ — это чья-то ошибка, а не ваша хозяйственная работа.
Если у вас под рукой есть бумага, один дополнительный эскиз стоит пяти минут. Напишите столбцы в ряд и обведите рамкой те, которые вместе обозначают одну реальную вещь. Затем напишите рядом с полем, что вы будете делать, если две строки внутри него не согласятся. Эта коробка твоя subset и это предложение вашего типа, и оба решения вы в противном случае приняли бы случайно.
Все на одном экране
Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.
| вещь | Что он делает |
|---|---|
| Два вопроса | Является ли эта строка копией? Должен ли этот ключ появиться один раз? Разные ответы. |
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 в заказе сохранялся тот, который поступил первым, поэтому примерно в один вечер в месяц сообщалось о меньшем количестве, чем фактически было выполнено предприятием. Что для вас незаметно выбрала дедупликация?
Ссылки
- Кодд, EF (1970). Реляционная модель данных для крупных общих банков данных. Коммуникации ACM, 13(6), 377–387.
- Эльмагармид А.К., Ипейротис П.Г. и Верикиос В.С. (2007). Обнаружение повторяющихся записей: опрос. Транзакции IEEE по знаниям и инженерии данных, 19(1), 1–16.
- Свеллер, Дж. (1988). Когнитивная нагрузка во время решения задач: влияние на обучение. Когнитивная наука, 12(2), 257–285.
Python для аналитиков составляет 203 страницы, дублированные и drop_duplications читаются аргумент за аргументом, включая то, какую копию вы собираетесь сохранить.
Python для аналитиков, 19 $ →тот Курс Python дает вам повторы: точные копии, общие ключи и варианты корпуса в одном кадре, а также карточки с информацией о том, что каждый keep возвраты и пробный экзамен, на котором запрашивается количество строк, прежде чем запускать удаление. Теперь вы владеете методом; комплект - это практика. Ничего устанавливать, учетная запись не требуется.