Сводка Аналитического Наблюдения — считаем сумму по столбцу в Python

от автора

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

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

Пример работы функции

Пример работы функции

Самый простой пример это merge который при неправильном использовании умножит наши данные и groupby который эти данные сократит (при наличии NaN). Помню на одном из самых первых занятий по питону лектор написал что-то вроде:

sum_df_sales = df['sales'].sum()sum_df_sales 

Рабочий способ, но в повседневном использовании мне показался неудобным, когда число измеряется миллиардами, ничего кроме кучи цифр с «е06» на конце я не вижу. И сравнение работает только при повторном написании формулы с другой переменной и вычитания одной из другой, а для нормального форматирования числа еще нужно помучать print

А еще постепенно с опытом, я стал приходить к написанию функций под себя, написал, закинул себе конфиг, импортировал один раз и пользуешься своими функциями не засоряя общий код кучей лишнего кода. Этим мы сейчас и займёмся. Функция detective_sum — покажет нам сколько денег в столбце, сохранит результат в переменную и посчитает разницу с прошлой проверкой. А что бы не было скучно — обернём нашу функцию в детективную оболочку. Пусть у нас будет Аналитическая сводка, под наблюдением у нас как всегда будут данные, а вдохновением послужат хорошие и плохие российские сериалы про ментов. Функция получилась очень простой, но наглядной, разберётся даже начинающий аналитик.

Итак начнём:

Первое что мы сделаем это объявим глобальные переменные для хранения предыдущей суммы, номера дела, и словаря соответствия id таблицы с кодовым именем:

_previous_sum = None_case_number = 0_table_names = {} # Словарь для хранения соответствия между id таблицы и кодовым именем

Начинаем писать функцию:

def det_sum(table, column):

Аргументов у нас будет всего два. Это это имя таблицы и колонка, таблица у нас будет в формате «pd.DataFrame» а колонка в формате «str», то есть потребует заключения в кавычки

Далее инициализируем библиотеки и переменные (не забываем про отступы):

import pandas as pdimport randomglobal _previous_sum, _case_number, _table_names_case_number += 1 # Каждый раз увеличиваем номер дела на единицу

Сразу же обращаемся к памяти всех миллиениалов, вспомнив слова Ларина из «Улицы разбитых фонарей». Первое что нужно сделать, это понять а вообще есть ли такой столбец:

  if column not in table.columns:    print(f"\n🔍 Сводка №{_case_number}: Андрюха! Пропал столбец '{column}'! Возможен криминал! По коням!")    return

Затем собственно говоря команда с первой лекции по питону — сохраним нашу сумму в переменную:

  current_sum = table[column].sum()

Дальше хочу чтобы мне выдавало не кучу цифр, а что-то более осязаемое для понимания, например миллиард, миллион с одним знаком после запятой. Ну и сразу форматируем наш результат.

# Функция для зашифровки чисел  def format_number(num):    abs_num = abs(num)    if abs_num >= 1_000_000_000:      return f"{num/1_000_000_000:.1f} млрд"    elif abs_num >= 1_000_000:      return f"{num/1_000_000:.1f} млн"    elif abs_num >= 1_000:      return f"{num/1_000:.1f} тыс"    else:      return f"{num:.1f}"     # Форматируем текущую сумму  formatted_current = format_number(current_sum)

Затем я пытался подтянуть имя таблицы в дальнейший текст, но мы не можем просто так вызвать это имя внутри нашей функции, и поэтому по правилам конспирации было решено каждой таблице присвоить своё уникальное условное наименование. Возьмём какое-нибудь случайное животное. Это позволит внутри кода проверять несколько разных таблиц, но сумма между вызовами функции будет сохраняться для удобства — ведь обычно редко имя до и после преобразования будет одним и тем же.

# Получаем уникальный идентификатор таблицы (по id в памяти)  table_id = id(table)   # Если для этой таблицы еще нет кодового имени - создаем новое  if table_id not in _table_names:    animals = ["Антилопа", "Бобр", "Барсук", "Волк", "Выдра", "Гепард", "Горилла", "Дикобраз", "Дельфин",          "Енот", "Жираф", "Зебра", "Заяц", "Игуана", "Йеменский хамелеон", "Кот", "Кенгуру",          "Лев", "Лама", "Медведь", "Морж", "Носорог", "Олень", "Панда", "Пума",          "Рысь", "Слон", "Сурикат", "Тигр", "Тюлень", "Утконос", "Фламинго", "Хомяк",          "Цапля", "Черепаха", "Шимпанзе", "Щука", "Эму", "Юрок", "Як"]     _table_names[table_id] = random.choice(animals)     # Получаем кодовое имя для этой таблицы  table_code = _table_names[table_id]

Ну и включаем фантазию, не забываем смайлики и поехали:

Первый и основной принт:

  print(f"\n🕵️ Сводка АН №{_case_number}: Наблюдение за объектом '{table_code}' по столбцу '{column}'")  print("=" * 50)

Затем проверяем, что предыдущей сумма пуста и принимаем таблицу под наблюдение, а иначе фиксируем разницу:

  if _previous_sum is None:    print(f"🔎 Объект '{table_animal}' был принят под наблюдение! Сумма: {formatted_current}")    print("📋 Фиксируем в сводке...")  else:    # Выводим текущую сумму    print(f"💼 Текущая сумма: {formatted_current}")    # Вычисляем разницу    difference = current_sum - _previous_sum

Далее идёт серия «if» и «else» — тут важно не напутать с отступами, так как всё это выражение это продолжение предыдущего «else» . Смотрим на разницу, и в зависимости от знака выводим соответствующее сообщение. Полный код в формате юпитер ноутбука будет в первом комментарии группы в телеграмме:

 if difference != 0:      # Форматируем разницу      formatted_diff = f"{abs(difference):,.0f}".replace(',', ' ')      if difference > 0:        print(f"⬆️ Хм... Сумма выросла на {formatted_diff}")        print("🕵️ Похоже, мы пропустили новые связи! Отправляем экипаж для проверки!")      else:        print(f"⬇️ Ого! Сумма уменьшилась на {formatted_diff}")        print("🔍 Кто-то пытается замести следы! Нужна проверка по камерам!")    else:      print("🤔 Сумма не изменилась...")      print("📝 Заносим в сводку: Объект не выходил, встреч не зафиксировано")

Ну и выйдя из блока «if/else» завершаем функцию сохранением суммы по столбцу в переменную:

  print("=" * 50)  _previous_sum = current_sum

Теперь нам нужно как нибудь встроить эту функцию в код, я использую свой конфиг файл, который лежит в проекте, вместе с кодами/паролями и другими функциями, он у меня лежит обычно в папке config с именем func.py как собственно говоря и видно из дальнейшего его вызова.

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

import importlibimport config.func     # импортируем модуль с функциямиimportlib.reload(config.func) # перезагружаем модуль (если нужно)from config.func import *  # импортируем все функции из config.func

Вот и всё. Один раз пишем и используем в работе удобную функцию. Еще увидимся.

ссылка на оригинал статьи https://habr.com/ru/articles/1023184/