datupapi 1.108.10__py3-none-any.whl → 1.109.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datupapi/evaluate/anomaly.py +58 -27
- {datupapi-1.108.10.dist-info → datupapi-1.109.0.dist-info}/METADATA +2 -1
- {datupapi-1.108.10.dist-info → datupapi-1.109.0.dist-info}/RECORD +5 -5
- {datupapi-1.108.10.dist-info → datupapi-1.109.0.dist-info}/WHEEL +1 -1
- {datupapi-1.108.10.dist-info → datupapi-1.109.0.dist-info}/top_level.txt +0 -0
datupapi/evaluate/anomaly.py
CHANGED
|
@@ -271,6 +271,9 @@ class Anomaly():
|
|
|
271
271
|
if alert_anomalies_item else "")
|
|
272
272
|
|
|
273
273
|
# 3. Alerta por items con poco histórico -------------------
|
|
274
|
+
items_alerta_max = np.array(items_alerta_max).astype(str)
|
|
275
|
+
items_alerta_media = np.array(items_alerta_media).astype(str)
|
|
276
|
+
|
|
274
277
|
alert_insufficient_history = alerta_max > 0 or alerta_media > 0
|
|
275
278
|
alert_insufficient_history_txt = (
|
|
276
279
|
f"Items con menos de 24 meses en el histórico: {alerta_max}. ({', '.join(map(str, items_alerta_max))}). "
|
|
@@ -377,6 +380,7 @@ class Anomaly():
|
|
|
377
380
|
# Aplicar LOF
|
|
378
381
|
lof4.fit(item_tmp[['SuggestedForecast', 'Target']])
|
|
379
382
|
probs = lof4.predict_proba(item_tmp[['SuggestedForecast', 'Target']])
|
|
383
|
+
# item_tmp["probabilidad"] = probs[:, 1] #Agregando probabilidad de anomalía a los items
|
|
380
384
|
item_tmp = item_tmp.copy()
|
|
381
385
|
item_tmp.loc[:, "probabilidad"] = probs[:, 1]
|
|
382
386
|
is_out4 = probs[:, 1] > (prob_lim_item_frcst/100)
|
|
@@ -450,39 +454,57 @@ class Anomaly():
|
|
|
450
454
|
log_forecast = log_forecast.drop(columns=drop_columns)
|
|
451
455
|
print(f'Logs descargados y recortados a {log_forecast.shape[0]} ejecuciones.')
|
|
452
456
|
|
|
457
|
+
if log_forecast.shape[0] < 2:
|
|
458
|
+
print('Logs insuficientes. Las alertas dependientes de logs pasarán a estado nulo')
|
|
459
|
+
|
|
453
460
|
#10. Cambios en el número total de items ----------------------
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
461
|
+
if log_forecast.shape[0] > 1:
|
|
462
|
+
num_actual_items = demand_item['item_id'].nunique()
|
|
463
|
+
num_ant_items = log_forecast['Items'].iloc[-2]
|
|
464
|
+
cambio_num_items = ((num_actual_items - num_ant_items) / num_ant_items) * 100
|
|
457
465
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
466
|
+
#Alerta
|
|
467
|
+
alert_items_var = True if abs(cambio_num_items) > 10 else False
|
|
468
|
+
alert_items_var_txt = f"Variación en items: {cambio_num_items:.2f}%." if alert_items_var else ""
|
|
469
|
+
print(f"El número de ítems únicos ha cambiado en un {cambio_num_items:.2f}%.")
|
|
462
470
|
|
|
463
|
-
#11. Cambios en el número total de ubicaciones -------------- DE UBICACIÓN
|
|
464
|
-
if location:
|
|
465
|
-
num_actual_loc = df_demand['location'].nunique()
|
|
466
|
-
num_ant_loc = log_forecast['Locations'].iloc[-2]
|
|
467
|
-
cambio_num_loc = ((num_actual_loc - num_ant_loc) / num_ant_loc) * 100
|
|
468
471
|
else:
|
|
469
|
-
|
|
472
|
+
alert_items_var = False
|
|
473
|
+
alert_items_var_txt = 'Nulo'
|
|
470
474
|
|
|
471
|
-
#
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
#11. Cambios en el número total de ubicaciones -------------- DE UBICACIÓN
|
|
476
|
+
if log_forecast.shape[0] > 1:
|
|
477
|
+
if location:
|
|
478
|
+
num_actual_loc = df_demand['location'].nunique()
|
|
479
|
+
num_ant_loc = log_forecast['Locations'].iloc[-2]
|
|
480
|
+
cambio_num_loc = ((num_actual_loc - num_ant_loc) / num_ant_loc) * 100
|
|
481
|
+
else:
|
|
482
|
+
cambio_num_loc = 0
|
|
483
|
+
|
|
484
|
+
#Alerta
|
|
485
|
+
alert_loc_var = True if abs(cambio_num_loc) > limite_cambio_loc else False
|
|
486
|
+
alert_loc_var_txt = f"Variación en ubicaciones: {cambio_num_loc:.2f}%." if alert_loc_var else ""
|
|
487
|
+
print(f"El número de ubicaciones únicos ha cambiado en un {cambio_num_loc:.2f}%.")
|
|
488
|
+
|
|
489
|
+
else:
|
|
490
|
+
alert_loc_var = False
|
|
491
|
+
alert_loc_var_txt = 'Nulo'
|
|
475
492
|
|
|
476
493
|
##12. Aumento en el WMAPE
|
|
477
|
-
|
|
478
|
-
|
|
494
|
+
if log_forecast.shape[0] > 1:
|
|
495
|
+
wmape_actual = log_forecast['WMAPE'].iloc[-1]
|
|
496
|
+
wmape_anterior = log_forecast['WMAPE'].iloc[-2]
|
|
479
497
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
498
|
+
#Alerta
|
|
499
|
+
alert_wmape_var = True if wmape_anterior < wmape_actual else False
|
|
500
|
+
alert_wmape_var_txt = ((f"El error promedio aumentó de {wmape_anterior:.2f}% a {wmape_actual:.2f}%") if alert_wmape_var
|
|
501
|
+
else (f"El error promedio disminuyó de {wmape_anterior:.2f}% a {wmape_actual:.2f}%"))
|
|
502
|
+
print(f"El error pasó de {wmape_anterior:.2f}% a {wmape_actual:.2f}%")
|
|
503
|
+
print(alert_wmape_var_txt)
|
|
504
|
+
|
|
505
|
+
else:
|
|
506
|
+
alert_wmape_var = False
|
|
507
|
+
alert_wmape_var_txt = 'Nulo'
|
|
486
508
|
|
|
487
509
|
##13. Fecha de corte
|
|
488
510
|
fecha_corte = log_forecast['DateDataPrep'].iloc[-1]
|
|
@@ -497,7 +519,7 @@ class Anomaly():
|
|
|
497
519
|
|
|
498
520
|
#####--------------------Creción tabla resumen
|
|
499
521
|
|
|
500
|
-
|
|
522
|
+
|
|
501
523
|
if location:
|
|
502
524
|
# Creo tabla para lo items con baja precisión
|
|
503
525
|
tabla = items_baja_precision[['Item', 'Location','ItemType', 'Ranking', 'AccuracyBestFit']]
|
|
@@ -516,7 +538,7 @@ class Anomaly():
|
|
|
516
538
|
tabla = tabla.merge(historico_meses, left_on=['Item', 'Location'], right_on=['item_id', 'location'], how='left')
|
|
517
539
|
tabla.drop(columns=['item_id', 'location'], inplace=True, errors='ignore')
|
|
518
540
|
tabla['Historico (Meses)'] = tabla.set_index(['Item', 'Location']).index.map(df_demand.groupby(['item_id', 'location']).size())
|
|
519
|
-
|
|
541
|
+
|
|
520
542
|
else:
|
|
521
543
|
# Creo tabla para lo items con baja precisión
|
|
522
544
|
tabla = items_baja_precision[['Item','ItemType', 'Ranking', 'AccuracyBestFit']]
|
|
@@ -534,8 +556,15 @@ class Anomaly():
|
|
|
534
556
|
tabla.drop(columns=['item_id'], inplace=True, errors='ignore')
|
|
535
557
|
tabla['Historico (Meses)'] = tabla.set_index(['Item']).index.map(df_demand.groupby(['item_id']).size())
|
|
536
558
|
|
|
559
|
+
|
|
537
560
|
#Añadir alertas LOF
|
|
561
|
+
out_if_item2['item_id'] = out_if_item2['item_id'].astype(str)
|
|
562
|
+
out_if_item4['Item'] = out_if_item4['Item'].astype(str)
|
|
563
|
+
|
|
538
564
|
def obtener_alerta(item):
|
|
565
|
+
|
|
566
|
+
item = str(item)
|
|
567
|
+
|
|
539
568
|
# Filtrar por item
|
|
540
569
|
alerta_prep = out_if_item2[out_if_item2['item_id'] == item][['timestamp', 'probabilidad']].copy()
|
|
541
570
|
alerta_forecast = out_if_item4[out_if_item4['Item'] == item][['Date', 'probabilidad']].copy()
|
|
@@ -554,6 +583,8 @@ class Anomaly():
|
|
|
554
583
|
return alertas.to_dict(orient='records') if not alertas.empty else None
|
|
555
584
|
|
|
556
585
|
tabla['AlertaAnomalia'] = tabla['Item'].apply(obtener_alerta)
|
|
586
|
+
items_con_alerta = set(out_if_item2['item_id']).union(set(out_if_item4['Item']))
|
|
587
|
+
print(f'Items con baja precisión con alertas de anomalías: {items_con_alerta}')
|
|
557
588
|
|
|
558
589
|
#---------------------Cargando tabla resumen
|
|
559
590
|
self.io.upload_csv(tabla, q_name='ResumenAlertas', datalake_path='output')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datupapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.109.0
|
|
4
4
|
Summary: Utility library to support Datup AI MLOps processes
|
|
5
5
|
Author: Datup AI
|
|
6
6
|
Author-email: ramiro@datup.ai
|
|
@@ -24,6 +24,7 @@ Requires-Dist: pulp>=2.7.0
|
|
|
24
24
|
Requires-Dist: pyarrow>=10.0.1
|
|
25
25
|
Requires-Dist: pymssql>=2.2.7
|
|
26
26
|
Requires-Dist: pymysql>=1.0.2
|
|
27
|
+
Requires-Dist: pyod>=2.0.2
|
|
27
28
|
Requires-Dist: pytest>=6.2.1
|
|
28
29
|
Requires-Dist: python-dateutil>=2.8.2
|
|
29
30
|
Requires-Dist: pyyaml>=5.3.1
|
|
@@ -7,7 +7,7 @@ datupapi/distribution/src/DistributionFunctions/functions_distribution.py,sha256
|
|
|
7
7
|
datupapi/distribution/src/Format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
datupapi/distribution/src/Format/distribution_format.py,sha256=CFqUHTk9StDvaOvlR3yLr3NZiFY2Ao1yVXoY-IsrNWE,3964
|
|
9
9
|
datupapi/evaluate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
datupapi/evaluate/anomaly.py,sha256=
|
|
10
|
+
datupapi/evaluate/anomaly.py,sha256=fjIDAvEPGBJdZjVXhz7Rk90WKCR5t3Hbe6zeTKVXFlw,33506
|
|
11
11
|
datupapi/evaluate/errors.py,sha256=9SRYAjwRDfEdP1EnBbfA7zoQEi4xU4qI16vBE8-jkeA,7039
|
|
12
12
|
datupapi/extract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
datupapi/extract/io.py,sha256=fYPXf-SmYyw4ywbN3SjQsdl6qBQvQz1K3i9kbpiEkkA,84343
|
|
@@ -48,7 +48,7 @@ datupapi/transform/forecasting.py,sha256=OboiVyErzWXJAv6R4fCXiPNaoVp5dNAP9F53EDq
|
|
|
48
48
|
datupapi/transform/ranking.py,sha256=XOI0XqMx9Cy52Xjc4LCzJCNUsJZNjgrPky7nrpELr-U,7943
|
|
49
49
|
datupapi/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
datupapi/utils/utils.py,sha256=pU3mXPupm-1gvODI-kPlIpOdMHa2F9lEXvqBn6t3ajc,4637
|
|
51
|
-
datupapi-1.
|
|
52
|
-
datupapi-1.
|
|
53
|
-
datupapi-1.
|
|
54
|
-
datupapi-1.
|
|
51
|
+
datupapi-1.109.0.dist-info/METADATA,sha256=v2bRsQxBoCgZAOHxv9DvZ0jReqahpI-5Cr1XTwNYpR8,1516
|
|
52
|
+
datupapi-1.109.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
|
53
|
+
datupapi-1.109.0.dist-info/top_level.txt,sha256=oERwtRZu8xq2u1TDGwJwuWK0iJbH4p7x9kYECAL5So0,9
|
|
54
|
+
datupapi-1.109.0.dist-info/RECORD,,
|
|
File without changes
|