Аналитик-экономист Саша устроилась на новую работу, у неё не было навыков программирования. В описании вакансии умение писать код не требовалось. Программирование преследовало экономиста в школе и университете. Саша старательно отмахивалась от написания кода, но избежать кодинга не вышло. 

В Сашин первый день предыдущий сотрудник оставил три скрипта на Python для подготовки отчетности, уволился и сменил номер телефона. Ближайших срок сдачи отчетности – через месяц. Запуска кода было недостаточно для подготовки отчетов, нужно адаптировать скрипт под запрос руководителя. 

Статья описывает ошибки, которые совершила Саша при изучение Python, и пути исправления этих ошибок. Статья расскажет об экстремальном опыте погружения в программирование на Python: анализ данных и визуализацию. Вот как это было…

Задача

Задание: провести ABC-анализ товаров по объёму выручки для оценки важности продуктов.

АВС-анализ – сортировка товаров от большего к меньшему по доле в выручке и разделение продуктов на 3 группы:

АВС-анализ: группы товаров
АВС-анализ: группы товаров

Данные: дата продажи, компания-покупатель, город покупателя, выручка и др.

Пример данных для анализа
Пример данных для анализа

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:

Источник: https://habr.com/ru/company/piter/blog/674234/
Источник: https://habr.com/ru/company/piter/blog/674234/

У экономиста возникает ряд вопросов. Функция? Аргумент? Выражение? Объект-функция? Саша злится, захлопывает крышку ноутбука и неделю не возвращается к изучению Python.

Ошибка

Погружение в программирование началось с конкретного кода и разбора его отдельных элементов. Фокус внимания направлен на чужое готовое решение, а не на понимание концепции и терминологии Python.

Как исправить

Сначала нужно узнать, что Python – объектно-ориентированный язык программирования. Затем разобраться в этом термине, составить собственный словарь понятий и их объяснений.

Объектно-ориентированный язык программирования означает, что программный код состоит из объектов, которые взаимодействуют между собой. Примеры объектов: человек, почтовый индекс, компания или автомобиль. 

Свойства – характеристики объекта, методы – возможные действия с объектом. Класс – набор свойств и методов для всех объектов, входящих в класс. 

Объект – красный Jeep 2010 года выпуска, который купил Иванов И.И. Свойства объекта: цвет – красный, марка – Jeep, год выпуска – 2010, владелец – Иванов И.И.. Метод объекта: покупка. Класс: автомобиль. Свойства класса: цвет, марка, год выпуска, владелец, объем двигателя, число лошадиных сил и пр. Методы класса: купить, разбить, продать.

Следующий этап – изучение элементов Python: 

  • Переменные,

  • Типы данных,

  • Операторы,

  • Функции.

Теперь стоит написать первые строки собственного кода. После двух часов самостоятельных упражнений с переменными и функциями можно приступить к разбору чужого кода.

Важно: начинать погружение в свой первый язык программирования с изучения чужого кода – спорный подход. Но анализировать код «со стороны» полезно на следующих этапах изучения. Это помогает узнать о новых решениях и оптимизировать свой код. Анализ скриптов других аналитиков повышает «насмотренность».

3. Торг: сторонние программы и ручные решения

Спустя неделю Саша возвращается к Python, ведь срок сдачи отчетов приближается. Саша не пишет чистый код Python из-за скромного опыта в программировании, на помощь приходят «костыли». «Костыли» – особенности кода, которые делают решение менее универсальным: использование сторонних программ и необходимость вручную корректировать код. 

Примеры:

  1. Экономист создает код для выполнения расчетов, но результаты этих расчетов анализирует с помощью сводных таблиц в Excel. 

  2. Саша подгружает данные в Python, использует циклы и условные конструкции, но базовые визуализации для проверки строит в Power BI. 

  3. Аналитик выгружает данные в виде 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()

Получается таблица:

Вывод данных: изначальный
Вывод данных: изначальный

Проблемы: пустой столбец и странные названия колонок. Саша решает проблему так:

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()

Получается таблица:

Вывод данных: скорректированный
Вывод данных: скорректированный

Новое решение короче изначального в 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 новых столбца в ней.

Ошибка

Нечитаемый код без четкой структуры:

Зеленые точки – важные элементы кода
Зеленые точки – важные элементы кода

Как исправить

Навести порядок в коде, убрать лишние блоки и подписать нужные. Внимательно относится к новым переменным и подписывать, зачем они нужны. Структурировать код:

Зеленые точки – важные элементы кода
Зеленые точки – важные элементы кода

Важно: в дальнейшем для оформления кода можно использовать 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:

  1. Отказываться от поиска и сравнения разных инструментов для оптимального решения задач;

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

  3. Создавать промежуточные ручные решения, снижающие универсальность кода;

  4. Делать скрипт нечитаемым, пренебрегать структурой;

  5. Не сохранять удачные решения в базу знаний.

Комментарии (9)


  1. Hungryee
    00.00.0000 00:00

    Отличная статья, хорошие примеры.
    Отдельный респект за включение фрагментов кода


  1. rutexd
    00.00.0000 00:00

    Начать погружение в питон с нуля в режиме стресса и дедлайна... Путь храбрых. Помнится мне подобным образом лет 10 назад эдак я начать изучать другой яп и так же лишь спустя некоторое время после сдачи проекта начал понимать что к чему по настоящему. К счастью такого перегруза не было - но боль я ощущаю.

    Если вас успокоит, по моему ощущению даже человеку с опытом сходу понять питон задача не из лёгких. Базовые концепты - можно. Поиграться - можно. Однако (!). Когда дело заходит до реального мира и реальных библиотек, их тысяч вариаций, тысяч нюансов, тысяч непонятных и разнящихся стандартов именований и концептов - тут черт ногу сломит. Язык даёт слишком много свободы - и от этого головная боль.

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


  1. mobilkip
    00.00.0000 00:00

    Скоро буду искать первую работу(в IT и вообще), и чувствую я что эта статья мне ОЧЕНЬ пригодится


  1. magiavr
    00.00.0000 00:00

    Тут мало того, что человек не знает python, не знает библиотеку pandas, так в принципе имеет смутное представление о программировании. Это как неподготовленному человеку перевести стихотворение с суахили на древнегреческий.


  1. economist75
    00.00.0000 00:00

    По своему опыту, толковый экономист с наставником - с нуля постигает Python за 2-3 недели, а на "уверенный" Pandas уходит около полугода. Такой специалист сам напишет UDF и сделает ее методом датафрейма, закрыв потребность в ABC-анализе раз и навсегда. Метод работает максимально просто и понятно, не забудешь никогда:

    df.ABC(['Контрагент', 'Выручка'])
    

    Именно такие UDF в итоге сделают работу аналитика на порядок легче и эффективней, чем прежняя. Python-way для аналитики однозначно быстрее, увлекательнее и дешевле чем VBA-макросятничество или веселое накликивание в PowerQuery/DAX/M, или трехэтажное формулописательство в Excel. Если они уже в активе - это хорошо: UDF для ABC-анализа появится быстрее и будет применяться чаще и шире.

    Но главное - ощущение простоты и надежности решения. С Python оно выше, чем с другими упомянутыми технологиями.


  1. psalamakhin
    00.00.0000 00:00

    Извините, но про визуализации некорректно сравнивать python и powerbi. Основное преимущество второго - интерактивность, при чем такая, что первому и не снилась. На питоне только простейшие визуализации без интерактивности и сложных связей. На питоне выгрузка, подготовка данных - да, вполне, но визуализация - это не его, не функционально


    1. economist75
      00.00.0000 00:00

      Очень даже корректно. PowerBI в ru-зоне скоро будет мертв, поэтому о покойниках "...либо ничего". Но планку визуализаций он задрал очень высоко, признаю. Сам python ничего не визуализирует, а вот та же pandas поддерживает несколько бэкендов визуализации в простой обертке df.plot(), которую вынуждены были повторить почти все остальные DS-платформы. Я назову по памяти интерактивные библиотеки графиков для python/pandas: altair, bokeh, echarts, holoview, hvplot, plotly, vega. Кол-во графиков, которые умеют эти либы (больше сотни) примерно в 2 раза превышает возможности PBI. Кроме того, есть matplotlib, seaborn, которые (считается) что умеют еще пол-сотню DS-экзотики.
      Если под связями вы имели ввиду готовый drill-down по связям таблиц, то это надо искать в прикладном ПО в Apache Superset, Dash или самому несложно кодить в Streamlit итп. Ах, да, все упомянутое мною ПО (кроме урезанного функционалом PowerBI) - полностью свободно и бесплатно для бизнеса. Pandas для drill-down предлагает лишь ipywidget в среде Jupyter/Lab, да пяток готовых EDA-инструментов. Все это освоить - не хватит жизни, но сопоставимый с PBI результат можно получить за погода с нуля. Сейчас самое время...


    1. lozy_rc
      00.00.0000 00:00
      -1

      Это давно уже не так. Связки streamlit+понравившаяся_библиотека, dash+plotly, panel+matplotlib(ну или любая библиотека на выбор) ну и всякие малоизвестные. Тот же streamlit можно запустить в офисе со своего компа на локальную сеть, и твой коллега сможет легко его посмотреть даже без выхода в интернет. Или сохранить plotly график как html и отправить его по почте. Наверняка есть еще способы, видел даже как делают интерактивные презентации на основе юпитера и запихивают их в html. А если майки завтра решат блокирнуть PowerBI, что можно будет сделать? кроме как надеть деревянную ногу и попугая на шею конечно А python это сотни команд и в основном open source.


  1. argonmaster
    00.00.0000 00:00

    Так держать, Алексанра)

    Спасибо за статью.

    От себя добавлю. Гуглим про докстринги в пайтон.

    Всегда пишем документацию к своим функциям

    Юзаем pyCharm. Там уже встроены линтеры, авто форматирование кода и инструменты для рефакторинга.