DavMelchi commited on
Commit
bd3da99
·
1 Parent(s): 9bd7cb4

Adding comments to 2G kpi analysis part1

Browse files
apps/kpi_analysis/gsm_capacity.py CHANGED
@@ -15,9 +15,9 @@ with doc_col:
15
  st.write(
16
  """
17
  The report should be run with a minimum of 3 days of data.
18
- - Daily Aggregated
19
- - Site level
20
- - Exported in CSV format.
21
  """
22
  )
23
 
@@ -73,7 +73,7 @@ if (
73
  "TCH ABIS Fails Threshold", min_value=0, value=10
74
  )
75
  with threshold_col3:
76
- sddch_blocking_threshold = st.number_input(
77
  "SDDCH Blocking Threshold", min_value=0.1, value=0.5
78
  )
79
  with threshold_col4:
@@ -90,15 +90,17 @@ if (
90
  number_of_threshold_days=number_of_threshold_days,
91
  availability_threshold=availability_threshold,
92
  tch_abis_fails_threshold=tch_abis_fails_threshold,
93
- sddch_blocking_threshold=sddch_blocking_threshold,
94
  tch_blocking_threshold=tch_blocking_threshold,
95
  )
96
 
97
  if dfs is not None:
98
  gsm_analysis_df = dfs[0]
99
  bh_kpi_df = dfs[1]
 
100
  GsmCapacity.final_results = convert_gsm_dfs(
101
- [gsm_analysis_df, bh_kpi_df], ["GSM_Analysis", "BH_KPI_Analysis"]
 
102
  )
103
 
104
  # GsmCapacity.final_results = convert_gsm_dfs(
@@ -115,4 +117,4 @@ if (
115
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
116
  )
117
 
118
- st.write(gsm_analysis_df)
 
15
  st.write(
16
  """
17
  The report should be run with a minimum of 3 days of data.
18
+ - Dump file required
19
+ - Daily Cell level KPI report in CSV format
20
+ - BH Cell level KPI report in CSV format
21
  """
22
  )
23
 
 
73
  "TCH ABIS Fails Threshold", min_value=0, value=10
74
  )
75
  with threshold_col3:
76
+ sdcch_blocking_threshold = st.number_input(
77
  "SDDCH Blocking Threshold", min_value=0.1, value=0.5
78
  )
79
  with threshold_col4:
 
90
  number_of_threshold_days=number_of_threshold_days,
91
  availability_threshold=availability_threshold,
92
  tch_abis_fails_threshold=tch_abis_fails_threshold,
93
+ sdcch_blocking_threshold=sdcch_blocking_threshold,
94
  tch_blocking_threshold=tch_blocking_threshold,
95
  )
96
 
97
  if dfs is not None:
98
  gsm_analysis_df = dfs[0]
99
  bh_kpi_df = dfs[1]
100
+ daily_kpi_df = dfs[2]
101
  GsmCapacity.final_results = convert_gsm_dfs(
102
+ [gsm_analysis_df, bh_kpi_df, daily_kpi_df],
103
+ ["GSM_Analysis", "BH_KPI_Analysis", "Daily_KPI_Analysis"],
104
  )
105
 
106
  # GsmCapacity.final_results = convert_gsm_dfs(
 
117
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
118
  )
119
 
120
+ st.write(daily_kpi_df)
process_kpi/process_gsm_capacity.py CHANGED
@@ -6,6 +6,8 @@ from utils.check_sheet_exist import execute_checks_sheets_exist
6
  from utils.convert_to_excel import convert_dfs, save_dataframe
7
  from utils.kpi_analysis_utils import (
8
  GsmAnalysis,
 
 
9
  create_daily_date,
10
  create_dfs_per_kpi,
11
  create_hourly_date,
@@ -66,39 +68,89 @@ KPI_COLUMNS = [
66
  BH_COLUMNS_FOR_CAPACITY = [
67
  "Max_Traffic BH",
68
  "Avg_Traffic BH",
69
- "Max_tch_call_blocking BH",
70
- "Avg_tch_call_blocking BH",
71
- "number_of_days_with_tch_blocking_exceeded",
72
- "Max_sdcch_real_blocking BH",
73
- "Avg_sdcch_real_blocking BH",
74
- "number_of_days_with_sdcch_blocking_exceeded",
75
  ]
76
 
77
 
78
- def bh_tch_call_blocking_analysis(
79
  df: pd.DataFrame,
80
  number_of_kpi_days: int,
81
- tch_blocking_threshold: int,
82
  number_of_threshold_days: int,
 
83
  ) -> pd.DataFrame:
84
 
85
  result_df = df.copy()
86
  last_days_df = result_df.iloc[:, -number_of_kpi_days:]
87
  # last_days_df = last_days_df.fillna(0)
88
 
89
- result_df["Avg_tch_call_blocking BH"] = last_days_df.mean(axis=1).round(2)
90
- result_df["Max_tch_call_blocking BH"] = last_days_df.max(axis=1)
 
 
91
  # Count the number of days above threshold
92
- result_df["number_of_days_with_tch_blocking_exceeded"] = last_days_df.apply(
93
- lambda row: sum(1 for x in row if x >= tch_blocking_threshold), axis=1
 
 
94
  )
 
 
 
 
 
 
 
 
 
95
  return result_df
96
 
97
 
98
- def bh_sdcch_call_blocking_analysis(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  df: pd.DataFrame,
100
  number_of_kpi_days: int,
101
  sdcch_blocking_threshold: int,
 
102
  number_of_threshold_days: int,
103
  ) -> pd.DataFrame:
104
 
@@ -106,12 +158,29 @@ def bh_sdcch_call_blocking_analysis(
106
  last_days_df = result_df.iloc[:, -number_of_kpi_days:]
107
  # last_days_df = last_days_df.fillna(0)
108
 
109
- result_df["Avg_sdcch_real_blocking BH"] = last_days_df.mean(axis=1).round(2)
110
- result_df["Max_sdcch_real_blocking BH"] = last_days_df.max(axis=1)
 
 
 
 
111
  # Count the number of days above threshold
112
- result_df["number_of_days_with_sdcch_blocking_exceeded"] = last_days_df.apply(
 
 
113
  lambda row: sum(1 for x in row if x >= sdcch_blocking_threshold), axis=1
114
  )
 
 
 
 
 
 
 
 
 
 
 
115
  return result_df
116
 
117
 
@@ -164,18 +233,20 @@ def bh_dfs_per_kpi(
164
 
165
  # ANALISYS
166
 
167
- tch_call_blocking_df = bh_tch_call_blocking_analysis(
168
  df=tch_call_blocking_df,
169
  number_of_kpi_days=number_of_kpi_days,
170
- tch_blocking_threshold=tch_blocking_threshold,
171
  number_of_threshold_days=number_of_threshold_days,
 
 
172
  )
173
 
174
- sdcch_real_blocking_df = bh_sdcch_call_blocking_analysis(
175
  df=sdcch_real_blocking_df,
176
  number_of_kpi_days=number_of_kpi_days,
177
  sdcch_blocking_threshold=sdcch_blocking_threshold,
178
  number_of_threshold_days=number_of_threshold_days,
 
179
  )
180
 
181
  Carried_Traffic_df = bh_traffic_analysis(
@@ -183,9 +254,6 @@ def bh_dfs_per_kpi(
183
  number_of_kpi_days=number_of_kpi_days,
184
  )
185
 
186
- # Carried_Traffic_df["Max_Traffic BH"] = Carried_Traffic_df.max(axis=1)
187
- # Carried_Traffic_df["Avg_Traffic BH"] = Carried_Traffic_df.mean(axis=1)
188
-
189
  bh_kpi_df = pd.concat(
190
  [
191
  tch_availability_ratio_df,
@@ -195,8 +263,6 @@ def bh_dfs_per_kpi(
195
  ],
196
  axis=1,
197
  )
198
- # print(Carried_Traffic_df)
199
-
200
  return bh_kpi_df
201
 
202
 
@@ -216,7 +282,6 @@ def analyse_bh_data(
216
  number_of_kpi_days=number_of_kpi_days,
217
  tch_blocking_threshold=tch_blocking_threshold,
218
  sdcch_blocking_threshold=sdcch_blocking_threshold,
219
- number_of_threshold_days=number_of_threshold_days,
220
  )
221
 
222
  bh_df_for_capacity = df.copy()
@@ -243,6 +308,8 @@ def daily_dfs_per_kpi(
243
  availability_threshold: int = 95,
244
  number_of_threshold_days: int = 3,
245
  tch_abis_fails_threshold: int = 10,
 
 
246
  ) -> pd.DataFrame:
247
  """
248
  Create pivoted DataFrames for each KPI and perform analysis.
@@ -271,6 +338,61 @@ def daily_dfs_per_kpi(
271
  tch_availability_ratio_df: pd.DataFrame = pivoted_kpi_dfs["TCH_availability_ratio"]
272
  tch_abis_fails_df: pd.DataFrame = pivoted_kpi_dfs["TCH_ABIS_FAIL_CALL_c001084"]
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
  def analyse_daily_data(
276
  daily_report_path: str,
@@ -278,6 +400,8 @@ def analyse_daily_data(
278
  tch_abis_fails_threshold: int,
279
  availability_threshold: int,
280
  number_of_threshold_days: int,
 
 
281
  ) -> pd.DataFrame:
282
  df = pd.read_csv(daily_report_path, delimiter=";")
283
  df = kpi_naming_cleaning(df)
@@ -289,8 +413,10 @@ def analyse_daily_data(
289
  availability_threshold=availability_threshold,
290
  tch_abis_fails_threshold=tch_abis_fails_threshold,
291
  number_of_threshold_days=number_of_threshold_days,
 
 
292
  )
293
- # print(df)
294
 
295
 
296
  def get_gsm_databases(dump_path: str) -> pd.DataFrame:
@@ -340,19 +466,18 @@ def analyze_gsm_data(
340
  number_of_threshold_days: int,
341
  availability_threshold: int,
342
  tch_abis_fails_threshold: int,
343
- sddch_blocking_threshold: float,
344
  tch_blocking_threshold: float,
345
  ):
346
- # print("Analyzing data...")
347
- # print(f"Number of days: {number_of_kpi_days}")
348
- # print(f"availability_threshold: {availability_threshold}")
349
 
350
- analyse_daily_data(
351
  daily_report_path=daily_report_path,
352
  number_of_kpi_days=number_of_kpi_days,
353
  availability_threshold=availability_threshold,
354
  tch_abis_fails_threshold=tch_abis_fails_threshold,
355
  number_of_threshold_days=number_of_threshold_days,
 
 
356
  )
357
 
358
  gsm_database_df: pd.DataFrame = get_gsm_databases(dump_path)
@@ -361,7 +486,7 @@ def analyze_gsm_data(
361
  bh_report_path=bh_report_path,
362
  number_of_kpi_days=number_of_kpi_days,
363
  tch_blocking_threshold=tch_blocking_threshold,
364
- sdcch_blocking_threshold=sddch_blocking_threshold,
365
  number_of_threshold_days=number_of_threshold_days,
366
  )
367
 
@@ -377,7 +502,7 @@ def analyze_gsm_data(
377
 
378
  # Add "ERLANGB value" =MAX TRAFFIC/(1-(MAX TCH call blocking/200))
379
  gsm_analysis_df["ErlabngB_value"] = gsm_analysis_df["Max_Traffic BH"] / (
380
- 1 - (gsm_analysis_df["Max_tch_call_blocking BH"] / 200)
381
  )
382
 
383
  # - Get "Target FR CHs" by mapping "ERLANG value" to 2G analysis_utility "erlangB" dict
@@ -405,4 +530,4 @@ def analyze_gsm_data(
405
  gsm_analysis_df["Target TRXs"] - gsm_analysis_df["number_trx_per_cell"]
406
  )
407
 
408
- return [gsm_analysis_df, bh_kpi_full_df]
 
6
  from utils.convert_to_excel import convert_dfs, save_dataframe
7
  from utils.kpi_analysis_utils import (
8
  GsmAnalysis,
9
+ cell_availability_analysis,
10
+ combine_comments,
11
  create_daily_date,
12
  create_dfs_per_kpi,
13
  create_hourly_date,
 
68
  BH_COLUMNS_FOR_CAPACITY = [
69
  "Max_Traffic BH",
70
  "Avg_Traffic BH",
71
+ "max_tch_call_blocking_bh",
72
+ "avg_tch_call_blocking_bh",
73
+ "number_of_days_with_tch_blocking_exceeded_bh",
74
+ "max_sdcch_real_blocking_bh",
75
+ "avg_sdcch_real_blocking_bh",
76
+ "number_of_days_with_sdcch_blocking_exceeded_bh",
77
  ]
78
 
79
 
80
+ def analyze_tch_abis_fails(
81
  df: pd.DataFrame,
82
  number_of_kpi_days: int,
83
+ analysis_type: str,
84
  number_of_threshold_days: int,
85
+ tch_abis_fails_threshold: int,
86
  ) -> pd.DataFrame:
87
 
88
  result_df = df.copy()
89
  last_days_df = result_df.iloc[:, -number_of_kpi_days:]
90
  # last_days_df = last_days_df.fillna(0)
91
 
92
+ result_df[f"avg_tch_abis_fail_{analysis_type.lower()}"] = last_days_df.mean(
93
+ axis=1
94
+ ).round(2)
95
+ result_df[f"max_tch_abis_fail_{analysis_type.lower()}"] = last_days_df.max(axis=1)
96
  # Count the number of days above threshold
97
+ result_df[f"number_of_days_with_tch_abis_fail_exceeded_{analysis_type.lower()}"] = (
98
+ last_days_df.apply(
99
+ lambda row: sum(1 for x in row if x >= tch_abis_fails_threshold), axis=1
100
+ )
101
  )
102
+
103
+ # Add the daily_tch_comment : if number_of_days_with_tch_abis_fail_exceeded_daily is >= number_of_threshold_days : tch abis fail exceeded treshold , else : None
104
+ result_df[f"tch_abis_fail_{analysis_type.lower()}_comment"] = np.where(
105
+ result_df[f"number_of_days_with_tch_abis_fail_exceeded_{analysis_type.lower()}"]
106
+ >= number_of_threshold_days,
107
+ "tch abis fail exceeded treshold",
108
+ None,
109
+ )
110
+
111
  return result_df
112
 
113
 
114
+ def analyze_tch_call_blocking(
115
+ df: pd.DataFrame,
116
+ number_of_kpi_days: int,
117
+ analysis_type: str,
118
+ number_of_threshold_days: int,
119
+ tch_blocking_threshold: int,
120
+ ) -> pd.DataFrame:
121
+
122
+ result_df = df.copy()
123
+ last_days_df = result_df.iloc[:, -number_of_kpi_days:]
124
+ # last_days_df = last_days_df.fillna(0)
125
+
126
+ result_df[f"avg_tch_call_blocking_{analysis_type.lower()}"] = last_days_df.mean(
127
+ axis=1
128
+ ).round(2)
129
+ result_df[f"max_tch_call_blocking_{analysis_type.lower()}"] = last_days_df.max(
130
+ axis=1
131
+ )
132
+ # Count the number of days above threshold
133
+ result_df[f"number_of_days_with_tch_blocking_exceeded_{analysis_type.lower()}"] = (
134
+ last_days_df.apply(
135
+ lambda row: sum(1 for x in row if x >= tch_blocking_threshold), axis=1
136
+ )
137
+ )
138
+
139
+ # Add the daily_tch_comment : if number_of_days_with_tch_blocking_exceeded_daily is >= number_of_threshold_days : tch blocking exceeded treshold , else : None
140
+ result_df[f"tch_call_blocking_{analysis_type.lower()}_comment"] = np.where(
141
+ result_df[f"number_of_days_with_tch_blocking_exceeded_{analysis_type.lower()}"]
142
+ >= number_of_threshold_days,
143
+ "TCH blocking exceeded threshold",
144
+ None,
145
+ )
146
+ return result_df
147
+
148
+
149
+ def analyze_sdcch_call_blocking(
150
  df: pd.DataFrame,
151
  number_of_kpi_days: int,
152
  sdcch_blocking_threshold: int,
153
+ analysis_type: str,
154
  number_of_threshold_days: int,
155
  ) -> pd.DataFrame:
156
 
 
158
  last_days_df = result_df.iloc[:, -number_of_kpi_days:]
159
  # last_days_df = last_days_df.fillna(0)
160
 
161
+ result_df[f"avg_sdcch_real_blocking_{analysis_type.lower()}"] = last_days_df.mean(
162
+ axis=1
163
+ ).round(2)
164
+ result_df[f"max_sdcch_real_blocking_{analysis_type.lower()}"] = last_days_df.max(
165
+ axis=1
166
+ )
167
  # Count the number of days above threshold
168
+ result_df[
169
+ f"number_of_days_with_sdcch_blocking_exceeded_{analysis_type.lower()}"
170
+ ] = last_days_df.apply(
171
  lambda row: sum(1 for x in row if x >= sdcch_blocking_threshold), axis=1
172
  )
173
+
174
+ # add daily_sdcch_comment : if number_of_days_with_sdcch_blocking_exceeded_daily is >= number_of_threshold_days : sdcch blocking exceeded treshold , else : None
175
+ result_df[f"sdcch_real_blocking_{analysis_type.lower()}_comment"] = np.where(
176
+ result_df[
177
+ f"number_of_days_with_sdcch_blocking_exceeded_{analysis_type.lower()}"
178
+ ]
179
+ >= number_of_threshold_days,
180
+ "SDCCH blocking exceeded threshold",
181
+ None,
182
+ )
183
+
184
  return result_df
185
 
186
 
 
233
 
234
  # ANALISYS
235
 
236
+ tch_call_blocking_df = analyze_tch_call_blocking(
237
  df=tch_call_blocking_df,
238
  number_of_kpi_days=number_of_kpi_days,
 
239
  number_of_threshold_days=number_of_threshold_days,
240
+ tch_blocking_threshold=tch_blocking_threshold,
241
+ analysis_type="BH",
242
  )
243
 
244
+ sdcch_real_blocking_df = analyze_sdcch_call_blocking(
245
  df=sdcch_real_blocking_df,
246
  number_of_kpi_days=number_of_kpi_days,
247
  sdcch_blocking_threshold=sdcch_blocking_threshold,
248
  number_of_threshold_days=number_of_threshold_days,
249
+ analysis_type="BH",
250
  )
251
 
252
  Carried_Traffic_df = bh_traffic_analysis(
 
254
  number_of_kpi_days=number_of_kpi_days,
255
  )
256
 
 
 
 
257
  bh_kpi_df = pd.concat(
258
  [
259
  tch_availability_ratio_df,
 
263
  ],
264
  axis=1,
265
  )
 
 
266
  return bh_kpi_df
267
 
268
 
 
282
  number_of_kpi_days=number_of_kpi_days,
283
  tch_blocking_threshold=tch_blocking_threshold,
284
  sdcch_blocking_threshold=sdcch_blocking_threshold,
 
285
  )
286
 
287
  bh_df_for_capacity = df.copy()
 
308
  availability_threshold: int = 95,
309
  number_of_threshold_days: int = 3,
310
  tch_abis_fails_threshold: int = 10,
311
+ sdcch_blocking_threshold: int = 0.5,
312
+ tch_blocking_threshold: int = 0.5,
313
  ) -> pd.DataFrame:
314
  """
315
  Create pivoted DataFrames for each KPI and perform analysis.
 
338
  tch_availability_ratio_df: pd.DataFrame = pivoted_kpi_dfs["TCH_availability_ratio"]
339
  tch_abis_fails_df: pd.DataFrame = pivoted_kpi_dfs["TCH_ABIS_FAIL_CALL_c001084"]
340
 
341
+ tch_availability_ratio_df = cell_availability_analysis(
342
+ df=tch_availability_ratio_df,
343
+ days=number_of_kpi_days,
344
+ availability_threshold=availability_threshold,
345
+ )
346
+ sdcch_real_blocking_df = analyze_sdcch_call_blocking(
347
+ df=sdcch_real_blocking_df,
348
+ number_of_kpi_days=number_of_kpi_days,
349
+ sdcch_blocking_threshold=sdcch_blocking_threshold,
350
+ number_of_threshold_days=number_of_threshold_days,
351
+ analysis_type="Daily",
352
+ )
353
+ tch_call_blocking_df = analyze_tch_call_blocking(
354
+ df=tch_call_blocking_df,
355
+ number_of_kpi_days=number_of_kpi_days,
356
+ number_of_threshold_days=number_of_threshold_days,
357
+ tch_blocking_threshold=tch_blocking_threshold,
358
+ analysis_type="Daily",
359
+ )
360
+ tch_abis_fails_df = analyze_tch_abis_fails(
361
+ df=tch_abis_fails_df,
362
+ number_of_kpi_days=number_of_kpi_days,
363
+ tch_abis_fails_threshold=tch_abis_fails_threshold,
364
+ number_of_threshold_days=number_of_threshold_days,
365
+ analysis_type="Daily",
366
+ )
367
+
368
+ daily_kpi_df = pd.concat(
369
+ [
370
+ tch_availability_ratio_df,
371
+ Carried_Traffic_df,
372
+ tch_call_blocking_df,
373
+ sdcch_real_blocking_df,
374
+ tch_abis_fails_df,
375
+ ],
376
+ axis=1,
377
+ )
378
+
379
+ daily_kpi_df = combine_comments(
380
+ daily_kpi_df,
381
+ "availability_comment",
382
+ "tch_abis_fail_daily_comment",
383
+ "sdcch_real_blocking_daily_comment",
384
+ new_column="sdcch_comments",
385
+ )
386
+
387
+ daily_kpi_df = combine_comments(
388
+ daily_kpi_df,
389
+ "availability_comment",
390
+ "tch_abis_fail_daily_comment",
391
+ "tch_call_blocking_daily_comment",
392
+ new_column="tch_comments",
393
+ )
394
+ return daily_kpi_df
395
+
396
 
397
  def analyse_daily_data(
398
  daily_report_path: str,
 
400
  tch_abis_fails_threshold: int,
401
  availability_threshold: int,
402
  number_of_threshold_days: int,
403
+ sdcch_blocking_threshold: int,
404
+ tch_blocking_threshold: int,
405
  ) -> pd.DataFrame:
406
  df = pd.read_csv(daily_report_path, delimiter=";")
407
  df = kpi_naming_cleaning(df)
 
413
  availability_threshold=availability_threshold,
414
  tch_abis_fails_threshold=tch_abis_fails_threshold,
415
  number_of_threshold_days=number_of_threshold_days,
416
+ sdcch_blocking_threshold=sdcch_blocking_threshold,
417
+ tch_blocking_threshold=tch_blocking_threshold,
418
  )
419
+ return df
420
 
421
 
422
  def get_gsm_databases(dump_path: str) -> pd.DataFrame:
 
466
  number_of_threshold_days: int,
467
  availability_threshold: int,
468
  tch_abis_fails_threshold: int,
469
+ sdcch_blocking_threshold: float,
470
  tch_blocking_threshold: float,
471
  ):
 
 
 
472
 
473
+ daily_kpi_df: pd.DataFrame = analyse_daily_data(
474
  daily_report_path=daily_report_path,
475
  number_of_kpi_days=number_of_kpi_days,
476
  availability_threshold=availability_threshold,
477
  tch_abis_fails_threshold=tch_abis_fails_threshold,
478
  number_of_threshold_days=number_of_threshold_days,
479
+ sdcch_blocking_threshold=sdcch_blocking_threshold,
480
+ tch_blocking_threshold=tch_blocking_threshold,
481
  )
482
 
483
  gsm_database_df: pd.DataFrame = get_gsm_databases(dump_path)
 
486
  bh_report_path=bh_report_path,
487
  number_of_kpi_days=number_of_kpi_days,
488
  tch_blocking_threshold=tch_blocking_threshold,
489
+ sdcch_blocking_threshold=sdcch_blocking_threshold,
490
  number_of_threshold_days=number_of_threshold_days,
491
  )
492
 
 
502
 
503
  # Add "ERLANGB value" =MAX TRAFFIC/(1-(MAX TCH call blocking/200))
504
  gsm_analysis_df["ErlabngB_value"] = gsm_analysis_df["Max_Traffic BH"] / (
505
+ 1 - (gsm_analysis_df["max_tch_call_blocking_bh"] / 200)
506
  )
507
 
508
  # - Get "Target FR CHs" by mapping "ERLANG value" to 2G analysis_utility "erlangB" dict
 
530
  gsm_analysis_df["Target TRXs"] - gsm_analysis_df["number_trx_per_cell"]
531
  )
532
 
533
+ return [gsm_analysis_df, bh_kpi_full_df, daily_kpi_df]
process_kpi/process_wbts_capacity.py CHANGED
@@ -1,6 +1,8 @@
1
  import pandas as pd
2
 
3
  from utils.kpi_analysis_utils import (
 
 
4
  create_daily_date,
5
  create_dfs_per_kpi,
6
  kpi_naming_cleaning,
@@ -78,39 +80,6 @@ def max_used_bb_subunits_analysis(
78
  return result_df
79
 
80
 
81
- def cell_availability_analysis(df: pd.DataFrame, days: int = 7) -> pd.DataFrame:
82
- """
83
- Analyze cell availability and categorize sites based on availability metrics.
84
-
85
- Args:
86
- df: DataFrame containing cell availability data
87
- days: Number of days to analyze
88
-
89
- Returns:
90
- DataFrame with availability analysis and site status comments
91
- """
92
- result_df = df.copy().fillna(0)
93
- last_days_df = result_df.iloc[:, -days:]
94
- result_df["Average_cell_availability"] = last_days_df.mean(axis=1).round(2)
95
-
96
- # Categorize sites based on availability
97
- def categorize_availability(x: float) -> str:
98
- if x == 0 or pd.isnull(x):
99
- return "Down Site"
100
- elif 0 < x <= 70:
101
- return "critical instability"
102
- elif 70 < x <= 95:
103
- return "instability"
104
- else:
105
- return "Site Ok"
106
-
107
- result_df["availability_comment"] = result_df["Average_cell_availability"].apply(
108
- categorize_availability
109
- )
110
-
111
- return result_df
112
-
113
-
114
  def max_used_ce_analysis(
115
  df: pd.DataFrame,
116
  days: int = 7,
@@ -190,33 +159,6 @@ def avail_ce_analysis(df: pd.DataFrame, days: int = 7) -> pd.DataFrame:
190
  return result_df
191
 
192
 
193
- def combine_comments(df: pd.DataFrame, *columns: str, new_column: str) -> pd.DataFrame:
194
- """
195
- Combine comments from multiple columns into one column.
196
-
197
- Args:
198
- df: DataFrame containing comment columns
199
- *columns: Variable number of column names containing comments
200
- new_column: Name for the new combined comments column
201
-
202
- Returns:
203
- DataFrame with a new column containing combined comments
204
- """
205
- result_df = df.copy()
206
- result_df[new_column] = result_df[list(columns)].apply(
207
- lambda row: ", ".join([x for x in row if x]), axis=1
208
- )
209
- # Trim all trailing commas
210
- result_df[new_column] = result_df[new_column].str.replace(
211
- r"^[,\s]+|[,\s]+$", "", regex=True
212
- )
213
- # Replace multiple commas with a single comma
214
- result_df[new_column] = result_df[new_column].str.replace(
215
- r",\s*,", ", ", regex=True
216
- )
217
- return result_df
218
-
219
-
220
  def bb_comments_analysis(df: pd.DataFrame) -> pd.DataFrame:
221
  """
222
  Combine baseband related comments into a single column.
 
1
  import pandas as pd
2
 
3
  from utils.kpi_analysis_utils import (
4
+ cell_availability_analysis,
5
+ combine_comments,
6
  create_daily_date,
7
  create_dfs_per_kpi,
8
  kpi_naming_cleaning,
 
80
  return result_df
81
 
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  def max_used_ce_analysis(
84
  df: pd.DataFrame,
85
  days: int = 7,
 
159
  return result_df
160
 
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  def bb_comments_analysis(df: pd.DataFrame) -> pd.DataFrame:
163
  """
164
  Combine baseband related comments into a single column.
utils/kpi_analysis_utils.py CHANGED
@@ -216,6 +216,33 @@ class GsmAnalysis:
216
  }
217
 
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  def kpi_naming_cleaning(df: pd.DataFrame) -> pd.DataFrame:
220
  """
221
  Clean KPI column names by replacing special characters and standardizing format.
@@ -295,7 +322,7 @@ def create_dfs_per_kpi(
295
  DataFrame with combined analysis results
296
  """
297
  kpi_columns = df.columns[kpi_columns_from:]
298
- # print(kpi_columns)
299
  pivoted_kpi_dfs = {}
300
 
301
  # Loop through each KPI and create pivoted DataFrames
@@ -310,7 +337,6 @@ def create_dfs_per_kpi(
310
  pivot_df = temp_df.pivot(
311
  index=pivot_name_column, columns=pivot_date_column, values=kpi
312
  )
313
- # print(pivot_df)
314
  pivot_df.columns = pd.MultiIndex.from_product([[kpi], pivot_df.columns])
315
  pivot_df.columns.names = ["KPI", "Date"]
316
 
@@ -318,3 +344,43 @@ def create_dfs_per_kpi(
318
  pivoted_kpi_dfs[kpi] = pivot_df
319
 
320
  return pivoted_kpi_dfs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  }
217
 
218
 
219
+ def combine_comments(df: pd.DataFrame, *columns: str, new_column: str) -> pd.DataFrame:
220
+ """
221
+ Combine comments from multiple columns into one column.
222
+
223
+ Args:
224
+ df: DataFrame containing comment columns
225
+ *columns: Variable number of column names containing comments
226
+ new_column: Name for the new combined comments column
227
+
228
+ Returns:
229
+ DataFrame with a new column containing combined comments
230
+ """
231
+ result_df = df.copy()
232
+ result_df[new_column] = result_df[list(columns)].apply(
233
+ lambda row: ", ".join([x for x in row if x]), axis=1
234
+ )
235
+ # Trim all trailing commas
236
+ result_df[new_column] = result_df[new_column].str.replace(
237
+ r"^[,\s]+|[,\s]+$", "", regex=True
238
+ )
239
+ # Replace multiple commas with a single comma
240
+ result_df[new_column] = result_df[new_column].str.replace(
241
+ r",\s*,", ", ", regex=True
242
+ )
243
+ return result_df
244
+
245
+
246
  def kpi_naming_cleaning(df: pd.DataFrame) -> pd.DataFrame:
247
  """
248
  Clean KPI column names by replacing special characters and standardizing format.
 
322
  DataFrame with combined analysis results
323
  """
324
  kpi_columns = df.columns[kpi_columns_from:]
325
+
326
  pivoted_kpi_dfs = {}
327
 
328
  # Loop through each KPI and create pivoted DataFrames
 
337
  pivot_df = temp_df.pivot(
338
  index=pivot_name_column, columns=pivot_date_column, values=kpi
339
  )
 
340
  pivot_df.columns = pd.MultiIndex.from_product([[kpi], pivot_df.columns])
341
  pivot_df.columns.names = ["KPI", "Date"]
342
 
 
344
  pivoted_kpi_dfs[kpi] = pivot_df
345
 
346
  return pivoted_kpi_dfs
347
+
348
+
349
+ def cell_availability_analysis(
350
+ df: pd.DataFrame, days: int = 7, availability_threshold: int = 95
351
+ ) -> pd.DataFrame:
352
+ """
353
+ Analyze cell availability and categorize sites based on availability metrics.
354
+
355
+ Args:
356
+ df: DataFrame containing cell availability data
357
+ days: Number of days to analyze
358
+
359
+ Returns:
360
+ DataFrame with availability analysis and site status comments
361
+ """
362
+ result_df = df.copy().fillna(0)
363
+ last_days_df = result_df.iloc[:, -days:]
364
+ result_df["Average_cell_availability"] = last_days_df.mean(axis=1).round(2)
365
+
366
+ # Count the number of days above threshold
367
+ result_df["number_of_days_exceeding_threshold"] = last_days_df.apply(
368
+ lambda row: sum(1 for x in row if x <= availability_threshold), axis=1
369
+ )
370
+
371
+ # Categorize sites based on availability
372
+ def categorize_availability(x: float) -> str:
373
+ if x == 0 or pd.isnull(x):
374
+ return "Down Site"
375
+ elif 0 < x <= 70:
376
+ return "critical instability"
377
+ elif 70 < x <= availability_threshold:
378
+ return "instability"
379
+ else:
380
+ return "Availability OK"
381
+
382
+ result_df["availability_comment"] = result_df["Average_cell_availability"].apply(
383
+ categorize_availability
384
+ )
385
+
386
+ return result_df