![](https://habrastorage.org/getpro/habr/upload_files/b13/94a/053/b1394a0533d7e4910f0cd5dfcc3153e3.jpg)
Аналитик-экономист Саша устроилась на новую работу, у неё не было навыков программирования. В описании вакансии умение писать код не требовалось. Программирование преследовало экономиста в школе и университете. Саша старательно отмахивалась от написания кода, но избежать кодинга не вышло.
В Сашин первый день предыдущий сотрудник оставил три скрипта на Python для подготовки отчетности, уволился и сменил номер телефона. Ближайших срок сдачи отчетности – через месяц. Запуска кода было недостаточно для подготовки отчетов, нужно адаптировать скрипт под запрос руководителя.
Статья описывает ошибки, которые совершила Саша при изучение Python, и пути исправления этих ошибок. Статья расскажет об экстремальном опыте погружения в программирование на Python: анализ данных и визуализацию. Вот как это было…
Задача
Задание: провести ABC-анализ товаров по объёму выручки для оценки важности продуктов.
АВС-анализ – сортировка товаров от большего к меньшему по доле в выручке и разделение продуктов на 3 группы:
![АВС-анализ: группы товаров АВС-анализ: группы товаров](https://habrastorage.org/getpro/habr/upload_files/5f1/4b2/8ca/5f14b28cadf4121014a2f29a953d669e.png)
Данные: дата продажи, компания-покупатель, город покупателя, выручка и др.
![Пример данных для анализа Пример данных для анализа](https://habrastorage.org/getpro/habr/upload_files/24e/f9d/869/24ef9d869e44ab9f4452c148243bb943.png)
1. Отрицание: отсутствие анализа инструментов
Представьте, человек, который раньше не писал программный код, открывает файл и видит нечто страшное и непонятное. Оно написано на инопланетном языке и выглядит так:
abc_per = []
good_list = pd.DataFrame({'good': df['good'].unique()})
for i in range (good_list.shape[0]):
abc = sum(df['revenue'][df['good'] == good_list['good'][i]])/sum(df['revenue'])
abc_per.append(round(abc*100,1))
good_list['abc_per'] = abc_per
good_list = good_list.sort_values(by = 'abc_per', ascending = False)
Первая реакция – отрицание. Экономист отказывается от изучения Python и пытается решить задачу в Excel. Саша работает в этой программе 2 года.
Сначала процесс идёт быстро: экономист готовит таблицы и пишет формулы. Но объем данных составляет 500 тыс. строк и выполнять обработку в Excel трудоемко. Программа зависает и вылетает в 70% случаев. Появляется ещё одна проблема: информация за разные периоды лежит в отдельных файлах. При попытке склеить эти файлы базовыми средствами Excel появляются новые ошибки.
Спустя 3 часа тщетных попыток выполнить расчет, экономист решает выйти из зоны комфорта и взяться за изучение Python.
Ошибка
Для решения задачи не проведен анализ инструментов. Экономист не оценил плюсы и минусы Excel и Python, не поискал в интернете другие программы.
Как исправить
Открыть Google и узнать, что для проведения ABC-анализа, кроме Excel и Python, подходят Power Query и SQL. Эти программы помогут объединить файлы и обработать объем данных в полмиллиона строк.
Важно: аналитик выбирает инструмент на основе своих предпочтений. Мы не называем этот выбор правильным или неправильным. Просто полагаем, что поиск разных решений помогает получить новые знания.
Power Query – дополнение к Excel, помогает преобразовывать и объединять данные из отдельных файлов. Power Query не требует навыков программирования. У программы русскоязычный интерфейс, похожий на другие продукты Microsoft.
SQL – язык структурированных запросов, используется для анализа данных. Также преобразовывает и объединяет данные. Придется программировать, но язык похож на английский. SQL использует команды: create, drop, select, join и пр.
2. Гнев: чужие коды и непоследовательное погружение
Экономист открывает код и внимательно читает его от начала до конца. Ничего не понимает, разбивает скрипт на отдельные элементы и старается изучить каждый из них в отдельности. Через 8 часов Саша разбирает 40% кода, выяснила предназначение библиотек и функций.
В середине кода оказалась строка:
good_list_new['abc_group'] = good_list_new['abc_per_accum'].apply(
lambda abc: 'A' if abc < 80 else('B' if 80 <= abc < 95 else 'C'))
Экономист не понимает, что еще за lambda. В интернете не оказалось нужного Саше ответа на вопрос о том, зачем нужна lambda и что она делает. Пример статьи о lambda:
У экономиста возникает ряд вопросов. Функция? Аргумент? Выражение? Объект-функция? Саша злится, захлопывает крышку ноутбука и неделю не возвращается к изучению Python.
Ошибка
Погружение в программирование началось с конкретного кода и разбора его отдельных элементов. Фокус внимания направлен на чужое готовое решение, а не на понимание концепции и терминологии Python.
Как исправить
Сначала нужно узнать, что Python – объектно-ориентированный язык программирования. Затем разобраться в этом термине, составить собственный словарь понятий и их объяснений.
Объектно-ориентированный язык программирования означает, что программный код состоит из объектов, которые взаимодействуют между собой. Примеры объектов: человек, почтовый индекс, компания или автомобиль.
Свойства – характеристики объекта, методы – возможные действия с объектом. Класс – набор свойств и методов для всех объектов, входящих в класс.
![](https://habrastorage.org/getpro/habr/upload_files/dd4/00d/9cd/dd400d9cd75c80fb2e220d46eb3bfcae.jpeg)
Объект – красный Jeep 2010 года выпуска, который купил Иванов И.И. Свойства объекта: цвет – красный, марка – Jeep, год выпуска – 2010, владелец – Иванов И.И.. Метод объекта: покупка. Класс: автомобиль. Свойства класса: цвет, марка, год выпуска, владелец, объем двигателя, число лошадиных сил и пр. Методы класса: купить, разбить, продать.
Следующий этап – изучение элементов Python:
Переменные,
Типы данных,
Операторы,
Функции.
Теперь стоит написать первые строки собственного кода. После двух часов самостоятельных упражнений с переменными и функциями можно приступить к разбору чужого кода.
Важно: начинать погружение в свой первый язык программирования с изучения чужого кода – спорный подход. Но анализировать код «со стороны» полезно на следующих этапах изучения. Это помогает узнать о новых решениях и оптимизировать свой код. Анализ скриптов других аналитиков повышает «насмотренность».
3. Торг: сторонние программы и ручные решения
Спустя неделю Саша возвращается к Python, ведь срок сдачи отчетов приближается. Саша не пишет чистый код Python из-за скромного опыта в программировании, на помощь приходят «костыли». «Костыли» – особенности кода, которые делают решение менее универсальным: использование сторонних программ и необходимость вручную корректировать код.
Примеры:
Экономист создает код для выполнения расчетов, но результаты этих расчетов анализирует с помощью сводных таблиц в Excel.
Саша подгружает данные в Python, использует циклы и условные конструкции, но базовые визуализации для проверки строит в Power BI.
Аналитик выгружает данные в виде csv-файла из хранилища данных с помощью SQL, затем анализирует в Python.
Саша использует первый вариант, потому что уверена в своих знаниях Excel. Однако, Python предлагает решения на этот случай: функции pivot_table или crosstab из библиотеки pandas. Для второго случая используются библиотеки matplotlib или seaborn, для третьей проблемы – psycopg2 или sqlalchemy.
Комбинированные решения требуют повышенного внимания. Нужно проверять правильность и полноту данных при переходе из одной программы в другую.
«Костыль» может быть и внутри скрипта Python, он заставляет корректировать код вручную при любом изменении данных. Например, нужно загрузить файл без лишних столбцов и с правильными названиями колонок. Вот, что делает Саша:
df = pd.read_excel('D:/Python/Good_revenue.xlsx')
df.head()
Получается таблица:
![Вывод данных: изначальный Вывод данных: изначальный](https://habrastorage.org/getpro/habr/upload_files/fd0/35c/fb2/fd035cfb27bcc84311098f3aa1790cb0.png)
Проблемы: пустой столбец и странные названия колонок. Саша решает проблему так:
df.drop('Unnamed: 0', axis = 1, inplace = True)
df.drop(0, axis = 0, inplace = True)
df.rename({'Unnamed: 1': 'date', 'Unnamed: 2': 'company', 'Unnamed: 3': 'city',
'Unnamed: 4': 'region', 'Unnamed: 5': 'category', 'Unnamed: 6': 'good',
'Unnamed: 7': 'price', 'Unnamed: 8': 'quantity', 'Unnamed: 9': 'revenue'},
axis = 1, inplace = True)
Саша думает: «Решение работает отлично!» Но экономист создал код, в котором много ручного труда и которые не получится использовать для других данных.
Альтернативное решение:
df = pd.read_excel('D:/Python/Good_revenue.xlsx', header=1, usecols="B:J")
df.head()
Получается таблица:
![Вывод данных: скорректированный Вывод данных: скорректированный](https://habrastorage.org/getpro/habr/upload_files/ac7/1b0/55e/ac71b055ec20c2a44f0076d7073bca80.png)
Новое решение короче изначального в 10 раз, применимо к любым данным и позволяет глубже понять функцию read_excel.
Ошибка
Использование промежуточных решений – «костылей». Это приводит к ошибкам, низкому уровню автоматизации и универсальности кода.
Как исправить
Если используются две программы, стоит разузнать о решениях внутри Python. Библиотеки уже содержат методы, похожие на функции Excel или Power Query, на графики Power BI и пр.
Важно: есть случаи, когда другие программы нужны. Например, визуализация финальных результатов для директора: Power BI или Power Point.
Если код состоит из блоков, которые редактируются вручную в 50% случаев, стоить изучить документацию функций. Еще один вариант – пользовательские функции.
Документация функций – подробное описание функции и ее аргументов с примерами. В Jupyter Notebook для вывода документации нужно написать имя функции, знак «?» и запустить ячейку:
pd.read_excel?
Результат:
Signature:
pd.read_excel(
io,
sheet_name: 'str | int | list[IntStrT] | None' = 0,
header: 'int | Sequence[int] | None' = 0,
names=None,
index_col: 'int | Sequence[int] | None' = None,
usecols=None
...
Пользовательская функция – функция, которую пользователь пишет самостоятельно. Для задания функции используется def:
def calculate_sum(a,b):
sum = a+b
return sum
Вывод функции:
print(calculate_sum(2,3))
Результат: 5
Не нужно изменять функцию и переписывать ее вручную. Достаточно менять аргументы и запускать код. В примере использована простая функция, поэтому эффект незаметен. Но если в функции стоит расчет из 20 строк с 5 переменными, рост универсальности и автоматизации будет ощутим.
4. Депрессия: бесструктурность и небрежность кода
Остается неделя до сдачи отчетности, Саша отчаивается. Кажется, что разобраться с кодом не выйдет: чем больше экономист корректирует скрипт, тем сильнее путается в нем. Треть кода работает без перебоев, но в остальной части – неразбериха. Новые переменные, длинные строки, дублирование ячеек, ветвление кода. Саша удаляет одно, перестает работать другое.
Несмотря на оптимизацию и адаптацию части кода, разобраться не выходит. Изначальный код:
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
df = pd.read_excel('D:/Python/Good_revenue.xlsx', header=1, usecols="B:J")
df['date'] = pd.to_datetime(df['date'], dayfirst = True, infer_datetime_format=True)
df = df.astype({'price': np.float, 'quantity': np.int64, 'revenue': np.float})
pd.DataFrame(round(df.isna().mean()*100,)).style.background_gradient('coolwarm')
abc_per = []
good_list = pd.DataFrame({'good': df['good'].unique()})
for i in range (good_list.shape[0]):
abc = sum(df['revenue'][df['good'] == good_list['good'][i]])/sum(df['revenue'])
abc_per.append(round(abc*100,1))
good_list['abc_per'] = abc_per
good_list = good_list.sort_values(by = 'abc_per', ascending = False)
good_list_new = good_list.reset_index(drop = True)
good_list_new['abc_per_accum'] = good_list_new['abc_per'].cumsum()
good_list_new['abc_group'] = good_list_new['abc_per_accum'].apply(lambda abc:
'A' if abc < 80 else('B' if 80 <= abc < 95 else 'C'))
Анализируем структуру кода, пробуем упростить, находим проблемы в строках:
10. Анализ пустых ячеек – опциональный элемент, можно вынести из расчета.
12-18. Эта часть кода создает уникальный список товаров и рассчитывает долю каждого товара в выручке. Используются 4 новых объекта: списки abc_per и good_list, переменная abc и столбец good_list['abc_per']. В конце – сортировка результатов.
20. Новая переменная good_list_new.
Остальные части кода нужные, но не хватает комментариев.
Попробуем применить знания из предыдущих пунктов и упростить код. Заодно добавим структуру и комментарии:
# импортируем библиотеки
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')
# загружаем файл, первая строка - зоголовок, нужные колонки - B:J
# df - изначальный датасет
df = pd.read_excel('D:/Python/Good_revenue.xlsx', header=1, usecols="B:J")
# меняем типы данных, дата - datetime, цена и выручка - вещ. число, кол-во - целое
df['date'] = pd.to_datetime(df['date'], dayfirst = True, infer_datetime_format=True)
df = df.astype({'price': np.float, 'quantity': np.int64, 'revenue': np.float})
# создаем сводную таблицу category_abc, строки - товары, значения - сумма выручки
category_abc = df.pivot_table(index = 'good', values = 'revenue',
aggfunc = 'sum').sort_values(by='revenue', ascending=False)
# добавляем в сводную таблицу столбец percent с процентом выручки товара
category_abc['percent']=round((category_abc['revenue']/
sum(category_abc['revenue']))*100,4)
# расчитываем накомпительный процент выручки - столбец accumulated_percent
category_abc['accumulated_percent']=category_abc['percent'].cumsum()
# присваиваем группу ABC по накопленному процент выручки
category_abc['abc_group'] = category_abc['accumulated_percent'].apply(lambda abc:
'A' if abc < 80 else('B' if 80 <= abc < 95 else 'C'))
Визуально новый код больше изначального, но количество строк с расчетом сократилось вдвое, а читаемость кода выросла. Выкидываем цикл, не создаем новых сущностей: используем одну сводная таблица и 3 новых столбца в ней.
Ошибка
Нечитаемый код без четкой структуры:
![Зеленые точки – важные элементы кода Зеленые точки – важные элементы кода](https://habrastorage.org/getpro/habr/upload_files/1f2/928/280/1f292828018110a4872fc886ac9e8c80.jpg)
Как исправить
Навести порядок в коде, убрать лишние блоки и подписать нужные. Внимательно относится к новым переменным и подписывать, зачем они нужны. Структурировать код:
![Зеленые точки – важные элементы кода Зеленые точки – важные элементы кода](https://habrastorage.org/getpro/habr/upload_files/070/dfe/84d/070dfe84d51c7454644018805b283483.jpg)
Важно: в дальнейшем для оформления кода можно использовать PEP 8 – руководство по стилю кода Python. Для анализа кода есть продвинутые инструменты pycodestyle, prospector и пр. Здесь же напоминаем о пользовательских функциях, они тоже увеличивают читаемость кода.
5. Принятие: отсутствие базы знаний
Саша принимает неизбежность программирования на Python, корректирует код и успевает подготовить отчеты в срок. Работа с Python движется медленно, но поступательно. Через три месяца аналитик лучше понимает код и может писать часть скрипта самостоятельно. Однако, в 4 случаях из 5 код экономиста – компиляция чужих решений из интернета. В этом нет ничего плохого, просто адаптированный чужой код запоминается хуже, чем выстраданный свой. Саша обнаруживает, что некоторые найденные скрипты работают, а другие – нет. Постепенно аналитик обрастает notebook'ами с работающими и неработающими кодами, кучей файлов и начинает путаться.
Ошибка
Отсутствие базы знаний.
Как исправить
Если код работает и решает задачу, которая часто встречается, можно добавить его в базу знаний. База знаний – файл с рабочим кодом, описанием и примерами.
# корреляционный анализ
numeric_col = ['revenue', 'price', 'quantity']
corr = df.loc[:,numeric_col].corr()
print(corr)
# один ряд на графике
sns.lineplot(data=df, x = "date", y = "revenue")
# регрессионный анализ, уравнение парной регрессии (выручка от количества)
data = df
x = df[['quantity']].values
y = df['revenue'].values
skm = LinearRegression()
skm.fit(x, y)
y_pred=skm.predict(x)
print('Уравнение регрессии = ' + str(round(skm.intercept_,1)) +
' + ' + str(round(skm.coef_[0],1)) + ' * x')
Заключение
Меня зовут Александра, я team lead BI-аналитиков, анализирую данные, визуализирую информацию, интересуюсь машинным обучением. История, которую я рассказала, реальна и произошла со мной в начале карьерного пути. Я выделила 5 ошибок, которые допустила при быстром погружении в Python:
Отказываться от поиска и сравнения разных инструментов для оптимального решения задач;
Начинать с чужого кода, а не с терминологии и концепции языка программирования;
Создавать промежуточные ручные решения, снижающие универсальность кода;
Делать скрипт нечитаемым, пренебрегать структурой;
Не сохранять удачные решения в базу знаний.