AeroViz 0.1.21__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.
- AeroViz/__init__.py +13 -0
- AeroViz/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/data/DEFAULT_DATA.csv +1417 -0
- AeroViz/data/DEFAULT_PNSD_DATA.csv +1417 -0
- AeroViz/data/hysplit_example_data.txt +101 -0
- AeroViz/dataProcess/Chemistry/__init__.py +149 -0
- AeroViz/dataProcess/Chemistry/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Chemistry/_calculate.py +557 -0
- AeroViz/dataProcess/Chemistry/_isoropia.py +150 -0
- AeroViz/dataProcess/Chemistry/_mass_volume.py +487 -0
- AeroViz/dataProcess/Chemistry/_ocec.py +172 -0
- AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
- AeroViz/dataProcess/Chemistry/isrpia2.exe +0 -0
- AeroViz/dataProcess/Optical/PyMieScatt_update.py +577 -0
- AeroViz/dataProcess/Optical/_IMPROVE.py +452 -0
- AeroViz/dataProcess/Optical/__init__.py +281 -0
- AeroViz/dataProcess/Optical/__pycache__/PyMieScatt_update.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/__pycache__/mie_theory.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/_derived.py +518 -0
- AeroViz/dataProcess/Optical/_extinction.py +123 -0
- AeroViz/dataProcess/Optical/_mie_sd.py +912 -0
- AeroViz/dataProcess/Optical/_retrieve_RI.py +243 -0
- AeroViz/dataProcess/Optical/coefficient.py +72 -0
- AeroViz/dataProcess/Optical/fRH.pkl +0 -0
- AeroViz/dataProcess/Optical/mie_theory.py +260 -0
- AeroViz/dataProcess/README.md +271 -0
- AeroViz/dataProcess/SizeDistr/__init__.py +245 -0
- AeroViz/dataProcess/SizeDistr/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/SizeDistr/__pycache__/_size_dist.cpython-312.pyc +0 -0
- AeroViz/dataProcess/SizeDistr/_size_dist.py +810 -0
- AeroViz/dataProcess/SizeDistr/merge/README.md +93 -0
- AeroViz/dataProcess/SizeDistr/merge/__init__.py +20 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v0.py +251 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v0_1.py +246 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v1.py +255 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v2.py +244 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v3.py +518 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v4.py +422 -0
- AeroViz/dataProcess/SizeDistr/prop.py +62 -0
- AeroViz/dataProcess/VOC/__init__.py +14 -0
- AeroViz/dataProcess/VOC/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/VOC/_potential_par.py +108 -0
- AeroViz/dataProcess/VOC/support_voc.json +446 -0
- AeroViz/dataProcess/__init__.py +66 -0
- AeroViz/dataProcess/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/core/__init__.py +272 -0
- AeroViz/dataProcess/core/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/mcp_server.py +352 -0
- AeroViz/plot/__init__.py +13 -0
- AeroViz/plot/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/bar.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/box.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/pie.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/radar.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/regression.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/scatter.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/violin.cpython-312.pyc +0 -0
- AeroViz/plot/bar.py +126 -0
- AeroViz/plot/box.py +69 -0
- AeroViz/plot/distribution/__init__.py +1 -0
- AeroViz/plot/distribution/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/distribution/__pycache__/distribution.cpython-312.pyc +0 -0
- AeroViz/plot/distribution/distribution.py +576 -0
- AeroViz/plot/meteorology/CBPF.py +295 -0
- AeroViz/plot/meteorology/__init__.py +3 -0
- AeroViz/plot/meteorology/__pycache__/CBPF.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/hysplit.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/wind_rose.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/hysplit.py +93 -0
- AeroViz/plot/meteorology/wind_rose.py +77 -0
- AeroViz/plot/optical/__init__.py +1 -0
- AeroViz/plot/optical/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/optical/__pycache__/optical.cpython-312.pyc +0 -0
- AeroViz/plot/optical/optical.py +388 -0
- AeroViz/plot/pie.py +210 -0
- AeroViz/plot/radar.py +184 -0
- AeroViz/plot/regression.py +200 -0
- AeroViz/plot/scatter.py +174 -0
- AeroViz/plot/templates/__init__.py +6 -0
- AeroViz/plot/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/ammonium_rich.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/contour.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/corr_matrix.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/diurnal_pattern.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/koschmieder.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/metal_heatmap.cpython-312.pyc +0 -0
- AeroViz/plot/templates/ammonium_rich.py +34 -0
- AeroViz/plot/templates/contour.py +47 -0
- AeroViz/plot/templates/corr_matrix.py +267 -0
- AeroViz/plot/templates/diurnal_pattern.py +61 -0
- AeroViz/plot/templates/koschmieder.py +95 -0
- AeroViz/plot/templates/metal_heatmap.py +164 -0
- AeroViz/plot/timeseries/__init__.py +2 -0
- AeroViz/plot/timeseries/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/__pycache__/template.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/__pycache__/timeseries.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/template.py +47 -0
- AeroViz/plot/timeseries/timeseries.py +446 -0
- AeroViz/plot/utils/__init__.py +4 -0
- AeroViz/plot/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/_color.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/_unit.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/plt_utils.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/sklearn_utils.cpython-312.pyc +0 -0
- AeroViz/plot/utils/_color.py +71 -0
- AeroViz/plot/utils/_unit.py +55 -0
- AeroViz/plot/utils/fRH.json +390 -0
- AeroViz/plot/utils/plt_utils.py +92 -0
- AeroViz/plot/utils/sklearn_utils.py +49 -0
- AeroViz/plot/utils/units.json +89 -0
- AeroViz/plot/violin.py +80 -0
- AeroViz/rawDataReader/FLOW.md +138 -0
- AeroViz/rawDataReader/__init__.py +220 -0
- AeroViz/rawDataReader/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/__init__.py +0 -0
- AeroViz/rawDataReader/config/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/__pycache__/supported_instruments.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/supported_instruments.py +135 -0
- AeroViz/rawDataReader/core/__init__.py +658 -0
- AeroViz/rawDataReader/core/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/logger.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/pre_process.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/qc.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/report.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/logger.py +171 -0
- AeroViz/rawDataReader/core/pre_process.py +308 -0
- AeroViz/rawDataReader/core/qc.py +961 -0
- AeroViz/rawDataReader/core/report.py +579 -0
- AeroViz/rawDataReader/script/AE33.py +173 -0
- AeroViz/rawDataReader/script/AE43.py +151 -0
- AeroViz/rawDataReader/script/APS.py +339 -0
- AeroViz/rawDataReader/script/Aurora.py +191 -0
- AeroViz/rawDataReader/script/BAM1020.py +90 -0
- AeroViz/rawDataReader/script/BC1054.py +161 -0
- AeroViz/rawDataReader/script/EPA.py +79 -0
- AeroViz/rawDataReader/script/GRIMM.py +68 -0
- AeroViz/rawDataReader/script/IGAC.py +140 -0
- AeroViz/rawDataReader/script/MA350.py +179 -0
- AeroViz/rawDataReader/script/Minion.py +218 -0
- AeroViz/rawDataReader/script/NEPH.py +199 -0
- AeroViz/rawDataReader/script/OCEC.py +173 -0
- AeroViz/rawDataReader/script/Q-ACSM.py +12 -0
- AeroViz/rawDataReader/script/SMPS.py +389 -0
- AeroViz/rawDataReader/script/TEOM.py +181 -0
- AeroViz/rawDataReader/script/VOC.py +106 -0
- AeroViz/rawDataReader/script/Xact.py +244 -0
- AeroViz/rawDataReader/script/__init__.py +28 -0
- AeroViz/rawDataReader/script/__pycache__/AE33.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/AE43.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/APS.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Aurora.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/BAM1020.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/BC1054.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/EPA.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/GRIMM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/IGAC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/MA350.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Minion.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/NEPH.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/OCEC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Q-ACSM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/SMPS.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/TEOM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/VOC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Xact.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/tools/__init__.py +2 -0
- AeroViz/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/tools/__pycache__/database.cpython-312.pyc +0 -0
- AeroViz/tools/__pycache__/dataclassifier.cpython-312.pyc +0 -0
- AeroViz/tools/database.py +95 -0
- AeroViz/tools/dataclassifier.py +117 -0
- AeroViz/tools/dataprinter.py +58 -0
- aeroviz-0.1.21.dist-info/METADATA +294 -0
- aeroviz-0.1.21.dist-info/RECORD +180 -0
- aeroviz-0.1.21.dist-info/WHEEL +5 -0
- aeroviz-0.1.21.dist-info/licenses/LICENSE +21 -0
- aeroviz-0.1.21.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# RawDataReader 處理流程
|
|
2
|
+
|
|
3
|
+
## 流程圖
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
7
|
+
│ RawDataReader(inst, path, start, end, qc=True) │
|
|
8
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
12
|
+
│ 1. _raw_reader() │
|
|
13
|
+
│ ├─ 讀取原始檔案 (*.dat, *.txt, *.csv) │
|
|
14
|
+
│ ├─ 時間解析度: 儀器原生 (1min/5min/6min/1h) │
|
|
15
|
+
│ └─ 欄位: 原始量測值 │
|
|
16
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
17
|
+
│
|
|
18
|
+
▼
|
|
19
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
20
|
+
│ 2. _timeIndex_process() │
|
|
21
|
+
│ └─ 對齊標準時間索引 │
|
|
22
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
23
|
+
│
|
|
24
|
+
▼
|
|
25
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
26
|
+
│ 3. _QC() │
|
|
27
|
+
│ ├─ QCFlagBuilder 根據規則標記資料 │
|
|
28
|
+
│ ├─ 新增 QC_Flag 欄位 ("Valid" / "Status Error" / "Insufficient" / ...) │
|
|
29
|
+
│ └─ 暫存 QC Summary (待 _process 輸出) │
|
|
30
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
34
|
+
│ 4. _process() │
|
|
35
|
+
│ ├─ 計算衍生參數 (BC, Abs, AAE, GMD, GSD, etc.) │
|
|
36
|
+
│ ├─ 額外 QC 驗證 (如 Invalid AAE) 並更新 QC_Flag │
|
|
37
|
+
│ └─ 輸出完整 QC Summary (含 _QC 規則 + _process 驗證結果) │
|
|
38
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
39
|
+
│
|
|
40
|
+
▼
|
|
41
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
42
|
+
│ 5. _save_data() │
|
|
43
|
+
│ ├─ 儲存 raw_data.pkl / raw_data.csv │
|
|
44
|
+
│ └─ 儲存 qc_data.pkl / qc_data.csv │
|
|
45
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
46
|
+
│
|
|
47
|
+
▼
|
|
48
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
49
|
+
│ 6. __call__() - QC 處理 │
|
|
50
|
+
│ ├─ 根據 QC_Flag 將無效資料設為 NaN │
|
|
51
|
+
│ └─ 移除 QC_Flag 欄位 │
|
|
52
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
53
|
+
│
|
|
54
|
+
▼
|
|
55
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
56
|
+
│ 7. _generate_report() │
|
|
57
|
+
│ ├─ calculate_rates(raw_data, qc_flag) │
|
|
58
|
+
│ │ ├─ Acquisition Rate: 有資料的時段 / 期望時段 │
|
|
59
|
+
│ │ ├─ Yield Rate: 通過 QC 的時段 / 有資料的時段 │
|
|
60
|
+
│ │ └─ Total Rate: 通過 QC 的時段 / 期望時段 │
|
|
61
|
+
│ └─ process_rates_report() - 生成週/月報告 │
|
|
62
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
63
|
+
│
|
|
64
|
+
▼
|
|
65
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
66
|
+
│ 8. 最終輸出 │
|
|
67
|
+
│ ├─ resample(mean_freq) - 重採樣至使用者指定頻率 (預設 1h) │
|
|
68
|
+
│ ├─ 儲存 output_{inst}.csv │
|
|
69
|
+
│ ├─ 儲存 output_{inst}_dN/dS/dVdlogDp.csv (SMPS/APS) │
|
|
70
|
+
│ ├─ 儲存 report.json │
|
|
71
|
+
│ └─ 返回 DataFrame │
|
|
72
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 資料欄位變化
|
|
76
|
+
|
|
77
|
+
| 階段 | 欄位範例 (AE33) | 欄位範例 (APS) |
|
|
78
|
+
|-----|----------------|---------------|
|
|
79
|
+
| `_raw_reader` | BC1-7, ATN, Flow, Status... | 粒徑 bins (0.5-20 μm) |
|
|
80
|
+
| `_QC` | + `QC_Flag` | + `QC_Flag` |
|
|
81
|
+
| `_process` | + Abs_370-950, AAE | + total, GMD, GSD, mode (num/surf/vol) |
|
|
82
|
+
| `__call__` | 無效資料 → NaN,移除 QC_Flag | 無效資料 → NaN,移除 QC_Flag |
|
|
83
|
+
| 最終輸出 | BC, Abs, AAE | 統計參數 (無 bins) |
|
|
84
|
+
|
|
85
|
+
## QC_Flag 的角色
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
QC_Flag 貫穿整個流程:
|
|
89
|
+
_QC() → 建立 QC_Flag,暫存 Summary
|
|
90
|
+
_process() → 更新 QC_Flag (如 Invalid AAE),輸出完整 QC Summary
|
|
91
|
+
_generate_report() → 使用 QC_Flag 計算 rates
|
|
92
|
+
__call__() → 使用 QC_Flag 標記無效資料為 NaN,然後移除
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## QC Summary 輸出格式
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
AE33 QC Summary:
|
|
99
|
+
Status Error: 24312 (4.9%)
|
|
100
|
+
Invalid BC: 29265 (5.9%)
|
|
101
|
+
Insufficient: 105481 (21.1%)
|
|
102
|
+
Invalid AAE: 25948 (5.2%)
|
|
103
|
+
Valid: 356025 (71.2%)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 各儀器原生時間解析度
|
|
107
|
+
|
|
108
|
+
| 儀器 | 原生頻率 | 主要輸出參數 |
|
|
109
|
+
|-----|---------|-------------|
|
|
110
|
+
| **NEPH** | 5 min | 散射係數 (G), sca_550, SAE |
|
|
111
|
+
| **Aurora** | 1 min | 散射係數 (G), sca_550, SAE |
|
|
112
|
+
| **SMPS** | 6 min | total, GMD, GSD, mode (num/surf/vol) |
|
|
113
|
+
| **APS** | 6 min | total (1μm/2.5μm/all), GMD, GSD, mode |
|
|
114
|
+
| **GRIMM** | 6 min | 粒徑分佈統計 |
|
|
115
|
+
| **AE33** | 1 min | BC, Abs coef, AAE |
|
|
116
|
+
| **AE43** | 1 min | BC, Abs coef, AAE |
|
|
117
|
+
| **BC1054** | 1 min | BC, Abs coef, AAE |
|
|
118
|
+
| **MA350** | 1 min | BC, Abs coef, AAE |
|
|
119
|
+
| **BAM1020** | 1 h | PM 質量濃度 |
|
|
120
|
+
| **TEOM** | 6 min | PM_Total, PM_NV |
|
|
121
|
+
| **OCEC** | 1 h | Thermal/Optical OC & EC |
|
|
122
|
+
| **IGAC** | 1 h | 離子濃度 (9種) |
|
|
123
|
+
| **XRF** | 1 h | 元素濃度 |
|
|
124
|
+
| **VOC** | 1 h | VOC 濃度 |
|
|
125
|
+
| **EPA** | 1 h | 環保署監測數據 |
|
|
126
|
+
|
|
127
|
+
## Rate 計算說明
|
|
128
|
+
|
|
129
|
+
- **Acquisition Rate**: `有資料的時段數 / 期望的時段數 × 100%`
|
|
130
|
+
- 反映儀器的資料取得率
|
|
131
|
+
|
|
132
|
+
- **Yield Rate**: `通過 QC 的時段數 / 有資料的時段數 × 100%`
|
|
133
|
+
- 反映資料的品質良率
|
|
134
|
+
|
|
135
|
+
- **Total Rate**: `通過 QC 的時段數 / 期望的時段數 × 100%`
|
|
136
|
+
- 反映整體有效資料比例
|
|
137
|
+
|
|
138
|
+
計算時使用 QC_Flag,每個時段內若超過 50% 的資料點為 "Valid",則該時段視為有效。
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pandas import Grouper, Timedelta
|
|
6
|
+
|
|
7
|
+
from AeroViz.rawDataReader.config.supported_instruments import meta
|
|
8
|
+
from AeroViz.rawDataReader.script import *
|
|
9
|
+
|
|
10
|
+
__all__ = ['RawDataReader']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def RawDataReader(instrument: str,
|
|
14
|
+
path: Path | str,
|
|
15
|
+
reset: bool | str = False,
|
|
16
|
+
qc: bool | str = True,
|
|
17
|
+
start: datetime | str = None,
|
|
18
|
+
end: datetime | str = None,
|
|
19
|
+
mean_freq: str = '1h',
|
|
20
|
+
size_range: tuple[float, float] | None = None,
|
|
21
|
+
suppress_warnings: bool = False,
|
|
22
|
+
log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR'] = 'INFO',
|
|
23
|
+
**kwargs):
|
|
24
|
+
"""
|
|
25
|
+
Factory function to instantiate the appropriate reader module for a given instrument and
|
|
26
|
+
return the processed data over the specified time range.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
instrument : str
|
|
31
|
+
The instrument name for which to read data, must be a valid key in the meta dictionary
|
|
32
|
+
|
|
33
|
+
path : Path or str
|
|
34
|
+
The directory where raw data files for the instrument are stored
|
|
35
|
+
|
|
36
|
+
reset : bool or str
|
|
37
|
+
Data processing control mode:
|
|
38
|
+
False (default) - Use existing processed data if available
|
|
39
|
+
True - Force reprocess all data from raw files
|
|
40
|
+
'append' - Add new data to existing processed data
|
|
41
|
+
|
|
42
|
+
qc : bool or str
|
|
43
|
+
Quality control and rate calculation mode:
|
|
44
|
+
True (default) - Apply QC and calculate overall rates
|
|
45
|
+
False - Skip QC and return raw data only
|
|
46
|
+
str - Calculate rates at specified intervals:
|
|
47
|
+
'W' - Weekly rates
|
|
48
|
+
'MS' - Month start rates
|
|
49
|
+
'QS' - Quarter start rates
|
|
50
|
+
'YS' - Year start rates
|
|
51
|
+
Can add number prefix (e.g., '2MS' for bi-monthly)
|
|
52
|
+
|
|
53
|
+
start : datetime
|
|
54
|
+
Start time for filtering the data
|
|
55
|
+
|
|
56
|
+
end : datetime
|
|
57
|
+
End time for filtering the data
|
|
58
|
+
|
|
59
|
+
mean_freq : str
|
|
60
|
+
Resampling frequency for averaging the data (e.g., '1h' for hourly mean)
|
|
61
|
+
|
|
62
|
+
size_range : tuple[float, float], optional
|
|
63
|
+
Size range in nanometers (min_size, max_size) for SMPS/APS data filtering
|
|
64
|
+
|
|
65
|
+
suppress_warnings : bool, optional
|
|
66
|
+
Whether to suppress warning messages (default: False)
|
|
67
|
+
|
|
68
|
+
log_level : {'DEBUG', 'INFO', 'WARNING', 'ERROR'}
|
|
69
|
+
Logging level (default: 'INFO')
|
|
70
|
+
|
|
71
|
+
**kwargs
|
|
72
|
+
Additional arguments to pass to the reader module
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
pd.DataFrame
|
|
77
|
+
Processed data with specified QC and time range
|
|
78
|
+
|
|
79
|
+
Raises
|
|
80
|
+
------
|
|
81
|
+
ValueError
|
|
82
|
+
If QC mode or mean_freq format is invalid
|
|
83
|
+
TypeError
|
|
84
|
+
If parameters are of incorrect type
|
|
85
|
+
KeyError
|
|
86
|
+
If instrument name is not found in the supported instruments list
|
|
87
|
+
FileNotFoundError
|
|
88
|
+
If path does not exist or cannot be accessed
|
|
89
|
+
|
|
90
|
+
See Also
|
|
91
|
+
--------
|
|
92
|
+
AeroViz.rawDataReader.core.AbstractReader
|
|
93
|
+
A abstract reader class for reading raw data from different instruments
|
|
94
|
+
|
|
95
|
+
Examples
|
|
96
|
+
--------
|
|
97
|
+
>>> from AeroViz import RawDataReader
|
|
98
|
+
>>>
|
|
99
|
+
>>> # Using string inputs
|
|
100
|
+
>>> df_ae33 = RawDataReader(
|
|
101
|
+
... instrument='AE33',
|
|
102
|
+
... path='/path/to/your/data/folder',
|
|
103
|
+
... reset=True,
|
|
104
|
+
... qc='1MS',
|
|
105
|
+
... start='2024-01-01',
|
|
106
|
+
... end='2024-06-30',
|
|
107
|
+
... mean_freq='1h',
|
|
108
|
+
... )
|
|
109
|
+
|
|
110
|
+
>>> # Using Path and datetime objects
|
|
111
|
+
>>> from pathlib import Path
|
|
112
|
+
>>> from datetime import datetime
|
|
113
|
+
>>>
|
|
114
|
+
>>> df_ae33 = RawDataReader(
|
|
115
|
+
... instrument='AE33',
|
|
116
|
+
... path=Path('/path/to/your/data/folder'),
|
|
117
|
+
... reset=True,
|
|
118
|
+
... qc='1MS',
|
|
119
|
+
... start=datetime(2024, 1, 1),
|
|
120
|
+
... end=datetime(2024, 6, 30),
|
|
121
|
+
... mean_freq='1h',
|
|
122
|
+
... )
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
# Dynamically build instrument class map from meta configuration
|
|
126
|
+
# This avoids hardcoding the list and automatically includes new instruments
|
|
127
|
+
import AeroViz.rawDataReader.script as script_module
|
|
128
|
+
instrument_class_map = {}
|
|
129
|
+
for instrument_name in meta.keys():
|
|
130
|
+
if hasattr(script_module, instrument_name):
|
|
131
|
+
instrument_class_map[instrument_name] = getattr(script_module, instrument_name)
|
|
132
|
+
|
|
133
|
+
# Check if the instrument name is in the map
|
|
134
|
+
if instrument not in instrument_class_map:
|
|
135
|
+
raise KeyError(f"Instrument name '{instrument}' is not valid. \nMust be one of: {list(instrument_class_map.keys())}")
|
|
136
|
+
|
|
137
|
+
# Check if path exists and is a directory
|
|
138
|
+
if not isinstance(path, Path):
|
|
139
|
+
path = Path(path)
|
|
140
|
+
if not path.exists() or not path.is_dir():
|
|
141
|
+
raise FileNotFoundError(f"The specified path '{path}' does not exist or is not a directory.")
|
|
142
|
+
|
|
143
|
+
# Validate the QC frequency
|
|
144
|
+
if isinstance(qc, str):
|
|
145
|
+
try:
|
|
146
|
+
Grouper(freq=qc)
|
|
147
|
+
except (ValueError, TypeError):
|
|
148
|
+
raise ValueError(f"Invalid frequency: {qc}. Must be one of: "
|
|
149
|
+
f"W (week), MS (month start), QS (quarter start), YS (year start)")
|
|
150
|
+
|
|
151
|
+
# Convert and verify input times
|
|
152
|
+
if not (start and end):
|
|
153
|
+
raise ValueError("Both start and end times must be provided.")
|
|
154
|
+
|
|
155
|
+
# Convert start time if it's a string
|
|
156
|
+
if isinstance(start, str):
|
|
157
|
+
try:
|
|
158
|
+
start = datetime.fromisoformat(start.replace('Z', '+00:00'))
|
|
159
|
+
except ValueError as e:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
f"Invalid start time format. Please use ISO format (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS): {e}")
|
|
162
|
+
|
|
163
|
+
# Convert end time if it's a string
|
|
164
|
+
if isinstance(end, str):
|
|
165
|
+
try:
|
|
166
|
+
end = datetime.fromisoformat(end.replace('Z', '+00:00'))
|
|
167
|
+
except ValueError as e:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"Invalid end time format. Please use ISO format (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS): {e}")
|
|
170
|
+
|
|
171
|
+
if end <= start:
|
|
172
|
+
raise ValueError(f"Invalid time range: start {start} is after end {end}")
|
|
173
|
+
|
|
174
|
+
end = end.replace(hour=23, minute=59, second=59) if end.hour == 0 and end.minute == 0 else end
|
|
175
|
+
|
|
176
|
+
# Verify that mean_freq format
|
|
177
|
+
try:
|
|
178
|
+
Timedelta(mean_freq)
|
|
179
|
+
except ValueError:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
f"Invalid mean_freq: '{mean_freq}'. It should be a valid frequency string (e.g., '1h', '30min', '1D').")
|
|
182
|
+
|
|
183
|
+
# Validate size range
|
|
184
|
+
if size_range is not None:
|
|
185
|
+
SIZE_RANGE_INSTRUMENTS = ['SMPS', 'APS', 'GRIMM']
|
|
186
|
+
if instrument not in SIZE_RANGE_INSTRUMENTS:
|
|
187
|
+
raise ValueError(f"Size range filtering is only supported for {SIZE_RANGE_INSTRUMENTS}")
|
|
188
|
+
|
|
189
|
+
min_size, max_size = size_range
|
|
190
|
+
if not isinstance(min_size, (int, float)) or not isinstance(max_size, (int, float)):
|
|
191
|
+
raise ValueError("Size range values must be numeric")
|
|
192
|
+
if min_size >= max_size:
|
|
193
|
+
raise ValueError("Minimum size must be less than maximum size")
|
|
194
|
+
|
|
195
|
+
if instrument == 'SMPS':
|
|
196
|
+
if not (1 <= min_size <= 1000) or not (1 <= max_size <= 1000):
|
|
197
|
+
raise ValueError("SMPS size range must be between 1 and 1000 nm")
|
|
198
|
+
elif instrument == 'APS':
|
|
199
|
+
if not (500 <= min_size <= 20000) or not (500 <= max_size <= 20000):
|
|
200
|
+
raise ValueError("APS size range must be between 500 and 20000 nm")
|
|
201
|
+
|
|
202
|
+
kwargs.update({'size_range': size_range})
|
|
203
|
+
|
|
204
|
+
kwargs.update({
|
|
205
|
+
'suppress_warnings': suppress_warnings,
|
|
206
|
+
'log_level': log_level
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
# Instantiate the class and return the instance
|
|
210
|
+
reader_module = instrument_class_map[instrument].Reader(
|
|
211
|
+
path=path,
|
|
212
|
+
reset=reset,
|
|
213
|
+
qc=qc,
|
|
214
|
+
**kwargs
|
|
215
|
+
)
|
|
216
|
+
return reader_module(
|
|
217
|
+
start=start,
|
|
218
|
+
end=end,
|
|
219
|
+
mean_freq=mean_freq,
|
|
220
|
+
)
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Description: Configuration file for rawDataReader
|
|
2
|
+
|
|
3
|
+
meta = {
|
|
4
|
+
"NEPH": {
|
|
5
|
+
"pattern": ["*.dat"],
|
|
6
|
+
"freq": "5min",
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
"Aurora": {
|
|
10
|
+
"pattern": ["*.csv"],
|
|
11
|
+
"freq": "1min",
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"SMPS": {
|
|
15
|
+
"pattern": ["*.txt", "*.csv"],
|
|
16
|
+
"freq": "6min",
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"GRIMM": {
|
|
20
|
+
"pattern": ["*.dat"],
|
|
21
|
+
"freq": "6min",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
"APS": {
|
|
25
|
+
"pattern": ["*.txt"],
|
|
26
|
+
"freq": "6min",
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"AE33": {
|
|
30
|
+
"pattern": ["[!ST|!CT|!FV]*[!log]_AE33*.dat"],
|
|
31
|
+
"freq": "1min",
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
"AE43": {
|
|
35
|
+
"pattern": ["[!ST|!CT|!FV]*[!log]_AE43*.dat"],
|
|
36
|
+
"freq": "1min",
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
"BC1054": {
|
|
40
|
+
"pattern": ["*.csv"],
|
|
41
|
+
"freq": "1min",
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
"MA350": {
|
|
45
|
+
"pattern": ["*.csv"],
|
|
46
|
+
"freq": "1min",
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"BAM1020": {
|
|
50
|
+
"pattern": ["*.csv"],
|
|
51
|
+
"freq": "1h",
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
"TEOM": {
|
|
55
|
+
"pattern": ["*.csv"],
|
|
56
|
+
"freq": "6min",
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
"OCEC": {
|
|
60
|
+
"pattern": ["*LCRes.csv"],
|
|
61
|
+
"freq": "1h",
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
"IGAC": {
|
|
65
|
+
"pattern": ["*.csv"],
|
|
66
|
+
"freq": "1h",
|
|
67
|
+
|
|
68
|
+
# https://www.yangyao-env.com/web/product/product_in2.jsp?pd_id=PD1640151884502
|
|
69
|
+
# HF: 0.08, F-: 0.08, PO43-: None is not measured
|
|
70
|
+
"MDL": {
|
|
71
|
+
'HF': None, 'HCl': 0.05, 'HNO2': 0.01, 'HNO3': 0.05, 'G-SO2': 0.05, 'NH3': 0.1,
|
|
72
|
+
'Na+': 0.05, 'NH4+': 0.08, 'K+': 0.08, 'Mg2+': 0.05, 'Ca2+': 0.05,
|
|
73
|
+
'F-': None, 'Cl-': 0.05, 'NO2-': 0.05, 'NO3-': 0.01, 'PO43-': None, 'SO42-': 0.05,
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
"MR": {
|
|
77
|
+
'HF': 200, 'HCl': 200, 'HNO2': 200, 'HNO3': 200, 'G-SO2': 200, 'NH3': 300,
|
|
78
|
+
'Na+': 300, 'NH4+': 300, 'K+': 300, 'Mg2+': 300, 'Ca2+': 300,
|
|
79
|
+
'F-': 300, 'Cl-': 300, 'NO2-': 300, 'NO3-': 300, 'PO43-': None, 'SO42-': 300,
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
"Xact": {
|
|
84
|
+
"pattern": ["*.csv"],
|
|
85
|
+
"freq": "1h",
|
|
86
|
+
|
|
87
|
+
# base on Xact 625i Minimum Decision Limit (MDL) for XRF in ng/m3, 60 min sample time
|
|
88
|
+
"MDL": {
|
|
89
|
+
'Al': 100, 'Si': 18, 'P': 5.2, 'S': 3.2, 'Cl': 1.7,
|
|
90
|
+
'K': 1.2, 'Ca': 0.3, 'Ti': 1.6, 'V': 0.12, 'Cr': 0.12,
|
|
91
|
+
'Mn': 0.14, 'Fe': 0.17, 'Co': 0.14, 'Ni': 0.096, 'Cu': 0.079,
|
|
92
|
+
'Zn': 0.067, 'Ga': 0.059, 'Ge': 0.056, 'As': 0.063, 'Se': 0.081,
|
|
93
|
+
'Br': 0.1, 'Rb': 0.19, 'Sr': 0.22, 'Y': 0.28, 'Zr': 0.33,
|
|
94
|
+
'Nb': 0.41, 'Mo': 0.48, 'Pd': 2.2, 'Ag': 1.9, 'Cd': 2.5,
|
|
95
|
+
'In': 3.1, 'Sn': 4.1, 'Sb': 5.2, 'Te': 0.6, 'Cs': 0.37,
|
|
96
|
+
'Ba': 0.39, 'La': 0.36, 'Ce': 0.3, 'W': 0.0001, 'Pt': 0.12,
|
|
97
|
+
'Au': 0.1, 'Hg': 0.12, 'Tl': 0.12, 'Pb': 0.13, 'Bi': 0.13
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
"Q-ACSM": {
|
|
102
|
+
"pattern": ["*.csv"],
|
|
103
|
+
"freq": "30min",
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
"VOC": {
|
|
107
|
+
"pattern": ["*.csv"],
|
|
108
|
+
"freq": "1h",
|
|
109
|
+
"key": [
|
|
110
|
+
'Benzene', 'Toluene', 'Ethylbenzene', 'm/p-Xylene', 'o-Xylene', 'Ethane', 'Propane', 'Isobutane',
|
|
111
|
+
'n-Butane', 'Isopentane', 'n-Pentane', 'n-Hexane', 'n-Heptane', 'n-Octane', 'n-Nonane', 'n-Decane',
|
|
112
|
+
'n-Undecane', 'n-Dodecane', 'Ethylene', 'Propylene', '1-Butene', 't-2-Butene', 'cis-2-Butene',
|
|
113
|
+
'1-Pentene', 't-2-Pentene', 'cis-2-Pentene', '1-Hexene', 'Acetylene', 'Cyclopentane', 'Methylcyclopentane',
|
|
114
|
+
'Cyclohexane', 'Methylcyclohexane', 'Isoprene', '2,2-Dimethylbutane', '2,3-Dimethylbutane',
|
|
115
|
+
'2-Methylpentane', '3-Methylpentane', '2,4-Dimethylpentane', '2-Methylhexane', '2,3-Dimethylpentane',
|
|
116
|
+
'3-Methylheptane', '2,2,4-Trimethylpentane', '2,3,4-Trimethylpentane', '2-Methylheptane', '3-Methylhexane',
|
|
117
|
+
'Styrene', 'Isopropylbenzene', 'n-Propylbenzene', 'm-Ethyltoluene', 'p-Ethyltoluene', 'm-Diethylbenzene',
|
|
118
|
+
'p-Diethylbenzene', '1,3,5-Trimethylbenzene', 'o-Ethyltoluene', '1,2,4-Trimethylbenzene',
|
|
119
|
+
'1,2,3-Trimethylbenzene',
|
|
120
|
+
'1.2-DCB', '1.4-DCB', '1.3-Butadiene', '1-Octene', '2-Ethyltoluene', '3.4-Ethyltoluene', 'Acetaldehyde',
|
|
121
|
+
'Acetone', 'Butyl Acetate', 'Ethanol', 'Ethyl Acetate', 'Hexane', 'IPA', 'Iso-Propylbenzene',
|
|
122
|
+
'PCE', 'Propene', 'TCE', 'VCM',
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
"EPA": {
|
|
127
|
+
"pattern": ["*.csv"],
|
|
128
|
+
"freq": "1h",
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
"Minion": {
|
|
132
|
+
"pattern": ["*.csv", "*.xlsx"],
|
|
133
|
+
"freq": "1h",
|
|
134
|
+
},
|
|
135
|
+
}
|