В современном мире большинство бизнес-процессов связаны с обработкой больших объемов данных, получаемых от различных источников. Часто эти данные содержат ошибки, дубликаты и пропуски, что может привести к неверным выводам и решениям. Одним из инструментов, которые позволяют очистить и преобразовать данные, является библиотека pandas для языка программирования Python.
Я собираюсь рассмотреть задачу по очистке данных с помощью pandas. Для этого возьмем данные, содержащие дубликаты строк, неправильные типы данных, пропуски и отрицательные значения. Затем я буду использовать функциональные возможности pandas для очистки и преобразования этих данных в форму, пригодную для дальнейшего анализа.
Предположим, у вас есть набор данных, содержащий информацию о продажах компании за последние несколько лет. Но данные не очень чистые, и вы заметили, что есть некоторые проблемы с форматированием и некоторые строки содержат ошибки.
Задача: Необходимо очистить данные о продажах компании за последние несколько лет с помощью библиотеки Pandas.
Исходные данные:
Файл CSV, содержащий информацию о продажах компании за последние несколько лет.
Файл содержит следующие столбцы: дата продажи, название продукта, количество проданного товара, цена за единицу, общая стоимость продажи, имя продавца и регион продажи.
В некоторых строках присутствуют ошибки, например, неправильный формат даты или отсутствие цены за единицу.
Задачи, которые необходимо выполнить:
Загрузить исходные данные из файла в Pandas DataFrame.
Удалить строки, которые содержат ошибки в данных.
Привести столбец с датами к формату
datetime
.Привести столбцы с числами к числовому формату (float или int).
Определить и удалить дубликаты строк.
Сохранить очищенные данные в новый файл CSV.
Описание столбцов:
date
- дата продажи, в формате "YYYY-MM-DD";product_name
- название продуктаquantity
- количество продуктаunit_price
- цена за единицу продуктаtotal_price
- общая стоимость продукта, равная произведению количества и цены за единицуseller_name
- имя продавцаregion
- регион продажи
Загрузка данных
Чтобы загрузить данные в pandas, можно использовать метод read_csv()
для загрузки данных из файла CSV
или read_excel()
для загрузки данных из файла Excel
. В нашем случае у нас csv
файл.
Импортируем необходимые библиотеки и загружаем данные.
import pandas as pd
import re
df = pd.read_csv('data_with_errors.csv')
Выводим наш DataFrame.
df.head(5)
На первый взгляд в данных видно наличие отрицательных значений и пропусков. Однако, в нашем задании сказано, что после загрузки мы должны удалить строки, в которых есть ошибки. Мы поступим немного по-другому. Сначала мы проверим типы столбцов, и если обнаружится, что какие-то столбцы не соответствуют данным, которые в них находятся, мы изменим тип на соответствующий. При возникновении проблем в ходе выполнения этой задачи, мы будем исправлять то, что будет необходимо.
Обработка данных
Для начала посмотрим на то, какие типы имеют наши столбцы. Для этого нам поможет команда info()
.
df.info()
Мы получаем информацию о нашем DataFrame, которая говорит нам о наличии пропусков в столбце total_price
. Также мы видим, что столбец с датами имеет строковый тип, также как и столбцы quantity
и unit_price
, которые содержат числовые данные. Нам необходимо это исправить.
Попробуем сразу привести столбец date к типу datetime
. Для этого нам понадобится следующий код:
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
К сожалению, этот код не сработает, так как в столбце с данными присутствуют значения, которые не позволяют сразу привести столбец к нужному нам типу. В результате работы выражения будет выведено сообщение об ошибке.
Существует множество способов решения данной проблемы, один из них представлен ниже. Так как даты в нашем столбце date указаны в формате "YYYY-MM-DD", мы можем использовать регулярное выражение для поиска всех значений столбца, которые не соответствуют данному формату. Для этого мы создадим лямбда-функцию, которая будет применена к столбцу методом map()
. Регулярное выражение будет выглядеть следующим образом: `\d{4}-\d{2}-\d{2}`.
Создаем лямбда-функцию.
search = lambda x: x if re.search(r"\d{4}-\d{2}-\d{2}", x) else 'not found'
Применяем лямбда-функцию к столбцу date
.
df['date'] = df['date'].map(search)
Проверим результат.
df.query('date == "not found"').count()
Мы видим, что в 53 строках данные не соответствуют формату..
Посмотрим на эти строки, чтобы понять, с чем мы имеем дело.
df.query('date == "not found"').head(5)
Мы замечаем отсутствие даты, а также латинские буквы вместо чисел в столбце количества quantity
. Кроме того, столбец total_price
содержит множество пропусков, а unit_price
имеет много повторяющихся значений. В таком виде данные не представляют ценности для анализа, и мы должны удалить строки, содержащие ошибки, как указано в задании.
df = df.drop(df.query('date == "not found"').index)
df.query('date == "not found"').count()
Приведем столбец date
к нужному нам типу данных.
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
df.info()
Видим что столбец date
теперь имеет тип datetime.
Для того чтобы привести столбец quantity
к числовому формату, мы можем использовать метод to_numeric()
с параметром errors='coerce'
, который преобразует значения в числа, а нечисловые значения заменяет на NaN.
df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce')
Тоже самое мы делаем с unit_price.
df['unit_price'] = pd.to_numeric(df['unit_price'], errors='coerce')
Для продолжения, мы сфокусируемся на отрицательных значениях в указанных столбцах и выведем их на экран.
df.query('quantity < 0')
Эти значения будут преобразованы с помощью функции abc()
, чтобы преобразовать отрицательные значения в столбцах. Это позволит получить абсолютные значения и избавиться от знака минус.
df.loc[df['quantity'] < 0,'quantity'] = abs(df['quantity'])
Таким же образом мы поступим unit_price.
df.query('unit_price < 0')
Заменяем отрицательные значения на положительные.
df.loc[df['unit_price'] < 0,'unit_price'] = abs(df['unit_price'])
Обработаем столбец total_price
. В задаче указано, что total_price
представляет собой общую стоимость продукта, которая равна произведению количества и цены за единицу. Значит, мы можем заполнить пустые значения в этом столбце, умножив значение quantity
на значение unit_price
. Так и поступим.
df.loc[df['total_price'].isna(), 'total_price'] = df['quantity'] * df['unit_price']
Данные содержат некоторое количество дубликатов, которые необходимо удалить в соответствии с заданием.
df[df.duplicated()]
Удаляем дубликаты.
df = df.drop(df[df.duplicated()].index)
Проверим категориальные переменные.
df.product_name.unique()
df.seller_name.unique()
df.region.unique()
Все значения категориальных переменных в порядке. Осталось только сохранить данные в csv. В этом нам поможет функция to_csv()
.
df.to_csv('processed_data.csv',index=False, header=True)
В результате мы получим файл processed_data.csv
.
Заключение
Такое задание позволяет закрепить навыки работы с pandas, например, загрузка данных из файла, очистка данных от дубликатов и пропусков, изменение типов данных столбцов и обработка пропущенных значений. Задание также поможет новичкам овладеть принципами анализа данных, включая методы pandas для анализа данных.
В скором времени я планирую выложить разбор реальной задачи для продуктового аналитика, который поможет вам лучше понять, как применять знания и навыки, полученные в процессе изучения данной темы. Я надеюсь, что этот материал будет интересен и полезен для вас, и вы сможете успешно применить полученные знания на практике. Буду благодарен за ваши комментарии! Спасибо!
Комментарии (13)
aborouhin
05.04.2023 15:22+1Именно для ручной очистки данных я привык OpenRefine использовать, инструмент конкретно под это заточен, и визуально удобнее... но Ваш подход тоже надо взять на вооружение, особенно если один раз вручную выработанную последовательность преобразований надо потом многократно применять ко всем данным из того же источника.
Ну и главный недостаток, что у Pandas, что у OpenRefine - очень сильно кушают оперативку :( Были задачи, при которых 192 Гб не хватало (а больше у меня нет). Тут уже рекомендуют смотреть в сторону Polars и Spark, как следующий раз столкнусь с десятками Гб, прилетевших на анализ, - придётся осваивать :)
Robastik
05.04.2023 15:22+1192 Гб не хватало
Почему бы не разделить на части? Или построчно?
CrazyElf
05.04.2023 15:22Или выкинуть не нужные текстовые колонки, наверняка там есть колонки, которые можно обрабатывать не сразу все одновременно. Или превратить тексты в вектора и опять же сэкономить память.
aborouhin
05.04.2023 15:22Ну в итоге и разделил, но задачу это усложняло. Там банковские выписки были, и куча критериев, по которым надо проверять.
economist75
05.04.2023 15:22+1.astype('category') уменьшит в 8-10X размер df в RAM и почти во столько же ускорит отборы/сортировки. В 32 Гб RAM поместится df на 100 млн. строк * 100 столбцов, это очень большая БД, скажем, примерно весь бухучет холдинга из Top-100 РФ за период 10 лет. В бухучете много повторяющихся сущностей, которые очень хорошо факторизуются (слова - в числа, т.е. коды).
Robastik
05.04.2023 15:22в нашем задании сказано, что после загрузки мы должны удалить строки, в которых есть ошибки
Чтобы определять что является ошибкой надо либо быть экспертом в домене, либо иметь указание в ТЗ. А при описанном подходе весьма вероятно не исключить, а приумножить ошибки.
CrazyElf
05.04.2023 15:22Люто-бешено плюсую ) Инициатива хороша, когда есть понимание, что делаешь. Гадать и додумывать не нужно, нужно спрашивать языком )
CrazyElf
Я бы не стал просто переводить отрицательные числа в положительные. Сначала хорошо бы сверить данные в этих строках с другими источниками. Возможно, там проблема не только в лишнем минусе. Может эти строки лучше вообще отбросить. Или обработать как-то по-другому. Как знать.
Autechre Автор
Спасибо! Обдумаю это.
IvaYan
Я думая, там вообще может не быть проблемы. В данных о продажах отрицательные значения могут говорить о возврате товара.
CrazyElf
Или так, например. В любом случае это повод сверить данные и проконсультироваться с кем-то, кто в курсе, как эти файлы заполняются и что значат эти минусы )
economist75
Отрицательные колво/цена/сумма в бухучете - это наверняка не ошибки ввода, а исправление ошибок (возможно методом "красного сторно" или исправительной проводкой, в т.ч. частичной, что часто бывает при пересортице).
Их нельзя удалять или лишать знака, чтобы не исказить итоги. Наиболее правильно - сгруппировать строки по всем аналитическим признакам, кроме даты. Кол-во строк в df уменьшится, останутся только положительные значения.
Если же останутся "минусы" - они будут постоянно мешать строить графики, смотреть статистики, приводить к ошибкам при расчетах std, средних итд.
CrazyElf
Кстати да, про сторно и всякое такое не надо забывать, всё может быть.