Дисклеймер:
Данная статья содержит описание личного опыта взаимодействия автора и нейросетей DeepSeek и GigaChat на уровне <рядовой пользователь – машина>. Автор не является профессиональным программистом и профессиональным промпт-инженером, поэтому воздерживается от комментариев относительно качества кода и обоснованности того или иного решения, предлагаемого ИИ. Автор также не является профессиональным аналитиком данных, в связи с чем выбор методов прогнозирования и само видение реализации замысла могут не соответствовать представлениям профессионалов. Автор прекрасно понимает, что на сегодняшний день существуют как отдельные специалисты, так и готовые технические решения, прекрасно справляющиеся с задачами, рассматриваемыми в данной статье.
Вступление
Начать следует с того, что по профессии я системный аналитик с примесью технического писателя. Если точнее, то технический писатель, который вынужденно начал выполнять функции аналитика и в итоге им стал. Помимо этого, из опыта управления производственными и экономическими процессами в малом бизнесе я вынес любовь к цифрам, их анализу и составлению несложных прогнозов, которые разрабатывал исключительно в excel.
В основе данной статьи лежат следующие предпосылки:
информационный шум вокруг стремительного развития нейросетей и разговоров о том, что мы все скоро лишимся работы;
остатки былых желаний об автоматизации процесса анализа и прогнозирования;
желание интегрировать нейросети в собственную профессиональную практику;
желание проверить реальные возможности нейросетей в плане кодинга и разработки пусть и несложных, но работающих решений на основе относительно структурированных; запросов среднестатистически грамотного пользователя.
Формулирование задачи
Итак, я решил посмореть, сможет ли среднестатистический пользователь, умеющий худо-бедно системно мыслить и более-менее структурированно излагать свои потребности, при помощи нейросетей создать несложный работающий прототип десктопного приложения, решающего 1-2 достаточно простые (на машинном уровне) задачи. При этом главным условием должно являться то, что я не буду (вот совсем) вмешиваться в код, а буду разговаривать с машиной только человеческим языком. Пусть это будет условие №1.
Условие №2 – выбор высокоуровневого, не архисложного и популярного языка, имеющего развитый бэкграунд в виде библиотек, фреймворков и т.п. Здесь я подумал, что это должен быть Питон. Кроме того, я могу понять какие-то основные вехи кода на этом языке.
Условие №3 – приложение не должно решать множество задач одновременно, а должно (исходя из вышеописанного):
Принимать массив данных из файла, загружаемого вручную (да, интеграции пока городить не будем).
Строить прогнозы по двум-трем несложным методикам, и выдавать в качестве результата усредненный прогноз (причем, выбор методик я предоставлю AI).
Выдавать минимальную визуализацию в виде простых инфографик.
Иметь возможность отфильтровать данные по дате.
Теперь предстояло найти объект для анализа и составления прогнозов. Идеальным кандидатом оказался, что вполне очевидно, семейный бюджет, а именно выгрузка из банка xls-файла с данными о движении денежный средств. Таким образом я решил анализировать траты по категориям за определенный период времени и строить прогноз по предстоящим тратам на несколько месяцев вперед.
Сразу оговорюсь, что данные никаким образом, кроме удаления ненужных столбцов (и то не в первой итерации), не модифицировались и не обрабатывались. Категории в онлайн-банке специально не настраивались. То есть это просто сырые данные в таблице, где запись представляет собой следующее:
Дата платежа | Категория | Описание | Сумма операции |
27.01.2025 | Рестораны | GrillZon | 2000,00 |
Что касается выбора AI, то я решил остановиться на доступных в России без всяких VPN нейронках GigaChat от Сбера (которой, к слову, время от времени пользуюсь) и нашумевшей китайской DeepSeek.
Также следует сказать пару слов о методологии. Тут все предельно просто: поскольку я решил выступить в роли "уверенного пользователя ПК со знанием Ворд", то я не буду уметь ничего, кроме как скопипастить код из чата с AI в VSCode и нажать кнопочку запуска. Отмечу, что, хоть это и очевидно, моя работа носила итеративный подход.
Итак, приступаем к написанию первого промпта.
Пишем приложение
Итерация 1
Промпт: есть таблица, содержащая следующие поля:
Дата операции.
Дата платежа.
Номер карты.
Статус.
Сумма операции.
Валюта операции.
Сумма платежа.
Валюта платежа.
Категория.
Описание.
Сумма операции с округлением.
Записи в таблице располагаются по убыванию от ближней даты к дальней.
Необходимо написать на python desktop-приложение, которое будет анализировать таблицу и выдавать следующие результаты:
Информацию о расходах, сгруппированную по годам, месяцам и числам, а также по категориям и описаниям.
Прогноз по расходам на ближайшие три месяца.
Для прогнозирования можно использовать усредненную информацию на основе любых трех- четырех методик прогнозирования, например, метод плавающего окна, линейная регрессия и тому подобное.
Итоговый анализ расходов должен выводиться в виде:
Таблицы анализа по категориям, где записи — это категории, а поля — это значения для каждого прогнозируемого месяца.
Таблицы анализа по описаниям, где записи — это описания, а поля — это значения для каждого прогнозируемого месяца.
Круговой диаграммы расходов по каждому месяцу.
Итоговый прогноз должен выводиться в виде:
Таблицы прогноза по категориям, где записи — это категории, а поля \ — это значения для каждого прогнозируемого месяца.
Таблицы прогноза по описаниям, где записи — это описания, а поля — это значения для каждого прогнозируемого месяца.
Круговой диаграммы прогноза по каждому месяцу.
И вот на этом самом месте GigaChat и отвалился, так как предложенный им код... просто отказался работать, а prettier выделил красным n-ное количество строк. И нет, дело не в том, что какие-то библиотеки не установлены, я проверял. Оговорюсь, что запрос к нему писался вторым (начал я с китайца), и в нем присутствуют только пять строк исходной таблицы, но сути это не меняет, ибо по наводке DeepSeek я уже установил некоторые компоненты.
С точки зрения "уверенного пользователя", которым я на время стал, это уже конкретное отсутствие результата, ибо, даже если ситуация требует лишь беглого взгляда и мелкой правки, я этого делать не умею (и не хочу). Снова чуть забегу вперед и скажу, что в отличие от китайского коллеги сберовский AI не дает никаких подсказок по поводу того, что надо сделать, чтобы все заработало, и не показывает ход своих мыслей. Ниже без купюр привожу решение от Сбера:
Шаг 1: Загрузка и подготовка данных
Предположим, что у нас есть файл expenses.csv со следующими столбцами:
Дата операции,Дата платежа,Категория,Описание,Сумма операции 2023-01-05,2023-01-10,Продукты,Покупка продуктов,5000
2023-02-15,2023-03-20,Транспорт,Проездной билет,2000
...
Начнем с загрузки данных с помощью библиотеки Pandas:
import pandas as pd
# Чтение CSV файла
df = pd.read_csv('expenses.csv', parse_dates=['Дата операции', 'Дата платежа'])
# Преобразование сумм в числа
df['Сумма операции'] = df['Сумма операции'].astype(float) Шаг 2: Анализ расходов
Группировка по годам, месяцам и дням
def group_by_date_and_category(df):
# Создаем колонки для года, месяца и дня df['Год'] = df['Дата операции'].dt.year df['Месяц'] = df['Дата операции'].dt.month df['День'] = df['Дата операции'].dt.day
# Группируем по году, месяцу, дню и категории
grouped_df = df.groupby(['Год', 'Месяц', 'День', 'Категория'])['Сумма операции'].sum().rese
return grouped_df Вывод анализа расходов
grouped_expenses = group_by_date_and_category(df) print(grouped_expenses)
Шаг 3: Прогнозирование расходов
Мы можем использовать несколько методов для прогнозирования будущих расходов. Например, методы
Метод скользящего среднего
Этот метод использует среднее значение за последние N периодов для предсказания следующего пери
from statsmodels.tsa.api import SimpleExpSmoothing
def forecast_with_sma(df, periods=3):
# Выбираем суммы операций по месяцам
monthly_sums = df.groupby(pd.Grouper(key='Дата операции', freq='M'))['Сумма операции'].sum(
# Применяем метод скользящего среднего
model = SimpleExpSmoothing(monthly_sums).fit(smoothing_level=0.6)
# Делаем прогноз на 3 месяца вперед forecast = model.forecast(steps=periods)
return forecast Линейная регрессия
Метод линейной регрессии может использоваться для построения тренда и предсказания значений на
from sklearn.linear_model import LinearRegression from sklearn.preprocessing import PolynomialFeatures
def forecast_with_linear_regression(df, degree=2, periods=3): # Выбираем суммы операций по месяцам
monthly_sums = df.groupby(pd.Grouper(key='Дата операции', freq='M'))['Сумма операции'].sum(
# Формируем массивы X и y
X = np.arange(len(monthly_sums)).reshape(-1, 1) y = monthly_sums.values.reshape(-1, 1)
# Используем полиномиальную регрессию poly = PolynomialFeatures(degree=degree) X_poly = poly.fit_transform(X)
# Обучаем модель
model = LinearRegression().fit(X_poly, y)
# Делаем прогноз на 3 месяца вперед
future_X = np.arange(len(monthly_sums), len(monthly_sums) + periods).reshape(-1, 1) future_X_poly = poly.transform(future_X)
forecast = model.predict(future_X_poly)
return forecast
Шаг 4: Визуализация результатов
Создание интерфейса с использованием Tkinter
Tkinter — стандартная библиотека Python для создания графического пользовательского интерфейса.
import tkinter as tk from tkinter import ttk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class ExpenseAnalyzerApp(tk.Tk): def init (self):
super(). init ()
self.title("Expense Analyzer") self.geometry("800x600")
# Добавляем вкладки
tab_control = ttk.Notebook(self) analysis_tab = ttk.Frame(tab_control) forecast_tab = ttk.Frame(tab_control)
tab_control.add(analysis_tab, text="Analysis") tab_control.add(forecast_tab, text="Forecast") tab_control.pack(expand=True, fill="both")
# Таблица анализа расходов self.create_analysis_table(analysis_tab)
# Графики прогноза self.create_forecast_plots(forecast_tab)
def create_analysis_table(self, parent):
treeview = ttk.Treeview(parent, columns=["Year", "Month", "Day", "Category", "Amount"]) treeview.heading("#0", text="ID")
treeview.column("#0", width=50)
for col in ["Year", "Month", "Day", "Category", "Amount"]: treeview.heading(col, text=col)
treeview.column(col, width=100)
for index, row in grouped_expenses.iterrows():
treeview.insert("", "end", iid=index, values=(row["Год"], row["Месяц"], row["День"] treeview.pack(side="left", fill="y")
scrollbar = ttk.Scrollbar(parent, orient="vertical", command=treeview.yview) treeview.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
def create_forecast_plots(self, parent):
figure = plt.Figure(figsize=(5, 4), dpi=100) ax = figure.add_subplot(111)
forecast_data = forecast_with_sma(df)
ax.plot(forecast_data.index, forecast_data.values) ax.set_title("Forecast with SMA")
canvas = FigureCanvasTkAgg(figure, parent) canvas.get_tk_widget().pack(side="top", fill="both", expand=True)
if name == " main ":
app = ExpenseAnalyzerApp() app.mainloop()
DeepSeek оказался куда талантливее и помимо кода тезисно расписал особенности реализации:
Загрузка данных:
поддерживает файлы Excel (.xlsx);
автоматически парсит даты операций.
Анализ расходов:
группировка по годам/месяцам/числам;
детализация по категориям и описаниям;
интерактивные таблицы с прокруткой.
Прогнозирование:
использует 3 метода;
экспоненциальное сглаживание;
линейная регрессия;
скользящее среднее;
усреднение результатов прогноза.
Визуализация:
интерактивные круговые диаграммы;
автоматическое обновление графиков;
поддержка динамического изменения временных периодов.
Кроме того, китайское чудо выдало следующее предупреждение-рекомендацию «установить зависимости»:
pip install pandas numpy matplotlib statsmodels scikit-learn openpyxl tk
Только не надо в меня ничего кидать, покрикивая, что "уверенный пользователь" не может знать, что такое pip, pandas, numpy, как все это влияет на работоспособность кода, и что вообще делать с этой строчкой. Да, это нарушает чистоту эксперимента. Но, честно, я не знаю, что с этим поделать. Конечно, было бы круто, если бы DeepSeek выдал еще и пошаговый алгоритм того, куда именно ткнуть, вставить и что нажать. Но увы.
Короче говоря, сделав, что попросили, и запустив предложенное решение, я увидел окно выбора нужного файла. После выбора мне предстала следующая картина:

Хочется отметить следующие моменты:
Достоинства:
Уже в первой итерации нейронка осознала, что анализ и прогноз — два разных процесса, под которые требуются разные пространства. Таким образом, у нас появились отдельные вкладки "Анализ" и "Прогноз".
Нейронка сама придумала название приложения, и сама предложила фавикон.
По центру расположена кнопочка загрузки данных, которая работает. Красота.
Недостатки:
В таблице полная ахинея. Но тут, не в последнюю очередь, следует сказать привет Т-банку с его особенностями выгрузки и представления данных. Но об этом как-нибудь в другой раз.
На вкладке "Анализ" зияющая пустота.
Никакими круговыми диаграммами даже не пахнет.
Вывод: прототип работает с первой попытки, но то, что хотелось, не делает. Код приведен ниже:
import pandas as pd import numpy as np import tkinter as tk
from tkinter import ttk, filedialog
from datetime import datetime, timedelta
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure
from statsmodels.tsa.api import ExponentialSmoothing, SimpleExpSmoothing from sklearn.linear_model import LinearRegression
class FinanceAnalyzerApp:
def init (self, root): self.root = root
self.root.title("Анализатор расходов") self.data = None
# Создание интерфейса self.create_widgets()
def create_widgets(self):
# Кнопка загрузки файла
self.load_btn = ttk.Button(self.root, text="Загрузить данные", command=self.load_data) self.load_btn.pack(pady=10)
# Notebook для вкладок
self.notebook = ttk.Notebook(self.root) self.analysis_frame = ttk.Frame(self.notebook) self.forecast_frame = ttk.Frame(self.notebook) self.notebook.add(self.analysis_frame, text="Анализ") self.notebook.add(self.forecast_frame, text="Прогноз") self.notebook.pack(expand=True, fill=tk.BOTH)
def load_data(self):
file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")]) if file_path:
self.data = pd.read_excel(file_path, parse_dates=['Дата операции']) self.process_data()
self.show_analysis()
self.generate_forecast()
def process_data(self):
# Преобразование данных
self.data['Год'] = self.data['Дата операции'].dt.year
self.data['Месяц'] = self.data['Дата операции'].dt.month self.data['День'] = self.data['Дата операции'].dt.day
def show_analysis(self):
# Анализ по категориям self.category_analysis = pd.pivot_table(
self.data,
values='Сумма операции', index='Категория', columns=['Год', 'Месяц'], aggfunc=np.sum
)
# Анализ по описаниям self.description_analysis = pd.pivot_table(
self.data,
values='Сумма операции', index='Описание', columns=['Год', 'Месяц'], aggfunc=np.sum
)
# Отображение таблиц
self.create_table(self.analysis_frame, self.category_analysis, "По категориям") self.create_table(self.analysis_frame, self.description_analysis, "По описаниям")
def generate_forecast(self):
# Подготовка данных для прогноза
monthly_data = self.data.resample('M', on='Дата операции')['Сумма операции'].sum()
# Метод 1: Экспоненциальное сглаживание
model_es = ExponentialSmoothing(monthly_data).fit() forecast_es = model_es.forecast(3)
# Метод 2: Линейная регрессия
X = np.arange(len(monthly_data)).reshape(-1, 1) y = monthly_data.values
model_lr = LinearRegression().fit(X, y)
forecast_lr = model_lr.predict([[len(monthly_data)+i] for i in range(3)])
# Метод 3: Скользящее среднее window_size = 3
forecast_ma = [monthly_data[-window_size:].mean()] * 3
# Усреднение прогнозов
self.final_forecast = (forecast_es + forecast_lr + forecast_ma) / 3
# Создание прогнозных таблиц self.create_forecast_tables() self.create_pie_charts()
def create_forecast_tables(self): # Прогноз по категориям
category_forecast = self.data.groupby('Категория')['Сумма операции'].mean() * self.fina
# Прогноз по описаниям
description_forecast = self.data.groupby('Описание')['Сумма операции'].mean() * self.fi
# Отображение таблиц
self.create_table(self.forecast_frame, category_forecast, "Прогноз по категориям") self.create_table(self.forecast_frame, description_forecast, "Прогноз по описаниям")
def create_table(self, parent, data, title): frame = ttk.Frame(parent)
tree = ttk.Treeview(frame)
# Настройка колонок
tree["columns"] = list(data.columns) tree.column("#0", width=200)
for col in data.columns:
tree.column(col, width=100)
# Заголовки
tree.heading("#0", text=title) for col in data.columns:
tree.heading(col, text=str(col))
# Данные
for index, row in data.iterrows():
tree.insert("", tk.END, text=index, values=list(row))
tree.pack(expand=True, fill=tk.BOTH) frame.pack(side=tk.LEFT, expand=True, fill=tk.BOTH)
def create_pie_charts(self):
fig = Figure(figsize=(8, 4))
for i, month in enumerate(self.final_forecast.index): ax = fig.add_subplot(1, 3, i+1)
self.data.groupby('Категория')['Сумма операции'].sum().plot.pie(ax=ax) ax.set_title(f"Прогноз на {month.strftime('%B %Y')}")
canvas = FigureCanvasTkAgg(fig, self.forecast_frame) canvas.get_tk_widget().pack()
if name == " main ": root = tk.Tk()
app = FinanceAnalyzerApp(root)
root.geometry("1200x800") root.mainloop()
Итерация 2
Я подумал, что дело может быть в кривизне данных именно в xlsx-выгрузке. Мысль показалась гениальной, и... я попросил китайца о следующем:
Промпт: измени код таким образом, чтобы можно было загружать информацию из файлов csv.
Далее DeepSeek приводит модифицированный код и список основных изменений:
1. Поддержка нескольких форматов:
добавлена возможность выбора как CSV (.csv), так и Excel (.xlsx) файлов;
автоматическое определение формата по расширению файла;
чтение CSV.
2. Использование pd.read_csv() с параметрами:
parse_datesдляправильногораспознаваниядат;
dayfirst=True для формата даты ДД.ММ.ГГГГ;
delimiter=',' (можно изменить на ';' если используется другой разделитель);
encoding='utf-8' (можно изменить на 'cp1251' для русских файлов).
3.Обработка ошибок:
добавленблокtry/exceptдляобработкиневалидныхфайлов;
показ ошибок через стандартный диалог.
И снова вышел ряд предупреждений-рекомендаций, чтобы все, типа, работало:
1. Установите зависимости (если еще не
установлены): pip install pandas numpy matplotlib statsmodels scikit-learn
2. Запустите приложение и выберите CSV-файл со следующей структурой: Дата операции;Сумма операции;Категория;Описание 01.01.2023;1500;Продукты;Покупка в магазине 05.01.2023;3000;Транспорт;Заправка
3. При возникновении проблем с кодировкой/разделителем измените параметры в
pd.read_csv():
delimiter=';', # Для разделителя "точка с запятой" encoding='cp1251' # Для русской кодировки Windows
Итог: код не срабатывает. При попытке загрузить файл csv выходит сообщение: ошибка загрузки файла: 'UTF-8' cjdec can't decode byte 0xc4 in position 0^ invalid continuation byte
Итерация 3
Как говорится, так и запишем.
Промпт:
Код не срабатывает. При попытке загрузить файл csv выходит сообщение: ошибка загрузки файла: 'UTF-8' cjdec can't decode byte 0xc4 in position 0^ invalid continuation byte
AI запускает цепочку рассуждений (да, кстати, это то, что интересно, и что выгодно отличает DeepSeek от всех остальных, ведь всегда можно проследить за ходом "мыслей" этого технологического чуда). В результате самодиагностики рождается следующий ответ: ошибка возникает из-за несовпадения кодировки файла и указанной в коде (utf-8 vs cp1251).
Нейронка приводит скорректированный кусок кода (заметьте, она понимает, где косяк, и правит именно это место) и список основных изменений:
1. Автоматический подбор кодировки:
последовательно пробуем utf-8 → cp1251 → iso-8859-1;
раскомментируйте другие кодировки при необходимости (например, 'windows-1251').
2. Проверка структуры файла:
обязательные колонки: Дата операции, Сумма операции, Категория, Описание;
если колонки названы иначе, появится понятное сообщение.
3. Гибкий разделитель:
по умолчанию используется; (для русских CSV из Excel);
если в вашем файле разделитель, поменяйте delimiter=','.
Рекомендации для того, чтобы все работало:
1. Убедитесь, что CSV-файл имеет правильный формат.
2. Если файл создан в Excel на Windows:
сохраните его как "CSV (разделитель - точка с запятой)";
выберите кодировку UTF-8 с BOM или Windows (по умолчанию).
3. Для файлов с запятыми как разделителями:
измените в коде delimiter=',';
убедитесь, что десятичные числа используют точку (например, 100.50).
Дабы не раздувать статью, код не привожу, так как он заработал, а в остальном там ничего существенно не изменилось, и в цифрах та же абракадабра (для "продвинутого пользователя").
Итог: при указании на ошибку AI способен самостоятельно сообразить, в чем загвоздка, обнаружить ее, исправить и выдать готовое решение проблемы. Но только при указании. Изначально он ее не видит.
Итерация 4
И вот здесь я вдруг понял, что надо несколько переформулировать изначальный промпт, ибо в таблице присутствуют избыточные данные, при анализе отсутствует возможность сортировки по датам, а также отсутствует группировка по категориям и статьям расходов внутри последних (в Т-банке этот столбец называется "Описание"). Также нужна какая-то примитивная инфографика, ибо нейросеть никак не хотела рисовать круговые диаграммы, о которых я просил в самом начале. Таким образом, задача стала выглядеть так:
Промпт: давай изменим задачу. Есть таблица, содержащая следующие поля:
1. Дата операции.
2. Дата платежа.
3. Категория.
4. Описание.
5. Сумма операции.
Записи в таблице располагаются по убыванию от ближней даты к дальней.
Необходимо написать на python desktop-приложение, которое будет анализировать таблицу и выдавать следующие результаты:
1. Информацию о расходах, сгруппированную по годам, месяцам и числам, а также по категориям и описаниям.
2. Прогноз по расходам на ближайшие три месяца.
Для прогнозирования можно использовать усредненную информацию на основе любых трех- четырех методик прогнозирования, например, метод плавающего окна, линейная регрессия и тому подобное.
Итоговый анализ расходов должен выводиться в виде таблицы, где приведены данные по категориям и описаниям, входящим в эти категории. Должна быть возможность выбрать период для просмотра данных по расходам. Должна быть возможность сворачивать и разворачивать категории для просмотра данных по входящим в категории описаниям. Под таблицей с данными по выбранному периоду должна приводиться гистограмма расходов по категориям.
Итоговый прогноз должен выводиться в виде:
Таблицы прогноза, где возможно сворачивать и разворачивать категории для просмотра данных по входящим в категории описаниям. Под таблицей должна приводиться гистограмма расходов по категориям отдельно по каждому прогнозируемому месяцу.
Итак, немного порассуждав, китаец привел работающий код, который дает нам следующее:

Итог: здесь уже можно констатировать наличие области гистограмм. Однако, выполнять свою функцию в полной мере приложение не может, ибо куда-то делась поддержка xls. Только xlsx и csv. Поэтому в следующей итерации я попрошу добавить поддержку xls.
Итерация 5
Промпт: добавь в код возможность загружать данные из файлов xls. Приведи полный текст кода.
Итог: код запускается, но при попытке загрузить данные выходит ошибка загрузки файла: Missing optional dependency 'xlrd'. Install xlrd>= for xls Excel
Итерация 6
Промпт: выходит сообщение об ошибке: ошибка загрузки файла: Missing optional dependency 'xlrd'. Install xlrd>= for xls Excel.
Ответ: ошибка возникает из-за отсутствия библиотеки xlrd, необходимой для чтения старых Excel-файлов (.xls). Далее нейросеть предлагает выполнить шаги для исправления ситуации:
1. Установите обязательные зависимости:
pip install pandas numpy matplotlib statsmodels scikit-learn tk xlrd openpyxl
2. Если возникает ошибка с xlrd: для современных версий .xls файлов может потребоваться дополнительная настройка. Добавьте в код явное указание движка (далее дается кусок кода, который надо вставить).
Также DeepSeek предлагает альтернативное решение по ручной конвертации xls в xlsx через Excel.
Еще он предлагает использовать следующий способ:
self.data = pd.read_excel(file_path, engine='calamine')
При этом предлагается установить calamine:
pip install python-calamine
После того, как я без особых раздумий сделал все, о чем меня попросил DeepSeek , я запустил код. GUI остался прежний, но при попытке загрузить данные вышла ошибка: too many values to unpack (expected 2)
Так и запишем.
Итерация 7
Промпт: выходит ошибка: too many values to unpack (expected 2)
Ответ: ошибка возникает из-за несоответствия количества элементов при распаковке данных. Давайте исправим код.
Далее искусственный разум предлагает исправить методы update_analysis и generate_forecast
на предмет обработки multiIndex и приводит куски кода для замены. Следует отметить, что подробный самоанализ приводится в довольно большой портянке рассуждений, откуда можно понять, в чем DeepSeek видит свои недоработки и как именно их исправляет.
Основные моменты:
1. Исправлена распаковка данных:
в цикле по описаниям убрана лишняя переменная cat;
в прогнозе исправлена работа с MultiIndex.
2. Исправлено создание дат:
используется pd.DateOffset для корректного расчета дат прогноза.
3. Изменена работа с массивами:
правильное использование reshape() для совместимости размерностей.
4. Обработка MultiIndex в методах update_analysis и generate_forecast.
5. Формирование прогнозных дат с использованием pd.DateOffset.
6. Матричные операции для расчета прогнозных значений.
7. Индексация данных при работе с группировками.
8. Визуализация прогноза с обновлением осей.
Также снова предлагается установить зависимости:
pip install pandas numpy matplotlib statsmodels scikit-learn tk xlrd openpyxl python-calamine
Итог: ничего не изменилось. Ошибка не ушла. Повторяем запрос.
Итерация 8
Промпт: снова выходит ошибка: too many values to unpack (expected 2). Исправь ошибку и приведи полный текст кода.
Ответ: вот полностью исправленный код с обработкой MultiIndex. Ошибка возникала из-за неправильной работы с группировками.
Основные моменты:
1. Добавлен параметр observed=True в группировки для правильной работы с категориальными данными.
2. Исправлена итерация по MultiIndex с использованием. xs() и .items().
3. Переработано создание прогнозных данных с использованием матричных операций.
4. Добавлена проверка наличия категории в индексе перед итерацией.
5. Исправлено создание дат прогноза с использованием pd.DateOffset.
И, хотя мы это уже делали неоднократно, снова предлагается установить зависимости (видимо, AI так перестраховывается):
pip install pandas numpy matplotlib statsmodels scikit-learn xlrd openpyxl
Итог: наконец-то мы видим более-менее работающий продукт:
1. Данные ложатся, ошибок не выходит;
2. Категории раскрываются до описаний;
3. Гистограммы работают;
4. На вкладке анализа появился фильтр по датам.
Но! Все-таки опус нуждается в доработке, так как все еще несколько кривоват: цифры прогнозов кривые (тут вопрос, скорее, к выбранным методам и к генеральной совокупности), подписи на гистограммах – просто ужас. Фильтр работает в режиме ручного введения дат. В окне данных месяцы не подписаны.


Итерация 9
Здесь я попытался заставить машину сгладить шероховатости.
Промпт: прекрасно. Все работает. Теперь давай доработаем некоторые моменты:
1. На вкладке «Прогноз гистограммы» данные должны располагаться последовательно сверху вниз. Над каждой диаграммой должно быть название прогнозируемого месяца.
2.Давай не будем брать для прогноза те описания, сумма по которым менее 500.
3. Давай не будем выводить в таблицу и на гистограммы описания, сумма прогноза по которым меньше 1000.
4. На гистограммах давай будем подписывать значение каждого столбца.
5. Давай сделаем фильтр по датам в виде выпадающего календаря.
Ответ (дословно):
для работы установите следующие зависимости:
pip install pandas numpy matplotlib statsmodels scikit-learn xlrd openpyxl tkcalendar
1. Добавлен красивый календарь с помощью tkcalendar.
2. Гистограммы прогноза теперь расположены вертикально.
3. Добавлены подписи значений над столбцами на всех графиках.
4. Реализована фильтрация:
исключены операции с суммой<500призагрузкеданных.
не выводятся описания с прогнозом < 1000.
5. Над каждой гистограммой прогноза добавлена подпись с месяцем.
6. Улучшено отображение числовых значений на графиках.
Теперь приложение соответствует всем указанным требованиям. Для выбора дат используйте выпадающие календари, а прогнозные данные автоматически фильтруются по заданным условиям.
Итог: при загрузке данных выходит буквально следующее: ошибка прогнозирования «Бонусы».
Итерация 10
Промпт: выдает ошибку: ошибка прогнозирования Бонусы. В предыдущей итерации такого не было.
Ответ: ошибка возникает из-за фильтрации данных. Давайте исправим код, добавив проверки на наличие данных после фильтрации. Далее выдается кусок раскомментированного кода для замены.
Основные изменения:
1. Добавлены проверки на наличие данных перед прогнозированием.
2. Исправлена фильтрация данных (сначала фильтруем исходные данные по 500, потом прогноз по 1000).
3. Добавлена обработка коротких временных рядов для ExponentialSmoothing.
4. Добавлена проверка на существование категорий после фильтрации.
5. Реализована отрисовка "Нет данных" для пустых графиков.
6. Исправлена логика фильтрации для сохранения родительских категорий. Для работы необходимо обновить зависимости:
pip install statsmodels --upgrade

Итог: промпт, в общем, отработан, однако гистограммы по-прежнему расположены горизонтально, а выпадающий календарь выходит за границы экрана, и им невозможно пользоваться. Будем пробовать дальше.
Итерация 11
Промпт: отлично. Все работает. Давай доработаем следующее:
1. Сделаем гистограммы полноразмерными и добавим возможность вертикальной прокрутки в окне гистограмм, чтобы была возможность их просмотра.
2. Уменьшим шрифты так, чтобы гистограммы были читаемы.
3. Расположим надписи на гистограммах вертикально. Итоговый размер гистограммы должен быть таким, чтобы все ее элементы были читаемы и входили на нее.
4. В конце таблицы добавим общую сумму прогноза на месяц и красиво выделим эту строку. Рядом с гистограммами справа также добавим итоговую сумму прогноза.
Ответ: цифровой помощник выдает код и уточняет что он "с учетом ваших требований".
Основные изменения:
1. Добавлен кастомный ScrollableFrame для графиков.
2. Уменьшены размеры шрифтов (основной график: 8pt, заголовки: 10pt).
3. Повернуты подписи на гистограммах на 90 градусов.
4. Добавлена строка "Итого" в таблицу прогноза с выделением.
5. Отображается общая сумма прогноза справа от каждого графика.
6. Улучшено адаптивное масштабирование элементов интерфейса.
7. Добавлены отступы и улучшено визуальное оформление.
Снова предлагается установить зависимости:
pip install pandas numpy matplotlib statsmodels scikit-learn xlrd openpyxl tkcalendar
Итог: на вкладке "Прогнозы" ситуация испортилась:
1. Исчезла таблица с цифрами прогнозов по категориям.
2. Графики переместились в правую часть экрана.
3. Прокрутка гистограмм работает только при наведении на полосу скролла.

Итерация 12
Промпт: на вкладке "Прогнозы" все стало плохо:
1. Исчезла таблица с цифрами прогнозов по категориям.
2. Графики переместились в правую часть экрана.
Давай сделаем следующее:
1. Вернем на вкладку прогнозов цифры по всем категориям и вложенным в них описаниям, чтобы категории можно было раскрывать и видеть описания.
2. Переместим окно с гистограммами вниз под таблицу.
3. На гистограммах сделаем так, чтобы числовые значения категорий не отображались, а при наведении на конкретный столбец всплывал поп-ап с числовым значением этого столбца.
4. Не будем включать в прогноз категории "Переводы" и "Пополнения".
5. На вкладке анализа в таблице и рядом с гистограммой будем выводить итоговую сумму всех расходов за период.
Ответ: нейросеть выдает код "с учетом всех требований".
Основные изменения:
1. Восстановлена иерархическая таблица прогнозов с возможностью раскрытия категорий.
2. Графики прогноза теперь отображаются под таблицей.
3. Добавлены всплывающие подсказки для значений на графиках.
4. Исключены категории "Переводы" и "Пополнения".
5. Добавлено отображение итоговой суммы на вкладке анализа.
6. Оптимизировано отображение календарей (не выходят за пределы экрана).
7. Улучшено форматирование графиков и таблиц.
8. Добавлены подписи итоговых сумм рядом с графиками.
И снова предлагается установить зависимости
pip install pandas numpy matplotlib statsmodels scikit-learn xlrd openpyxl tkcalendar
Итог: все стало поаккуратнее, но ситуация по-прежнему не айс, ибо проявились следующие недостатки:
1. В прогнозе перестали раскрываться категории, гистограммы снова переехали, но пропала возможность скролла, и их все не посмотреть.
2. В анализе гистограмма сжалась и уехала вправо, а середину занимает пустое пространство, где работает фильтр по дате с выпадающим календарем.
3. Не появилось поп-апа при наведении на столбцы гистограммы.
Далее я пытался убрать оставшиеся шероховатости. И все бы ничего, но на 15 итерации вылезла следующая проблема: прогнозные значения стали сильно завышенными. Суммы отображались очень большие и заведомо ложные.
Итерация 16
Промпт: в общем, все хорошо. Но надо исправить следующий недочет: на вкладке прогноза при раскрытии категории отображаются некорректные (сильно завышенные) прогнозные числовые значения по описаниям. Итоговая сумма значений по описаниям сильно выше прогнозного значения по категории, в которую они входят. Нужно сделать так, чтобы прогнозные значения по описаниям вычислялись корректно.
Ответ: для исправления этой проблемы необходимо скорректировать расчет прогнозных значений для описаний внутри категорий. Ошибка возникает из-за неправильного распределения прогнозных значений по описаниям. Вместо того чтобы умножать прогнозное значение категории на долю описания в общей сумме категории, мы должны распределить прогнозное значение категории пропорционально долям описаний. Теперь для каждого описания внутри категории вычисляется его доля от общей суммы категории (desc_ratios).
Итог: на этом я остановился, так как код более-менее начал соответствовать изначальному замыслу, и приложением стало возможно пользоваться, хотя, конечно, оно далеко от совершенства (наверное, профи в анализе данных просто плюнут в нашу с DeepSeek-ом сторону). Тем не менее, на мой взгляд, эксперимент поставлен худо-бедно, но корректно, результаты имеются, и можно делать выводы.


Выводы
1. Да, искусственный интеллект весьма неплохо (насколько я вообще могу об этом судить) пишет код, способен к самодиагностике и исправлению своих ошибок. Но!.. только с подачи "заказчика", который должен обладать как минимум:
умением структурированно мыслить и формулировать запросы, пусть даже и не на уровне техпрофи;
хотя бы поверхностным знанием предметной области (программирования), чтобы, например, выбрать нужный язык;
знанием того, как установить язык, что такое менеджер пакетов, и как с его помощью загрузить нужные компоненты;
как следствие из предыдущего пункта — умением работать с терминалом/командной строкой;
умением работать с IDE (notepad++ здесь вряд ли подойдет) хотя бы в части запуска кода;
способностью хотя бы к поверхностному анализу результатов запроса;
минимальными (можно сказать, примитивными) навыками тестирования на уровне "научного тыка".
Таким образом, для реализации изначального замысла, обозначенного в начале статьи, тот самый "опытный пользователь" по уровню компетенций приближается (пусть и не вплотную) к junior-питонисту.
2. Стопроцентное достижение изначального замысла видится практически невозможным (хотя, это может быть не так). Такое ощущение, что мы будем, подобно гиперболе, бесконечно к нему приближаться, но так и не достигнем.
3. Вне итеративного подхода, то есть за 1-2 промпта, мы можем получить работающий результат, но он, вероятно, будет далек от истины. Мне потребовалось 16 итераций чтобы более-менее приблизиться к истине.
4. Таким образом, среднестатистический пользователь вряд ли способен создать работающее решение, просто общаясь с нейросетью на человеческом языке.
5. Да, нейросети действительно могут разгрузить/дополнить IT-специалистов в части кодинга, дебага и т.п., но создать целостное решение, соответствующее требованиям заказчика без участия человека, они неспособны. Так что работаем дальше, коллеги!
P.S. Ниже приведен итоговый код, на котором я решил остановиться. В этом виде приложение действительно способно показать более-менее понятную картину расходов и более-менее внятно (не говорю, что правильно или корректно) спрогнозировать расходы. Думается, что доработку этого кода можно продолжить тем же самым способом, заложив туда более подходящие методы, и в итоге получить более пристойный результат.
import pandas as pd import numpy as np import tkinter as tk
from tkinter import ttk, filedialog, messagebox from tkcalendar import DateEntry
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure
from statsmodels.tsa.api import ExponentialSmoothing from sklearn.linear_model import LinearRegression import matplotlib.pyplot as plt
from matplotlib.backend_bases import PickEvent
class Tooltip:
def init (self, ax, patches): self.ax = ax
self.patches = patches
self.annot = ax.annotate("", xy=(0,0), xytext=(20,20),
textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"))
self.annot.set_visible(False)
def hover(self, event):
vis = self.annot.get_visible() if event.inaxes == self.ax:
for patch in self.patches:
cont, ind = patch.contains(event) if cont:
self.annot.xy = (patch.get_x() + patch.get_width()/2, patch.get_height()) self.annot.set_text(f"{patch.get_height():.2f}") self.annot.get_bbox_patch().set_alpha(0.8)
self.annot.set_visible(True) self.ax.figure.canvas.draw_idle() return
if vis:
self.annot.set_visible(False) self.ax.figure.canvas.draw_idle()
class FinanceAnalyzerApp:
def init (self, root): self.root = root
self.root.title("Анализатор расходов v3.2") self.data = None
self.forecast_data = None
self.create_widgets() self.create_date_filters()
def create_widgets(self):
self.file_frame = ttk.Frame(self.root)
self.load_btn = ttk.Button(self.file_frame, text="Загрузить данные", command=self.load_ self.load_btn.pack(side=tk.LEFT, padx=5)
self.file_frame.pack(fill=tk.X, pady=5) self.notebook = ttk.Notebook(self.root)
self.analysis_tab = ttk.Frame(self.notebook) self.create_analysis_widgets()
self.forecast_tab = ttk.Frame(self.notebook) self.create_forecast_widgets()
self.notebook.add(self.analysis_tab, text="Анализ") self.notebook.add(self.forecast_tab, text="Прогноз") self.notebook.pack(expand=True, fill=tk.BOTH)
def create_date_filters(self):
self.date_filter_frame = ttk.Frame(self.analysis_tab)
self.start_date_label = ttk.Label(self.date_filter_frame, text="Начальная дата:") self.start_date_label.pack(side=tk.LEFT, padx=5)
self.start_date_entry = DateEntry( self.date_filter_frame, date_pattern='yyyy-mm-dd', locale='ru_RU', firstweekday='sunday', mindate=pd.Timestamp('2000-01-01'), maxdate=pd.Timestamp('2100-01-01')
)
self.start_date_entry.pack(side=tk.LEFT, padx=5)
self.end_date_label = ttk.Label(self.date_filter_frame, text="Конечная дата:") self.end_date_label.pack(side=tk.LEFT, padx=5)
self.end_date_entry = DateEntry(
self.date_filter_frame, date_pattern='yyyy-mm-dd', locale='ru_RU', firstweekday='sunday', mindate=pd.Timestamp('2000-01-01'), maxdate=pd.Timestamp('2100-01-01')
)
self.end_date_entry.pack(side=tk.LEFT, padx=5)
self.apply_filter_btn = ttk.Button( self.date_filter_frame, text="Применить фильтр", command=self.update_analysis
)
self.apply_filter_btn.pack(side=tk.LEFT, padx=5) self.date_filter_frame.pack(fill=tk.X, pady=5)
def create_analysis_widgets(self):
self.analysis_tree = ttk.Treeview(self.analysis_tab, columns=("Сумма"), show="tree head self.analysis_tree.heading("#0", text="Категория/Описание") self.analysis_tree.heading("Сумма", text="Сумма")
self.analysis_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
self.analysis_fig = Figure(figsize=(6, 4), dpi=100) self.analysis_ax = self.analysis_fig.add_subplot(111)
self.analysis_canvas = FigureCanvasTkAgg(self.analysis_fig, self.analysis_tab)
self.analysis_canvas.get_tk_widget().pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
self.total_label = ttk.Label(self.analysis_tab, text="Итого: 0.00", font=('TkDefaultFon self.total_label.pack(side=tk.BOTTOM, fill=tk.X)
def create_forecast_widgets(self):
self.forecast_paned = ttk.PanedWindow(self.forecast_tab, orient=tk.HORIZONTAL)
self.forecast_tree_frame = ttk.Frame(self.forecast_paned, width=400) self.forecast_tree = ttk.Treeview(
self.forecast_tree_frame,
columns=("Месяц 1", "Месяц 2", "Месяц 3"), show="tree headings"
)
self.forecast_tree.heading("#0", text="Категория/Описание") self.forecast_tree.column("#0", width=200)
self.forecast_tree.heading("Месяц 1", text="Месяц 1")
self.forecast_tree.heading("Месяц 2", text="Месяц 2")
self.forecast_tree.heading("Месяц 3", text="Месяц 3") self.forecast_tree.tag_configure('total', background='#e0e0e0', font=('TkDefaultFont',
tree_scroll = ttk.Scrollbar(self.forecast_tree_frame, orient="vertical", command=self.f self.forecast_tree.configure(yscrollcommand=tree_scroll.set) self.forecast_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) tree_scroll.pack(side=tk.RIGHT, fill="y")
self.forecast_graph_frame = ttk.Frame(self.forecast_paned) self.forecast_canvas = tk.Canvas(self.forecast_graph_frame)
self.scrollbar = ttk.Scrollbar(self.forecast_graph_frame, orient="vertical", command=se self.scrollable_frame = ttk.Frame(self.forecast_canvas)
self.scrollable_frame.bind( "<Configure>",
lambda e: self.forecast_canvas.configure( scrollregion=self.forecast_canvas.bbox("all")
)
)
self.forecast_canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.forecast_canvas.configure(yscrollcommand=self.scrollbar.set)
self.forecast_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.scrollbar.pack(side=tk.RIGHT, fill="y")
self.forecast_paned.add(self.forecast_tree_frame, weight=1) self.forecast_paned.add(self.forecast_graph_frame, weight=3) self.forecast_paned.pack(fill=tk.BOTH, expand=True)
def load_data(self):
file_path = filedialog.askopenfilename( filetypes=[
("CSV files", "*.csv"),
("Excel files", "*.xls"),
("Excel files", "*.xlsx")
]
)
if file_path: try:
if file_path.endswith('.csv'): self.data = pd.read_csv(
file_path,
parse_dates=['Дата операции'], dayfirst=True,
delimiter=';',
encoding='cp1251'
)
elif file_path.endswith('.xls'): self.data = pd.read_excel(
file_path,
parse_dates=['Дата операции'], engine='xlrd'
)
elif file_path.endswith('.xlsx'): self.data = pd.read_excel(
file_path,
parse_dates=['Дата операции'], engine='openpyxl'
)
if not self.data.empty: self.data = self.data[
(self.data['Сумма операции'] >= 500) &
(~self.data['Категория'].isin(['Переводы', 'Пополнения']))
]
self.prepare_data() self.update_analysis() self.generate_forecast()
except Exception as e:
messagebox.showerror("Ошибка", f"Ошибка загрузки файла: {str(e)}")
def prepare_data(self): if self.data.empty:
return
self.data['Дата операции'] = pd.to_datetime(self.data['Дата операции']) self.data['Год'] = self.data['Дата операции'].dt.year self.data['Месяц'] = self.data['Дата операции'].dt.month self.data['День'] = self.data['Дата операции'].dt.day
self.start_date_entry.set_date(self.data['Дата операции'].min()) self.end_date_entry.set_date(self.data['Дата операции'].max())
def update_analysis(self): self.clear_tree(self.analysis_tree) self.analysis_ax.clear()
try:
if self.data.empty:
raise ValueError("Нет данных для анализа")
start_date = pd.to_datetime(self.start_date_entry.get_date()) end_date = pd.to_datetime(self.end_date_entry.get_date()) filtered_data = self.data[
(self.data['Дата операции'] >= start_date) & (self.data['Дата операции'] <= end_date)
]
if filtered_data.empty:
raise ValueError("Нет данных в выбранном диапазоне дат")
total = filtered_data['Сумма операции'].sum() self.total_label.config(text=f"Итого: {total:.2f}")
categories = filtered_data.groupby('Категория', observed=True)['Сумма операции'].su details = filtered_data.groupby(['Категория', 'Описание'], observed=True)['Сумма оп
for category in categories.index:
parent = self.analysis_tree.insert("", "end", text=category, values=(f"{categor if (category,) in details.index:
for desc, amount in details.xs(category).items(): self.analysis_tree.insert(parent, "end", text=desc, values=(f"{amount:.
if not categories.empty:
categories.plot(kind='barh', ax=self.analysis_ax) self.analysis_ax.set_title('Распределение расходов по категориям', fontsize=10) self.analysis_ax.tick_params(axis='both', which='major', labelsize=8) self.analysis_ax.set_xlabel('Сумма', fontsize=8)
self.analysis_ax.text(0.95, 1.02, f"Итого: {total:.2f}",
transform=self.analysis_ax.transAxes, ha='right', fontsize=9)
self.analysis_fig.tight_layout()
else:
self.analysis_ax.text(0.5, 0.5, "Нет данных", ha='center', va='center') self.analysis_canvas.draw()
except Exception as e:
messagebox.showerror("Ошибка", f"Ошибка анализа: {str(e)}")
def generate_forecast(self): self.clear_tree(self.forecast_tree)
for widget in self.scrollable_frame.winfo_children(): widget.destroy()
try:
if self.data.empty:
raise ValueError("Нет данных для прогнозирования")
monthly_data = self.data.resample('M', on='Дата операции')['Сумма операции'].sum()
if len(monthly_data) < 3:
raise ValueError("Недостаточно данных для прогнозирования (требуется минимум 3
models = {
'Экспоненциальное сглаживание': ExponentialSmoothing( monthly_data,
seasonal='add',
seasonal_periods=min(12, len(monthly_data))
).fit().forecast(3),
'Линейная регрессия': LinearRegression().fit( np.arange(len(monthly_data)).reshape(-1, 1), monthly_data.values
).predict(np.arange(len(monthly_data), len(monthly_data)+3).reshape(-1, 1)), 'Скользящее среднее': [monthly_data.rolling(3).mean().iloc[-1]] * 3
}
self.forecast_data = pd.DataFrame(models).mean(axis=1)
category_ratios = self.data.groupby('Категория', observed=True)['Сумма операции'].s total = category_ratios.sum()
if total == 0:
raise ValueError("Нет данных для прогнозирования") category_ratios = category_ratios / total
forecast_categories = pd.DataFrame(
np.outer(self.forecast_data, category_ratios), columns=category_ratios.index
)
forecast_dates = pd.date_range(
start=monthly_data.index[-1] + pd.DateOffset(months=1), periods=3,
freq='M'
)
self.forecast_tree.heading("Месяц 1", text=forecast_dates[0].strftime("%b %Y")) self.forecast_tree.heading("Месяц 2", text=forecast_dates[1].strftime("%b %Y")) self.forecast_tree.heading("Месяц 3", text=forecast_dates[2].strftime("%b %Y"))
total_values = [] for i in range(3):
total_values.append(forecast_categories.iloc[i].sum())
category_details = self.data.groupby(['Категория', 'Описание'], observed=True)['Сум
for category in forecast_categories.columns:
parent = self.forecast_tree.insert("", "end", text=category,
values=tuple(f"{x:.2f}" for x in forecast_categories[catego
if (category,) in category_details.index:
desc_ratios = category_details.xs(category) / category_details.xs(category) for desc, ratio in desc_ratios.items():
desc_values = [
f"{forecast_categories[category][i] * ratio:.2f}" for i in range(3)
]
self.forecast_tree.insert(parent, "end", text=desc, values=desc_values)
self.forecast_tree.insert("", "end", text="Итого",
values=tuple(f"{v:.2f}" for v in total_values), tags=('total',))
for i, date in enumerate(forecast_dates): fig = Figure(figsize=(10, 5), dpi=80) ax = fig.add_subplot(111)
data_to_plot = forecast_categories.iloc[i] if not data_to_plot.empty:
patches = ax.bar(data_to_plot.index, data_to_plot.values) ax.set_title(date.strftime("%B %Y"), fontsize=10) ax.tick_params(axis='x', rotation=90, labelsize=8) ax.tick_params(axis='y', labelsize=8)
tooltip = Tooltip(ax, patches) fig.canvas.mpl_connect("motion_notify_event", tooltip.hover)
fig.tight_layout()
canvas = FigureCanvasTkAgg(fig, self.scrollable_frame) canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=5)
except Exception as e:
messagebox.showerror("Ошибка", f"Ошибка прогнозирования: {str(e)}")
def clear_tree(self, tree):
for item in tree.get_children(): tree.delete(item)
if name == " main ": root = tk.Tk()
app = FinanceAnalyzerApp(root)
root.geometry("1400x900") root.mainloop()