Всем привет, меня зовут Виталий, автор телеграмм канала Детектив данных, про мой путь в аналитике данных, мучаю питон и 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/