gsm capacity part2 with comments analysis and combinations
Browse files- README.md +1 -1
- apps/kpi_analysis/gsm_capacity.py +47 -3
- process_kpi/process_gsm_capacity.py +94 -110
- utils/kpi_analysis_utils.py +109 -1
README.md
CHANGED
@@ -49,7 +49,7 @@ You can access the hosted version of the app at [https://davmelchi-db-query.hf.s
|
|
49 |
- [x] Symetric neighbours checking
|
50 |
- [x] Add the ability to select columns
|
51 |
- [x] Add authentication
|
|
|
52 |
- [ ] Improve Dashboard
|
53 |
- [ ] Error handling
|
54 |
-
- [ ] frequency distribution GSM
|
55 |
- [ ] Add KPI analysis App
|
|
|
49 |
- [x] Symetric neighbours checking
|
50 |
- [x] Add the ability to select columns
|
51 |
- [x] Add authentication
|
52 |
+
- [x] Initial frequency distribution chart GSM
|
53 |
- [ ] Improve Dashboard
|
54 |
- [ ] Error handling
|
|
|
55 |
- [ ] Add KPI analysis App
|
apps/kpi_analysis/gsm_capacity.py
CHANGED
@@ -42,6 +42,8 @@ col1, col2 = st.columns(2)
|
|
42 |
|
43 |
threshold_col1, threshold_col2 = st.columns(2)
|
44 |
threshold_col3, threshold_col4 = st.columns(2)
|
|
|
|
|
45 |
|
46 |
if (
|
47 |
uploaded_dump is not None
|
@@ -80,6 +82,14 @@ if (
|
|
80 |
tch_blocking_threshold = st.number_input(
|
81 |
"TCH Blocking Threshold", min_value=0.1, value=0.5
|
82 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
if st.button("Analyze Data", type="primary"):
|
85 |
dfs = analyze_gsm_data(
|
@@ -92,12 +102,13 @@ if (
|
|
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"],
|
@@ -118,3 +129,36 @@ if (
|
|
118 |
)
|
119 |
|
120 |
st.write(daily_kpi_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
threshold_col1, threshold_col2 = st.columns(2)
|
44 |
threshold_col3, threshold_col4 = st.columns(2)
|
45 |
+
max_traffic_threshold_col1, max_traffic_threshold_col2 = st.columns(2)
|
46 |
+
|
47 |
|
48 |
if (
|
49 |
uploaded_dump is not None
|
|
|
82 |
tch_blocking_threshold = st.number_input(
|
83 |
"TCH Blocking Threshold", min_value=0.1, value=0.5
|
84 |
)
|
85 |
+
with max_traffic_threshold_col1:
|
86 |
+
max_traffic_threshold = st.number_input(
|
87 |
+
"TCH Utilization Max Traffic Threshold", min_value=0, value=90
|
88 |
+
)
|
89 |
+
# with max_traffic_threshold_col2:
|
90 |
+
# max_traffic_threshold = st.number_input(
|
91 |
+
# "Max Traffic Threshold", min_value=0, value=10
|
92 |
+
# )
|
93 |
|
94 |
if st.button("Analyze Data", type="primary"):
|
95 |
dfs = analyze_gsm_data(
|
|
|
102 |
tch_abis_fails_threshold=tch_abis_fails_threshold,
|
103 |
sdcch_blocking_threshold=sdcch_blocking_threshold,
|
104 |
tch_blocking_threshold=tch_blocking_threshold,
|
105 |
+
max_traffic_threshold=max_traffic_threshold,
|
106 |
)
|
107 |
|
108 |
if dfs is not None:
|
109 |
+
gsm_analysis_df: pd.DataFrame = dfs[0]
|
110 |
+
bh_kpi_df: pd.DataFrame = dfs[1]
|
111 |
+
daily_kpi_df: pd.DataFrame = dfs[2]
|
112 |
GsmCapacity.final_results = convert_gsm_dfs(
|
113 |
[gsm_analysis_df, bh_kpi_df, daily_kpi_df],
|
114 |
["GSM_Analysis", "BH_KPI_Analysis", "Daily_KPI_Analysis"],
|
|
|
129 |
)
|
130 |
|
131 |
st.write(daily_kpi_df)
|
132 |
+
|
133 |
+
# Add dataframe and ploty bar chart with "Final comment" distribution in gsm_analysis_df in 2 columns
|
134 |
+
final_comments_df = (
|
135 |
+
gsm_analysis_df.groupby("Final comment").size().reset_index(name="count")
|
136 |
+
)
|
137 |
+
fig = px.bar(final_comments_df, x="Final comment", y="count")
|
138 |
+
fig.update_layout(height=1500)
|
139 |
+
fig.update_traces(texttemplate="%{value}", textposition="outside")
|
140 |
+
st.plotly_chart(fig, use_container_width=True)
|
141 |
+
st.write(final_comments_df)
|
142 |
+
|
143 |
+
# Add dataframe and ploty bar chart with "BH Congestion status" distribution in gsm_analysis_df in 2 columns
|
144 |
+
bh_congestion_status_df = (
|
145 |
+
gsm_analysis_df.groupby("BH Congestion status")
|
146 |
+
.size()
|
147 |
+
.reset_index(name="count")
|
148 |
+
)
|
149 |
+
fig = px.bar(bh_congestion_status_df, x="BH Congestion status", y="count")
|
150 |
+
fig.update_layout(height=1500)
|
151 |
+
fig.update_traces(texttemplate="%{value}", textposition="outside")
|
152 |
+
st.plotly_chart(fig, use_container_width=True)
|
153 |
+
st.write(bh_congestion_status_df)
|
154 |
+
# Add dataframe and ploty bar chart with "operational_comment" distribution in gsm_analysis_df in 2 columns
|
155 |
+
operational_comments_df = (
|
156 |
+
gsm_analysis_df.groupby("operational_comment")
|
157 |
+
.size()
|
158 |
+
.reset_index(name="count")
|
159 |
+
)
|
160 |
+
fig = px.bar(operational_comments_df, x="operational_comment", y="count")
|
161 |
+
fig.update_layout(height=1500)
|
162 |
+
fig.update_traces(texttemplate="%{value}", textposition="outside")
|
163 |
+
st.plotly_chart(fig, use_container_width=True)
|
164 |
+
st.write(operational_comments_df)
|
process_kpi/process_gsm_capacity.py
CHANGED
@@ -6,6 +6,9 @@ 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 |
cell_availability_analysis,
|
10 |
combine_comments,
|
11 |
create_daily_date,
|
@@ -74,114 +77,19 @@ BH_COLUMNS_FOR_CAPACITY = [
|
|
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 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
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 |
-
|
157 |
-
result_df = df.copy()
|
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 |
|
187 |
def bh_traffic_analysis(
|
@@ -190,7 +98,7 @@ def bh_traffic_analysis(
|
|
190 |
) -> pd.DataFrame:
|
191 |
|
192 |
result_df = df.copy()
|
193 |
-
last_days_df = result_df.iloc[:, -number_of_kpi_days:]
|
194 |
# last_days_df = last_days_df.fillna(0)
|
195 |
|
196 |
result_df["Avg_Traffic BH"] = last_days_df.mean(axis=1).round(2)
|
@@ -282,6 +190,7 @@ def analyse_bh_data(
|
|
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()
|
@@ -416,7 +325,19 @@ def analyse_daily_data(
|
|
416 |
sdcch_blocking_threshold=sdcch_blocking_threshold,
|
417 |
tch_blocking_threshold=tch_blocking_threshold,
|
418 |
)
|
419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
420 |
|
421 |
|
422 |
def get_gsm_databases(dump_path: str) -> pd.DataFrame:
|
@@ -468,9 +389,10 @@ def analyze_gsm_data(
|
|
468 |
tch_abis_fails_threshold: int,
|
469 |
sdcch_blocking_threshold: float,
|
470 |
tch_blocking_threshold: float,
|
|
|
471 |
):
|
472 |
|
473 |
-
|
474 |
daily_report_path=daily_report_path,
|
475 |
number_of_kpi_days=number_of_kpi_days,
|
476 |
availability_threshold=availability_threshold,
|
@@ -493,13 +415,32 @@ def analyze_gsm_data(
|
|
493 |
bh_kpi_df = bh_kpi_dfs[0]
|
494 |
bh_kpi_full_df = bh_kpi_dfs[1]
|
495 |
|
|
|
|
|
|
|
496 |
gsm_analysis_df = gsm_database_df.merge(bh_kpi_df, on="name", how="left")
|
|
|
497 |
|
498 |
# "TCH UTILIZATION (@Max Traffic)" equal to "(Max_Trafic" divided by "Offered Traffic BH)*100"
|
499 |
gsm_analysis_df["TCH UTILIZATION (@Max Traffic)"] = (
|
500 |
gsm_analysis_df["Max_Traffic BH"] / gsm_analysis_df["Offered Traffic BH"]
|
501 |
) * 100
|
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)
|
@@ -530,4 +471,47 @@ def analyze_gsm_data(
|
|
530 |
gsm_analysis_df["Target TRXs"] - gsm_analysis_df["number_trx_per_cell"]
|
531 |
)
|
532 |
|
533 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
from utils.convert_to_excel import convert_dfs, save_dataframe
|
7 |
from utils.kpi_analysis_utils import (
|
8 |
GsmAnalysis,
|
9 |
+
analyze_sdcch_call_blocking,
|
10 |
+
analyze_tch_abis_fails,
|
11 |
+
analyze_tch_call_blocking,
|
12 |
cell_availability_analysis,
|
13 |
combine_comments,
|
14 |
create_daily_date,
|
|
|
77 |
"max_sdcch_real_blocking_bh",
|
78 |
"avg_sdcch_real_blocking_bh",
|
79 |
"number_of_days_with_sdcch_blocking_exceeded_bh",
|
80 |
+
"tch_call_blocking_bh_comment",
|
81 |
+
"sdcch_real_blocking_bh_comment",
|
82 |
]
|
83 |
|
84 |
+
DAILY_COLUMNS_FOR_CAPACITY = [
|
85 |
+
"Average_cell_availability",
|
86 |
+
"number_of_days_exceeding_threshold",
|
87 |
+
"availability_comment",
|
88 |
+
"avg_tch_abis_fail_daily",
|
89 |
+
"max_tch_abis_fail_daily",
|
90 |
+
"number_of_days_with_tch_abis_fail_exceeded_daily",
|
91 |
+
"tch_abis_fail_daily_comment",
|
92 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
|
95 |
def bh_traffic_analysis(
|
|
|
98 |
) -> pd.DataFrame:
|
99 |
|
100 |
result_df = df.copy()
|
101 |
+
last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
|
102 |
# last_days_df = last_days_df.fillna(0)
|
103 |
|
104 |
result_df["Avg_Traffic BH"] = last_days_df.mean(axis=1).round(2)
|
|
|
190 |
number_of_kpi_days=number_of_kpi_days,
|
191 |
tch_blocking_threshold=tch_blocking_threshold,
|
192 |
sdcch_blocking_threshold=sdcch_blocking_threshold,
|
193 |
+
number_of_threshold_days=number_of_threshold_days,
|
194 |
)
|
195 |
|
196 |
bh_df_for_capacity = df.copy()
|
|
|
325 |
sdcch_blocking_threshold=sdcch_blocking_threshold,
|
326 |
tch_blocking_threshold=tch_blocking_threshold,
|
327 |
)
|
328 |
+
daily_df_for_capacity = df.copy()
|
329 |
+
daily_df_for_capacity = daily_df_for_capacity[DAILY_COLUMNS_FOR_CAPACITY]
|
330 |
+
daily_df_for_capacity = daily_df_for_capacity.reset_index()
|
331 |
+
|
332 |
+
if isinstance(daily_df_for_capacity.columns, pd.MultiIndex):
|
333 |
+
daily_df_for_capacity.columns = [
|
334 |
+
"_".join([str(el) for el in col if el])
|
335 |
+
for col in daily_df_for_capacity.columns.values
|
336 |
+
]
|
337 |
+
# Rename "BTS_name" to "name"
|
338 |
+
daily_df_for_capacity = daily_df_for_capacity.rename(columns={"BTS_name": "name"})
|
339 |
+
|
340 |
+
return daily_df_for_capacity, df
|
341 |
|
342 |
|
343 |
def get_gsm_databases(dump_path: str) -> pd.DataFrame:
|
|
|
389 |
tch_abis_fails_threshold: int,
|
390 |
sdcch_blocking_threshold: float,
|
391 |
tch_blocking_threshold: float,
|
392 |
+
max_traffic_threshold: int,
|
393 |
):
|
394 |
|
395 |
+
daily_kpi_dfs: pd.DataFrame = analyse_daily_data(
|
396 |
daily_report_path=daily_report_path,
|
397 |
number_of_kpi_days=number_of_kpi_days,
|
398 |
availability_threshold=availability_threshold,
|
|
|
415 |
bh_kpi_df = bh_kpi_dfs[0]
|
416 |
bh_kpi_full_df = bh_kpi_dfs[1]
|
417 |
|
418 |
+
daily_kpi_df = daily_kpi_dfs[0]
|
419 |
+
daily_kpi_full_df = daily_kpi_dfs[1]
|
420 |
+
|
421 |
gsm_analysis_df = gsm_database_df.merge(bh_kpi_df, on="name", how="left")
|
422 |
+
gsm_analysis_df = gsm_analysis_df.merge(daily_kpi_df, on="name", how="left")
|
423 |
|
424 |
# "TCH UTILIZATION (@Max Traffic)" equal to "(Max_Trafic" divided by "Offered Traffic BH)*100"
|
425 |
gsm_analysis_df["TCH UTILIZATION (@Max Traffic)"] = (
|
426 |
gsm_analysis_df["Max_Traffic BH"] / gsm_analysis_df["Offered Traffic BH"]
|
427 |
) * 100
|
428 |
|
429 |
+
# Add column "Tch utilization comments" : if "TCH UTILIZATION (@Max Traffic)" exceeded it's threshold then "Tch utilization exceeded threshold else None
|
430 |
+
gsm_analysis_df["Tch utilization comments"] = np.where(
|
431 |
+
gsm_analysis_df["TCH UTILIZATION (@Max Traffic)"] > max_traffic_threshold,
|
432 |
+
"Tch utilization exceeded threshold",
|
433 |
+
None,
|
434 |
+
)
|
435 |
+
# Add "BH Congestion status" : concatenate "Tch utilization comments" + "tch_call_blocking_bh_comment" + "sdcch_real_blocking_bh_comment"
|
436 |
+
gsm_analysis_df = combine_comments(
|
437 |
+
gsm_analysis_df,
|
438 |
+
"Tch utilization comments",
|
439 |
+
"tch_call_blocking_bh_comment",
|
440 |
+
"sdcch_real_blocking_bh_comment",
|
441 |
+
new_column="BH Congestion status",
|
442 |
+
)
|
443 |
+
|
444 |
# Add "ERLANGB value" =MAX TRAFFIC/(1-(MAX TCH call blocking/200))
|
445 |
gsm_analysis_df["ErlabngB_value"] = gsm_analysis_df["Max_Traffic BH"] / (
|
446 |
1 - (gsm_analysis_df["max_tch_call_blocking_bh"] / 200)
|
|
|
471 |
gsm_analysis_df["Target TRXs"] - gsm_analysis_df["number_trx_per_cell"]
|
472 |
)
|
473 |
|
474 |
+
# if "availability_comment" equal to "Down Site" then "Down Site"
|
475 |
+
# if "availability_comment" is not "Availability OK" and "tch_abis_fail_daily_comment" equal to "tch abis fail exceeded threshold" then "Availability and TX issues"
|
476 |
+
# if "availability_comment" is not "Availability OK" and "tch_abis_fail_daily_comment" is empty then "Availability issues"
|
477 |
+
# if "availability_comment" is "Availability OK" and "tch_abis_fail_daily_comment" equal to "tch abis fail exceeded threshold" then "TX issues"
|
478 |
+
# Else "Operational is OK"
|
479 |
+
gsm_analysis_df["operational_comment"] = np.select(
|
480 |
+
[
|
481 |
+
gsm_analysis_df["availability_comment"] == "Down Site", # 1
|
482 |
+
(gsm_analysis_df["availability_comment"] != "Availability OK")
|
483 |
+
& (
|
484 |
+
gsm_analysis_df["tch_abis_fail_daily_comment"]
|
485 |
+
== "tch abis fail exceeded threshold"
|
486 |
+
), # 2
|
487 |
+
(gsm_analysis_df["availability_comment"] != "Availability OK")
|
488 |
+
& pd.isna(gsm_analysis_df["tch_abis_fail_daily_comment"]), # 3
|
489 |
+
(gsm_analysis_df["availability_comment"] == "Availability OK")
|
490 |
+
& (
|
491 |
+
gsm_analysis_df["tch_abis_fail_daily_comment"]
|
492 |
+
== "tch abis fail exceeded threshold"
|
493 |
+
), # 4
|
494 |
+
],
|
495 |
+
[
|
496 |
+
"Down Site", # 1
|
497 |
+
"Availability and TX issues", # 2
|
498 |
+
"Availability issues", # 3
|
499 |
+
"TX issues", # 4
|
500 |
+
],
|
501 |
+
default="Operational is OK",
|
502 |
+
)
|
503 |
+
|
504 |
+
# Add "Final comment" with "BH Congestion status" + "operational_comment"
|
505 |
+
gsm_analysis_df = combine_comments(
|
506 |
+
gsm_analysis_df,
|
507 |
+
"BH Congestion status",
|
508 |
+
"operational_comment",
|
509 |
+
new_column="Final comment",
|
510 |
+
)
|
511 |
+
|
512 |
+
# Replace 'nan, nan, Availability issues' with None
|
513 |
+
# gsm_analysis_df["Final comment"] = gsm_analysis_df["Final comment"].replace(
|
514 |
+
# "nan, nan, Availability issues", None
|
515 |
+
# )
|
516 |
+
|
517 |
+
return [gsm_analysis_df, bh_kpi_full_df, daily_kpi_full_df]
|
utils/kpi_analysis_utils.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import pandas as pd
|
2 |
|
3 |
|
@@ -230,7 +231,7 @@ def combine_comments(df: pd.DataFrame, *columns: str, new_column: str) -> pd.Dat
|
|
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(
|
@@ -384,3 +385,110 @@ def cell_availability_analysis(
|
|
384 |
)
|
385 |
|
386 |
return result_df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
import pandas as pd
|
3 |
|
4 |
|
|
|
231 |
"""
|
232 |
result_df = df.copy()
|
233 |
result_df[new_column] = result_df[list(columns)].apply(
|
234 |
+
lambda row: ", ".join([str(x) for x in row if x]), axis=1
|
235 |
)
|
236 |
# Trim all trailing commas
|
237 |
result_df[new_column] = result_df[new_column].str.replace(
|
|
|
385 |
)
|
386 |
|
387 |
return result_df
|
388 |
+
|
389 |
+
|
390 |
+
def analyze_tch_abis_fails(
|
391 |
+
df: pd.DataFrame,
|
392 |
+
number_of_kpi_days: int,
|
393 |
+
analysis_type: str,
|
394 |
+
number_of_threshold_days: int,
|
395 |
+
tch_abis_fails_threshold: int,
|
396 |
+
) -> pd.DataFrame:
|
397 |
+
|
398 |
+
result_df = df.copy()
|
399 |
+
last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
|
400 |
+
# last_days_df = last_days_df.fillna(0)
|
401 |
+
|
402 |
+
result_df[f"avg_tch_abis_fail_{analysis_type.lower()}"] = last_days_df.mean(
|
403 |
+
axis=1
|
404 |
+
).round(2)
|
405 |
+
result_df[f"max_tch_abis_fail_{analysis_type.lower()}"] = last_days_df.max(axis=1)
|
406 |
+
# Count the number of days above threshold
|
407 |
+
result_df[f"number_of_days_with_tch_abis_fail_exceeded_{analysis_type.lower()}"] = (
|
408 |
+
last_days_df.apply(
|
409 |
+
lambda row: sum(1 for x in row if x >= tch_abis_fails_threshold), axis=1
|
410 |
+
)
|
411 |
+
)
|
412 |
+
|
413 |
+
# Add the daily_tch_comment : if number_of_days_with_tch_abis_fail_exceeded_daily is >= number_of_threshold_days : tch abis fail exceeded threshold , else : None
|
414 |
+
result_df[f"tch_abis_fail_{analysis_type.lower()}_comment"] = np.where(
|
415 |
+
result_df[f"number_of_days_with_tch_abis_fail_exceeded_{analysis_type.lower()}"]
|
416 |
+
>= number_of_threshold_days,
|
417 |
+
"tch abis fail exceeded threshold",
|
418 |
+
None,
|
419 |
+
)
|
420 |
+
|
421 |
+
return result_df
|
422 |
+
|
423 |
+
|
424 |
+
def analyze_tch_call_blocking(
|
425 |
+
df: pd.DataFrame,
|
426 |
+
number_of_kpi_days: int,
|
427 |
+
analysis_type: str,
|
428 |
+
number_of_threshold_days: int,
|
429 |
+
tch_blocking_threshold: int,
|
430 |
+
) -> pd.DataFrame:
|
431 |
+
|
432 |
+
result_df = df.copy()
|
433 |
+
last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
|
434 |
+
# last_days_df = last_days_df.fillna(0)
|
435 |
+
|
436 |
+
result_df[f"avg_tch_call_blocking_{analysis_type.lower()}"] = last_days_df.mean(
|
437 |
+
axis=1
|
438 |
+
).round(2)
|
439 |
+
result_df[f"max_tch_call_blocking_{analysis_type.lower()}"] = last_days_df.max(
|
440 |
+
axis=1
|
441 |
+
)
|
442 |
+
# Count the number of days above threshold
|
443 |
+
result_df[f"number_of_days_with_tch_blocking_exceeded_{analysis_type.lower()}"] = (
|
444 |
+
last_days_df.apply(
|
445 |
+
lambda row: sum(1 for x in row if x >= tch_blocking_threshold), axis=1
|
446 |
+
)
|
447 |
+
)
|
448 |
+
|
449 |
+
# Add the daily_tch_comment : if number_of_days_with_tch_blocking_exceeded_daily is >= number_of_threshold_days : tch blocking exceeded threshold , else : None
|
450 |
+
result_df[f"tch_call_blocking_{analysis_type.lower()}_comment"] = np.where(
|
451 |
+
result_df[f"number_of_days_with_tch_blocking_exceeded_{analysis_type.lower()}"]
|
452 |
+
>= number_of_threshold_days,
|
453 |
+
"TCH blocking exceeded threshold",
|
454 |
+
None,
|
455 |
+
)
|
456 |
+
return result_df
|
457 |
+
|
458 |
+
|
459 |
+
def analyze_sdcch_call_blocking(
|
460 |
+
df: pd.DataFrame,
|
461 |
+
number_of_kpi_days: int,
|
462 |
+
sdcch_blocking_threshold: int,
|
463 |
+
analysis_type: str,
|
464 |
+
number_of_threshold_days: int,
|
465 |
+
) -> pd.DataFrame:
|
466 |
+
|
467 |
+
result_df = df.copy()
|
468 |
+
last_days_df: pd.DataFrame = result_df.iloc[:, -number_of_kpi_days:]
|
469 |
+
# last_days_df = last_days_df.fillna(0)
|
470 |
+
|
471 |
+
result_df[f"avg_sdcch_real_blocking_{analysis_type.lower()}"] = last_days_df.mean(
|
472 |
+
axis=1
|
473 |
+
).round(2)
|
474 |
+
result_df[f"max_sdcch_real_blocking_{analysis_type.lower()}"] = last_days_df.max(
|
475 |
+
axis=1
|
476 |
+
)
|
477 |
+
# Count the number of days above threshold
|
478 |
+
result_df[
|
479 |
+
f"number_of_days_with_sdcch_blocking_exceeded_{analysis_type.lower()}"
|
480 |
+
] = last_days_df.apply(
|
481 |
+
lambda row: sum(1 for x in row if x >= sdcch_blocking_threshold), axis=1
|
482 |
+
)
|
483 |
+
|
484 |
+
# add daily_sdcch_comment : if number_of_days_with_sdcch_blocking_exceeded_daily is >= number_of_threshold_days : sdcch blocking exceeded threshold , else : None
|
485 |
+
result_df[f"sdcch_real_blocking_{analysis_type.lower()}_comment"] = np.where(
|
486 |
+
result_df[
|
487 |
+
f"number_of_days_with_sdcch_blocking_exceeded_{analysis_type.lower()}"
|
488 |
+
]
|
489 |
+
>= number_of_threshold_days,
|
490 |
+
"SDCCH blocking exceeded threshold",
|
491 |
+
None,
|
492 |
+
)
|
493 |
+
|
494 |
+
return result_df
|