Spaces:
Runtime error
Runtime error
anomaly detection: iforest
Browse files- app/analyzer.py +75 -0
- app/service.py +52 -18
- pyproject.toml +1 -0
- uv.lock +86 -0
app/analyzer.py
CHANGED
@@ -8,6 +8,7 @@ from typing import Any
|
|
8 |
import numpy as np
|
9 |
import polars as pl
|
10 |
from loguru import logger
|
|
|
11 |
from sqlalchemy import Engine, create_engine, text
|
12 |
|
13 |
from data.get_mock import get_df
|
@@ -232,3 +233,77 @@ class TimeSeriesAnalyzer:
|
|
232 |
"end": timestamps[-1] if len(timestamps) > 0 else None,
|
233 |
},
|
234 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
import numpy as np
|
9 |
import polars as pl
|
10 |
from loguru import logger
|
11 |
+
from sklearn.ensemble import IsolationForest
|
12 |
from sqlalchemy import Engine, create_engine, text
|
13 |
|
14 |
from data.get_mock import get_df
|
|
|
233 |
"end": timestamps[-1] if len(timestamps) > 0 else None,
|
234 |
},
|
235 |
}
|
236 |
+
|
237 |
+
def detect_anomalies_isolation_forest(
|
238 |
+
self, data: pl.DataFrame, contamination: float = 0.1
|
239 |
+
) -> dict[str, Any]:
|
240 |
+
"""Detect anomalies in the time series data using Isolation Forest algorithm.
|
241 |
+
|
242 |
+
Args:
|
243 |
+
data: expect only 1 timeserie with columns datetime and value
|
244 |
+
contamination: expected proportion of anomalies in the data (default: 0.1)
|
245 |
+
|
246 |
+
Returns:
|
247 |
+
{
|
248 |
+
"anomalies_found": int,
|
249 |
+
"anomalies": list[dict[str, int]],
|
250 |
+
"statistics": {
|
251 |
+
"mean": float,
|
252 |
+
"std": float,
|
253 |
+
"min": float,
|
254 |
+
"max": float
|
255 |
+
}
|
256 |
+
|
257 |
+
"""
|
258 |
+
values = data["value"].to_numpy().reshape(-1, 1)
|
259 |
+
|
260 |
+
iso_forest = IsolationForest(
|
261 |
+
contamination=contamination, random_state=42, n_estimators=100
|
262 |
+
)
|
263 |
+
|
264 |
+
# Predict anomalies (-1 for anomalies, 1 for normal)
|
265 |
+
predictions = iso_forest.fit_predict(values)
|
266 |
+
|
267 |
+
anomaly_scores = -iso_forest.score_samples(values)
|
268 |
+
|
269 |
+
anomaly_mask = predictions == -1
|
270 |
+
|
271 |
+
mean_val = data["value"].mean()
|
272 |
+
std_val = data["value"].std()
|
273 |
+
|
274 |
+
logger.debug(f"anaomaly_mask: {anomaly_mask}")
|
275 |
+
logger.debug(f"anomaly_scores: {anomaly_scores}")
|
276 |
+
|
277 |
+
logger.debug(
|
278 |
+
pl.Series(anomaly_scores)
|
279 |
+
.filter(anomaly_mask)
|
280 |
+
.alias("anomaly_score"),
|
281 |
+
)
|
282 |
+
# Prepare anomalies data
|
283 |
+
anomalies = (
|
284 |
+
data.select(
|
285 |
+
data["timestamp"].cast(pl.Utf8).alias("timestamp"),
|
286 |
+
data["value"].cast(pl.Float64),
|
287 |
+
pl.Series(anomaly_scores).alias("anomaly_score"),
|
288 |
+
pl.Series(anomaly_scores > np.percentile(anomaly_scores, 90))
|
289 |
+
.cast(pl.Utf8)
|
290 |
+
.alias("severity")
|
291 |
+
.map_elements(
|
292 |
+
lambda x: "high" if x else "medium",
|
293 |
+
return_dtype=pl.String,
|
294 |
+
),
|
295 |
+
)
|
296 |
+
.filter(anomaly_mask)
|
297 |
+
.to_dicts()
|
298 |
+
)
|
299 |
+
logger.debug(f"anomalies: {anomalies}")
|
300 |
+
return {
|
301 |
+
"anomalies_found": len(anomalies),
|
302 |
+
"anomalies": anomalies,
|
303 |
+
"statistics": {
|
304 |
+
"mean": mean_val,
|
305 |
+
"std": std_val,
|
306 |
+
"min": data["value"].min(),
|
307 |
+
"max": data["value"].max(),
|
308 |
+
},
|
309 |
+
}
|
app/service.py
CHANGED
@@ -58,14 +58,41 @@ def query_timeseries(sensor_id: str, start_time: str, end_time: str) -> str:
|
|
58 |
|
59 |
|
60 |
def detect_anomalies(
|
61 |
-
sensor_id: str,
|
|
|
|
|
|
|
|
|
|
|
62 |
) -> str:
|
63 |
-
"""Detect anomalies in the time series data for a specific sensor.
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
if not ensure_analyzer_connected():
|
66 |
return json.dumps({"error": "Database not connected"})
|
67 |
data = analyzer.query_metrics(sensor_id, start_time, end_time)
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
return json.dumps(anomalies, indent=2)
|
70 |
|
71 |
|
@@ -103,25 +130,25 @@ def generate_analysis_report(
|
|
103 |
include_anomalies: bool = True,
|
104 |
include_trends: bool = True,
|
105 |
user_question: str | None = None,
|
|
|
|
|
|
|
106 |
) -> str:
|
107 |
"""Generate a comprehensive analysis report for a specific sensor.
|
108 |
|
109 |
-
This function creates a detailed report containing information about the sensor data,
|
110 |
-
including optional trend analysis and anomaly detection sections based on the parameters.
|
111 |
-
|
112 |
Args:
|
113 |
-
sensor_id
|
114 |
-
start_time
|
115 |
-
end_time
|
116 |
-
include_anomalies
|
117 |
-
include_trends
|
118 |
-
user_question
|
|
|
|
|
|
|
119 |
|
120 |
Returns:
|
121 |
-
str: A formatted string containing the comprehensive analysis report
|
122 |
-
|
123 |
-
Note:
|
124 |
-
Returns an error message if the database is not connected.
|
125 |
|
126 |
"""
|
127 |
logger.info("generate report")
|
@@ -148,8 +175,15 @@ def generate_analysis_report(
|
|
148 |
)
|
149 |
report_sections.append(f"- **End Value:** {trends['end_value']:.2f}")
|
150 |
if include_anomalies:
|
151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
report_sections.append("\n### Anomaly Detection")
|
|
|
153 |
report_sections.append(
|
154 |
f"- **Anomalies Found:** {anomalies['anomalies_found']}"
|
155 |
)
|
|
|
58 |
|
59 |
|
60 |
def detect_anomalies(
|
61 |
+
sensor_id: str,
|
62 |
+
start_time: str,
|
63 |
+
end_time: str,
|
64 |
+
threshold: float = 2.0,
|
65 |
+
algorithm: str = "zscore",
|
66 |
+
contamination: float = 0.1,
|
67 |
) -> str:
|
68 |
+
"""Detect anomalies in the time series data for a specific sensor.
|
69 |
+
|
70 |
+
Args:
|
71 |
+
sensor_id: The identifier of the sensor to analyze
|
72 |
+
start_time: The start time of the analysis period in ISO format
|
73 |
+
end_time: The end time of the analysis period in ISO format
|
74 |
+
threshold: Threshold for z-score based detection (default: 2.0)
|
75 |
+
algorithm: Algorithm to use for detection ("zscore" or "isolation_forest")
|
76 |
+
contamination: Expected proportion of anomalies for isolation forest (default: 0.1)
|
77 |
+
|
78 |
+
Returns:
|
79 |
+
str: JSON string containing anomaly detection results
|
80 |
+
|
81 |
+
"""
|
82 |
+
logger.info(f"detect anomalies using {algorithm}")
|
83 |
if not ensure_analyzer_connected():
|
84 |
return json.dumps({"error": "Database not connected"})
|
85 |
data = analyzer.query_metrics(sensor_id, start_time, end_time)
|
86 |
+
|
87 |
+
if algorithm == "zscore":
|
88 |
+
anomalies = analyzer.detect_anomalies(data, threshold)
|
89 |
+
elif algorithm == "isolation_forest":
|
90 |
+
anomalies = analyzer.detect_anomalies_isolation_forest(
|
91 |
+
data, contamination
|
92 |
+
)
|
93 |
+
else:
|
94 |
+
return json.dumps({"error": f"Unknown algorithm: {algorithm}"})
|
95 |
+
|
96 |
return json.dumps(anomalies, indent=2)
|
97 |
|
98 |
|
|
|
130 |
include_anomalies: bool = True,
|
131 |
include_trends: bool = True,
|
132 |
user_question: str | None = None,
|
133 |
+
anomaly_algorithm: str = "zscore",
|
134 |
+
anomaly_threshold: float = 2.0,
|
135 |
+
anomaly_contamination: float = 0.1,
|
136 |
) -> str:
|
137 |
"""Generate a comprehensive analysis report for a specific sensor.
|
138 |
|
|
|
|
|
|
|
139 |
Args:
|
140 |
+
sensor_id: The identifier of the sensor to analyze
|
141 |
+
start_time: The start time of the analysis period in ISO format
|
142 |
+
end_time: The end time of the analysis period in ISO format
|
143 |
+
include_anomalies: Whether to include anomaly detection in the report
|
144 |
+
include_trends: Whether to include trend analysis in the report
|
145 |
+
user_question: A specific question from the user to be included in the report
|
146 |
+
anomaly_algorithm: Algorithm to use for anomaly detection ("zscore" or "isolation_forest")
|
147 |
+
anomaly_threshold: Threshold for z-score based detection
|
148 |
+
anomaly_contamination: Expected proportion of anomalies for isolation forest
|
149 |
|
150 |
Returns:
|
151 |
+
str: A formatted string containing the comprehensive analysis report
|
|
|
|
|
|
|
152 |
|
153 |
"""
|
154 |
logger.info("generate report")
|
|
|
175 |
)
|
176 |
report_sections.append(f"- **End Value:** {trends['end_value']:.2f}")
|
177 |
if include_anomalies:
|
178 |
+
if anomaly_algorithm == "zscore":
|
179 |
+
anomalies = analyzer.detect_anomalies(data, anomaly_threshold)
|
180 |
+
else:
|
181 |
+
anomalies = analyzer.detect_anomalies_isolation_forest(
|
182 |
+
data, anomaly_contamination
|
183 |
+
)
|
184 |
+
|
185 |
report_sections.append("\n### Anomaly Detection")
|
186 |
+
report_sections.append(f"- **Algorithm:** {anomaly_algorithm}")
|
187 |
report_sections.append(
|
188 |
f"- **Anomalies Found:** {anomalies['anomalies_found']}"
|
189 |
)
|
pyproject.toml
CHANGED
@@ -13,6 +13,7 @@ dependencies = [
|
|
13 |
"polars>=1.30.0",
|
14 |
"psycopg2-binary>=2.9.10",
|
15 |
"pyarrow>=20.0.0",
|
|
|
16 |
"sqlalchemy>=2.0.41",
|
17 |
]
|
18 |
|
|
|
13 |
"polars>=1.30.0",
|
14 |
"psycopg2-binary>=2.9.10",
|
15 |
"pyarrow>=20.0.0",
|
16 |
+
"scikit-learn>=1.7.0",
|
17 |
"sqlalchemy>=2.0.41",
|
18 |
]
|
19 |
|
uv.lock
CHANGED
@@ -415,6 +415,15 @@ wheels = [
|
|
415 |
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
416 |
]
|
417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
[[package]]
|
419 |
name = "loguru"
|
420 |
version = "0.7.3"
|
@@ -966,6 +975,72 @@ wheels = [
|
|
966 |
{ url = "https://files.pythonhosted.org/packages/4d/c0/1108ad9f01567f66b3154063605b350b69c3c9366732e09e45f9fd0d1deb/safehttpx-0.1.6-py3-none-any.whl", hash = "sha256:407cff0b410b071623087c63dd2080c3b44dc076888d8c5823c00d1e58cb381c", size = 8692, upload-time = "2024-12-02T18:44:08.555Z" },
|
967 |
]
|
968 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
969 |
[[package]]
|
970 |
name = "semantic-version"
|
971 |
version = "2.10.0"
|
@@ -1055,6 +1130,15 @@ wheels = [
|
|
1055 |
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" },
|
1056 |
]
|
1057 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1058 |
[[package]]
|
1059 |
name = "tomlkit"
|
1060 |
version = "0.13.2"
|
@@ -1089,6 +1173,7 @@ dependencies = [
|
|
1089 |
{ name = "polars" },
|
1090 |
{ name = "psycopg2-binary" },
|
1091 |
{ name = "pyarrow" },
|
|
|
1092 |
{ name = "sqlalchemy" },
|
1093 |
]
|
1094 |
|
@@ -1102,6 +1187,7 @@ requires-dist = [
|
|
1102 |
{ name = "polars", specifier = ">=1.30.0" },
|
1103 |
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
1104 |
{ name = "pyarrow", specifier = ">=20.0.0" },
|
|
|
1105 |
{ name = "sqlalchemy", specifier = ">=2.0.41" },
|
1106 |
]
|
1107 |
|
|
|
415 |
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
416 |
]
|
417 |
|
418 |
+
[[package]]
|
419 |
+
name = "joblib"
|
420 |
+
version = "1.5.1"
|
421 |
+
source = { registry = "https://pypi.org/simple" }
|
422 |
+
sdist = { url = "https://files.pythonhosted.org/packages/dc/fe/0f5a938c54105553436dbff7a61dc4fed4b1b2c98852f8833beaf4d5968f/joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444", size = 330475, upload-time = "2025-05-23T12:04:37.097Z" }
|
423 |
+
wheels = [
|
424 |
+
{ url = "https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a", size = 307746, upload-time = "2025-05-23T12:04:35.124Z" },
|
425 |
+
]
|
426 |
+
|
427 |
[[package]]
|
428 |
name = "loguru"
|
429 |
version = "0.7.3"
|
|
|
975 |
{ url = "https://files.pythonhosted.org/packages/4d/c0/1108ad9f01567f66b3154063605b350b69c3c9366732e09e45f9fd0d1deb/safehttpx-0.1.6-py3-none-any.whl", hash = "sha256:407cff0b410b071623087c63dd2080c3b44dc076888d8c5823c00d1e58cb381c", size = 8692, upload-time = "2024-12-02T18:44:08.555Z" },
|
976 |
]
|
977 |
|
978 |
+
[[package]]
|
979 |
+
name = "scikit-learn"
|
980 |
+
version = "1.7.0"
|
981 |
+
source = { registry = "https://pypi.org/simple" }
|
982 |
+
dependencies = [
|
983 |
+
{ name = "joblib" },
|
984 |
+
{ name = "numpy" },
|
985 |
+
{ name = "scipy" },
|
986 |
+
{ name = "threadpoolctl" },
|
987 |
+
]
|
988 |
+
sdist = { url = "https://files.pythonhosted.org/packages/df/3b/29fa87e76b1d7b3b77cc1fcbe82e6e6b8cd704410705b008822de530277c/scikit_learn-1.7.0.tar.gz", hash = "sha256:c01e869b15aec88e2cdb73d27f15bdbe03bce8e2fb43afbe77c45d399e73a5a3", size = 7178217, upload-time = "2025-06-05T22:02:46.703Z" }
|
989 |
+
wheels = [
|
990 |
+
{ url = "https://files.pythonhosted.org/packages/70/3a/bffab14e974a665a3ee2d79766e7389572ffcaad941a246931c824afcdb2/scikit_learn-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2c7243d34aaede0efca7a5a96d67fddaebb4ad7e14a70991b9abee9dc5c0379", size = 11646758, upload-time = "2025-06-05T22:02:09.51Z" },
|
991 |
+
{ url = "https://files.pythonhosted.org/packages/58/d8/f3249232fa79a70cb40595282813e61453c1e76da3e1a44b77a63dd8d0cb/scikit_learn-1.7.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:9f39f6a811bf3f15177b66c82cbe0d7b1ebad9f190737dcdef77cfca1ea3c19c", size = 10673971, upload-time = "2025-06-05T22:02:12.217Z" },
|
992 |
+
{ url = "https://files.pythonhosted.org/packages/67/93/eb14c50533bea2f77758abe7d60a10057e5f2e2cdcf0a75a14c6bc19c734/scikit_learn-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63017a5f9a74963d24aac7590287149a8d0f1a0799bbe7173c0d8ba1523293c0", size = 11818428, upload-time = "2025-06-05T22:02:14.947Z" },
|
993 |
+
{ url = "https://files.pythonhosted.org/packages/08/17/804cc13b22a8663564bb0b55fb89e661a577e4e88a61a39740d58b909efe/scikit_learn-1.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b2f8a0b1e73e9a08b7cc498bb2aeab36cdc1f571f8ab2b35c6e5d1c7115d97d", size = 12505887, upload-time = "2025-06-05T22:02:17.824Z" },
|
994 |
+
{ url = "https://files.pythonhosted.org/packages/68/c7/4e956281a077f4835458c3f9656c666300282d5199039f26d9de1dabd9be/scikit_learn-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:34cc8d9d010d29fb2b7cbcd5ccc24ffdd80515f65fe9f1e4894ace36b267ce19", size = 10668129, upload-time = "2025-06-05T22:02:20.536Z" },
|
995 |
+
{ url = "https://files.pythonhosted.org/packages/9a/c3/a85dcccdaf1e807e6f067fa95788a6485b0491d9ea44fd4c812050d04f45/scikit_learn-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5b7974f1f32bc586c90145df51130e02267e4b7e77cab76165c76cf43faca0d9", size = 11559841, upload-time = "2025-06-05T22:02:23.308Z" },
|
996 |
+
{ url = "https://files.pythonhosted.org/packages/d8/57/eea0de1562cc52d3196eae51a68c5736a31949a465f0b6bb3579b2d80282/scikit_learn-1.7.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:014e07a23fe02e65f9392898143c542a50b6001dbe89cb867e19688e468d049b", size = 10616463, upload-time = "2025-06-05T22:02:26.068Z" },
|
997 |
+
{ url = "https://files.pythonhosted.org/packages/10/a4/39717ca669296dfc3a62928393168da88ac9d8cbec88b6321ffa62c6776f/scikit_learn-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7e7ced20582d3a5516fb6f405fd1d254e1f5ce712bfef2589f51326af6346e8", size = 11766512, upload-time = "2025-06-05T22:02:28.689Z" },
|
998 |
+
{ url = "https://files.pythonhosted.org/packages/d5/cd/a19722241d5f7b51e08351e1e82453e0057aeb7621b17805f31fcb57bb6c/scikit_learn-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1babf2511e6ffd695da7a983b4e4d6de45dce39577b26b721610711081850906", size = 12461075, upload-time = "2025-06-05T22:02:31.233Z" },
|
999 |
+
{ url = "https://files.pythonhosted.org/packages/f3/bc/282514272815c827a9acacbe5b99f4f1a4bc5961053719d319480aee0812/scikit_learn-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:5abd2acff939d5bd4701283f009b01496832d50ddafa83c90125a4e41c33e314", size = 10652517, upload-time = "2025-06-05T22:02:34.139Z" },
|
1000 |
+
{ url = "https://files.pythonhosted.org/packages/ea/78/7357d12b2e4c6674175f9a09a3ba10498cde8340e622715bcc71e532981d/scikit_learn-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e39d95a929b112047c25b775035c8c234c5ca67e681ce60d12413afb501129f7", size = 12111822, upload-time = "2025-06-05T22:02:36.904Z" },
|
1001 |
+
{ url = "https://files.pythonhosted.org/packages/d0/0c/9c3715393343f04232f9d81fe540eb3831d0b4ec351135a145855295110f/scikit_learn-1.7.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:0521cb460426c56fee7e07f9365b0f45ec8ca7b2d696534ac98bfb85e7ae4775", size = 11325286, upload-time = "2025-06-05T22:02:39.739Z" },
|
1002 |
+
{ url = "https://files.pythonhosted.org/packages/64/e0/42282ad3dd70b7c1a5f65c412ac3841f6543502a8d6263cae7b466612dc9/scikit_learn-1.7.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:317ca9f83acbde2883bd6bb27116a741bfcb371369706b4f9973cf30e9a03b0d", size = 12380865, upload-time = "2025-06-05T22:02:42.137Z" },
|
1003 |
+
{ url = "https://files.pythonhosted.org/packages/4e/d0/3ef4ab2c6be4aa910445cd09c5ef0b44512e3de2cfb2112a88bb647d2cf7/scikit_learn-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:126c09740a6f016e815ab985b21e3a0656835414521c81fc1a8da78b679bdb75", size = 11549609, upload-time = "2025-06-05T22:02:44.483Z" },
|
1004 |
+
]
|
1005 |
+
|
1006 |
+
[[package]]
|
1007 |
+
name = "scipy"
|
1008 |
+
version = "1.15.3"
|
1009 |
+
source = { registry = "https://pypi.org/simple" }
|
1010 |
+
dependencies = [
|
1011 |
+
{ name = "numpy" },
|
1012 |
+
]
|
1013 |
+
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
|
1014 |
+
wheels = [
|
1015 |
+
{ url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" },
|
1016 |
+
{ url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" },
|
1017 |
+
{ url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" },
|
1018 |
+
{ url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" },
|
1019 |
+
{ url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" },
|
1020 |
+
{ url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" },
|
1021 |
+
{ url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" },
|
1022 |
+
{ url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" },
|
1023 |
+
{ url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" },
|
1024 |
+
{ url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" },
|
1025 |
+
{ url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" },
|
1026 |
+
{ url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" },
|
1027 |
+
{ url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" },
|
1028 |
+
{ url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" },
|
1029 |
+
{ url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" },
|
1030 |
+
{ url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" },
|
1031 |
+
{ url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" },
|
1032 |
+
{ url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" },
|
1033 |
+
{ url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" },
|
1034 |
+
{ url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" },
|
1035 |
+
{ url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" },
|
1036 |
+
{ url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" },
|
1037 |
+
{ url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" },
|
1038 |
+
{ url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" },
|
1039 |
+
{ url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" },
|
1040 |
+
{ url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" },
|
1041 |
+
{ url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" },
|
1042 |
+
]
|
1043 |
+
|
1044 |
[[package]]
|
1045 |
name = "semantic-version"
|
1046 |
version = "2.10.0"
|
|
|
1130 |
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037, upload-time = "2025-04-13T13:56:16.21Z" },
|
1131 |
]
|
1132 |
|
1133 |
+
[[package]]
|
1134 |
+
name = "threadpoolctl"
|
1135 |
+
version = "3.6.0"
|
1136 |
+
source = { registry = "https://pypi.org/simple" }
|
1137 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" }
|
1138 |
+
wheels = [
|
1139 |
+
{ url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
|
1140 |
+
]
|
1141 |
+
|
1142 |
[[package]]
|
1143 |
name = "tomlkit"
|
1144 |
version = "0.13.2"
|
|
|
1173 |
{ name = "polars" },
|
1174 |
{ name = "psycopg2-binary" },
|
1175 |
{ name = "pyarrow" },
|
1176 |
+
{ name = "scikit-learn" },
|
1177 |
{ name = "sqlalchemy" },
|
1178 |
]
|
1179 |
|
|
|
1187 |
{ name = "polars", specifier = ">=1.30.0" },
|
1188 |
{ name = "psycopg2-binary", specifier = ">=2.9.10" },
|
1189 |
{ name = "pyarrow", specifier = ">=20.0.0" },
|
1190 |
+
{ name = "scikit-learn", specifier = ">=1.7.0" },
|
1191 |
{ name = "sqlalchemy", specifier = ">=2.0.41" },
|
1192 |
]
|
1193 |
|