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.
Files changed (180) hide show
  1. AeroViz/__init__.py +13 -0
  2. AeroViz/__pycache__/__init__.cpython-312.pyc +0 -0
  3. AeroViz/data/DEFAULT_DATA.csv +1417 -0
  4. AeroViz/data/DEFAULT_PNSD_DATA.csv +1417 -0
  5. AeroViz/data/hysplit_example_data.txt +101 -0
  6. AeroViz/dataProcess/Chemistry/__init__.py +149 -0
  7. AeroViz/dataProcess/Chemistry/__pycache__/__init__.cpython-312.pyc +0 -0
  8. AeroViz/dataProcess/Chemistry/_calculate.py +557 -0
  9. AeroViz/dataProcess/Chemistry/_isoropia.py +150 -0
  10. AeroViz/dataProcess/Chemistry/_mass_volume.py +487 -0
  11. AeroViz/dataProcess/Chemistry/_ocec.py +172 -0
  12. AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
  13. AeroViz/dataProcess/Chemistry/isrpia2.exe +0 -0
  14. AeroViz/dataProcess/Optical/PyMieScatt_update.py +577 -0
  15. AeroViz/dataProcess/Optical/_IMPROVE.py +452 -0
  16. AeroViz/dataProcess/Optical/__init__.py +281 -0
  17. AeroViz/dataProcess/Optical/__pycache__/PyMieScatt_update.cpython-312.pyc +0 -0
  18. AeroViz/dataProcess/Optical/__pycache__/__init__.cpython-312.pyc +0 -0
  19. AeroViz/dataProcess/Optical/__pycache__/mie_theory.cpython-312.pyc +0 -0
  20. AeroViz/dataProcess/Optical/_derived.py +518 -0
  21. AeroViz/dataProcess/Optical/_extinction.py +123 -0
  22. AeroViz/dataProcess/Optical/_mie_sd.py +912 -0
  23. AeroViz/dataProcess/Optical/_retrieve_RI.py +243 -0
  24. AeroViz/dataProcess/Optical/coefficient.py +72 -0
  25. AeroViz/dataProcess/Optical/fRH.pkl +0 -0
  26. AeroViz/dataProcess/Optical/mie_theory.py +260 -0
  27. AeroViz/dataProcess/README.md +271 -0
  28. AeroViz/dataProcess/SizeDistr/__init__.py +245 -0
  29. AeroViz/dataProcess/SizeDistr/__pycache__/__init__.cpython-312.pyc +0 -0
  30. AeroViz/dataProcess/SizeDistr/__pycache__/_size_dist.cpython-312.pyc +0 -0
  31. AeroViz/dataProcess/SizeDistr/_size_dist.py +810 -0
  32. AeroViz/dataProcess/SizeDistr/merge/README.md +93 -0
  33. AeroViz/dataProcess/SizeDistr/merge/__init__.py +20 -0
  34. AeroViz/dataProcess/SizeDistr/merge/_merge_v0.py +251 -0
  35. AeroViz/dataProcess/SizeDistr/merge/_merge_v0_1.py +246 -0
  36. AeroViz/dataProcess/SizeDistr/merge/_merge_v1.py +255 -0
  37. AeroViz/dataProcess/SizeDistr/merge/_merge_v2.py +244 -0
  38. AeroViz/dataProcess/SizeDistr/merge/_merge_v3.py +518 -0
  39. AeroViz/dataProcess/SizeDistr/merge/_merge_v4.py +422 -0
  40. AeroViz/dataProcess/SizeDistr/prop.py +62 -0
  41. AeroViz/dataProcess/VOC/__init__.py +14 -0
  42. AeroViz/dataProcess/VOC/__pycache__/__init__.cpython-312.pyc +0 -0
  43. AeroViz/dataProcess/VOC/_potential_par.py +108 -0
  44. AeroViz/dataProcess/VOC/support_voc.json +446 -0
  45. AeroViz/dataProcess/__init__.py +66 -0
  46. AeroViz/dataProcess/__pycache__/__init__.cpython-312.pyc +0 -0
  47. AeroViz/dataProcess/core/__init__.py +272 -0
  48. AeroViz/dataProcess/core/__pycache__/__init__.cpython-312.pyc +0 -0
  49. AeroViz/mcp_server.py +352 -0
  50. AeroViz/plot/__init__.py +13 -0
  51. AeroViz/plot/__pycache__/__init__.cpython-312.pyc +0 -0
  52. AeroViz/plot/__pycache__/bar.cpython-312.pyc +0 -0
  53. AeroViz/plot/__pycache__/box.cpython-312.pyc +0 -0
  54. AeroViz/plot/__pycache__/pie.cpython-312.pyc +0 -0
  55. AeroViz/plot/__pycache__/radar.cpython-312.pyc +0 -0
  56. AeroViz/plot/__pycache__/regression.cpython-312.pyc +0 -0
  57. AeroViz/plot/__pycache__/scatter.cpython-312.pyc +0 -0
  58. AeroViz/plot/__pycache__/violin.cpython-312.pyc +0 -0
  59. AeroViz/plot/bar.py +126 -0
  60. AeroViz/plot/box.py +69 -0
  61. AeroViz/plot/distribution/__init__.py +1 -0
  62. AeroViz/plot/distribution/__pycache__/__init__.cpython-312.pyc +0 -0
  63. AeroViz/plot/distribution/__pycache__/distribution.cpython-312.pyc +0 -0
  64. AeroViz/plot/distribution/distribution.py +576 -0
  65. AeroViz/plot/meteorology/CBPF.py +295 -0
  66. AeroViz/plot/meteorology/__init__.py +3 -0
  67. AeroViz/plot/meteorology/__pycache__/CBPF.cpython-312.pyc +0 -0
  68. AeroViz/plot/meteorology/__pycache__/__init__.cpython-312.pyc +0 -0
  69. AeroViz/plot/meteorology/__pycache__/hysplit.cpython-312.pyc +0 -0
  70. AeroViz/plot/meteorology/__pycache__/wind_rose.cpython-312.pyc +0 -0
  71. AeroViz/plot/meteorology/hysplit.py +93 -0
  72. AeroViz/plot/meteorology/wind_rose.py +77 -0
  73. AeroViz/plot/optical/__init__.py +1 -0
  74. AeroViz/plot/optical/__pycache__/__init__.cpython-312.pyc +0 -0
  75. AeroViz/plot/optical/__pycache__/optical.cpython-312.pyc +0 -0
  76. AeroViz/plot/optical/optical.py +388 -0
  77. AeroViz/plot/pie.py +210 -0
  78. AeroViz/plot/radar.py +184 -0
  79. AeroViz/plot/regression.py +200 -0
  80. AeroViz/plot/scatter.py +174 -0
  81. AeroViz/plot/templates/__init__.py +6 -0
  82. AeroViz/plot/templates/__pycache__/__init__.cpython-312.pyc +0 -0
  83. AeroViz/plot/templates/__pycache__/ammonium_rich.cpython-312.pyc +0 -0
  84. AeroViz/plot/templates/__pycache__/contour.cpython-312.pyc +0 -0
  85. AeroViz/plot/templates/__pycache__/corr_matrix.cpython-312.pyc +0 -0
  86. AeroViz/plot/templates/__pycache__/diurnal_pattern.cpython-312.pyc +0 -0
  87. AeroViz/plot/templates/__pycache__/koschmieder.cpython-312.pyc +0 -0
  88. AeroViz/plot/templates/__pycache__/metal_heatmap.cpython-312.pyc +0 -0
  89. AeroViz/plot/templates/ammonium_rich.py +34 -0
  90. AeroViz/plot/templates/contour.py +47 -0
  91. AeroViz/plot/templates/corr_matrix.py +267 -0
  92. AeroViz/plot/templates/diurnal_pattern.py +61 -0
  93. AeroViz/plot/templates/koschmieder.py +95 -0
  94. AeroViz/plot/templates/metal_heatmap.py +164 -0
  95. AeroViz/plot/timeseries/__init__.py +2 -0
  96. AeroViz/plot/timeseries/__pycache__/__init__.cpython-312.pyc +0 -0
  97. AeroViz/plot/timeseries/__pycache__/template.cpython-312.pyc +0 -0
  98. AeroViz/plot/timeseries/__pycache__/timeseries.cpython-312.pyc +0 -0
  99. AeroViz/plot/timeseries/template.py +47 -0
  100. AeroViz/plot/timeseries/timeseries.py +446 -0
  101. AeroViz/plot/utils/__init__.py +4 -0
  102. AeroViz/plot/utils/__pycache__/__init__.cpython-312.pyc +0 -0
  103. AeroViz/plot/utils/__pycache__/_color.cpython-312.pyc +0 -0
  104. AeroViz/plot/utils/__pycache__/_unit.cpython-312.pyc +0 -0
  105. AeroViz/plot/utils/__pycache__/plt_utils.cpython-312.pyc +0 -0
  106. AeroViz/plot/utils/__pycache__/sklearn_utils.cpython-312.pyc +0 -0
  107. AeroViz/plot/utils/_color.py +71 -0
  108. AeroViz/plot/utils/_unit.py +55 -0
  109. AeroViz/plot/utils/fRH.json +390 -0
  110. AeroViz/plot/utils/plt_utils.py +92 -0
  111. AeroViz/plot/utils/sklearn_utils.py +49 -0
  112. AeroViz/plot/utils/units.json +89 -0
  113. AeroViz/plot/violin.py +80 -0
  114. AeroViz/rawDataReader/FLOW.md +138 -0
  115. AeroViz/rawDataReader/__init__.py +220 -0
  116. AeroViz/rawDataReader/__pycache__/__init__.cpython-312.pyc +0 -0
  117. AeroViz/rawDataReader/config/__init__.py +0 -0
  118. AeroViz/rawDataReader/config/__pycache__/__init__.cpython-312.pyc +0 -0
  119. AeroViz/rawDataReader/config/__pycache__/supported_instruments.cpython-312.pyc +0 -0
  120. AeroViz/rawDataReader/config/supported_instruments.py +135 -0
  121. AeroViz/rawDataReader/core/__init__.py +658 -0
  122. AeroViz/rawDataReader/core/__pycache__/__init__.cpython-312.pyc +0 -0
  123. AeroViz/rawDataReader/core/__pycache__/logger.cpython-312.pyc +0 -0
  124. AeroViz/rawDataReader/core/__pycache__/pre_process.cpython-312.pyc +0 -0
  125. AeroViz/rawDataReader/core/__pycache__/qc.cpython-312.pyc +0 -0
  126. AeroViz/rawDataReader/core/__pycache__/report.cpython-312.pyc +0 -0
  127. AeroViz/rawDataReader/core/logger.py +171 -0
  128. AeroViz/rawDataReader/core/pre_process.py +308 -0
  129. AeroViz/rawDataReader/core/qc.py +961 -0
  130. AeroViz/rawDataReader/core/report.py +579 -0
  131. AeroViz/rawDataReader/script/AE33.py +173 -0
  132. AeroViz/rawDataReader/script/AE43.py +151 -0
  133. AeroViz/rawDataReader/script/APS.py +339 -0
  134. AeroViz/rawDataReader/script/Aurora.py +191 -0
  135. AeroViz/rawDataReader/script/BAM1020.py +90 -0
  136. AeroViz/rawDataReader/script/BC1054.py +161 -0
  137. AeroViz/rawDataReader/script/EPA.py +79 -0
  138. AeroViz/rawDataReader/script/GRIMM.py +68 -0
  139. AeroViz/rawDataReader/script/IGAC.py +140 -0
  140. AeroViz/rawDataReader/script/MA350.py +179 -0
  141. AeroViz/rawDataReader/script/Minion.py +218 -0
  142. AeroViz/rawDataReader/script/NEPH.py +199 -0
  143. AeroViz/rawDataReader/script/OCEC.py +173 -0
  144. AeroViz/rawDataReader/script/Q-ACSM.py +12 -0
  145. AeroViz/rawDataReader/script/SMPS.py +389 -0
  146. AeroViz/rawDataReader/script/TEOM.py +181 -0
  147. AeroViz/rawDataReader/script/VOC.py +106 -0
  148. AeroViz/rawDataReader/script/Xact.py +244 -0
  149. AeroViz/rawDataReader/script/__init__.py +28 -0
  150. AeroViz/rawDataReader/script/__pycache__/AE33.cpython-312.pyc +0 -0
  151. AeroViz/rawDataReader/script/__pycache__/AE43.cpython-312.pyc +0 -0
  152. AeroViz/rawDataReader/script/__pycache__/APS.cpython-312.pyc +0 -0
  153. AeroViz/rawDataReader/script/__pycache__/Aurora.cpython-312.pyc +0 -0
  154. AeroViz/rawDataReader/script/__pycache__/BAM1020.cpython-312.pyc +0 -0
  155. AeroViz/rawDataReader/script/__pycache__/BC1054.cpython-312.pyc +0 -0
  156. AeroViz/rawDataReader/script/__pycache__/EPA.cpython-312.pyc +0 -0
  157. AeroViz/rawDataReader/script/__pycache__/GRIMM.cpython-312.pyc +0 -0
  158. AeroViz/rawDataReader/script/__pycache__/IGAC.cpython-312.pyc +0 -0
  159. AeroViz/rawDataReader/script/__pycache__/MA350.cpython-312.pyc +0 -0
  160. AeroViz/rawDataReader/script/__pycache__/Minion.cpython-312.pyc +0 -0
  161. AeroViz/rawDataReader/script/__pycache__/NEPH.cpython-312.pyc +0 -0
  162. AeroViz/rawDataReader/script/__pycache__/OCEC.cpython-312.pyc +0 -0
  163. AeroViz/rawDataReader/script/__pycache__/Q-ACSM.cpython-312.pyc +0 -0
  164. AeroViz/rawDataReader/script/__pycache__/SMPS.cpython-312.pyc +0 -0
  165. AeroViz/rawDataReader/script/__pycache__/TEOM.cpython-312.pyc +0 -0
  166. AeroViz/rawDataReader/script/__pycache__/VOC.cpython-312.pyc +0 -0
  167. AeroViz/rawDataReader/script/__pycache__/Xact.cpython-312.pyc +0 -0
  168. AeroViz/rawDataReader/script/__pycache__/__init__.cpython-312.pyc +0 -0
  169. AeroViz/tools/__init__.py +2 -0
  170. AeroViz/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  171. AeroViz/tools/__pycache__/database.cpython-312.pyc +0 -0
  172. AeroViz/tools/__pycache__/dataclassifier.cpython-312.pyc +0 -0
  173. AeroViz/tools/database.py +95 -0
  174. AeroViz/tools/dataclassifier.py +117 -0
  175. AeroViz/tools/dataprinter.py +58 -0
  176. aeroviz-0.1.21.dist-info/METADATA +294 -0
  177. aeroviz-0.1.21.dist-info/RECORD +180 -0
  178. aeroviz-0.1.21.dist-info/WHEEL +5 -0
  179. aeroviz-0.1.21.dist-info/licenses/LICENSE +21 -0
  180. 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
+ )
File without changes
@@ -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
+ }