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,93 @@
|
|
|
1
|
+
# SMPS-APS Merge Algorithms
|
|
2
|
+
|
|
3
|
+
本資料夾包含 SMPS 與 APS 粒徑分布合併演算法的各版本實現。
|
|
4
|
+
|
|
5
|
+
## 版本演進
|
|
6
|
+
|
|
7
|
+
| 版本 | 檔案 | 主要特點 |
|
|
8
|
+
|------|------|----------|
|
|
9
|
+
| v0 | `_merge_v0.py` | 最原始版本,無 `union_index` 索引對齊 |
|
|
10
|
+
| v0.1 | `_merge_v0_1.py` | 加入 `union_index` 索引對齊功能 |
|
|
11
|
+
| v1 | `_merge_v1.py` | 加入 `shift_mode` 參數 (mobility/aerodynamic) |
|
|
12
|
+
| v2 | `_merge_v2.py` | 簡化輸出,移除 qc_cond 過濾 |
|
|
13
|
+
| v3 | `_merge_v3.py` | 加入 multiprocessing 平行運算 + dN/dS/dV 演算法 |
|
|
14
|
+
| v4 | `_merge_v4.py` | 加入 PM2.5 fitness 函數 + SMPS times 校正 |
|
|
15
|
+
|
|
16
|
+
## 版本詳細說明
|
|
17
|
+
|
|
18
|
+
### v0 (原始版本)
|
|
19
|
+
- 基本的 power law fitting 密度估算
|
|
20
|
+
- 使用 UnivariateSpline 進行重疊區域平滑
|
|
21
|
+
- rho 閾值: 0.3 < ρ² < 2.0
|
|
22
|
+
- 輸出: `data_all`, `data_qc`, `density_all`, `density_qc`
|
|
23
|
+
|
|
24
|
+
### v0.1
|
|
25
|
+
- 加入 `union_index` 確保 SMPS 與 APS 時間索引對齊
|
|
26
|
+
- 導出函數名稱改為 `merge_SMPS_APS`(無底線前綴)
|
|
27
|
+
|
|
28
|
+
### v1
|
|
29
|
+
- 加入 `shift_mode` 參數,支援:
|
|
30
|
+
- `'mobility'`: 移動 APS 粒徑到 mobility diameter
|
|
31
|
+
- `'aerodynamic'`: 移動 SMPS 粒徑到 aerodynamic diameter
|
|
32
|
+
- rho 閾值放寬: 0.3 < ρ² < 2.6
|
|
33
|
+
|
|
34
|
+
### v2
|
|
35
|
+
- 移除 `shift_mode` 參數(固定使用 mobility)
|
|
36
|
+
- 簡化輸出結構,移除 `_qc` 過濾版本
|
|
37
|
+
- 加入 APS 校正迭代(2次)
|
|
38
|
+
- 輸出: `data_all`, `data_all_aer`, `density_all`
|
|
39
|
+
|
|
40
|
+
### v3
|
|
41
|
+
- 引入 multiprocessing 平行運算加速
|
|
42
|
+
- 新增 dN/dS/dV 相關性演算法 (`_corr_with_dNdSdV`)
|
|
43
|
+
- 同時計算多種演算法結果:
|
|
44
|
+
- `dn`: 純 power law fitting
|
|
45
|
+
- `dndsdv`: dN/dS/dV 相關性
|
|
46
|
+
- `cor_dn`: APS 校正後的 power law
|
|
47
|
+
- `cor_dndsdv`: APS 校正後的 dN/dS/dV
|
|
48
|
+
- 輸出: `data_dn`, `data_dndsdv`, `data_cor_dn`, `data_cor_dndsdv`, `density`
|
|
49
|
+
|
|
50
|
+
### v4 (最新版本)
|
|
51
|
+
- 加入 PM2.5 質量約束的 fitness 函數
|
|
52
|
+
- 引入 SMPS times 校正因子(搜尋最佳倍率)
|
|
53
|
+
- 參數 `times_range`: SMPS 數據乘數範圍 (預設 0.8~1.25)
|
|
54
|
+
- 輸出新增 `times` 欄位記錄各時間點的最佳校正倍率
|
|
55
|
+
|
|
56
|
+
## 使用方式
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
from AeroViz.dataProcess import DataProcess
|
|
61
|
+
|
|
62
|
+
dp = DataProcess('SizeDistr', Path('./output'))
|
|
63
|
+
|
|
64
|
+
# v1 (基本版)
|
|
65
|
+
result = dp.merge_SMPS_APS(df_smps, df_aps)
|
|
66
|
+
|
|
67
|
+
# v2
|
|
68
|
+
result = dp.merge_SMPS_APS_v2(df_smps, df_aps)
|
|
69
|
+
|
|
70
|
+
# v3 (含 dN/dS/dV 演算法)
|
|
71
|
+
result = dp.merge_SMPS_APS_v3(df_smps, df_aps, dndsdv_alg=True)
|
|
72
|
+
|
|
73
|
+
# v4 (含 PM2.5 校正,推薦)
|
|
74
|
+
result = dp.merge_SMPS_APS_v4(df_smps, df_aps, df_pm25)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 演算法核心概念
|
|
78
|
+
|
|
79
|
+
### Power Law Fitting
|
|
80
|
+
利用 SMPS 上端粒徑的 power law 擬合(y = Ax^B),計算 APS 粒徑的位移因子(shift factor),用以估算有效密度。
|
|
81
|
+
|
|
82
|
+
### Shift Factor
|
|
83
|
+
位移因子的平方即為有效密度估計值:
|
|
84
|
+
- ρ_eff = shift_factor²
|
|
85
|
+
- 合理範圍: 0.6 ~ 2.6 g/cm³
|
|
86
|
+
|
|
87
|
+
### dN/dS/dV 相關性
|
|
88
|
+
v3+ 版本加入的演算法,同時考慮:
|
|
89
|
+
- dN/dlogDp (數量分布)
|
|
90
|
+
- dS/dlogDp (表面積分布)
|
|
91
|
+
- dV/dlogDp (體積分布)
|
|
92
|
+
|
|
93
|
+
通過最大化三者的相關性來決定最佳位移因子。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SMPS-APS Merge Algorithms
|
|
3
|
+
|
|
4
|
+
Version history:
|
|
5
|
+
- v0: Original implementation
|
|
6
|
+
- v0.1: Added union_index alignment
|
|
7
|
+
- v1: Added shift_mode parameter
|
|
8
|
+
- v2: Simplified output, removed qc filtering
|
|
9
|
+
- v3: Multiprocessing + dN/dS/dV algorithm
|
|
10
|
+
- v4: PM2.5 fitness + SMPS times correction
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from ._merge_v0 import _merge_SMPS_APS as merge_v0
|
|
14
|
+
from ._merge_v0_1 import merge_SMPS_APS as merge_v0_1
|
|
15
|
+
from ._merge_v1 import _merge_SMPS_APS as merge_v1
|
|
16
|
+
from ._merge_v2 import merge_SMPS_APS as merge_v2
|
|
17
|
+
from ._merge_v3 import merge_SMPS_APS as merge_v3
|
|
18
|
+
from ._merge_v4 import merge_SMPS_APS as merge_v4
|
|
19
|
+
|
|
20
|
+
__all__ = ['merge_v0', 'merge_v0_1', 'merge_v1', 'merge_v2', 'merge_v3', 'merge_v4']
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from datetime import datetime as dtm
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pandas import DataFrame, to_datetime
|
|
5
|
+
# from scipy.interpolate import interp1d
|
|
6
|
+
from scipy.interpolate import UnivariateSpline as unvpline, interp1d
|
|
7
|
+
|
|
8
|
+
__all__ = ['_merge_SMPS_APS']
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __test_plot(smpsx, smps, apsx, aps, mergex, merge, mergeox, mergeo, _sh):
|
|
12
|
+
from matplotlib.pyplot import subplots, close, show
|
|
13
|
+
|
|
14
|
+
## parameter
|
|
15
|
+
# '''
|
|
16
|
+
## plot
|
|
17
|
+
fig, ax = subplots()
|
|
18
|
+
|
|
19
|
+
ax.plot(smpsx, smps, c='#ff794c', label='smps', marker='o', lw=2)
|
|
20
|
+
ax.plot(apsx, aps, c='#4c79ff', label='aps', marker='o', lw=2)
|
|
21
|
+
ax.plot(mergex, merge, c='#79796a', label='merge')
|
|
22
|
+
# ax.plot(mergeox,mergeo,c='#111111',label='mergeo',marker='o',lw=.75)
|
|
23
|
+
|
|
24
|
+
ax.set(xscale='log', yscale='log', )
|
|
25
|
+
|
|
26
|
+
ax.legend(framealpha=0, )
|
|
27
|
+
ax.set_title((_sh ** 2)[0], fontsize=13)
|
|
28
|
+
|
|
29
|
+
show()
|
|
30
|
+
close()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# '''
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Overlap fitting
|
|
37
|
+
## Create a fitting func. by smps data
|
|
38
|
+
## return : shift factor
|
|
39
|
+
def _overlap_fitting(_smps_ori, _aps_ori, _smps_lb, _aps_hb):
|
|
40
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting\033[0m")
|
|
41
|
+
|
|
42
|
+
## overlap fitting
|
|
43
|
+
## parmeter
|
|
44
|
+
_dt_indx = _smps_ori.index
|
|
45
|
+
|
|
46
|
+
## overlap diameter data
|
|
47
|
+
_aps = _aps_ori[_aps_ori.keys()[_aps_ori.keys() < _aps_hb]].copy()
|
|
48
|
+
_smps = _smps_ori[_smps_ori.keys()[_smps_ori.keys() > _smps_lb]].copy()
|
|
49
|
+
|
|
50
|
+
## use SMPS data apply power law fitting
|
|
51
|
+
## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
|
|
52
|
+
## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
|
53
|
+
## power law fit to SMPS num conc at upper bins to log curve
|
|
54
|
+
|
|
55
|
+
## coefficient A, B
|
|
56
|
+
_smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
|
|
57
|
+
_smps_qc = _smps.where(_smps_qc_cond)
|
|
58
|
+
|
|
59
|
+
_size = _smps_qc_cond.sum(axis=1)
|
|
60
|
+
_size = _size.where(_size != 0.).copy()
|
|
61
|
+
|
|
62
|
+
_logx, _logy = np.log(_smps_qc.keys()._data.astype(float)), np.log(_smps_qc)
|
|
63
|
+
_x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
|
|
64
|
+
|
|
65
|
+
_coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
|
|
66
|
+
_coeA = np.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
|
|
67
|
+
_coeB = _coeB.values.reshape(-1, 1)
|
|
68
|
+
|
|
69
|
+
## rebuild shift smps data by coe. A, B
|
|
70
|
+
## x_shift = (y_ori/A)**(1/B)
|
|
71
|
+
_aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
|
|
72
|
+
_aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
|
|
73
|
+
|
|
74
|
+
## the least squares of diameter
|
|
75
|
+
## the shift factor which the cklosest to 1
|
|
76
|
+
_shift_factor = (_aps_shift_x.keys()._data.astype(float) / _aps_shift_x)
|
|
77
|
+
_shift_factor.columns = range(len(_aps_shift_x.keys()))
|
|
78
|
+
|
|
79
|
+
_dropna_idx = _shift_factor.dropna(how='all').index.copy()
|
|
80
|
+
|
|
81
|
+
## use the target function to get the similar aps and smps bin
|
|
82
|
+
## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
|
|
83
|
+
## assumption : the same diameter between smps and aps should get the same conc.
|
|
84
|
+
|
|
85
|
+
## be sure they art in log value
|
|
86
|
+
_S2 = DataFrame(index=_aps_shift_x.index)
|
|
87
|
+
_dia_table = DataFrame(np.full(_aps_shift_x.shape, _aps_shift_x.keys()),
|
|
88
|
+
columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
|
|
89
|
+
for _idx, _factor in _shift_factor.items():
|
|
90
|
+
_smps_fit_df = _coeA * (_dia_table / _factor.to_frame().values) ** _coeB
|
|
91
|
+
_S2[_idx] = ((_smps_fit_df - _aps) ** 2).sum(axis=1)
|
|
92
|
+
|
|
93
|
+
_least_squ_idx = _S2.idxmin(axis=1).loc[_dropna_idx]
|
|
94
|
+
|
|
95
|
+
_shift_factor_out = DataFrame(_shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), _least_squ_idx.values],
|
|
96
|
+
index=_dropna_idx).reindex(_dt_indx)
|
|
97
|
+
|
|
98
|
+
return _shift_factor_out, (DataFrame(_coeA, index=_dt_indx), DataFrame(_coeB, index=_dt_indx))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## Remove big shift data ()
|
|
102
|
+
## Return : aps, smps, shift (without big shift data)
|
|
103
|
+
def _shift_data_process(_shift):
|
|
104
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mshift-data quality control\033[0m")
|
|
105
|
+
|
|
106
|
+
_rho = _shift ** 2
|
|
107
|
+
_shift = _shift.mask((~np.isfinite(_shift)) | (_rho > 2) | (_rho < 0.3))
|
|
108
|
+
|
|
109
|
+
_qc_index = _shift.mask((_rho < 0.6) | (_shift.isna())).dropna().index
|
|
110
|
+
|
|
111
|
+
return _qc_index, _shift
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# return _smps.loc[~_big_shift], _aps.loc[~_big_shift], _shift[~_big_shift].reshape(-1,1)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
## Create merge data
|
|
118
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
119
|
+
## Return : merge bins, merge data, density
|
|
120
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _shift_mode, _smps_lb, _aps_hb, _coe):
|
|
121
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data\033[0m")
|
|
122
|
+
|
|
123
|
+
_ori_idx = _smps_ori.index
|
|
124
|
+
_merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
125
|
+
|
|
126
|
+
_uni_idx, _count = np.unique(np.hstack((_smps_ori.dropna(how='all').index, _aps_ori.dropna(how='all').index,
|
|
127
|
+
_shift_ori.dropna(how='all').index)), return_counts=True)
|
|
128
|
+
|
|
129
|
+
_merge_idx = to_datetime(np.unique(_uni_idx[_count == 3]))
|
|
130
|
+
|
|
131
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
132
|
+
|
|
133
|
+
## parameter
|
|
134
|
+
_coeA, _coeB = _coe[0].loc[_merge_idx], _coe[1].loc[_merge_idx]
|
|
135
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
136
|
+
|
|
137
|
+
_test = 1000
|
|
138
|
+
|
|
139
|
+
# _cntr = (_smps_lb+_aps_hb)/2
|
|
140
|
+
_cntr = _test
|
|
141
|
+
_bin_lb = _smps_key[-1]
|
|
142
|
+
|
|
143
|
+
## make shift bins
|
|
144
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
145
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
146
|
+
# _std_bin = _smps_key.tolist()+_aps_key[_aps_key>_smps_key[-1]].tolist()
|
|
147
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
148
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
149
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
150
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
151
|
+
|
|
152
|
+
if _shift_mode == 'mobility':
|
|
153
|
+
_aps_bin /= _shift
|
|
154
|
+
|
|
155
|
+
elif _shift_mode == 'aerodynamic':
|
|
156
|
+
_smps_bin *= _shift
|
|
157
|
+
|
|
158
|
+
## merge
|
|
159
|
+
_merge_lst = []
|
|
160
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
161
|
+
## remove
|
|
162
|
+
|
|
163
|
+
## keep complete smps bins and data
|
|
164
|
+
## remove the aps bin data lower than smps bin
|
|
165
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
166
|
+
|
|
167
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
168
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
169
|
+
|
|
170
|
+
# _merge_fit_loc = (_merge_bin<_aps_hb)&(_merge_bin>_smps_lb)
|
|
171
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
172
|
+
|
|
173
|
+
## coeA and coeB
|
|
174
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
175
|
+
# _unvpl_fc = unvpline(_merge_bin[_merge_fit_loc],_merge_dt[_merge_fit_loc],s=150)
|
|
176
|
+
# _inte_log_fc = interp1d(n.log10(_merge_bin[_merge_fit_loc]),n.log10(_merge_dt[_merge_fit_loc]),
|
|
177
|
+
# kind='linear',fill_value='extrapolate')
|
|
178
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
179
|
+
|
|
180
|
+
__merge = np.exp(_unvpl_fc(np.log(_std_bin_merge)))
|
|
181
|
+
# __merge = _unvpl_fc(_std_bin_merge)
|
|
182
|
+
|
|
183
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), __merge, _inte_fc(_std_bin_inte2)))
|
|
184
|
+
# _merge_dt_fit = __merge
|
|
185
|
+
# __test_plot(_bin_smps,_dt_smps,_bin_aps,_dt_aps,_std_bin,_merge_dt_fit,_merge_bin,_merge_dt,_sh)
|
|
186
|
+
|
|
187
|
+
_merge_lst.append(_merge_dt_fit)
|
|
188
|
+
|
|
189
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
190
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
191
|
+
|
|
192
|
+
## process output df
|
|
193
|
+
## average, align with index
|
|
194
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
195
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
196
|
+
_df.index.name = 'time'
|
|
197
|
+
return _df
|
|
198
|
+
|
|
199
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
## aps_fit_highbound : the diameter I choose randomly
|
|
203
|
+
def _merge_SMPS_APS(df_smps, df_aps, aps_unit, shift_mode, smps_overlap_lowbound, aps_fit_highbound):
|
|
204
|
+
# print(f'\nMerge data :')
|
|
205
|
+
# print(f' APS fittint higher diameter : {aps_fit_highbound:4d} nm')
|
|
206
|
+
# print(f' SMPS overlap lower diameter : {smps_overlap_lowbound:4d} nm')
|
|
207
|
+
# print(f' Average time : {self.data_freq:>4s}\n')
|
|
208
|
+
|
|
209
|
+
## get data, remove 'total' and 'mode'
|
|
210
|
+
## set to the same units
|
|
211
|
+
smps, aps = df_smps, df_aps
|
|
212
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
213
|
+
aps.columns = aps.keys().to_numpy(float)
|
|
214
|
+
|
|
215
|
+
if aps_unit == 'um':
|
|
216
|
+
aps.columns = aps.keys() * 1e3
|
|
217
|
+
|
|
218
|
+
## shift infomation, calculate by powerlaw fitting
|
|
219
|
+
shift, coe = _overlap_fitting(smps, aps, smps_overlap_lowbound, aps_fit_highbound)
|
|
220
|
+
|
|
221
|
+
## process data by shift infomation, and average data
|
|
222
|
+
qc_cond, shift = _shift_data_process(shift)
|
|
223
|
+
|
|
224
|
+
## merge aps and smps..
|
|
225
|
+
merge_data, density = _merge_data(smps, aps, shift, shift_mode, smps_overlap_lowbound, aps_fit_highbound, coe)
|
|
226
|
+
density.columns = ['density']
|
|
227
|
+
|
|
228
|
+
## add total and mode
|
|
229
|
+
# merge_total = merge_data.sum(axis=1,min_count=1).copy()
|
|
230
|
+
# merge_mode = merge_data.idxmax(axis=1).astype(float).copy()
|
|
231
|
+
|
|
232
|
+
# merge_data['total'] = merge_total
|
|
233
|
+
# merge_data['mode'] = merge_mode
|
|
234
|
+
|
|
235
|
+
## out
|
|
236
|
+
out_dic = {
|
|
237
|
+
'data_all': merge_data,
|
|
238
|
+
'data_qc': merge_data.loc[qc_cond],
|
|
239
|
+
'density_all': density,
|
|
240
|
+
'density_qc': density.loc[qc_cond],
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
## process data
|
|
244
|
+
|
|
245
|
+
for _nam, _df in out_dic.items():
|
|
246
|
+
out_dic[_nam] = _df.reindex(df_aps.index).copy()
|
|
247
|
+
|
|
248
|
+
# merge_data = merge_data.reindex(df_aps.index)
|
|
249
|
+
# density = density.reindex(df_aps.index)
|
|
250
|
+
|
|
251
|
+
return out_dic
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from datetime import datetime as dtm
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from pandas import DataFrame, to_datetime
|
|
5
|
+
# from scipy.interpolate import interp1d
|
|
6
|
+
from scipy.interpolate import UnivariateSpline as unvpline, interp1d
|
|
7
|
+
|
|
8
|
+
from AeroViz.dataProcess.core import union_index
|
|
9
|
+
|
|
10
|
+
__all__ = ['merge_SMPS_APS']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __test_plot(smpsx, smps, apsx, aps, mergex, merge, mergeox, mergeo, _sh):
|
|
14
|
+
from matplotlib.pyplot import subplots, close, show
|
|
15
|
+
|
|
16
|
+
## parameter
|
|
17
|
+
# '''
|
|
18
|
+
## plot
|
|
19
|
+
fig, ax = subplots()
|
|
20
|
+
|
|
21
|
+
ax.plot(smpsx, smps, c='#ff794c', label='smps', marker='o', lw=2)
|
|
22
|
+
ax.plot(apsx, aps, c='#4c79ff', label='aps', marker='o', lw=2)
|
|
23
|
+
ax.plot(mergex, merge, c='#79796a', label='merge')
|
|
24
|
+
# ax.plot(mergeox,mergeo,c='#111111',label='mergeo',marker='o',lw=.75)
|
|
25
|
+
|
|
26
|
+
ax.set(xscale='log', yscale='log', )
|
|
27
|
+
|
|
28
|
+
ax.legend(framealpha=0, )
|
|
29
|
+
ax.set_title((_sh ** 2)[0], fontsize=13)
|
|
30
|
+
|
|
31
|
+
show()
|
|
32
|
+
close()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# '''
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Overlap fitting
|
|
39
|
+
## Create a fitting func. by smps data
|
|
40
|
+
## return : shift factor
|
|
41
|
+
def _overlap_fitting(_smps_ori, _aps_ori, _smps_lb, _aps_hb):
|
|
42
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting\033[0m")
|
|
43
|
+
|
|
44
|
+
## overlap fitting
|
|
45
|
+
## parmeter
|
|
46
|
+
_dt_indx = _smps_ori.index
|
|
47
|
+
|
|
48
|
+
## overlap diameter data
|
|
49
|
+
_aps = _aps_ori[_aps_ori.keys()[_aps_ori.keys() < _aps_hb]].copy()
|
|
50
|
+
_smps = _smps_ori[_smps_ori.keys()[_smps_ori.keys() > _smps_lb]].copy()
|
|
51
|
+
|
|
52
|
+
## use SMPS data apply power law fitting
|
|
53
|
+
## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
|
|
54
|
+
## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
|
55
|
+
## power law fit to SMPS num conc at upper bins to log curve
|
|
56
|
+
|
|
57
|
+
## coefficient A, B
|
|
58
|
+
_smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
|
|
59
|
+
_smps_qc = _smps.where(_smps_qc_cond)
|
|
60
|
+
|
|
61
|
+
_size = _smps_qc_cond.sum(axis=1)
|
|
62
|
+
_size = _size.where(_size != 0.).copy()
|
|
63
|
+
|
|
64
|
+
_logx, _logy = np.log(_smps_qc.keys()._data.astype(float)), np.log(_smps_qc)
|
|
65
|
+
_x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
|
|
66
|
+
|
|
67
|
+
_coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
|
|
68
|
+
_coeA = np.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
|
|
69
|
+
_coeB = _coeB.values.reshape(-1, 1)
|
|
70
|
+
|
|
71
|
+
## rebuild shift smps data by coe. A, B
|
|
72
|
+
## x_shift = (y_ori/A)**(1/B)
|
|
73
|
+
_aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
|
|
74
|
+
_aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
|
|
75
|
+
|
|
76
|
+
## the least squares of diameter
|
|
77
|
+
## the shift factor which the cklosest to 1
|
|
78
|
+
_shift_factor = (_aps_shift_x.keys()._data.astype(float) / _aps_shift_x)
|
|
79
|
+
_shift_factor.columns = range(len(_aps_shift_x.keys()))
|
|
80
|
+
|
|
81
|
+
_dropna_idx = _shift_factor.dropna(how='all').index.copy()
|
|
82
|
+
|
|
83
|
+
## use the target function to get the similar aps and smps bin
|
|
84
|
+
## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
|
|
85
|
+
## assumption : the same diameter between smps and aps should get the same conc.
|
|
86
|
+
|
|
87
|
+
## be sure they art in log value
|
|
88
|
+
_S2 = DataFrame(index=_aps_shift_x.index)
|
|
89
|
+
_dia_table = DataFrame(np.full(_aps_shift_x.shape, _aps_shift_x.keys()),
|
|
90
|
+
columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
|
|
91
|
+
for _idx, _factor in _shift_factor.items():
|
|
92
|
+
_smps_fit_df = _coeA * (_dia_table / _factor.to_frame().values) ** _coeB
|
|
93
|
+
_S2[_idx] = ((_smps_fit_df - _aps) ** 2).sum(axis=1)
|
|
94
|
+
|
|
95
|
+
_least_squ_idx = _S2.idxmin(axis=1).loc[_dropna_idx]
|
|
96
|
+
|
|
97
|
+
_shift_factor_out = DataFrame(_shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), _least_squ_idx.values],
|
|
98
|
+
index=_dropna_idx).reindex(_dt_indx)
|
|
99
|
+
|
|
100
|
+
return _shift_factor_out, (DataFrame(_coeA, index=_dt_indx), DataFrame(_coeB, index=_dt_indx))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Remove big shift data ()
|
|
104
|
+
## Return : aps, smps, shift (without big shift data)
|
|
105
|
+
def _shift_data_process(_shift):
|
|
106
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mshift-data quality control\033[0m")
|
|
107
|
+
|
|
108
|
+
_rho = _shift ** 2
|
|
109
|
+
_shift = _shift.mask((~np.isfinite(_shift)) | (_rho > 2) | (_rho < 0.3))
|
|
110
|
+
|
|
111
|
+
_qc_index = _shift.mask((_rho < 0.6) | (_shift.isna())).dropna().index
|
|
112
|
+
|
|
113
|
+
return _qc_index, _shift
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# return _smps.loc[~_big_shift], _aps.loc[~_big_shift], _shift[~_big_shift].reshape(-1,1)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## Create merge data
|
|
120
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
121
|
+
## Return : merge bins, merge data, density
|
|
122
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _coe, _shift_mode):
|
|
123
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data : {_shift_mode}\033[0m")
|
|
124
|
+
|
|
125
|
+
_ori_idx = _smps_ori.index
|
|
126
|
+
_merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
127
|
+
|
|
128
|
+
_corr_aps_cond = _aps_ori.keys() < 700
|
|
129
|
+
_corr_aps_ky = _aps_ori.keys()[_corr_aps_cond]
|
|
130
|
+
|
|
131
|
+
_uni_idx, _count = np.unique(np.hstack((_smps_ori.dropna(how='all').index, _aps_ori.dropna(how='all').index,
|
|
132
|
+
_shift_ori.dropna(how='all').index)), return_counts=True)
|
|
133
|
+
|
|
134
|
+
_merge_idx = to_datetime(np.unique(_uni_idx[_count == 3]))
|
|
135
|
+
|
|
136
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
137
|
+
|
|
138
|
+
## parameter
|
|
139
|
+
_coeA, _coeB = _coe[0].loc[_merge_idx], _coe[1].loc[_merge_idx]
|
|
140
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
141
|
+
|
|
142
|
+
_cntr = 1000
|
|
143
|
+
_bin_lb = _smps_key[-1]
|
|
144
|
+
|
|
145
|
+
## make shift bins
|
|
146
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
147
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
148
|
+
|
|
149
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
150
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
151
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
152
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
153
|
+
|
|
154
|
+
if _shift_mode == 'mobility':
|
|
155
|
+
_aps_bin /= _shift
|
|
156
|
+
|
|
157
|
+
elif _shift_mode == 'aerodynamic':
|
|
158
|
+
_smps_bin *= _shift
|
|
159
|
+
|
|
160
|
+
## merge
|
|
161
|
+
_merge_lst, _corr_lst = [], []
|
|
162
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
163
|
+
## keep complete smps bins and data
|
|
164
|
+
## remove the aps bin data lower than smps bin
|
|
165
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
166
|
+
|
|
167
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
168
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
169
|
+
|
|
170
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
171
|
+
|
|
172
|
+
## coeA and coeB
|
|
173
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
174
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
175
|
+
|
|
176
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), np.exp(_unvpl_fc(np.log(_std_bin_merge))),
|
|
177
|
+
_inte_fc(_std_bin_inte2)))
|
|
178
|
+
|
|
179
|
+
_merge_lst.append(_merge_dt_fit)
|
|
180
|
+
_corr_lst.append(interp1d(_std_bin, _merge_dt_fit)(_bin_aps[_corr_aps_cond]))
|
|
181
|
+
|
|
182
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
183
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
184
|
+
|
|
185
|
+
_df_corr = DataFrame(_corr_lst, columns=_corr_aps_ky, index=_merge_idx) / _aps_ori.loc[_merge_idx, _corr_aps_ky]
|
|
186
|
+
|
|
187
|
+
## process output df
|
|
188
|
+
## average, align with index
|
|
189
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
190
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
191
|
+
_df.index.name = 'time'
|
|
192
|
+
return _df
|
|
193
|
+
|
|
194
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2), _out_df(_df_corr)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def merge_SMPS_APS(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000):
|
|
198
|
+
df_smps, df_aps = union_index(df_smps, df_aps)
|
|
199
|
+
|
|
200
|
+
## set to the same units
|
|
201
|
+
smps, aps_ori = df_smps.copy(), df_aps.copy()
|
|
202
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
203
|
+
aps_ori.columns = aps_ori.keys().to_numpy(float)
|
|
204
|
+
|
|
205
|
+
if aps_unit == 'um':
|
|
206
|
+
aps_ori.columns = aps_ori.keys() * 1e3
|
|
207
|
+
|
|
208
|
+
den_lst, mer_lst = [], []
|
|
209
|
+
aps_input = aps_ori.loc[:, aps_ori.keys() > 700].copy()
|
|
210
|
+
|
|
211
|
+
for _count in range(2):
|
|
212
|
+
|
|
213
|
+
## shift infomation, calculate by powerlaw fitting
|
|
214
|
+
shift, coe = _overlap_fitting(smps, aps_input, smps_overlap_lowbound, aps_fit_highbound)
|
|
215
|
+
|
|
216
|
+
## process data by shift infomation, and average data
|
|
217
|
+
qc_cond, shift = _shift_data_process(shift)
|
|
218
|
+
|
|
219
|
+
## merge aps and smps
|
|
220
|
+
merge_arg = (smps, aps_ori, shift, smps_overlap_lowbound, aps_fit_highbound, coe)
|
|
221
|
+
merge_data_mob, density, _corr = _merge_data(*merge_arg, 'mobility')
|
|
222
|
+
merge_data_aer, density, _ = _merge_data(*merge_arg, 'aerodynamic')
|
|
223
|
+
density.columns = ['density']
|
|
224
|
+
|
|
225
|
+
if _count == 0:
|
|
226
|
+
corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
|
|
227
|
+
corr = corr.mask(corr < 1, 1)
|
|
228
|
+
aps_ori.loc[:, corr.keys()] *= corr
|
|
229
|
+
|
|
230
|
+
aps_input = aps_ori.copy()
|
|
231
|
+
|
|
232
|
+
## out
|
|
233
|
+
out_dic = {
|
|
234
|
+
'data_all': merge_data_mob,
|
|
235
|
+
'data_qc': merge_data_mob.loc[qc_cond],
|
|
236
|
+
'data_all_aer': merge_data_aer,
|
|
237
|
+
'data_qc_aer': merge_data_aer.loc[qc_cond],
|
|
238
|
+
'density_all': density,
|
|
239
|
+
'density_qc': density.loc[qc_cond],
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
## process data
|
|
243
|
+
for _nam, _df in out_dic.items():
|
|
244
|
+
out_dic[_nam] = _df.reindex(smps.index).copy()
|
|
245
|
+
|
|
246
|
+
return out_dic
|