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
|