DavMelchi commited on
Commit
d1de5db
·
1 Parent(s): 5a8534e

Lte capacity V2

Browse files
apps/kpi_analysis/lte_capacity.py CHANGED
@@ -34,6 +34,7 @@ with file2:
34
  # Parameters
35
  param_col1, param_col2 = st.columns(2)
36
  param_col3, param_col4 = st.columns(2)
 
37
 
38
  with param_col1:
39
  num_last_days = st.number_input(
@@ -55,12 +56,23 @@ with param_col4:
55
  "PRB usage threshold (%)", value=80.0, min_value=0.0, max_value=100.0
56
  )
57
 
58
- prb_diff_between_cells = st.number_input(
59
- "Maximum PRB usage difference between cells (%)",
60
- value=20.0,
61
- min_value=0.0,
62
- max_value=100.0,
63
- )
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  if uploaded_dump is not None and uploaded_bh_report is not None:
66
  if st.button("Analyze Data", type="primary"):
@@ -73,6 +85,7 @@ if uploaded_dump is not None and uploaded_bh_report is not None:
73
  availability_threshold=availability_threshold,
74
  prb_usage_threshold=prb_usage_threshold,
75
  prb_diff_between_cells_threshold=prb_diff_between_cells,
 
76
  )
77
  if results is not None:
78
  bh_report: pd.DataFrame = results[0]
@@ -205,3 +218,29 @@ if uploaded_dump is not None and uploaded_bh_report is not None:
205
  texttemplate="%{value}", textfont_size=15, textposition="outside"
206
  )
207
  st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  # Parameters
35
  param_col1, param_col2 = st.columns(2)
36
  param_col3, param_col4 = st.columns(2)
37
+ param_col5, param_col6 = st.columns(2)
38
 
39
  with param_col1:
40
  num_last_days = st.number_input(
 
56
  "PRB usage threshold (%)", value=80.0, min_value=0.0, max_value=100.0
57
  )
58
 
59
+ with param_col5:
60
+ prb_diff_between_cells = st.number_input(
61
+ "Maximum PRB usage difference between cells (%)",
62
+ value=20.0,
63
+ min_value=0.0,
64
+ max_value=100.0,
65
+ )
66
+
67
+ with param_col6:
68
+ # DL PRB Util p TTI Lev_10
69
+ # E-UTRAN Avg PRB usage per TTI DL
70
+ main_prb_to_use = st.selectbox(
71
+ "Main PRB to use",
72
+ ["DL PRB Util p TTI Lev_10", "E-UTRAN Avg PRB usage per TTI DL"],
73
+ index=1,
74
+ )
75
+
76
 
77
  if uploaded_dump is not None and uploaded_bh_report is not None:
78
  if st.button("Analyze Data", type="primary"):
 
85
  availability_threshold=availability_threshold,
86
  prb_usage_threshold=prb_usage_threshold,
87
  prb_diff_between_cells_threshold=prb_diff_between_cells,
88
+ main_prb_to_use=main_prb_to_use,
89
  )
90
  if results is not None:
91
  bh_report: pd.DataFrame = results[0]
 
218
  texttemplate="%{value}", textfont_size=15, textposition="outside"
219
  )
220
  st.plotly_chart(fig, use_container_width=True)
221
+ # create a map plot with scatter_map with code ,Longitude,Latitude,final_comments
222
+ st.markdown("***")
223
+ st.markdown(":blue[**Final comments distribution**]")
224
+ map_df = lte_analysis_df[
225
+ ["code", "Longitude", "Latitude", "final_comments"]
226
+ ].dropna(subset=["code", "Longitude", "Latitude", "final_comments"])
227
+ # add size column equalt to 20
228
+ map_df["size"] = 20
229
+
230
+ fig = px.scatter_map(
231
+ map_df,
232
+ lat="Latitude",
233
+ lon="Longitude",
234
+ color="final_comments",
235
+ size="size",
236
+ zoom=10,
237
+ height=600,
238
+ title="Final comments distribution",
239
+ hover_data={
240
+ "code": True,
241
+ "final_comments": True,
242
+ },
243
+ hover_name="code",
244
+ )
245
+ fig.update_layout(mapbox_style="open-street-map")
246
+ st.plotly_chart(fig, use_container_width=True)
process_kpi/process_lte_capacity.py CHANGED
@@ -17,6 +17,8 @@ LTE_ANALYSIS_COLUMNS = [
17
  "code_sector",
18
  "Region",
19
  "site_config_band",
 
 
20
  "LNCEL_name_l800",
21
  "LNCEL_name_l1800",
22
  "LNCEL_name_l2300",
@@ -27,6 +29,26 @@ LTE_ANALYSIS_COLUMNS = [
27
  "avg_prb_usage_bh_l2300",
28
  "avg_prb_usage_bh_l2600",
29
  "avg_prb_usage_bh_l1800s",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  "num_congested_cells",
31
  "num_cells",
32
  "num_cell_with_kpi",
@@ -42,6 +64,8 @@ LTE_DATABASE_COLUMNS = [
42
  "Region",
43
  "site_config_band",
44
  "final_name",
 
 
45
  ]
46
 
47
  KPI_COLUMNS = [
@@ -50,11 +74,17 @@ KPI_COLUMNS = [
50
  "Cell_Avail_excl_BLU",
51
  "E_UTRAN_Avg_PRB_usage_per_TTI_DL",
52
  "DL_PRB_Util_p_TTI_Lev_10",
 
 
 
53
  ]
54
  PRB_COLUMNS = [
55
  "LNCEL_name",
56
  "avg_prb_usage_bh",
57
- # "avg_prb_usage_bh_lev_10",
 
 
 
58
  ]
59
 
60
 
@@ -221,7 +251,9 @@ def lte_analysis_logic(
221
 
222
  def dfs_per_band_cell(df: pd.DataFrame) -> pd.DataFrame:
223
  # Base DataFrame with unique codes, Region, and site_config_band
224
- all_codes_df = df[["code", "Region", "site_config_band"]].drop_duplicates()
 
 
225
 
226
  # Configuration for sector groups and their respective LNCEL patterns and column suffixes
227
  # Format: { "group_key": [(lncel_name_pattern_part, column_suffix), ...] }
@@ -265,11 +297,23 @@ def dfs_per_band_cell(df: pd.DataFrame) -> pd.DataFrame:
265
  # Select relevant columns and rename them for the merge
266
  # This avoids pandas automatically adding _x, _y suffixes and then needing to rename them
267
  df_to_merge = filtered_band_df[
268
- ["code", "LNCEL_name", "avg_prb_usage_bh"]
 
 
 
 
 
 
 
 
269
  ].rename(
270
  columns={
271
  "LNCEL_name": f"LNCEL_name_{column_suffix}",
272
  "avg_prb_usage_bh": f"avg_prb_usage_bh_{column_suffix}",
 
 
 
 
273
  }
274
  )
275
 
@@ -282,6 +326,7 @@ def dfs_per_band_cell(df: pd.DataFrame) -> pd.DataFrame:
282
 
283
  # Concatenate all the processed sector DataFrames
284
  all_sectors_dfs = pd.concat(all_processed_sectors_dfs, axis=0, ignore_index=True)
 
285
 
286
  return all_sectors_dfs
287
 
@@ -311,6 +356,7 @@ def lte_bh_dfs_per_kpi(
311
  prb_usage_threshold: int = 80,
312
  prb_diff_between_cells_threshold: int = 20,
313
  number_of_threshold_days: int = 3,
 
314
  ) -> pd.DataFrame:
315
 
316
  # print(df.columns)
@@ -326,22 +372,46 @@ def lte_bh_dfs_per_kpi(
326
  days=number_of_kpi_days,
327
  availability_threshold=availability_threshold,
328
  )
329
- # prb_usage_df = analyze_prb_usage(
330
- # df=pivoted_kpi_dfs["E_UTRAN_Avg_PRB_usage_per_TTI_DL"],
331
- # number_of_kpi_days=number_of_kpi_days,
332
- # prb_usage_threshold=prb_usage_threshold,
333
- # analysis_type="BH",
334
- # number_of_threshold_days=number_of_threshold_days,
335
- # )
 
336
  prb_lev10_usage_df = analyze_prb_usage(
337
  df=pivoted_kpi_dfs["DL_PRB_Util_p_TTI_Lev_10"],
338
  number_of_kpi_days=number_of_kpi_days,
339
  prb_usage_threshold=prb_usage_threshold,
340
  analysis_type="BH",
341
  number_of_threshold_days=number_of_threshold_days,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  )
343
-
344
- bh_kpi_df = pd.concat([cell_availability_df, prb_lev10_usage_df], axis=1)
345
  bh_kpi_df = bh_kpi_df.reset_index()
346
  prb_df = bh_kpi_df[PRB_COLUMNS]
347
 
@@ -382,6 +452,7 @@ def process_lte_bh_report(
382
  availability_threshold: float,
383
  prb_usage_threshold: float,
384
  prb_diff_between_cells_threshold: float,
 
385
  ) -> dict:
386
  """
387
  Process LTE Busy Hour report and perform capacity analysis
@@ -414,6 +485,7 @@ def process_lte_bh_report(
414
  prb_usage_threshold=prb_usage_threshold,
415
  prb_diff_between_cells_threshold=prb_diff_between_cells_threshold,
416
  number_of_threshold_days=num_threshold_days,
 
417
  )
418
 
419
  # save_dataframe(pivoted_kpi_dfs, "LTE_BH_Report.csv")
 
17
  "code_sector",
18
  "Region",
19
  "site_config_band",
20
+ "Longitude",
21
+ "Latitude",
22
  "LNCEL_name_l800",
23
  "LNCEL_name_l1800",
24
  "LNCEL_name_l2300",
 
29
  "avg_prb_usage_bh_l2300",
30
  "avg_prb_usage_bh_l2600",
31
  "avg_prb_usage_bh_l1800s",
32
+ "avg_prb_usage_bh_l800_2nd",
33
+ "avg_prb_usage_bh_l1800_2nd",
34
+ "avg_prb_usage_bh_l2300_2nd",
35
+ "avg_prb_usage_bh_l2600_2nd",
36
+ "avg_prb_usage_bh_l1800s_2nd",
37
+ "avg_act_ues_l800",
38
+ "avg_act_ues_l1800",
39
+ "avg_act_ues_l2300",
40
+ "avg_act_ues_l2600",
41
+ "avg_act_ues_l1800s",
42
+ "avg_dl_thp_l800",
43
+ "avg_dl_thp_l1800",
44
+ "avg_dl_thp_l2300",
45
+ "avg_dl_thp_l2600",
46
+ "avg_dl_thp_l1800s",
47
+ "avg_ul_thp_l800",
48
+ "avg_ul_thp_l1800",
49
+ "avg_ul_thp_l2300",
50
+ "avg_ul_thp_l2600",
51
+ "avg_ul_thp_l1800s",
52
  "num_congested_cells",
53
  "num_cells",
54
  "num_cell_with_kpi",
 
64
  "Region",
65
  "site_config_band",
66
  "final_name",
67
+ "Longitude",
68
+ "Latitude",
69
  ]
70
 
71
  KPI_COLUMNS = [
 
74
  "Cell_Avail_excl_BLU",
75
  "E_UTRAN_Avg_PRB_usage_per_TTI_DL",
76
  "DL_PRB_Util_p_TTI_Lev_10",
77
+ "Avg_PDCP_cell_thp_UL",
78
+ "Avg_PDCP_cell_thp_DL",
79
+ "Avg_act_UEs_DL",
80
  ]
81
  PRB_COLUMNS = [
82
  "LNCEL_name",
83
  "avg_prb_usage_bh",
84
+ "avg_prb_usage_bh_2nd",
85
+ "avg_act_ues",
86
+ "avg_dl_thp",
87
+ "avg_ul_thp",
88
  ]
89
 
90
 
 
251
 
252
  def dfs_per_band_cell(df: pd.DataFrame) -> pd.DataFrame:
253
  # Base DataFrame with unique codes, Region, and site_config_band
254
+ all_codes_df = df[
255
+ ["code", "Region", "site_config_band", "Longitude", "Latitude"]
256
+ ].drop_duplicates()
257
 
258
  # Configuration for sector groups and their respective LNCEL patterns and column suffixes
259
  # Format: { "group_key": [(lncel_name_pattern_part, column_suffix), ...] }
 
297
  # Select relevant columns and rename them for the merge
298
  # This avoids pandas automatically adding _x, _y suffixes and then needing to rename them
299
  df_to_merge = filtered_band_df[
300
+ [
301
+ "code",
302
+ "LNCEL_name",
303
+ "avg_prb_usage_bh",
304
+ "avg_prb_usage_bh_2nd",
305
+ "avg_act_ues",
306
+ "avg_dl_thp",
307
+ "avg_ul_thp",
308
+ ]
309
  ].rename(
310
  columns={
311
  "LNCEL_name": f"LNCEL_name_{column_suffix}",
312
  "avg_prb_usage_bh": f"avg_prb_usage_bh_{column_suffix}",
313
+ "avg_prb_usage_bh_2nd": f"avg_prb_usage_bh_{column_suffix}_2nd",
314
+ "avg_act_ues": f"avg_act_ues_{column_suffix}",
315
+ "avg_dl_thp": f"avg_dl_thp_{column_suffix}",
316
+ "avg_ul_thp": f"avg_ul_thp_{column_suffix}",
317
  }
318
  )
319
 
 
326
 
327
  # Concatenate all the processed sector DataFrames
328
  all_sectors_dfs = pd.concat(all_processed_sectors_dfs, axis=0, ignore_index=True)
329
+ # save_dataframe(all_sectors_dfs, "all_sectors_dfs.csv")
330
 
331
  return all_sectors_dfs
332
 
 
356
  prb_usage_threshold: int = 80,
357
  prb_diff_between_cells_threshold: int = 20,
358
  number_of_threshold_days: int = 3,
359
+ main_prb_to_use: str = "",
360
  ) -> pd.DataFrame:
361
 
362
  # print(df.columns)
 
372
  days=number_of_kpi_days,
373
  availability_threshold=availability_threshold,
374
  )
375
+ prb_usage_df = analyze_prb_usage(
376
+ df=pivoted_kpi_dfs["E_UTRAN_Avg_PRB_usage_per_TTI_DL"],
377
+ number_of_kpi_days=number_of_kpi_days,
378
+ prb_usage_threshold=prb_usage_threshold,
379
+ analysis_type="BH",
380
+ number_of_threshold_days=number_of_threshold_days,
381
+ suffix="" if main_prb_to_use == "E-UTRAN Avg PRB usage per TTI DL" else "_2nd",
382
+ )
383
  prb_lev10_usage_df = analyze_prb_usage(
384
  df=pivoted_kpi_dfs["DL_PRB_Util_p_TTI_Lev_10"],
385
  number_of_kpi_days=number_of_kpi_days,
386
  prb_usage_threshold=prb_usage_threshold,
387
  analysis_type="BH",
388
  number_of_threshold_days=number_of_threshold_days,
389
+ suffix="" if main_prb_to_use == "DL PRB Util p TTI Lev_10" else "_2nd",
390
+ )
391
+ act_ues_df = pivoted_kpi_dfs["Avg_act_UEs_DL"]
392
+ # Add Max and avg columns for act_ues_df
393
+ act_ues_df["max_act_ues"] = act_ues_df.max(axis=1)
394
+ act_ues_df["avg_act_ues"] = act_ues_df.mean(axis=1)
395
+ dl_thp_df = pivoted_kpi_dfs["Avg_PDCP_cell_thp_DL"]
396
+ # Add Max and avg columns for dl_thp_df
397
+ dl_thp_df["max_dl_thp"] = dl_thp_df.max(axis=1)
398
+ dl_thp_df["avg_dl_thp"] = dl_thp_df.mean(axis=1)
399
+ ul_thp_df = pivoted_kpi_dfs["Avg_PDCP_cell_thp_UL"]
400
+ # Add Max and avg columns for ul_thp_df
401
+ ul_thp_df["max_ul_thp"] = ul_thp_df.max(axis=1)
402
+ ul_thp_df["avg_ul_thp"] = ul_thp_df.mean(axis=1)
403
+
404
+ bh_kpi_df = pd.concat(
405
+ [
406
+ cell_availability_df,
407
+ prb_lev10_usage_df,
408
+ prb_usage_df,
409
+ act_ues_df,
410
+ dl_thp_df,
411
+ ul_thp_df,
412
+ ],
413
+ axis=1,
414
  )
 
 
415
  bh_kpi_df = bh_kpi_df.reset_index()
416
  prb_df = bh_kpi_df[PRB_COLUMNS]
417
 
 
452
  availability_threshold: float,
453
  prb_usage_threshold: float,
454
  prb_diff_between_cells_threshold: float,
455
+ main_prb_to_use: str,
456
  ) -> dict:
457
  """
458
  Process LTE Busy Hour report and perform capacity analysis
 
485
  prb_usage_threshold=prb_usage_threshold,
486
  prb_diff_between_cells_threshold=prb_diff_between_cells_threshold,
487
  number_of_threshold_days=num_threshold_days,
488
+ main_prb_to_use=main_prb_to_use,
489
  )
490
 
491
  # save_dataframe(pivoted_kpi_dfs, "LTE_BH_Report.csv")
utils/convert_to_excel.py CHANGED
@@ -149,16 +149,38 @@ def get_format_map_by_format_type(formats: dict, format_type: str) -> dict:
149
  "code_sector": formats["blue"],
150
  "Region": formats["blue"],
151
  "site_config_band": formats["blue"],
152
- "LNCEL_name_l800": formats["beurre"],
153
- "LNCEL_name_l1800": formats["purple5"],
154
- "LNCEL_name_l2300": formats["purple6"],
155
- "LNCEL_name_l2600": formats["blue_light"],
156
- "LNCEL_name_l1800s": formats["gray"],
 
 
157
  "avg_prb_usage_bh_l800": formats["beurre"],
158
- "avg_prb_usage_bh_l1800": formats["purple5"],
159
- "avg_prb_usage_bh_l2300": formats["purple6"],
160
- "avg_prb_usage_bh_l2600": formats["blue_light"],
161
- "avg_prb_usage_bh_l1800s": formats["gray"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  "num_congested_cells": formats["orange"],
163
  "num_cells": formats["orange"],
164
  "num_cell_with_kpi": formats["orange"],
 
149
  "code_sector": formats["blue"],
150
  "Region": formats["blue"],
151
  "site_config_band": formats["blue"],
152
+ "Longitude": formats["blue"],
153
+ "Latitude": formats["blue"],
154
+ # "LNCEL_name_l800": formats["beurre"],
155
+ # "LNCEL_name_l1800": formats["purple5"],
156
+ # "LNCEL_name_l2300": formats["purple6"],
157
+ # "LNCEL_name_l2600": formats["blue_light"],
158
+ # "LNCEL_name_l1800s": formats["gray"],
159
  "avg_prb_usage_bh_l800": formats["beurre"],
160
+ "avg_prb_usage_bh_l1800": formats["beurre"],
161
+ "avg_prb_usage_bh_l2300": formats["beurre"],
162
+ "avg_prb_usage_bh_l2600": formats["beurre"],
163
+ "avg_prb_usage_bh_l1800s": formats["beurre"],
164
+ "avg_prb_usage_bh_l800_2nd": formats["purple5"],
165
+ "avg_prb_usage_bh_l1800_2nd": formats["purple5"],
166
+ "avg_prb_usage_bh_l2300_2nd": formats["purple5"],
167
+ "avg_prb_usage_bh_l2600_2nd": formats["purple5"],
168
+ "avg_prb_usage_bh_l1800s_2nd": formats["purple5"],
169
+ "avg_act_ues_l800": formats["purple6"],
170
+ "avg_act_ues_l1800": formats["purple6"],
171
+ "avg_act_ues_l2300": formats["purple6"],
172
+ "avg_act_ues_l2600": formats["purple6"],
173
+ "avg_act_ues_l1800s": formats["purple6"],
174
+ "avg_dl_thp_l800": formats["blue_light"],
175
+ "avg_dl_thp_l1800": formats["blue_light"],
176
+ "avg_dl_thp_l2300": formats["blue_light"],
177
+ "avg_dl_thp_l2600": formats["blue_light"],
178
+ "avg_dl_thp_l1800s": formats["blue_light"],
179
+ "avg_ul_thp_l800": formats["gray"],
180
+ "avg_ul_thp_l1800": formats["gray"],
181
+ "avg_ul_thp_l2300": formats["gray"],
182
+ "avg_ul_thp_l2600": formats["gray"],
183
+ "avg_ul_thp_l1800s": formats["gray"],
184
  "num_congested_cells": formats["orange"],
185
  "num_cells": formats["orange"],
186
  "num_cell_with_kpi": formats["orange"],
utils/kpi_analysis_utils.py CHANGED
@@ -561,25 +561,30 @@ def analyze_prb_usage(
561
  prb_usage_threshold: int,
562
  analysis_type: str,
563
  number_of_threshold_days: int,
 
564
  ) -> pd.DataFrame:
565
  result_df = df.copy()
566
  last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
567
  # last_days_df = last_days_df.fillna(0)
568
 
569
- result_df[f"avg_prb_usage_{analysis_type.lower()}"] = last_days_df.mean(
570
  axis=1
571
  ).round(2)
572
- result_df[f"max_prb_usage_{analysis_type.lower()}"] = last_days_df.max(axis=1)
 
 
573
  # Count the number of days above threshold
574
- result_df[f"number_of_days_with_prb_usage_exceeded_{analysis_type.lower()}"] = (
575
- last_days_df.apply(
576
- lambda row: sum(1 for x in row if x >= prb_usage_threshold), axis=1
577
- )
578
  )
579
 
580
  # Add the daily_prb_comment : if number_of_days_with_prb_usage_exceeded_daily is >= number_of_threshold_days : prb usage exceeded threshold , else : None
581
- result_df[f"prb_usage_{analysis_type.lower()}_comment"] = np.where(
582
- result_df[f"number_of_days_with_prb_usage_exceeded_{analysis_type.lower()}"]
 
 
583
  >= number_of_threshold_days,
584
  "PRB usage exceeded threshold",
585
  None,
 
561
  prb_usage_threshold: int,
562
  analysis_type: str,
563
  number_of_threshold_days: int,
564
+ suffix: str = "",
565
  ) -> pd.DataFrame:
566
  result_df = df.copy()
567
  last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
568
  # last_days_df = last_days_df.fillna(0)
569
 
570
+ result_df[f"avg_prb_usage_{analysis_type.lower()}{suffix}"] = last_days_df.mean(
571
  axis=1
572
  ).round(2)
573
+ result_df[f"max_prb_usage_{analysis_type.lower()}{suffix}"] = last_days_df.max(
574
+ axis=1
575
+ )
576
  # Count the number of days above threshold
577
+ result_df[
578
+ f"number_of_days_with_prb_usage_exceeded_{analysis_type.lower()}{suffix}"
579
+ ] = last_days_df.apply(
580
+ lambda row: sum(1 for x in row if x >= prb_usage_threshold), axis=1
581
  )
582
 
583
  # Add the daily_prb_comment : if number_of_days_with_prb_usage_exceeded_daily is >= number_of_threshold_days : prb usage exceeded threshold , else : None
584
+ result_df[f"prb_usage_{analysis_type.lower()}{suffix}_comment"] = np.where(
585
+ result_df[
586
+ f"number_of_days_with_prb_usage_exceeded_{analysis_type.lower()}{suffix}"
587
+ ]
588
  >= number_of_threshold_days,
589
  "PRB usage exceeded threshold",
590
  None,