В современном мире большинство бизнес-процессов связаны с обработкой больших объемов данных, получаемых от различных источников. Часто эти данные содержат ошибки, дубликаты и пропуски, что может привести к неверным выводам и решениям. Одним из инструментов, которые позволяют очистить и преобразовать данные, является библиотека 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)


  1. CrazyElf
    05.04.2023 15:22
    +4

    Я бы не стал просто переводить отрицательные числа в положительные. Сначала хорошо бы сверить данные в этих строках с другими источниками. Возможно, там проблема не только в лишнем минусе. Может эти строки лучше вообще отбросить. Или обработать как-то по-другому. Как знать.


    1. Autechre Автор
      05.04.2023 15:22

      Спасибо! Обдумаю это.


    1. IvaYan
      05.04.2023 15:22

      Я думая, там вообще может не быть проблемы. В данных о продажах отрицательные значения могут говорить о возврате товара.


      1. CrazyElf
        05.04.2023 15:22
        +2

        Или так, например. В любом случае это повод сверить данные и проконсультироваться с кем-то, кто в курсе, как эти файлы заполняются и что значат эти минусы )


    1. economist75
      05.04.2023 15:22

      Отрицательные колво/цена/сумма в бухучете - это наверняка не ошибки ввода, а исправление ошибок (возможно методом "красного сторно" или исправительной проводкой, в т.ч. частичной, что часто бывает при пересортице).

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

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


      1. CrazyElf
        05.04.2023 15:22

        Кстати да, про сторно и всякое такое не надо забывать, всё может быть.


  1. aborouhin
    05.04.2023 15:22
    +1

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

    Ну и главный недостаток, что у Pandas, что у OpenRefine - очень сильно кушают оперативку :( Были задачи, при которых 192 Гб не хватало (а больше у меня нет). Тут уже рекомендуют смотреть в сторону Polars и Spark, как следующий раз столкнусь с десятками Гб, прилетевших на анализ, - придётся осваивать :)


    1. Robastik
      05.04.2023 15:22
      +1

      192 Гб не хватало

      Почему бы не разделить на части? Или построчно?


      1. CrazyElf
        05.04.2023 15:22

        Или выкинуть не нужные текстовые колонки, наверняка там есть колонки, которые можно обрабатывать не сразу все одновременно. Или превратить тексты в вектора и опять же сэкономить память.


      1. aborouhin
        05.04.2023 15:22

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


        1. economist75
          05.04.2023 15:22
          +1

          .astype('category') уменьшит в 8-10X размер df в RAM и почти во столько же ускорит отборы/сортировки. В 32 Гб RAM поместится df на 100 млн. строк * 100 столбцов, это очень большая БД, скажем, примерно весь бухучет холдинга из Top-100 РФ за период 10 лет. В бухучете много повторяющихся сущностей, которые очень хорошо факторизуются (слова - в числа, т.е. коды).


  1. Robastik
    05.04.2023 15:22

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

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


    1. CrazyElf
      05.04.2023 15:22

      Люто-бешено плюсую ) Инициатива хороша, когда есть понимание, что делаешь. Гадать и додумывать не нужно, нужно спрашивать языком )