- Введение
- Шаг 1. Импорт Pandas и NumPy
- Шаг 2. Импорт из Excel
- Шаг 3. Проверка данных
- Шаг 3.1. Как выглядят данные
- Шаг 3.2. Проверка типов данных столбцов
- Шаг 4. Реимпорт из Excel, настройка и преобразование типов данных при импорте
- Шаг 4.1 Реимпортирование из файла Excel
- Шаг 4.2 Как теперь выглядят данные
- Шаг 4.3 Перепроверьте типы данных столбцов
- Шаг 4.4 Проверка на нулевые значения (NaN)
- Шаг 5. Добавление пользовательских столбцов
- Шаг 5.1 Что такое NumPy?
- Шаг 5.2 Создание колонки «Сумма заказа (без НДС)» для валюты, в которой был куплен заказ
- Метод 1: Умножение на основе Pandas
- Метод 2: Умножение на NumPy с использованием данных из датафрейма sales_data напрямую
- Метод 3: Умножение на основе NumPy
- Шаг 5.3 Удаление двух неиспользуемых столбцов
- Шаг 5.4 Создание столбца «НДС в заказе» для валюты, в которой был куплен заказ
- Шаг 5.5 Создание колонки «Итого по заказу (включая НДС)» для валюты, в которой был куплен заказ
- Шаг 5.6 Создание колонки конвертации валют
- Шаг 5.7 Создание колонки «Итого по заказу (без НДС)» для конвертированной валюты
- Шаг 5.8 Создание колонки «Итого по заказу с НДС» для конвертированной валюты
- Шаг 5.9 Создание колонки «Итого по заказу (включая НДС)» для конвертированной валюты
- Шаг 6. Изучение заполненного датафрейма sales_data
- Шаг 7. Экспорт данных обратно в Excel
- Ресурсы
- Введение
- Шаг 1. Импорт Pandas и NumPy
- Шаг 2. Импорт из Excel
- Шаг 3. Проверка данных
- Шаг 3.1. Как выглядят данные
- Шаг 3.2. Проверка типов данных столбцов
- Шаг 4. Реимпорт из Excel, установка и преобразование типов данных при импорте
- Шаг 4.1 Реимпортирование из файла Excel
- Шаг 4.2 Как теперь выглядят данные
- Шаг 4.3 Перепроверка типов данных столбцов
- Шаг 4.4 Проверка на наличие значений NaN (Null)
- Шаг 5. Добавление пользовательских столбцов
- Шаг 5.1 Что такое NumPy?
- Шаг 5.2 Создание колонки «Сумма заказа (без НДС)» для валюты, в которой был куплен заказ
- Метод 1: Умножение на основе Pandas
- Метод 2: Умножение с помощью NumPy, используя данные из датафрейма sales_data напрямую
- Метод 3: Умножение на основе NumPy
- Шаг 5.3 Удаление двух неиспользуемых столбцов
- Шаг 5.4 Создание колонки Order Total VAT для валюты, в которой был куплен заказ
- Шаг 5.5 Создание колонки «Итого по заказу (включая НДС)» для валюты, в которой был куплен заказ
- Шаг 5.6 Создание колонки конвертации валют
- Шаг 5.7 Создание колонки Order Total (Excluding VAT) для конвертированной валюты
- Шаг 5.8 Создание колонки «Итоговая сумма заказа с НДС» для конвертированной валюты
- Шаг 5.9 Создание колонки «Итого по заказу (включая НДС)» для конвертированной валюты
- Шаг 6. Изучение заполненного датафрейма sales_data
- Шаг 7. Экспорт данных обратно в Excel
- Ресурсы
Введение
В этой серии статей, состоящей из нескольких частей, я рассмотрю некоторые основы Pandas, NumPy и Matplotlib, которые я изучил за последние несколько недель.
В первой части я рассмотрел следующие темы:
- Импорт данных из CSV-файла в Pandas
- Очистка данных и удаление непригодных данных.
- Преобразование между типами данных.
- Экспорт и импорт данных в файлы Excel и из них.
В этой части я расскажу о том, как выполнять математические операции над данными в Pandas Dataframe для создания дополнительных данных, таких как, например, общий итог. Я также расскажу об использовании NumPy для выполнения математических операций с данными вместо использования методов в Pandas, а также сравню производительность каждого из них с одной из выполняемых операций.
Блокнот Jupyter, а также все остальные необходимые файлы находятся в репозитории GitHub, ссылка на который есть в разделе «Ресурсы».
Давайте приступим.
Шаг 1. Импорт Pandas и NumPy
Прежде всего, необходимо импортировать библиотеки Pandas и NumPy.
import pandas as pd
import numpy as np
Шаг 2. Импорт из Excel
После импорта библиотек следующим шагом будет импорт данных.
sales_data = pd.read_excel(io = "data/orders-export.xlsx",
sheet_name = "orders-export",
skiprows = 1)
Вышеописанная процедура будет искать файл Excel с именем orders-export.xlsx в папке data и использовать рабочий лист с именем orders-export в качестве источника данных.
Первая строка будет пропущена, так как в ней есть заголовок. Если бы строка не была пропущена, данные были бы импортированы неправильно.
Шаг 3. Проверка данных
Теперь, когда данные были импортированы из файла Excel, давайте посмотрим на содержащиеся в нем данные.
Шаг 3.1. Как выглядят данные
Сначала посмотрим на первые пять строк данных в рамке данных sales_data.
sales_data.head(n = 5)
]
Данные представляют собой набор продаж для нескольких клиентов, причем каждый заказ занимает одну строку.
Шаг 3.2. Проверка типов данных столбцов
Далее давайте посмотрим на типы данных, которые были назначены каждому столбцу в рамке данных sales_data.
sales_data.dtypes
Как вы можете видеть, типы данных отличаются от тех, которые были заданы при импорте CSV-файла в первой части. Причина этого в том, что когда вы используете pd.read_excel, он проверяет, какой тип данных присвоен каждому столбцу, который ему сказали импортировать, и присваивает ему ближайший эквивалент. При желании вы можете изменить тип данных, присвоенный при импорте данных из файла. Например, изменить числовой (например, целочисленный) столбец в Excel на объектный (строковый) тип данных во фрейме данных.
Шаг 4. Реимпорт из Excel, установка и преобразование типов данных при импорте
Шаг 4.1 Реимпортирование из файла Excel
В предыдущем импорте файла Excel файлу pd.read_excel было разрешено импортировать данные и использовать тип данных, который лучше всего соответствовал тому, что было установлено для каждого столбца в Excel. По большей части это работало хорошо, но было заметное исключение — столбец order_date, которому был присвоен тип данных object.
Чтобы решить эту проблему, столбец можно импортировать, а затем преобразовать в формат времени даты, совместимый с Pandas, или преобразовать столбец в процессе импорта.
Кроме того, в столбце item_description есть несколько символов ‘,’, которые необходимо удалить, если данные нужно экспортировать в CSV-файл. Опять же, это можно сделать после импорта данных во фрейм, но вместо этого их можно преобразовать во время импорта.
В дополнение к преобразованию двух столбцов, приведенных выше, давайте жестко закодируем типы данных для каждого столбца. Это может быть полезно для выявления проблем в данных, например, строк в столбце, который имеет значение float или integer.
sales_data = pd.read_excel(io = "data/orders-export.xlsx",
sheet_name = "orders-export",
skiprows = 1,
dtype = {"order_id": np.int64,
"customer_id": np.int64,
"customer_first_name": str,
"customer_last_name": str,
"customer_gender": str,
"customer_city": str,
"customer_country": str,
"item_qty": np.int64,
"item_price": np.float64,
"order_currency": str,
"order_vat_rate": np.float64},
converters = {"order_date": lambda date: pd.to_datetime(date, dayfirst=True),
"item_description": lambda description: description.replace(",", " -")})
Чтобы повторить то, что только что произошло, в качестве источника данных использовался тот же файл Excel и тот же рабочий лист, при этом первая строка рабочего листа была пропущена. В рамках импорта были заданы типы данных для каждого столбца, а для настройки типа данных использовался NumPy для любого числового поля (подробнее о NumPy в следующем разделе). После этого (конвертеры) дата заказа была преобразована с помощью лямбда-функции, которая преобразовывает дату из DD/MM/YYYY в YYYY-MM-DD. Наконец, еще одна лямбда-функция была использована для замены любого ‘,’ в описании товара на ‘ -‘.
Шаг 4.2 Как теперь выглядят данные
sales_data.head()
Из вышеприведенного видно, что столбец даты теперь имеет формат, отличный от предыдущего импорта, а все ‘,’ были заменены на ‘ -‘ (см. описание товара для order_id 200003 (индекс № 2)).
Остальные данные в датафрейме sales_data остались такими же, как и раньше.
Шаг 4.3 Перепроверка типов данных столбцов
sales_data.dtypes
Как и в предыдущем импорте, типы данных установлены правильно, единственным изменением является тип данных order_date, который теперь установлен в datetime64.
Шаг 4.4 Проверка на наличие значений NaN (Null)
sales_data.isna().sum()
Как вы можете видеть, в данных нет значений NaN, так как они были очищены перед использованием. Если вам нужно знать, как выполнить базовую очистку данных, смотрите первую часть серии, где я рассказываю о некоторых методах очистки данных и преобразования типов данных.
Шаг 5. Добавление пользовательских столбцов
В этом разделе я расскажу о добавлении дополнительных столбцов данных, а также об удалении некоторых столбцов.
Для одного из столбцов я покажу вам три различных метода умножения двух чисел вместе, а для остальных дополнительных столбцов буду использовать наиболее эффективный метод из трех. Эти три метода следующие:
- Создайте два массива NumPy, один для item_qty, а другой для price_per_unit.
- Умножьте два массива NumPy вместе с помощью np.multiply.
- Умножьте количество_товаров на цену_за_единицу непосредственно из массива данных sales_data с помощью NumPy.
- Умножьте количество_товаров на цену_за_единицу непосредственно из массива данных sales_data.
Зачем использовать NumPy для этого, если можно просто выполнить умножение непосредственно из фрейма данных sales_data? Одно слово: SPEEEEEEEEEEEEEEEED!
Шаг 5.1 Что такое NumPy?
NumPy — это библиотека, специально созданная для выполнения математических операций над числовыми данными.
NumPy в своей основе написана на C и C++, поверх нее наложен код Python для взаимодействия с ней как с библиотекой Python. Основная причина, по которой NumPy намного быстрее списков Python (в некоторых случаях в ~50 раз), заключается в том, что данные хранятся в массиве, который занимает одно непрерывное пространство памяти, что позволяет быстро получить к ним доступ (также называется локальностью ссылок).
Шаг 5.2 Создание колонки «Сумма заказа (без НДС)» для валюты, в которой был куплен заказ
В этом столбце будут взяты столбцы item_price и order_qty и перемножены вместе. Теперь, для этой конкретной части, мы сделаем это три раза, используя ранее упомянутые методы (только NumPy, Pandas с NumPy и только Pandas).
Примечание: Каждый из этих методов займет определенное количество секунд, некоторые больше, чем другие. Это связано с магической функцией %timeit. Она используется для определения времени, которое требуется для создания каждого массива или столбца. Она выполняет цикл несколько раз, чтобы найти наилучшее время, так как в это время могли быть запущены процессы, которые могли повлиять на производительность. Вот почему это занимает больше времени.
Обычно все операции выполняются быстро, и вы не заметите особой разницы, но если бы набор данных был больше, все могло бы быть по-другому.
Метод 1: Умножение на основе Pandas
Этот метод создаст столбец, в котором для выполнения умножения используется только Pandas. При этом немного используется NumPy, но только для округления (np.around) результата до двух знаков после запятой. То же самое будет сделано и для двух других методов.
Примечание: » используется в качестве символа прерывания. Он нужен для того, чтобы можно было поставить перенос строки при использовании %timeit, иначе он бы не сработал.
%timeit sales_data["order_total_ex_vat_local_currency_pandas"] = np.around(sales_data["item_qty"] * sales_data["item_price"],
decimals = 2)
Как видно из результата, наилучшее время выполнения составило 220 мкс (мкс — микросекунды) или 0,00022 секунды.
Теперь перейдем к следующему методу.
Метод 2: Умножение с помощью NumPy, используя данные из датафрейма sales_data напрямую
Процесс выполнения этого метода аналогичен тому, что использовался в первом методе, но вместо этого будет использоваться NumPy для выполнения умножения с использованием данных непосредственно из кадра sales_data.
%timeit sales_data["order_total_ex_vat_local_currency_mixed"] = np.around(np.multiply(sales_data["item_qty"],
sales_data["item_price"]),
decimals = 2)
Как видно из результата, наилучшее время выполнения составило 247 мкс или 0,000247 секунды.
Теперь перейдем к последнему методу.
Метод 3: Умножение на основе NumPy
В этом методе необходимо выполнить четыре шага.
- Создайте массив NumPy (ndarray), который будет создан из данных в столбце sales_data[«item_qty»]. Этот массив будет называться sales_data_order_qty.
- Создайте массив NumPy (ndarray), который будет создан из данных в столбце sales_data[«item_price»]. Этот массив будет называться sales_data_item_price.
- Создайте массив NumPy (ndarray), который будет создан путем умножения двух массивов NumPy вместе. Этот массив будет называться sales_data_order_total_ex_vat_local_currency.
- Вставьте результаты из массива sales_data_order_total_ex_vat_local_currency обратно в массив данных sales_data.
В этом методе больше шагов, чем в предыдущих двух, но результаты говорят сами за себя.
Примечание: %timeit имеет неприятную проблему, заключающуюся в том, что при его использовании не удается выполнить присвоение переменных. Вот почему создание массивов отображается дважды для каждого массива. Однако это не влияет на вставку или переназначение столбцов в кадр данных.
%timeit sales_data_item_qty = np.array(sales_data["item_qty"])
sales_data_item_qty = np.array(sales_data["item_qty"])
%timeit sales_data_item_price = np.array(sales_data["item_price"])
sales_data_item_price = np.array(sales_data["item_price"])
%timeit sales_data_order_total_ex_vat_local_currency = np.around(np.multiply(sales_data_item_qty,
sales_data_item_price),
decimals = 2)
sales_data_order_total_ex_vat_local_currency = np.around(np.multiply(sales_data_item_qty,
sales_data_item_price),
decimals = 2)
%timeit sales_data["order_total_ex_vat_local_currency"] = sales_data_order_total_ex_vat_local_currency
Как видно из результата, наилучшее время выполнения всех четырех шагов составляет 54,78 мкс или 0,00005478 секунды. Это значительно быстрее, чем при использовании двух предыдущих методов, поскольку все данные из обоих источников хранились в массивах NumPy.
Теперь кадр данных sales_data выглядит следующим образом:
sales_data.head(n = 1)
Вам может быть интересно, как NumPy определяет порядок умножения чисел из каждого массива.
Когда NumPy выполняет операции, такие как умножение или сложение двух массивов, он использует так называемое сопоставление по элементам. Это означает, что массивы сопоставляются как одно целое и выполняются любые операции.
Например, если у нас есть два массива с десятью числами, которые нужно сложить, NumPy возьмет первый элемент (элемент) первого массива и добавит его к первому элементу второго массива. Затем второй ко второму, третий к третьему и так далее.
Для остальных столбцов, которые необходимо создать, будет использоваться этот метод. Но сначала необходимо удалить некоторые столбцы из массива данных sales_data, так как они не будут использоваться.
Шаг 5.3 Удаление двух неиспользуемых столбцов
Поскольку нам нужна только одна копия колонки order_total_ex_vat_local_currency, мы можем удалить две из них из датафрейма sales_data с помощью функции Pandas drop.
sales_data.drop(labels = ["order_total_ex_vat_local_currency_pandas","order_total_ex_vat_local_currency_mixed"],
axis = 1,
inplace = True)
sales_data.head(n = 1)
Как вы можете видеть, два столбца теперь удалены. Что же это сделало?
Функции drop был передан список, состоящий из двух столбцов, которые были дубликатами order_total_ex_vat_local_currency, но с немного другими именами. Ось столбца (axis = 1
) была задана таким образом, чтобы подразумевать, что функция должна отбросить столбец, соответствующий каждому имени в списке, и зафиксировать эти изменения в датафрейме sales_data (inplace = True
).
Шаг 5.4 Создание колонки Order Total VAT для валюты, в которой был куплен заказ
sales_data_order_vat_rate = np.array(sales_data["order_vat_rate"])
sales_data_order_total_vat_local_currency = np.around(np.multiply(sales_data_order_total_ex_vat_local_currency,
sales_data_order_vat_rate) / 100,
decimals = 2)
sales_data["order_total_vat_local_currency"] = sales_data_order_total_vat_local_currency
Шаг 5.5 Создание колонки «Итого по заказу (включая НДС)» для валюты, в которой был куплен заказ
В этом разделе значения, хранящиеся в массивах sales_data_order_total_ex_vat_local_currency и sales_data_order_total_vat_local_currency, будут сложены вместе и добавлены в датафрейм sales_data в виде нового столбца с именем order_total_inc_vat_local_currency.
sales_data_order_total_inc_vat_local_currency = np.around(np.add(sales_data_order_total_ex_vat_local_currency,
sales_data_order_total_vat_local_currency),
decimals = 2)
sales_data["order_total_inc_vat_local_currency"] = sales_data_order_total_inc_vat_local_currency
Шаг 5.6 Создание колонки конвертации валют
В рамках отчетности, которую ведет компания, может потребоваться, чтобы все итоговые суммы заказов были показаны в одной валюте. Для этого нам нужен столбец, который будет показывать курс конвертации валюты, который будет использоваться. Для этого в качестве валюты для отчета будет использоваться GBP (британские фунты).
Давайте сначала посмотрим, какие валюты присутствуют в датафрейме sales_data.
sales_data["order_currency"].value_counts()
В списке есть две валюты, GBP (британские фунты) и EUR (евро). Для значений конвертации GBP будет иметь курс конвертации 1, а евро будет иметь курс конвертации 0,84.
sales_data["order_currency_conversion_rate"] = sales_data["order_currency"].apply(lambda currency: 1.00 if currency == "GBP" else
(0.84 if currency == "EUR" else 1.00))
Вышеописанное позволило создать колонку под названием order_currency_conversion_rate, которая затем будет иметь значение 1 (GBP) или 0,84 (Euro) для каждой строки, определяемое тем, что было в колонке order_currency для этой строки. Для определения значения использовалась лямбда-функция с операторами if.
Давайте рассмотрим запись, валютой которой является фунт стерлингов:
sales_data.head(n = 1)
Как вы можете видеть, значение order_currency_conversion_rate для первой строки установлено в 1, поскольку в столбце order_currency указан GBP.
Теперь давайте посмотрим на запись, валютой которой является EUR:
sales_data.tail(n = 1)
Как видите, значение order_currency_conversion_rate для строки установлено в 0,84, так как в столбце order_currency у него стоит EUR.
Теперь, когда столбец order_currency_conversion_rate создан, давайте создадим из него массив NumPy для использования в следующих нескольких шагах.
sales_data_currency_conversion_rate = np.array(sales_data["order_currency_conversion_rate"])
Шаг 5.7 Создание колонки Order Total (Excluding VAT) для конвертированной валюты
Теперь, когда курсы конвертации валют добавлены для каждой строки, итоговые значения, которые ранее были созданы с использованием местной валюты, в которой был оплачен заказ, можно перевести в фунты стерлингов.
Для этого просто умножьте предыдущую итоговую сумму на курс конвертации валюты.
sales_data["order_total_ex_vat_converted_gbp"] = np.around(np.multiply(sales_data_order_total_ex_vat_local_currency,
sales_data_currency_conversion_rate),
decimals = 2)
Шаг 5.8 Создание колонки «Итоговая сумма заказа с НДС» для конвертированной валюты
Аналогичный процесс будет использоваться для конвертации общей суммы НДС в британские фунты.
sales_data["order_total_vat_converted_gbp"] = np.around(np.multiply(sales_data_order_total_vat_local_currency,
sales_data_currency_conversion_rate),
decimals = 2)
Шаг 5.9 Создание колонки «Итого по заказу (включая НДС)» для конвертированной валюты
И, наконец, то же самое для конвертации общей суммы (включая НДС) в GBP.
sales_data["order_total_inc_vat_converted_gbp"] = np.around(np.multiply(sales_data_order_total_inc_vat_local_currency,
sales_data_currency_conversion_rate),
decimals = 2)
Шаг 6. Изучение заполненного датафрейма sales_data
Теперь, когда в датафрейме sales_data есть необходимые итоговые значения, давайте посмотрим на него. Сначала посмотрим на строку, которая была оплачена в фунтах стерлингов и была конвертирована (вернее, не конвертирована, а конвертирована) в фунты стерлингов.
sales_data.head(n = 1)
Далее посмотрим на строку, в которой итоги были переведены из EUR в GBP.
sales_data.tail(n = 1)
Значения выглядят корректно, поэтому они были преобразованы, как и ожидалось.
Теперь давайте проверим типы данных для всех столбцов, особенно для вновь созданных столбцов.
sales_data.dtypes
Все они имеют тип данных float64, что и требуется.
И наконец, есть ли отсутствующие значения (NaN).
sales_data.isna().sum()
Никаких пропущенных значений не было, так что кадр данных sales_data теперь полный, чистый и все столбцы имеют правильные типы данных.
Шаг 7. Экспорт данных обратно в Excel
В последнем разделе второй части данные из таблицы данных sales_data будут экспортированы в новый файл Excel, который будет содержать все данные, включая все итоги, созданные в предыдущем разделе.
Этот файл будет использоваться в последующих частях этой серии для проведения анализа и просмотра статистики.
with pd.ExcelWriter(path = "data/order_data_with_totals.xlsx",
engine = "xlsxwriter",
date_format = "YYYY-MM-DD",
datetime_format = "YYYY-MM-DD") as writer:
sales_data.to_excel(writer,
index = False,
sheet_name = "order_data_with_totals")
Пример содержимого файла Excel показан ниже.
Ресурсы
Файлы GitHub для части 2