pbp-anomaly 0.2.0__tar.gz
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.
- pbp_anomaly-0.2.0/LICENSE +21 -0
- pbp_anomaly-0.2.0/PKG-INFO +181 -0
- pbp_anomaly-0.2.0/README.md +153 -0
- pbp_anomaly-0.2.0/pbp_anomaly/__init__.py +20 -0
- pbp_anomaly-0.2.0/pbp_anomaly/cli.py +104 -0
- pbp_anomaly-0.2.0/pbp_anomaly/detector.py +349 -0
- pbp_anomaly-0.2.0/pbp_anomaly/parsers.py +387 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/PKG-INFO +181 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/SOURCES.txt +15 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/dependency_links.txt +1 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/entry_points.txt +2 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/requires.txt +19 -0
- pbp_anomaly-0.2.0/pbp_anomaly.egg-info/top_level.txt +1 -0
- pbp_anomaly-0.2.0/pyproject.toml +38 -0
- pbp_anomaly-0.2.0/setup.cfg +4 -0
- pbp_anomaly-0.2.0/tests/test_detector.py +183 -0
- pbp_anomaly-0.2.0/tests/test_parsers.py +61 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tendai Chikake, Boris Goldengorin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pbp-anomaly
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Training-free anomaly detection via pseudo-Boolean polynomial decomposition
|
|
5
|
+
Author: Boris Goldengorin
|
|
6
|
+
Author-email: Tendai Chikake <tendaichikake@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Repository, https://github.com/Tenfleques/pbp-anomaly
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: numpy>=1.21
|
|
13
|
+
Requires-Dist: pandas>=1.3
|
|
14
|
+
Requires-Dist: scipy>=1.7
|
|
15
|
+
Requires-Dist: scikit-learn>=1.0
|
|
16
|
+
Requires-Dist: tmc-pbp>=0.1.0
|
|
17
|
+
Provides-Extra: noaa
|
|
18
|
+
Requires-Dist: requests; extra == "noaa"
|
|
19
|
+
Provides-Extra: figures
|
|
20
|
+
Requires-Dist: matplotlib>=3.5; extra == "figures"
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: requests; extra == "all"
|
|
25
|
+
Requires-Dist: matplotlib>=3.5; extra == "all"
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == "all"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# pbp-anomaly
|
|
30
|
+
|
|
31
|
+
Training-free anomaly detection in environmental sensor networks via pseudo-Boolean polynomial (PBP) decomposition.
|
|
32
|
+
|
|
33
|
+
Reference implementation of the PBP anomaly scoring described in:
|
|
34
|
+
|
|
35
|
+
> T. M. Chikake and B. Goldengorin, "Pseudo-Boolean polynomial anomaly scoring for intelligent multisensor environmental monitoring," in *International Conference on Advanced Sensing and Intelligent Systems (ICASIS 2026)*, Proc. SPIE 14309, 2026. https://doi.org/10.1117/12.3121130
|
|
36
|
+
|
|
37
|
+
The repository also contains the multi-dataset experiments of an extended study.
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install pbp-anomaly
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This also installs the PBP core, [`tmc-pbp`](https://pypi.org/project/tmc-pbp/) (imported as `pbp`).
|
|
46
|
+
|
|
47
|
+
## Quick start (reproducing the experiments)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/Tenfleques/pbp-anomaly.git
|
|
51
|
+
cd pbp-anomaly
|
|
52
|
+
pip install -e ".[all]"
|
|
53
|
+
|
|
54
|
+
# Download large datasets (Beijing, NOAA weather stations)
|
|
55
|
+
python experiments/download_data.py
|
|
56
|
+
|
|
57
|
+
# Verify pre-computed results (should report 200+ PASS, 0 FAIL)
|
|
58
|
+
python experiments/verify_consistency.py
|
|
59
|
+
|
|
60
|
+
# Generate all figures
|
|
61
|
+
python experiments/generate_figures.py
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## What this repo contains
|
|
65
|
+
|
|
66
|
+
- `pbp_anomaly/` -- Python package implementing the PBP anomaly detector
|
|
67
|
+
- `data/` -- Small datasets shipped with the repo (UCI, OpenMeteo); large datasets downloaded via script
|
|
68
|
+
- `results/precomputed/` -- Pre-computed experiment results (JSON/CSV) for instant verification
|
|
69
|
+
- `experiments/` -- Scripts to reproduce all results and figures from the paper
|
|
70
|
+
- `tests/` -- Unit tests (27 tests)
|
|
71
|
+
|
|
72
|
+
## Datasets
|
|
73
|
+
|
|
74
|
+
The paper evaluates PBP across 9 datasets spanning two sensor domains:
|
|
75
|
+
|
|
76
|
+
| Dataset | Domain | Sensors | Readings | Source | Shipped |
|
|
77
|
+
|---------|--------|---------|----------|--------|---------|
|
|
78
|
+
| UCI Air Quality (Italy) | Air quality | 8 | 9,357 | [UCI MLR](https://archive.ics.uci.edu/dataset/360/air+quality) | Yes |
|
|
79
|
+
| Beijing Dongsi | Air quality | 10 | 35,064 | [Zhang et al. 2017](https://archive.ics.uci.edu/dataset/501/beijing+multi+site+air+quality+data) | Download |
|
|
80
|
+
| EPA AQS (Los Angeles) | Air quality | 4 | 26,280 | [EPA AQS](https://aqs.epa.gov/aqsweb/airdata/download_files.html) | Optional |
|
|
81
|
+
| Sao Paulo (CAMS) | Air quality | 6 | 17,520 | [Open-Meteo](https://open-meteo.com/en/docs/air-quality-api) | Yes |
|
|
82
|
+
| Cape Town (CAMS) | Air quality | 6 | 17,520 | [Open-Meteo](https://open-meteo.com/en/docs/air-quality-api) | Yes |
|
|
83
|
+
| Chicago O'Hare | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
84
|
+
| Miami | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
85
|
+
| San Francisco | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
86
|
+
| Fairbanks | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
87
|
+
|
|
88
|
+
"Shipped" means the CSV is included in this repo. "Download" means `download_data.py` fetches it automatically.
|
|
89
|
+
|
|
90
|
+
## Reproducing results
|
|
91
|
+
|
|
92
|
+
### Verify pre-computed results
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
python experiments/verify_consistency.py
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This checks every number cited in the manuscript against the pre-computed result files in `results/precomputed/`. No data download required.
|
|
99
|
+
|
|
100
|
+
### Full replication from raw data
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Download datasets
|
|
104
|
+
python experiments/download_data.py
|
|
105
|
+
|
|
106
|
+
# Run all experiments (may take several hours)
|
|
107
|
+
python experiments/replicate_all.py --output-dir results/local
|
|
108
|
+
|
|
109
|
+
# Verify fresh results
|
|
110
|
+
python experiments/verify_consistency.py --results-dir results/local
|
|
111
|
+
|
|
112
|
+
# Generate figures from fresh results
|
|
113
|
+
python experiments/generate_figures.py --results-dir results/local
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For a quick smoke test on a single dataset:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python experiments/replicate_all.py --datasets UCI --quick --output-dir results/local
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Figures
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python experiments/generate_figures.py
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Generates Fig 1 (temporal robustness), Fig 2 (hub variable heatmap), and Fig 3 (sensor-pair z-score case study) in `figures/`.
|
|
129
|
+
|
|
130
|
+
## Library usage
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from pbp_anomaly import AnomalyDetector
|
|
134
|
+
import pandas as pd
|
|
135
|
+
|
|
136
|
+
df = pd.read_csv('your_sensor_data.csv')
|
|
137
|
+
detector = AnomalyDetector(window_size=6, mode='both')
|
|
138
|
+
result = detector.fit_score(df, sensor_cols=['CO', 'NO2', 'O3'])
|
|
139
|
+
|
|
140
|
+
print(f"AUC-ROC: {result['standard']['auc_roc']:.3f}")
|
|
141
|
+
print(f"Top sensor pairs: {result['pair_ranking'][:3]}")
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Or via CLI:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
pbp-anomaly detect data.csv --sensors CO,NO2,O3 --mode both
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Running tests
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pytest tests/ -v
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Dependencies
|
|
157
|
+
|
|
158
|
+
Core: numpy, pandas, scipy, scikit-learn, [tmc-pbp](https://github.com/Tenfleques/tmc-pbp) (Python >= 3.10)
|
|
159
|
+
|
|
160
|
+
Optional: matplotlib (figures), requests (NOAA download), pytest (tests)
|
|
161
|
+
|
|
162
|
+
All installed automatically via `pip install -e ".[all]"`.
|
|
163
|
+
|
|
164
|
+
## Citation
|
|
165
|
+
|
|
166
|
+
```bibtex
|
|
167
|
+
@inproceedings{Chikake2026icasis,
|
|
168
|
+
author = {Chikake, Tendai M. and Goldengorin, Boris},
|
|
169
|
+
title = {Pseudo-Boolean polynomial anomaly scoring for intelligent multisensor environmental monitoring},
|
|
170
|
+
booktitle = {International Conference on Advanced Sensing and Intelligent Systems (ICASIS 2026)},
|
|
171
|
+
series = {Proc. SPIE},
|
|
172
|
+
volume = {14309},
|
|
173
|
+
publisher = {SPIE},
|
|
174
|
+
year = {2026},
|
|
175
|
+
doi = {10.1117/12.3121130}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# pbp-anomaly
|
|
2
|
+
|
|
3
|
+
Training-free anomaly detection in environmental sensor networks via pseudo-Boolean polynomial (PBP) decomposition.
|
|
4
|
+
|
|
5
|
+
Reference implementation of the PBP anomaly scoring described in:
|
|
6
|
+
|
|
7
|
+
> T. M. Chikake and B. Goldengorin, "Pseudo-Boolean polynomial anomaly scoring for intelligent multisensor environmental monitoring," in *International Conference on Advanced Sensing and Intelligent Systems (ICASIS 2026)*, Proc. SPIE 14309, 2026. https://doi.org/10.1117/12.3121130
|
|
8
|
+
|
|
9
|
+
The repository also contains the multi-dataset experiments of an extended study.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install pbp-anomaly
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This also installs the PBP core, [`tmc-pbp`](https://pypi.org/project/tmc-pbp/) (imported as `pbp`).
|
|
18
|
+
|
|
19
|
+
## Quick start (reproducing the experiments)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone https://github.com/Tenfleques/pbp-anomaly.git
|
|
23
|
+
cd pbp-anomaly
|
|
24
|
+
pip install -e ".[all]"
|
|
25
|
+
|
|
26
|
+
# Download large datasets (Beijing, NOAA weather stations)
|
|
27
|
+
python experiments/download_data.py
|
|
28
|
+
|
|
29
|
+
# Verify pre-computed results (should report 200+ PASS, 0 FAIL)
|
|
30
|
+
python experiments/verify_consistency.py
|
|
31
|
+
|
|
32
|
+
# Generate all figures
|
|
33
|
+
python experiments/generate_figures.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## What this repo contains
|
|
37
|
+
|
|
38
|
+
- `pbp_anomaly/` -- Python package implementing the PBP anomaly detector
|
|
39
|
+
- `data/` -- Small datasets shipped with the repo (UCI, OpenMeteo); large datasets downloaded via script
|
|
40
|
+
- `results/precomputed/` -- Pre-computed experiment results (JSON/CSV) for instant verification
|
|
41
|
+
- `experiments/` -- Scripts to reproduce all results and figures from the paper
|
|
42
|
+
- `tests/` -- Unit tests (27 tests)
|
|
43
|
+
|
|
44
|
+
## Datasets
|
|
45
|
+
|
|
46
|
+
The paper evaluates PBP across 9 datasets spanning two sensor domains:
|
|
47
|
+
|
|
48
|
+
| Dataset | Domain | Sensors | Readings | Source | Shipped |
|
|
49
|
+
|---------|--------|---------|----------|--------|---------|
|
|
50
|
+
| UCI Air Quality (Italy) | Air quality | 8 | 9,357 | [UCI MLR](https://archive.ics.uci.edu/dataset/360/air+quality) | Yes |
|
|
51
|
+
| Beijing Dongsi | Air quality | 10 | 35,064 | [Zhang et al. 2017](https://archive.ics.uci.edu/dataset/501/beijing+multi+site+air+quality+data) | Download |
|
|
52
|
+
| EPA AQS (Los Angeles) | Air quality | 4 | 26,280 | [EPA AQS](https://aqs.epa.gov/aqsweb/airdata/download_files.html) | Optional |
|
|
53
|
+
| Sao Paulo (CAMS) | Air quality | 6 | 17,520 | [Open-Meteo](https://open-meteo.com/en/docs/air-quality-api) | Yes |
|
|
54
|
+
| Cape Town (CAMS) | Air quality | 6 | 17,520 | [Open-Meteo](https://open-meteo.com/en/docs/air-quality-api) | Yes |
|
|
55
|
+
| Chicago O'Hare | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
56
|
+
| Miami | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
57
|
+
| San Francisco | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
58
|
+
| Fairbanks | Weather | 6 | 17,520 | [NOAA ISD](https://www.ncei.noaa.gov/products/land-based-station/integrated-surface-database) | Download |
|
|
59
|
+
|
|
60
|
+
"Shipped" means the CSV is included in this repo. "Download" means `download_data.py` fetches it automatically.
|
|
61
|
+
|
|
62
|
+
## Reproducing results
|
|
63
|
+
|
|
64
|
+
### Verify pre-computed results
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python experiments/verify_consistency.py
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This checks every number cited in the manuscript against the pre-computed result files in `results/precomputed/`. No data download required.
|
|
71
|
+
|
|
72
|
+
### Full replication from raw data
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Download datasets
|
|
76
|
+
python experiments/download_data.py
|
|
77
|
+
|
|
78
|
+
# Run all experiments (may take several hours)
|
|
79
|
+
python experiments/replicate_all.py --output-dir results/local
|
|
80
|
+
|
|
81
|
+
# Verify fresh results
|
|
82
|
+
python experiments/verify_consistency.py --results-dir results/local
|
|
83
|
+
|
|
84
|
+
# Generate figures from fresh results
|
|
85
|
+
python experiments/generate_figures.py --results-dir results/local
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For a quick smoke test on a single dataset:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
python experiments/replicate_all.py --datasets UCI --quick --output-dir results/local
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Figures
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python experiments/generate_figures.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Generates Fig 1 (temporal robustness), Fig 2 (hub variable heatmap), and Fig 3 (sensor-pair z-score case study) in `figures/`.
|
|
101
|
+
|
|
102
|
+
## Library usage
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from pbp_anomaly import AnomalyDetector
|
|
106
|
+
import pandas as pd
|
|
107
|
+
|
|
108
|
+
df = pd.read_csv('your_sensor_data.csv')
|
|
109
|
+
detector = AnomalyDetector(window_size=6, mode='both')
|
|
110
|
+
result = detector.fit_score(df, sensor_cols=['CO', 'NO2', 'O3'])
|
|
111
|
+
|
|
112
|
+
print(f"AUC-ROC: {result['standard']['auc_roc']:.3f}")
|
|
113
|
+
print(f"Top sensor pairs: {result['pair_ranking'][:3]}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Or via CLI:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pbp-anomaly detect data.csv --sensors CO,NO2,O3 --mode both
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Running tests
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pytest tests/ -v
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Dependencies
|
|
129
|
+
|
|
130
|
+
Core: numpy, pandas, scipy, scikit-learn, [tmc-pbp](https://github.com/Tenfleques/tmc-pbp) (Python >= 3.10)
|
|
131
|
+
|
|
132
|
+
Optional: matplotlib (figures), requests (NOAA download), pytest (tests)
|
|
133
|
+
|
|
134
|
+
All installed automatically via `pip install -e ".[all]"`.
|
|
135
|
+
|
|
136
|
+
## Citation
|
|
137
|
+
|
|
138
|
+
```bibtex
|
|
139
|
+
@inproceedings{Chikake2026icasis,
|
|
140
|
+
author = {Chikake, Tendai M. and Goldengorin, Boris},
|
|
141
|
+
title = {Pseudo-Boolean polynomial anomaly scoring for intelligent multisensor environmental monitoring},
|
|
142
|
+
booktitle = {International Conference on Advanced Sensing and Intelligent Systems (ICASIS 2026)},
|
|
143
|
+
series = {Proc. SPIE},
|
|
144
|
+
volume = {14309},
|
|
145
|
+
publisher = {SPIE},
|
|
146
|
+
year = {2026},
|
|
147
|
+
doi = {10.1117/12.3121130}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pbp-anomaly: Training-free anomaly detection via pseudo-Boolean polynomial decomposition.
|
|
3
|
+
|
|
4
|
+
Detects anomalies in multivariate sensor streams by extracting polynomial features
|
|
5
|
+
from binarized sliding windows. No training data required.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from pbp_anomaly import detect, AnomalyDetector
|
|
9
|
+
|
|
10
|
+
detector = AnomalyDetector(window_size=6, thresholds='auto')
|
|
11
|
+
scores = detector.fit_score(dataframe, sensor_cols=['CO', 'NO2', 'O3'])
|
|
12
|
+
|
|
13
|
+
# Or one-shot:
|
|
14
|
+
result = detect(dataframe, sensor_cols=['CO', 'NO2', 'O3'])
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from pbp_anomaly.detector import AnomalyDetector, detect
|
|
18
|
+
|
|
19
|
+
__all__ = ['AnomalyDetector', 'detect']
|
|
20
|
+
__version__ = '0.2.0'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI for pbp-anomaly.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
pbp-anomaly detect data.csv --sensors CO,NO2,O3 --mode standard
|
|
6
|
+
pbp-anomaly detect data.csv --sensors CO,NO2,O3 --mode both --window 12
|
|
7
|
+
pbp-anomaly info
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from pbp_anomaly.detector import AnomalyDetector, get_backend
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def cmd_detect(args):
|
|
18
|
+
from pbp_anomaly.parsers import load_csv
|
|
19
|
+
|
|
20
|
+
sensor_cols = [s.strip() for s in args.sensors.split(',')]
|
|
21
|
+
ref_cols = [s.strip() for s in args.ref.split(',')] if args.ref else None
|
|
22
|
+
|
|
23
|
+
ds = load_csv(args.csv, sensor_cols, ref_cols, name=args.csv)
|
|
24
|
+
|
|
25
|
+
detector = AnomalyDetector(
|
|
26
|
+
window_size=args.window,
|
|
27
|
+
mode=args.mode,
|
|
28
|
+
percentile=args.percentile,
|
|
29
|
+
two_tailed=args.two_tailed,
|
|
30
|
+
top_k=args.top_k,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
print(f"Dataset: {ds['name']}")
|
|
34
|
+
print(f"Readings: {len(ds['data'])}, Sensors: {len(sensor_cols)}")
|
|
35
|
+
print(f"Backend: {get_backend()}")
|
|
36
|
+
print(f"Mode: {args.mode}, Window: {args.window}, Percentile: {args.percentile}")
|
|
37
|
+
print()
|
|
38
|
+
|
|
39
|
+
result = detector.fit_score(ds['data'], ds['sensor_cols'], ds['ref_cols'])
|
|
40
|
+
|
|
41
|
+
print(f"Windows: {result['n_windows']}")
|
|
42
|
+
print(f"Anomaly rate: {100 * result['anomaly_fraction']:.1f}%")
|
|
43
|
+
print()
|
|
44
|
+
|
|
45
|
+
if 'standard' in result:
|
|
46
|
+
r = result['standard']
|
|
47
|
+
print(f"PBP standard: AUC = {r['auc_roc']:.4f} CI = {r['ci_95']}")
|
|
48
|
+
if 'pair' in result:
|
|
49
|
+
r = result['pair']
|
|
50
|
+
print(f"PBP pair: AUC = {r['auc_roc']:.4f} CI = {r['ci_95']}")
|
|
51
|
+
if 'pair_ranking' in result:
|
|
52
|
+
print(f"\nTop 5 sensor pairs:")
|
|
53
|
+
for p in result['pair_ranking'][:5]:
|
|
54
|
+
print(f" {p['pair']:<20s} AUC = {p['auc']:.4f}")
|
|
55
|
+
|
|
56
|
+
if args.output:
|
|
57
|
+
# Remove non-serializable arrays
|
|
58
|
+
out = {k: v for k, v in result.items() if k not in ('scores', 'labels')}
|
|
59
|
+
with open(args.output, 'w') as f:
|
|
60
|
+
json.dump(out, f, indent=2, default=str)
|
|
61
|
+
print(f"\nResults saved to {args.output}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def cmd_info(args):
|
|
65
|
+
from pbp_anomaly import __version__
|
|
66
|
+
print(f"pbp-anomaly v{__version__}")
|
|
67
|
+
print(f"PBP backend: {get_backend()}")
|
|
68
|
+
print(f"Features per window: 6 (degree, monomial_count, entropy, coeff_std, l1_norm, coeff_range)")
|
|
69
|
+
print(f"Modes: standard (full-matrix), pair (sensor-pair decomposition), both")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main():
|
|
73
|
+
parser = argparse.ArgumentParser(
|
|
74
|
+
prog='pbp-anomaly',
|
|
75
|
+
description='Training-free anomaly detection via PBP decomposition',
|
|
76
|
+
)
|
|
77
|
+
sub = parser.add_subparsers(dest='command')
|
|
78
|
+
|
|
79
|
+
# detect
|
|
80
|
+
p_detect = sub.add_parser('detect', help='Run anomaly detection on a CSV file')
|
|
81
|
+
p_detect.add_argument('csv', help='Path to CSV file')
|
|
82
|
+
p_detect.add_argument('--sensors', required=True, help='Comma-separated sensor column names')
|
|
83
|
+
p_detect.add_argument('--ref', default=None, help='Reference columns for labels (default: same as sensors)')
|
|
84
|
+
p_detect.add_argument('--mode', default='standard', choices=['standard', 'pair', 'both'])
|
|
85
|
+
p_detect.add_argument('--window', type=int, default=6, help='Window size (default: 6)')
|
|
86
|
+
p_detect.add_argument('--percentile', type=int, default=95, help='Threshold percentile (default: 95)')
|
|
87
|
+
p_detect.add_argument('--two-tailed', action='store_true', help='Flag both high and low extremes')
|
|
88
|
+
p_detect.add_argument('--top-k', type=int, default=5, help='Top-K features for composite (default: 5)')
|
|
89
|
+
p_detect.add_argument('--output', '-o', help='Save results to JSON file')
|
|
90
|
+
|
|
91
|
+
# info
|
|
92
|
+
sub.add_parser('info', help='Show library info')
|
|
93
|
+
|
|
94
|
+
args = parser.parse_args()
|
|
95
|
+
if args.command == 'detect':
|
|
96
|
+
cmd_detect(args)
|
|
97
|
+
elif args.command == 'info':
|
|
98
|
+
cmd_info(args)
|
|
99
|
+
else:
|
|
100
|
+
parser.print_help()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
if __name__ == '__main__':
|
|
104
|
+
main()
|