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,255 @@
|
|
|
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.6) | (_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, _shift_mode, _smps_lb, _aps_hb, _coe):
|
|
123
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data\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
|
+
_uni_idx, _count = np.unique(np.hstack((_smps_ori.dropna(how='all').index, _aps_ori.dropna(how='all').index,
|
|
129
|
+
_shift_ori.dropna(how='all').index)), return_counts=True)
|
|
130
|
+
|
|
131
|
+
_merge_idx = to_datetime(np.unique(_uni_idx[_count == 3]))
|
|
132
|
+
|
|
133
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
134
|
+
|
|
135
|
+
## parameter
|
|
136
|
+
_coeA, _coeB = _coe[0].loc[_merge_idx], _coe[1].loc[_merge_idx]
|
|
137
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
138
|
+
|
|
139
|
+
_test = 1000
|
|
140
|
+
|
|
141
|
+
# _cntr = (_smps_lb+_aps_hb)/2
|
|
142
|
+
_cntr = _test
|
|
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
|
+
# _std_bin = _smps_key.tolist()+_aps_key[_aps_key>_smps_key[-1]].tolist()
|
|
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 = []
|
|
162
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
163
|
+
## remove
|
|
164
|
+
|
|
165
|
+
## keep complete smps bins and data
|
|
166
|
+
## remove the aps bin data lower than smps bin
|
|
167
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
168
|
+
|
|
169
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
170
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
171
|
+
|
|
172
|
+
# _merge_fit_loc = (_merge_bin<_aps_hb)&(_merge_bin>_smps_lb)
|
|
173
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
174
|
+
|
|
175
|
+
## coeA and coeB
|
|
176
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
177
|
+
# _unvpl_fc = unvpline(_merge_bin[_merge_fit_loc],_merge_dt[_merge_fit_loc],s=150)
|
|
178
|
+
# _inte_log_fc = interp1d(n.log10(_merge_bin[_merge_fit_loc]),n.log10(_merge_dt[_merge_fit_loc]),
|
|
179
|
+
# kind='linear',fill_value='extrapolate')
|
|
180
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
181
|
+
|
|
182
|
+
__merge = np.exp(_unvpl_fc(np.log(_std_bin_merge)))
|
|
183
|
+
# __merge = _unvpl_fc(_std_bin_merge)
|
|
184
|
+
|
|
185
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), __merge, _inte_fc(_std_bin_inte2)))
|
|
186
|
+
# _merge_dt_fit = __merge
|
|
187
|
+
# __test_plot(_bin_smps,_dt_smps,_bin_aps,_dt_aps,_std_bin,_merge_dt_fit,_merge_bin,_merge_dt,_sh)
|
|
188
|
+
|
|
189
|
+
_merge_lst.append(_merge_dt_fit)
|
|
190
|
+
|
|
191
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
192
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
193
|
+
|
|
194
|
+
## process output df
|
|
195
|
+
## average, align with index
|
|
196
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
197
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
198
|
+
_df.index.name = 'time'
|
|
199
|
+
return _df
|
|
200
|
+
|
|
201
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
## aps_fit_highbound : the diameter I choose randomly
|
|
205
|
+
def _merge_SMPS_APS(df_smps, df_aps, aps_unit, shift_mode, smps_overlap_lowbound, aps_fit_highbound):
|
|
206
|
+
df_smps, df_aps = union_index(df_smps, df_aps)
|
|
207
|
+
|
|
208
|
+
# print(f'\nMerge data :')
|
|
209
|
+
# print(f' APS fittint higher diameter : {aps_fit_highbound:4d} nm')
|
|
210
|
+
# print(f' SMPS overlap lower diameter : {smps_overlap_lowbound:4d} nm')
|
|
211
|
+
# print(f' Average time : {self.data_freq:>4s}\n')
|
|
212
|
+
|
|
213
|
+
## get data, remove 'total' and 'mode'
|
|
214
|
+
## set to the same units
|
|
215
|
+
smps, aps = df_smps, df_aps
|
|
216
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
217
|
+
aps.columns = aps.keys().to_numpy(float)
|
|
218
|
+
|
|
219
|
+
if aps_unit == 'um':
|
|
220
|
+
aps.columns = aps.keys() * 1e3
|
|
221
|
+
|
|
222
|
+
## shift infomation, calculate by powerlaw fitting
|
|
223
|
+
shift, coe = _overlap_fitting(smps, aps, smps_overlap_lowbound, aps_fit_highbound)
|
|
224
|
+
|
|
225
|
+
## process data by shift infomation, and average data
|
|
226
|
+
qc_cond, shift = _shift_data_process(shift)
|
|
227
|
+
|
|
228
|
+
## merge aps and smps..
|
|
229
|
+
merge_data, density = _merge_data(smps, aps, shift, shift_mode, smps_overlap_lowbound, aps_fit_highbound, coe)
|
|
230
|
+
density.columns = ['density']
|
|
231
|
+
|
|
232
|
+
## add total and mode
|
|
233
|
+
# merge_total = merge_data.sum(axis=1,min_count=1).copy()
|
|
234
|
+
# merge_mode = merge_data.idxmax(axis=1).astype(float).copy()
|
|
235
|
+
|
|
236
|
+
# merge_data['total'] = merge_total
|
|
237
|
+
# merge_data['mode'] = merge_mode
|
|
238
|
+
|
|
239
|
+
## out
|
|
240
|
+
out_dic = {
|
|
241
|
+
'data_all': merge_data,
|
|
242
|
+
'data_qc': merge_data.loc[qc_cond],
|
|
243
|
+
'density_all': density,
|
|
244
|
+
'density_qc': density.loc[qc_cond],
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
## process data
|
|
248
|
+
|
|
249
|
+
for _nam, _df in out_dic.items():
|
|
250
|
+
out_dic[_nam] = _df.reindex(df_aps.index).copy()
|
|
251
|
+
|
|
252
|
+
# merge_data = merge_data.reindex(df_aps.index)
|
|
253
|
+
# density = density.reindex(df_aps.index)
|
|
254
|
+
|
|
255
|
+
return out_dic
|
|
@@ -0,0 +1,244 @@
|
|
|
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.6) | (_rho < 0.6))
|
|
110
|
+
|
|
111
|
+
# _qc_index = _shift.mask((_rho<0.6) | (_shift.isna())).dropna().index
|
|
112
|
+
|
|
113
|
+
# return _qc_index, _shift
|
|
114
|
+
return _shift
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# return _smps.loc[~_big_shift], _aps.loc[~_big_shift], _shift[~_big_shift].reshape(-1,1)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## Create merge data
|
|
121
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
122
|
+
## Return : merge bins, merge data, density
|
|
123
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _coe, _shift_mode):
|
|
124
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data : {_shift_mode}\033[0m")
|
|
125
|
+
|
|
126
|
+
_ori_idx = _smps_ori.index
|
|
127
|
+
_merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
128
|
+
|
|
129
|
+
_corr_aps_cond = _aps_ori.keys() < 700
|
|
130
|
+
_corr_aps_ky = _aps_ori.keys()[_corr_aps_cond]
|
|
131
|
+
|
|
132
|
+
_uni_idx, _count = np.unique(np.hstack((_smps_ori.dropna(how='all').index, _aps_ori.dropna(how='all').index,
|
|
133
|
+
_shift_ori.dropna(how='all').index)), return_counts=True)
|
|
134
|
+
|
|
135
|
+
_merge_idx = to_datetime(np.unique(_uni_idx[_count == 3]))
|
|
136
|
+
|
|
137
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
138
|
+
|
|
139
|
+
## parameter
|
|
140
|
+
_coeA, _coeB = _coe[0].loc[_merge_idx], _coe[1].loc[_merge_idx]
|
|
141
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
142
|
+
|
|
143
|
+
_cntr = 1000
|
|
144
|
+
_bin_lb = _smps_key[-1]
|
|
145
|
+
|
|
146
|
+
## make shift bins
|
|
147
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
148
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
149
|
+
|
|
150
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
151
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
152
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
153
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
154
|
+
|
|
155
|
+
if _shift_mode == 'mobility':
|
|
156
|
+
_aps_bin /= _shift
|
|
157
|
+
|
|
158
|
+
elif _shift_mode == 'aerodynamic':
|
|
159
|
+
_smps_bin *= _shift
|
|
160
|
+
|
|
161
|
+
## merge
|
|
162
|
+
_merge_lst, _corr_lst = [], []
|
|
163
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
164
|
+
## keep complete smps bins and data
|
|
165
|
+
## remove the aps bin data lower than smps bin
|
|
166
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
167
|
+
|
|
168
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
169
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
170
|
+
|
|
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
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
176
|
+
|
|
177
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), np.exp(_unvpl_fc(np.log(_std_bin_merge))),
|
|
178
|
+
_inte_fc(_std_bin_inte2)))
|
|
179
|
+
|
|
180
|
+
_merge_lst.append(_merge_dt_fit)
|
|
181
|
+
_corr_lst.append(interp1d(_std_bin, _merge_dt_fit)(_bin_aps[_corr_aps_cond]))
|
|
182
|
+
|
|
183
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
184
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
185
|
+
|
|
186
|
+
_df_corr = DataFrame(_corr_lst, columns=_corr_aps_ky, index=_merge_idx) / _aps_ori.loc[_merge_idx, _corr_aps_ky]
|
|
187
|
+
|
|
188
|
+
## process output df
|
|
189
|
+
## average, align with index
|
|
190
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
191
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
192
|
+
_df.index.name = 'time'
|
|
193
|
+
return _df
|
|
194
|
+
|
|
195
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2), _out_df(_df_corr)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def merge_SMPS_APS(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000):
|
|
199
|
+
df_smps, df_aps = union_index(df_smps, df_aps)
|
|
200
|
+
|
|
201
|
+
## set to the same units
|
|
202
|
+
smps, aps_ori = df_smps.copy(), df_aps.copy()
|
|
203
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
204
|
+
aps_ori.columns = aps_ori.keys().to_numpy(float)
|
|
205
|
+
|
|
206
|
+
if aps_unit == 'um':
|
|
207
|
+
aps_ori.columns = aps_ori.keys() * 1e3
|
|
208
|
+
|
|
209
|
+
den_lst, mer_lst = [], []
|
|
210
|
+
aps_input = aps_ori.loc[:, aps_ori.keys() > 700].copy()
|
|
211
|
+
|
|
212
|
+
for _count in range(2):
|
|
213
|
+
|
|
214
|
+
## shift infomation, calculate by powerlaw fitting
|
|
215
|
+
shift, coe = _overlap_fitting(smps, aps_input, smps_overlap_lowbound, aps_fit_highbound)
|
|
216
|
+
|
|
217
|
+
## process data by shift infomation, and average data
|
|
218
|
+
shift = _shift_data_process(shift)
|
|
219
|
+
|
|
220
|
+
## merge aps and smps
|
|
221
|
+
merge_arg = (smps, aps_ori, shift, smps_overlap_lowbound, aps_fit_highbound, coe)
|
|
222
|
+
merge_data_mob, density, _corr = _merge_data(*merge_arg, 'mobility')
|
|
223
|
+
merge_data_aer, density, _ = _merge_data(*merge_arg, 'aerodynamic')
|
|
224
|
+
density.columns = ['density']
|
|
225
|
+
|
|
226
|
+
if _count == 0:
|
|
227
|
+
corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
|
|
228
|
+
corr = corr.mask(corr < 1, 1)
|
|
229
|
+
aps_ori.loc[:, corr.keys()] *= corr
|
|
230
|
+
|
|
231
|
+
aps_input = aps_ori.copy()
|
|
232
|
+
|
|
233
|
+
## out
|
|
234
|
+
out_dic = {
|
|
235
|
+
'data_all': merge_data_mob,
|
|
236
|
+
'data_all_aer': merge_data_aer,
|
|
237
|
+
'density_all': density,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
## process data
|
|
241
|
+
for _nam, _df in out_dic.items():
|
|
242
|
+
out_dic[_nam] = _df.reindex(smps.index).copy()
|
|
243
|
+
|
|
244
|
+
return out_dic
|