fruitpicker01 commited on
Commit
53691dd
·
verified ·
1 Parent(s): 709be4e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -40
app.py CHANGED
@@ -2,61 +2,60 @@ import gradio as gr
2
  import pandas as pd
3
  import plotly.express as px
4
 
 
 
 
5
  # Ссылки на CSV-файлы
6
  URL_DASHA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Dasha_2025/main/messages.csv"
7
  URL_LERA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Lera_2025/main/messages.csv"
8
  URL_SVETA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Sveta_2025/main/messages.csv"
9
 
10
- # Функция для чтения данных, подсчёта уникальных SMS и подготовки данных для графиков
11
  def read_and_process_data(url, user_name):
 
 
 
 
 
 
12
  df = pd.read_csv(url, na_values=["Не выбрано"])
13
-
14
- # Оставляем только нужные столбцы (если какие-то отсутствуют — пропускаем аккуратно)
15
  cols = ["gender", "generation", "industry", "opf", "timestamp"]
16
- df = df[[col for col in cols if col in df.columns]].copy()
17
 
18
- # Убираем дубликаты по gender, generation, industry, opf
19
  df_unique = df.drop_duplicates(subset=["gender", "generation", "industry", "opf"])
20
 
21
- # Считаем абсолютное количество уникальных SMS
22
  unique_count = len(df_unique)
23
 
24
- # Готовим столбец date
25
  if "timestamp" in df_unique.columns:
26
- # Переводим timestamp в datetime
27
  df_unique["timestamp"] = pd.to_numeric(df_unique["timestamp"], errors='coerce')
28
  df_unique["date"] = pd.to_datetime(df_unique["timestamp"], unit="s", origin="unix", errors='coerce').dt.date
29
  else:
30
  df_unique["date"] = pd.NaT
31
-
32
- # Группируем по дате, считаем, сколько уникальных SMS приходится на каждую дату
33
- # (снова нужно учесть комбинацию gender, generation, industry, opf)
34
- # Но т.к. мы уже отфильтровали уникальные, достаточно просто groupby date
35
  df_daily = df_unique.groupby("date").size().reset_index(name="count")
36
- df_daily["user"] = user_name # чтобы потом можно было объединить данные для всех
37
 
38
  return unique_count, df_daily
39
 
40
  def process_data():
41
- # Считываем и обрабатываем все три репозитория
42
  dasha_count, dasha_daily = read_and_process_data(URL_DASHA, "Даша")
43
  lera_count, lera_daily = read_and_process_data(URL_LERA, "Лера")
44
  sveta_count, sveta_daily = read_and_process_data(URL_SVETA, "Света")
45
 
46
- # Суммарные значения
47
  total_count = dasha_count + lera_count + sveta_count
48
 
49
- # Считаем % (округлим до целого)
50
- # Каждое хранилище: процент относительно 117
51
- dasha_percent = round((dasha_count / 117) * 100) if 117 else 0
52
- lera_percent = round((lera_count / 117) * 100) if 117 else 0
53
- sveta_percent = round((sveta_count / 117) * 100) if 117 else 0
54
-
55
- # Суммарный % от 702
56
  total_percent = round((total_count / 702) * 100) if 702 else 0
57
 
58
- # Формируем HTML с "прогресс-барами"
59
- # Простейшая полоска: ширина = процент, внутри пишем "X SMS (Y%)"
60
  def get_progress_bar(label, abs_val, pct):
61
  return f"""
62
  <div style='margin-bottom: 1em;'>
@@ -76,33 +75,116 @@ def process_data():
76
  get_progress_bar("Всего", total_count, total_percent)
77
  )
78
 
79
- # Делаем один общий DataFrame для построения графика:
80
  daily_all = pd.concat([dasha_daily, lera_daily, sveta_daily], ignore_index=True)
81
- # Уберём строки, где date может быть NaT (если timestamp некорректный)
82
  daily_all = daily_all.dropna(subset=["date"])
83
 
84
- # Построим столбчатую диаграмму, показывающую, кто в какой день сколько сохранил SMS
85
- # (уникальных по комбинации gender, generation, industry, opf)
86
- fig = px.bar(
87
- daily_all,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  x="date",
89
- y="count",
90
  color="user",
91
- title="Количество уникальных SMS по датам",
92
- labels={"date": "Дата", "count": "Количество SMS", "user": "Пользователь"},
93
- barmode="group"
 
 
 
94
  )
95
 
96
- return bars_html, fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- # Создаём интерфейс Gradio
99
  with gr.Blocks() as demo:
100
- gr.Markdown("<h2>Подсчёт уникальных SMS для Даши, Леры и Светы</h2>")
101
  btn = gr.Button("Обновить данные и показать результат")
102
- html_output = gr.HTML(label="Прогресс-бары (Количество SMS и %)")
103
- plot_output = gr.Plot(label="График количества уникальных SMS по датам")
 
104
 
105
- btn.click(fn=process_data, outputs=[html_output, plot_output])
106
 
107
  if __name__ == "__main__":
108
  demo.launch()
 
2
  import pandas as pd
3
  import plotly.express as px
4
 
5
+ # Для Prophet
6
+ from prophet import Prophet
7
+
8
  # Ссылки на CSV-файлы
9
  URL_DASHA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Dasha_2025/main/messages.csv"
10
  URL_LERA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Lera_2025/main/messages.csv"
11
  URL_SVETA = "https://raw.githubusercontent.com/fruitpicker01/Storage_Sveta_2025/main/messages.csv"
12
 
 
13
  def read_and_process_data(url, user_name):
14
+ """
15
+ Возвращает:
16
+ 1) unique_count: количество уникальных SMS (по gender, generation, industry, opf)
17
+ 2) df_daily: дата, пользователь, дневное кол-во уникальных SMS (НЕ накопленное),
18
+ но уже после удаления дубликатов по 4 столбцам.
19
+ """
20
  df = pd.read_csv(url, na_values=["Не выбрано"])
 
 
21
  cols = ["gender", "generation", "industry", "opf", "timestamp"]
22
+ df = df[[c for c in cols if c in df.columns]].copy()
23
 
24
+ # Убираем дубликаты по 4 ключевым столбцам
25
  df_unique = df.drop_duplicates(subset=["gender", "generation", "industry", "opf"])
26
 
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
34
  else:
35
  df_unique["date"] = pd.NaT
36
+
37
+ # Сгруппируем по дате, чтобы получить количество за каждый день
 
 
38
  df_daily = df_unique.groupby("date").size().reset_index(name="count")
39
+ df_daily["user"] = 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, "Даша")
46
  lera_count, lera_daily = read_and_process_data(URL_LERA, "Лера")
47
  sveta_count, sveta_daily = read_and_process_data(URL_SVETA, "Света")
48
 
49
+ # Суммарное количество
50
  total_count = dasha_count + lera_count + sveta_count
51
 
52
+ # Проценты (округляем до целого)
53
+ dasha_percent = round((dasha_count / 234) * 100) if 234 else 0
54
+ lera_percent = round((lera_count / 234) * 100) if 234 else 0
55
+ sveta_percent = round((sveta_count / 234) * 100) if 234 else 0
 
 
 
56
  total_percent = round((total_count / 702) * 100) if 702 else 0
57
 
58
+ # Генерируем HTML-прогресс-бары
 
59
  def get_progress_bar(label, abs_val, pct):
60
  return f"""
61
  <div style='margin-bottom: 1em;'>
 
75
  get_progress_bar("Всего", total_count, total_percent)
76
  )
77
 
78
+ # Собираем единый DataFrame по дням
79
  daily_all = pd.concat([dasha_daily, lera_daily, sveta_daily], ignore_index=True)
80
+ # Уберём NaT
81
  daily_all = daily_all.dropna(subset=["date"])
82
 
83
+ # Считаем кумулятивное количество SMS отдельно для каждого пользователя
84
+ # Для этого сначала отсортируем по user + date
85
+ daily_all = daily_all.sort_values(by=["user", "date"])
86
+ # Группируем по user, считаем cumsum
87
+ daily_all["cumulative"] = daily_all.groupby("user")["count"].cumsum()
88
+
89
+ # Чтобы отразить "Всего", добавим user="Всего",
90
+ # суммируя по каждой дате. Потом возьмём кумулятивную сумму этой суммы
91
+ total_by_date = daily_all.groupby("date")["count"].sum().reset_index(name="count")
92
+ total_by_date = total_by_date.sort_values(by="date")
93
+ total_by_date["cumulative"] = total_by_date["count"].cumsum()
94
+ total_by_date["user"] = "Всего"
95
+
96
+ # Объединим с daily_all
97
+ daily_all_final = pd.concat([daily_all, total_by_date], ignore_index=True)
98
+
99
+ # Строим накопительный (кумулятивный) график (линии)
100
+ # Используем Plotly, делая line chart
101
+ fig = px.line(
102
+ daily_all_final,
103
  x="date",
104
+ y="cumulative",
105
  color="user",
106
+ title="Кумулятивное количество уникальных SMS по датам (с линией 'Всего')",
107
+ labels={
108
+ "date": "Дата",
109
+ "cumulative": "Накопительное количество SMS",
110
+ "user": "Пользователь"
111
+ }
112
  )
113
 
114
+ # Прогноз с помощью Prophet (будем прогнозировать "Всего" до 28.02.2025)
115
+ # Берём total_by_date (уже есть 'date', 'cumulative')
116
+ forecast_fig = None
117
+ if len(total_by_date) > 1:
118
+ # Prophet требует колонки ds, y
119
+ df_prophet = total_by_date[["date", "cumulative"]].copy()
120
+ df_prophet.columns = ["ds", "y"]
121
+ df_prophet["ds"] = pd.to_datetime(df_prophet["ds"])
122
+
123
+ # Создаём и обучаем модель
124
+ model = Prophet()
125
+ model.fit(df_prophet)
126
+
127
+ # Создаём DataFrame для будущего (до 28.02.2025)
128
+ future = model.make_future_dataframe(periods=0) # сначала без будущего
129
+ last_date = df_prophet["ds"].max()
130
+ # Посчитаем, сколько дней до 28.02.2025
131
+ end_date = pd.to_datetime("2025-02-28")
132
+ additional_days = (end_date - last_date).days
133
+ if additional_days > 0:
134
+ future = model.make_future_dataframe(periods=additional_days)
135
+
136
+ # Прогноз
137
+ forecast = model.predict(future)
138
+
139
+ # Построим график: отобразим исторические данные + прогноз
140
+ # Можно с plotly, чтобы добавить пунктир.
141
+ # Для наглядности создадим merge таблицу:
142
+ # ds, y (история), yhat (прогноз) и т.д.
143
+ df_plot = pd.merge(
144
+ forecast[["ds", "yhat"]],
145
+ df_prophet[["ds", "y"]],
146
+ on="ds",
147
+ how="left"
148
+ )
149
+
150
+ # Чтобы отобразить пунктиром прогноз, разделим:
151
+ # - где y есть (история)
152
+ # - где y нет (будущее)
153
+ df_history = df_plot.dropna(subset=["y"])
154
+ df_future = df_plot[df_plot["y"].isna()]
155
+
156
+ forecast_fig = px.line(
157
+ df_history,
158
+ x="ds",
159
+ y="y",
160
+ title="Прогноз общего кумулятивного количества SMS до 28.02.2025",
161
+ labels={
162
+ "ds": "Дата",
163
+ "value": "Количество SMS"
164
+ }
165
+ )
166
+ forecast_fig.add_scatter(
167
+ x=df_future["ds"],
168
+ y=df_future["yhat"],
169
+ mode="lines",
170
+ name="Прогноз",
171
+ line=dict(dash="dash", color="red")
172
+ )
173
+ forecast_fig.update_layout(
174
+ showlegend=True,
175
+ legend=dict(x=0, y=1)
176
+ )
177
+
178
+ return (bars_html, fig, forecast_fig)
179
 
 
180
  with gr.Blocks() as demo:
181
+ gr.Markdown("<h2>Подсчёт и прогноз уникальных SMS (Даша, Лера, Света)</h2>")
182
  btn = gr.Button("Обновить данные и показать результат")
183
+ html_output = gr.HTML(label="Прогресс-бары: количество SMS и %")
184
+ plot_output = gr.Plot(label="Кумулятивный график по датам (Даша, Лера, Света, Всего)")
185
+ forecast_output = gr.Plot(label="Прогноз до 28.02.2025 (Всего)")
186
 
187
+ btn.click(fn=process_data, outputs=[html_output, plot_output, forecast_output])
188
 
189
  if __name__ == "__main__":
190
  demo.launch()