fruitpicker01 commited on
Commit
d50ead7
·
verified ·
1 Parent(s): 74d77e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -31
app.py CHANGED
@@ -1,8 +1,6 @@
1
  import gradio as gr
2
  import pandas as pd
3
  import plotly.express as px
4
-
5
- # Для Prophet
6
  from prophet import Prophet
7
 
8
  # Ссылки на CSV-файлы
@@ -27,7 +25,7 @@ def read_and_process_data(url, user_name):
27
  # Количество уникальных SMS
28
  unique_count = len(df_unique)
29
 
30
- # Переводим timestamp -> date (UTC)
31
  if "timestamp" in df_unique.columns:
32
  df_unique["timestamp"] = pd.to_numeric(df_unique["timestamp"], errors='coerce')
33
  df_unique["date"] = pd.to_datetime(df_unique["timestamp"], unit="s", origin="unix", errors='coerce').dt.date
@@ -40,6 +38,54 @@ def read_and_process_data(url, user_name):
40
 
41
  return unique_count, df_daily
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def process_data():
44
  # Считываем и обрабатываем все репозитории
45
  dasha_count, dasha_daily = read_and_process_data(URL_DASHA, "Даша")
@@ -57,7 +103,6 @@ def process_data():
57
 
58
  # Генерируем HTML-прогресс-бары
59
  def get_progress_bar(label, abs_val, pct):
60
- # Определяем "ёмкость"
61
  if label in ["Даша", "Лера", "Света"]:
62
  capacity = 234
63
  else:
@@ -82,14 +127,13 @@ def process_data():
82
 
83
  # Собираем единый DataFrame по дням
84
  daily_all = pd.concat([dasha_daily, lera_daily, sveta_daily], ignore_index=True)
85
- # Уберём NaT
86
- daily_all = daily_all.dropna(subset=["date"])
87
 
88
  # Считаем кумулятивное количество SMS отдельно для каждого пользователя
89
  daily_all = daily_all.sort_values(by=["user", "date"])
90
  daily_all["cumulative"] = daily_all.groupby("user")["count"].cumsum()
91
 
92
- # Чтобы отразить "Всего", добавим user="Всего"
93
  total_by_date = daily_all.groupby("date")["count"].sum().reset_index(name="count")
94
  total_by_date = total_by_date.sort_values(by="date")
95
  total_by_date["cumulative"] = total_by_date["count"].cumsum()
@@ -98,10 +142,9 @@ def process_data():
98
  # Объединим с daily_all
99
  daily_all_final = pd.concat([daily_all, total_by_date], ignore_index=True)
100
 
101
- # Определим порядок легенды, отсортировав по последнему кумулятивному значению
102
- # (у кого больше — тот выше)
103
  last_values = daily_all_final.groupby("user")["cumulative"].last().sort_values(ascending=False)
104
- sorted_users = last_values.index.tolist() # в порядке убывания
105
 
106
  # Явно зададим цвета
107
  color_map = {
@@ -111,69 +154,88 @@ def process_data():
111
  "Всего": "#9467bd" # фиолетовый
112
  }
113
 
114
- # Строим накопительный (кумулятивный) график (линии)
115
  fig = px.line(
116
  daily_all_final,
117
  x="date",
118
  y="cumulative",
119
  color="user",
120
  title="Накопительное количество SMS",
121
- labels={
122
- "date": "Дата",
123
- "cumulative": "Накопительное количество SMS",
124
- "user": "Редактор"
125
- },
126
- category_orders={"user": sorted_users}, # порядок в легенде
127
  color_discrete_map=color_map
128
  )
129
 
130
- # Прогноз с помощью Prophet (делаем для "Всего" до 28.02.2025)
 
 
 
131
  forecast_fig = None
132
- if len(total_by_date) > 1:
 
 
 
133
  df_prophet = total_by_date[["date", "cumulative"]].copy()
134
  df_prophet.columns = ["ds", "y"]
135
  df_prophet["ds"] = pd.to_datetime(df_prophet["ds"])
136
 
 
137
  model = Prophet()
138
  model.fit(df_prophet)
139
 
140
- # Рассчитаем, сколько дней до 28.02.2025
141
- future = model.make_future_dataframe(periods=0) # на случай, если уже после
142
- last_date = df_prophet["ds"].max()
143
  end_date = pd.to_datetime("2025-02-28")
 
144
  additional_days = (end_date - last_date).days
 
 
145
  if additional_days > 0:
146
  future = model.make_future_dataframe(periods=additional_days)
147
 
148
  forecast = model.predict(future)
149
 
150
- # Сопоставим исторические данные и прогноз
151
  df_plot = pd.merge(
152
  forecast[["ds", "yhat"]],
153
  df_prophet[["ds", "y"]],
154
  on="ds",
155
  how="left"
156
  )
157
- df_history = df_plot.dropna(subset=["y"]) # исторические точки
158
- df_future = df_plot[df_plot["y"].isna()] # будущее
 
 
 
 
 
159
 
160
- # Отдельный график
161
  forecast_fig = px.line(
162
  df_history,
163
  x="ds",
164
  y="y",
165
- title="Прогноз до конца февраля 2025 (всего)",
166
- labels={"ds": "Дата", "y": "Накопительное количество SMS"}
167
  )
168
  forecast_fig.add_scatter(
169
  x=df_future["ds"],
170
  y=df_future["yhat"],
171
  mode="lines",
172
- name="Прогноз",
173
  line=dict(dash="dash", color="red")
174
  )
 
 
 
 
 
 
 
 
 
 
 
175
  forecast_fig.update_layout(showlegend=True)
176
-
177
  return (bars_html, fig, forecast_fig)
178
 
179
  with gr.Blocks() as demo:
@@ -181,7 +243,7 @@ with gr.Blocks() as demo:
181
  btn = gr.Button("Обновить данные и показать результат")
182
  html_output = gr.HTML(label="Прогресс-бары: количество SMS и %")
183
  plot_output = gr.Plot(label="Накопительный график по датам (Даша, Лера, Света, Всего)")
184
- forecast_output = gr.Plot(label="Прогноз до конца февраля 2025 (всего)")
185
 
186
  btn.click(fn=process_data, outputs=[html_output, plot_output, forecast_output])
187
 
 
1
  import gradio as gr
2
  import pandas as pd
3
  import plotly.express as px
 
 
4
  from prophet import Prophet
5
 
6
  # Ссылки на CSV-файлы
 
25
  # Количество уникальных SMS
26
  unique_count = len(df_unique)
27
 
28
+ # Переводим timestamp -> date
29
  if "timestamp" in df_unique.columns:
30
  df_unique["timestamp"] = pd.to_numeric(df_unique["timestamp"], errors='coerce')
31
  df_unique["date"] = pd.to_datetime(df_unique["timestamp"], unit="s", origin="unix", errors='coerce').dt.date
 
38
 
39
  return unique_count, df_daily
40
 
41
+ def make_naive_forecast(total_by_date, end_date_str="2025-02-28"):
42
+ """
43
+ Строит «наивный» прогноз до указанной даты (end_date_str),
44
+ считая среднее дневное приращение только по будням (Mon-Fri).
45
+ В выходные (Sat/Sun) прирост = 0.
46
+
47
+ Возвращает DataFrame с колонками ["ds", "yhat"], начиная с (last_date+1) по end_date_str.
48
+ """
49
+ if total_by_date.empty:
50
+ return pd.DataFrame(columns=["ds", "yhat"])
51
+
52
+ # Превращаем date -> datetime, чтобы работать с днем недели
53
+ df_tmp = total_by_date.copy()
54
+ df_tmp["date"] = pd.to_datetime(df_tmp["date"])
55
+
56
+ # Средний дневной прирост по будням
57
+ # Прирост за день хранится в столбце "count"
58
+ # Фильтруем по будням (понедельник=0 ... пятница=4)
59
+ df_weekdays = df_tmp[df_tmp["date"].dt.weekday < 5]
60
+ if len(df_weekdays) == 0:
61
+ avg_inc = 0
62
+ else:
63
+ avg_inc = df_weekdays["count"].mean()
64
+
65
+ last_date = df_tmp["date"].max()
66
+ last_cumulative = df_tmp["cumulative"].iloc[-1] # последнее накопленное значение
67
+
68
+ end_date = pd.to_datetime(end_date_str)
69
+
70
+ # Составляем прогноз, двигаясь по календарю день за днём
71
+ current_date = last_date
72
+ naive_data = []
73
+ running_total = last_cumulative
74
+
75
+ while current_date < end_date:
76
+ current_date = current_date + pd.Timedelta(days=1)
77
+ if current_date > end_date:
78
+ break
79
+ # Проверяем, будний ли это день (0..4)
80
+ if current_date.weekday() < 5:
81
+ running_total += avg_inc # добавляем среднее приращение
82
+ # Записываем точку прогноза
83
+ naive_data.append({"ds": current_date, "yhat": running_total})
84
+
85
+ # Превращаем в DataFrame
86
+ df_naive = pd.DataFrame(naive_data)
87
+ return df_naive
88
+
89
  def process_data():
90
  # Считываем и обрабатываем все репозитории
91
  dasha_count, dasha_daily = read_and_process_data(URL_DASHA, "Даша")
 
103
 
104
  # Генерируем HTML-прогресс-бары
105
  def get_progress_bar(label, abs_val, pct):
 
106
  if label in ["Даша", "Лера", "Света"]:
107
  capacity = 234
108
  else:
 
127
 
128
  # Собираем единый DataFrame по дням
129
  daily_all = pd.concat([dasha_daily, lera_daily, sveta_daily], ignore_index=True)
130
+ daily_all = daily_all.dropna(subset=["date"]) # убираем NaT
 
131
 
132
  # Считаем кумулятивное количество SMS отдельно для каждого пользователя
133
  daily_all = daily_all.sort_values(by=["user", "date"])
134
  daily_all["cumulative"] = daily_all.groupby("user")["count"].cumsum()
135
 
136
+ # Складываем в "Всего" по датам
137
  total_by_date = daily_all.groupby("date")["count"].sum().reset_index(name="count")
138
  total_by_date = total_by_date.sort_values(by="date")
139
  total_by_date["cumulative"] = total_by_date["count"].cumsum()
 
142
  # Объединим с daily_all
143
  daily_all_final = pd.concat([daily_all, total_by_date], ignore_index=True)
144
 
145
+ # Определим порядок легенды по последнему кумулятивному значению (убывание)
 
146
  last_values = daily_all_final.groupby("user")["cumulative"].last().sort_values(ascending=False)
147
+ sorted_users = last_values.index.tolist()
148
 
149
  # Явно зададим цвета
150
  color_map = {
 
154
  "Всего": "#9467bd" # фиолетовый
155
  }
156
 
157
+ # Строим накопительный (кумулятивный) график (линии) по факту
158
  fig = px.line(
159
  daily_all_final,
160
  x="date",
161
  y="cumulative",
162
  color="user",
163
  title="Накопительное количество SMS",
164
+ labels={"date": "Дата", "cumulative": "Накопленное количество SMS", "user": "Редактор"},
165
+ category_orders={"user": sorted_users},
 
 
 
 
166
  color_discrete_map=color_map
167
  )
168
 
169
+ # -----------------------
170
+ # Прогнозы (Prophet + Наивный)
171
+ # -----------------------
172
+
173
  forecast_fig = None
174
+
175
+ # Сформируем DF для Prophet из "Всего"
176
+ # (Если данных мало, проверим, чтобы не было пусто)
177
+ if not total_by_date.empty:
178
  df_prophet = total_by_date[["date", "cumulative"]].copy()
179
  df_prophet.columns = ["ds", "y"]
180
  df_prophet["ds"] = pd.to_datetime(df_prophet["ds"])
181
 
182
+ # 1) Prophet
183
  model = Prophet()
184
  model.fit(df_prophet)
185
 
 
 
 
186
  end_date = pd.to_datetime("2025-02-28")
187
+ last_date = df_prophet["ds"].max()
188
  additional_days = (end_date - last_date).days
189
+
190
+ future = model.make_future_dataframe(periods=0)
191
  if additional_days > 0:
192
  future = model.make_future_dataframe(periods=additional_days)
193
 
194
  forecast = model.predict(future)
195
 
196
+ # Сопоставим исторические точки (y) и прогноз (yhat)
197
  df_plot = pd.merge(
198
  forecast[["ds", "yhat"]],
199
  df_prophet[["ds", "y"]],
200
  on="ds",
201
  how="left"
202
  )
203
+ df_history = df_plot.dropna(subset=["y"]) # исторические данные
204
+ df_future = df_plot[df_plot["y"].isna()] # будущее (прогноз prophet)
205
+
206
+ # 2) Наивный прогноз (будни)
207
+ # Используем total_by_date (уже есть date, count, cumulative)
208
+ # Функция вернёт df_naive: ds, yhat
209
+ df_naive = make_naive_forecast(total_by_date, "2025-02-28")
210
 
211
+ # Формируем общий график для сравнения
212
  forecast_fig = px.line(
213
  df_history,
214
  x="ds",
215
  y="y",
216
+ title="Два прогноза до конца февраля 2025 (всего)",
217
+ labels={"ds": "Дата", "y": "Накопленное число SMS"}
218
  )
219
  forecast_fig.add_scatter(
220
  x=df_future["ds"],
221
  y=df_future["yhat"],
222
  mode="lines",
223
+ name="Прогноз (Prophet)",
224
  line=dict(dash="dash", color="red")
225
  )
226
+
227
+ # Добавим наивный прогноз
228
+ if not df_naive.empty:
229
+ forecast_fig.add_scatter(
230
+ x=df_naive["ds"],
231
+ y=df_naive["yhat"],
232
+ mode="lines",
233
+ name="Прогноз (Наивн., будни)",
234
+ line=dict(dash="dash", color="green")
235
+ )
236
+
237
  forecast_fig.update_layout(showlegend=True)
238
+
239
  return (bars_html, fig, forecast_fig)
240
 
241
  with gr.Blocks() as demo:
 
243
  btn = gr.Button("Обновить данные и показать результат")
244
  html_output = gr.HTML(label="Прогресс-бары: количество SMS и %")
245
  plot_output = gr.Plot(label="Накопительный график по датам (Даша, Лера, Света, Всего)")
246
+ forecast_output = gr.Plot(label="Сравнение прогнозов до конца февраля 2025 (всего)")
247
 
248
  btn.click(fn=process_data, outputs=[html_output, plot_output, forecast_output])
249