Медведь бухгалтер или эксперимент над личными финансами на python и polars

от автора

Личные финансы: важная тема в области финансовой грамотности. Хорошо бы понимать, сколько приходит, сколько уходит, откуда и куда, чтобы планировать долгосрочные финансовые цели. Для этого можно использовать базовую аналитику банка, приложения для учета финансов, но у них есть минусы.

Но если вы знаете python и хотя бы раз сталкивались с pandas, для вас финансовая аналитика может показаться куда более интересной.

Подготовим рабочую поверхность

Для анализа данных нам потребуются данные, python и polars. Чтобы получить данные о финансах, можно воспользоваться банковским сайтом. У Т-Банка это можно сделать во вкладке операции, выбрать «за весь период» и нажать кнопку «скачать в формате csv», хотя я все-таки рекомендую формат экселя, потому что csv в polars у меня почему-то не завелся, хотя это вроде бы самый простой формат данных.

Также нам понадобится python и polars, чтобы скачать python достаточно зайти на python.org и проследовать инструкции по загрузке или найти гайд. Чтобы скачать polars, достаточно создать папку с проектом, добавить туда наш датасет, создать виртуальное окружение командой python -m venv .venv и написать pip install polars, чтобы установить библиотеку polars.

Немнго про нашего медведя

Polars это новая библиотека для обработки данных на python, она созвучна с pandas и pyspark по области применения. Polars написан на rust, что в сравнении с pandas дает ощутимый прирост скорости. Об этом писали тут.

Начнем с простого

Чтобы немного вкатиться в инструмент, можно сделать что-то супер простое. Поэтому давайте для начала сделаем разбивку трат по категориям. Я буду использовать функциональный подход. Все манипуляции с данными я буду оформлять в виде функций. Загрузка данных описывается через функцию load_data(), вот ее код:

import polars as pl load_data():     return pl.read_excel('money.xls') 

Здесь я работаю именно с экселем, потому что в моем случае csv файл неккоректно читался.

Дальше я использовал функции, чтобы выбрать нужные колонки, в polars для этого используется DataFrame.select(), обратите внимание, название колонки нужно передавать, используя pl.col('Название колонки'). Также я добавил фильтр, чтобы получить только отрицательные значения, так как Тинькофф хранит расходы именно со знаком минус. Итоговая функция выглядит вот так:

def get_valid_columns():     return load_data() \     .select(pl.col('Дата операции', ... , "Описание")) \     .filter(pl.col('Сумма операции') < 0) 

Дальше мы пишем функцию для агрегации суммы расходов по категориям. Для этого нужно выполнить несколько шагов:

  1. Сгруппируем данные по категориям data.group_by('Категория'), здесь можно использовать название колонки без pl.col()

  2. Суммируем по категориям и сумме операции. data.agg(pl.sum('Сумма операции'))

  3. Сортируем данные data.sort() Итоговая функция выглядит вот так:

def get_categories_agg():     return get_valid_columns() \     .group_by('Категория') \     .agg(pl.sum('Сумма операции')) \     .sort('Сумма операции') 

Дорогой день

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

Чтобы разбить средние траты по дням недели нам нужно выполнить несколько шагов:

  1. Получить нужные колонки с уже написанной функцией get_valid_columns()

  2. Написать функцию для конвертации строки в день недели с помощью модуля datetime

from datetime import datetime  def date_converter(date: str):     format = "%d.%m.%Y %H:%M:%S"     return datetime.strptime(date, format).strftime("%A") 
  1. Создать новую колонку с днями недели используя функцию with_columns: df = get_valid_columns().with_columns(pl.col('Дата операции').map_elements(lambda x: date_converter(x), return_dtype=pl.String).alias('День недели'))

  2. Тут мы с помощью маппинга применяем к каждой строчке функцию date_converter(), указываем возвращаемое значение и элиас

  3. Группируем по дню недели и выводим медиану для суммы операции: df.group_by('День недели').agg(pl.col('Сумма операции').median())

Итоговые преобразования выглядят так:

def date_converter(date: str):     format = "%d.%m.%Y %H:%M:%S"     return datetime.strptime(date, format).strftime("%A")  def extract_week():     return get_valid_columns() \         .with_columns(pl.col('Дата операции') \         .map_elements(lambda x: date_converter(x), return_dtype=pl.String) \         .alias('День недели'))  def get_median_week_exp():     return extract_week()\         .group_by('День недели') \         .agg(pl.col('Сумма операции') \         .median()) \         .sort('Сумма операции')  get_median_week_exp() 

Медианные расходы по годам

  1. Теперь посмотрим на сколько изменились медианные траты в месяц от года к году. Для этого сначала создадим отдельные колонки с указанием месяца и года.

format = "%d.%m.%Y %H:%M:%S"  def create_month_and_year_col():     return get_valid_columns() \         .with_columns(             pl.col('Дата операции').map_elements(lambda x: datetime.strptime(x,format).month, return_dtype=pl.Int64).alias('Месяц'),             pl.col('Дата операции').map_elements(lambda x: datetime.strptime(x,format).year, return_dtype=pl.Int64).alias('Год')         ) 
  1. Потом сгруппируем данные по Месяцу и Году

def group_by_month_and_year():     return create_month_and_year_col() \         .group_by('Месяц','Год') \         .agg(pl.all().sum()) \         .sort(pl.col('Год'), pl.col('Месяц')) \         .select(pl.col('Месяц'), pl.col('Год'), pl.col('Сумма операции')) 
  1. И посчитаем все вместе. Добавим сортировку по году

def aggregate_by_year():     return group_by_month_and_year() \         .select(pl.col('Год'),pl.col('Сумма операции')) \         .group_by('Год') \         .agg(pl.all() \         .median()) \         .sort('Год') 

И вот как в итоге у нас получилось:

format = "%d.%m.%Y %H:%M:%S"  def create_month_and_year_col():     return get_valid_columns() \         .with_columns(             pl.col('Дата операции').map_elements(lambda x: datetime.strptime(x,format).month, return_dtype=pl.Int64).alias('Месяц'),             pl.col('Дата операции').map_elements(lambda x: datetime.strptime(x,format).year, return_dtype=pl.Int64).alias('Год')         )  def group_by_month_and_year():     return create_month_and_year_col() \         .group_by('Месяц','Год') \         .agg(pl.all().sum()) \         .sort(pl.col('Год'), pl.col('Месяц')) \         .select(pl.col('Месяц'), pl.col('Год'), pl.col('Сумма операции'))  def aggregate_by_year():     return group_by_month_and_year() \         .select(pl.col('Год'),pl.col('Сумма операции')) \         .group_by('Год') \         .agg(pl.all() \         .median()) \         .sort('Год')  aggregate_by_year() 

Выводы

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

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

  2. В какой день выходить из дома без карточки.

  3. Насколько растет мое потребление от года к году. Составить прогноз.

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


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


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *