Загрузка данных
import pandas as pd
import numpy as np
import tkinter as tk
from tkinter import messagebox, ttk, Toplevel, Text
import os
class MeterApp:
def __init__(self, root):
self.root = root
self.root.title("Анализ показаний счетчика 2026")
self.df_raw = None
self.df_melted = None
self.file_path = 'data.xlsx'
self.create_widgets()
self.load_file(self.file_path)
def create_widgets(self):
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
self.tree = ttk.Treeview(main_frame, show='headings')
vsb = ttk.Scrollbar(main_frame, orient="vertical", command=self.tree.yview)
hsb = ttk.Scrollbar(main_frame, orient="horizontal", command=self.tree.xview)
self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
self.tree.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.E, tk.W))
vsb.grid(row=0, column=1, sticky='ns')
hsb.grid(row=1, column=0, sticky='ew')
button_frame = ttk.Frame(main_frame, padding="10 0 0 0")
button_frame.grid(row=2, column=0, columnspan=2, sticky=tk.W)
buttons = [
("Максимум", lambda: self.show_extreme('max')),
("Минимум", lambda: self.show_extreme('min')),
("Сумма/Месяц", self.show_monthly_sums),
("Сумма/Число", self.show_daily_sums),
("Изменить данные", self.show_edit_popup),
("Среднее/Медиана", self.show_mean_median),
("Лучший день недели", self.show_best_weekday)
]
for text, cmd in buttons:
btn = tk.Button(button_frame, text=text, command=cmd, padx=8, pady=5)
btn.pack(side=tk.LEFT, padx=2)
def process_data(self):
if self.df_raw is None: return
df = self.df_raw.copy()
df.columns = [str(col).strip().lower() for col in df.columns]
day_col = df.columns[0]
for col in df.columns[1:]:
df[col] = pd.to_numeric(df[col], errors='coerce')
self.df_melted = df.melt(id_vars=[day_col], var_name='Месяц', value_name='Значение').dropna()
self.df_melted.columns = ['День', 'Месяц', 'Значение']
def load_file(self, filepath):
try:
if os.path.exists(filepath):
self.df_raw = pd.read_excel(filepath)
self.process_data()
self.update_table_display()
else:
messagebox.showwarning("Внимание", f"Файл {filepath} не найден.")
except Exception as e:
messagebox.showerror("Ошибка", f"Ошибка: {e}")
def update_table_display(self):
if self.df_raw is None: return
self.tree.delete(*self.tree.get_children())
cols = list(self.df_raw.columns)
self.tree["columns"] = cols
for col in cols:
self.tree.heading(col, text=col)
self.tree.column(col, width=70, anchor="center")
for idx, row in self.df_raw.iterrows():
self.tree.insert("", tk.END, values=list(row))
def show_extreme(self, mode):
if self.df_melted is None: return
val = self.df_melted['Значение'].max() if mode == 'max' else self.df_melted['Значение'].min()
title = "Максимум" if mode == 'max' else "Минимум"
dates = self.df_melted[self.df_melted['Значение'] == val]
res = f"Значение: {val}\n\nДаты:\n"
for _, r in dates.iterrows():
res += f" - {int(r['День'])} {r['Месяц'].capitalize()}\n"
self.display_text_popup(title, res)
def show_monthly_sums(self):
if self.df_melted is None: return
res = self.df_melted.groupby('Месяц')['Значение'].sum().to_string()
self.display_text_popup("Сумма по месяцам", res)
def show_daily_sums(self):
if self.df_melted is None: return
res = self.df_melted.groupby('День')['Значение'].sum().to_string()
self.display_text_popup("Сумма по числам", res)
def show_edit_popup(self):
popup = Toplevel(self.root)
popup.title("Изменение")
fields = ["День:", "Месяц:", "Значение:"]
self.entries = []
for i, f in enumerate(fields):
tk.Label(popup, text=f).grid(row=i, column=0, padx=10, pady=5)
e = tk.Entry(popup)
e.grid(row=i, column=1, padx=10, pady=5)
self.entries.append(e)
tk.Button(popup, text="ОК", command=lambda: self.save_edit(popup)).grid(row=3, columnspan=2, pady=10)
def save_edit(self, win):
try:
day = int(self.entries[0].get())
month = self.entries[1].get().strip().lower()
val = float(self.entries[2].get())
col = next((c for c in self.df_raw.columns if str(c).lower() == month), None)
if col:
idx = self.df_raw[self.df_raw.iloc[:, 0] == day].index
if not idx.empty:
self.df_raw.at[idx[0], col] = val
self.process_data()
self.update_table_display()
win.destroy()
else: messagebox.showerror("Ошибка", "День не найден")
else: messagebox.showerror("Ошибка", "Месяц не найден")
except: messagebox.showerror("Ошибка", "Неверный ввод")
def show_mean_median(self):
if self.df_melted is None: return
res = self.df_melted.groupby('Месяц')['Значение'].agg(['mean', 'median'])
res.columns = ['Среднее', 'Медиана']
self.display_text_popup("Статистика", res.to_string())
def show_best_weekday(self):
if self.df_melted is None: return
m_map = {'январь': 1, 'февраль': 2, 'март': 3, 'апрель': 4, 'май': 5, 'июнь': 6,
'июль': 7, 'август': 8, 'сентябрь': 9, 'октябрь': 10, 'ноябрь': 11, 'декабрь': 12}
temp = self.df_melted.copy()
def get_wd(row):
try:
return pd.Timestamp(2026, m_map[str(row['Месяц']).lower().strip()], int(row['День'])).day_name()
except: return None
temp['WD'] = temp.apply(get_wd, axis=1)
stats = temp.groupby('WD')['Значение'].sum()
days_ru = {'Monday': 'Понедельник', 'Tuesday': 'Вторник', 'Wednesday': 'Среда',
'Thursday': 'Четверг', 'Friday': 'Пятница', 'Saturday': 'Суббота', 'Sunday': 'Воскресенье'}
if not stats.empty:
best = stats.idxmax()
messagebox.showinfo("Результат", f"Лучший день: {days_ru[best]}\nСумма: {stats.max():.2f}")
def display_text_popup(self, title, content):
win = Toplevel(self.root)
win.title(title)
t = Text(win, height=15, width=45, padx=10, pady=10)
t.pack()
t.insert(tk.END, content)
t.config(state=tk.DISABLED)
if __name__ == "__main__":
root = tk.Tk()
app = MeterApp(root)
root.mainloop()