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,422 @@
|
|
|
1
|
+
# from ContainerHandle.dataProcess.config import _union_index
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from datetime import datetime as dtm
|
|
5
|
+
from functools import partial
|
|
6
|
+
from multiprocessing import Pool, cpu_count
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from pandas import DataFrame, concat, DatetimeIndex
|
|
10
|
+
# from scipy.interpolate import interp1d
|
|
11
|
+
from scipy.interpolate import UnivariateSpline as unvpline, interp1d
|
|
12
|
+
|
|
13
|
+
warnings.filterwarnings("ignore")
|
|
14
|
+
|
|
15
|
+
__all__ = ['_merge_SMPS_APS']
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _powerlaw_fit(_coeA, _coeB, _aps, _idx, _factor):
|
|
19
|
+
# breakpoint()
|
|
20
|
+
|
|
21
|
+
_smps_fit_df = _coeA * (_aps.keys().values / _factor) ** _coeB
|
|
22
|
+
return DataFrame(((_smps_fit_df.copy() - _aps.copy()) ** 2).sum(axis=1), columns=[_idx])
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Calculate S2
|
|
26
|
+
## 1. SMPS and APS power law fitting
|
|
27
|
+
## 2. shift factor from 0.5 ~ 3
|
|
28
|
+
## 3. calculate S2
|
|
29
|
+
## return : S2
|
|
30
|
+
# def _S2_calculate_dN(_smps, _aps):
|
|
31
|
+
def _powerlaw_fit_dN(_smps, _aps, _alg_type):
|
|
32
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range fitting : {_alg_type}\033[0m")
|
|
33
|
+
|
|
34
|
+
## overlap fitting
|
|
35
|
+
## parmeter
|
|
36
|
+
_dt_indx = _smps.index
|
|
37
|
+
|
|
38
|
+
## use SMPS data apply power law fitting
|
|
39
|
+
## y = Ax^B, A = e**coefa, B = coefb, x = logx, y = logy
|
|
40
|
+
## ref : http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
|
41
|
+
## power law fit to SMPS num conc at upper bins to log curve
|
|
42
|
+
|
|
43
|
+
## coefficient A, B
|
|
44
|
+
_smps_qc_cond = ((_smps != 0) & np.isfinite(_smps))
|
|
45
|
+
_smps_qc = _smps.where(_smps_qc_cond)
|
|
46
|
+
|
|
47
|
+
_size = _smps_qc_cond.sum(axis=1)
|
|
48
|
+
_size = _size.where(_size != 0.).copy()
|
|
49
|
+
|
|
50
|
+
_logx, _logy = np.log(_smps_qc.keys()._data.astype(float)), np.log(_smps_qc)
|
|
51
|
+
_x, _y, _xy, _xx = _logx.sum(), _logy.sum(axis=1), (_logx * _logy).sum(axis=1), (_logx ** 2).sum()
|
|
52
|
+
|
|
53
|
+
_coeB = ((_size * _xy - _x * _y) / (_size * _xx - _x ** 2.))
|
|
54
|
+
_coeA = np.exp((_y - _coeB * _x) / _size).values.reshape(-1, 1)
|
|
55
|
+
_coeB = _coeB.values.reshape(-1, 1)
|
|
56
|
+
|
|
57
|
+
## rebuild shift smps data by coe. A, B
|
|
58
|
+
## x_shift = (y_ori/A)**(1/B)
|
|
59
|
+
_aps_shift_x = (_aps / _coeA) ** (1 / _coeB)
|
|
60
|
+
_aps_shift_x = _aps_shift_x.where(np.isfinite(_aps_shift_x))
|
|
61
|
+
|
|
62
|
+
## the least squares of diameter
|
|
63
|
+
## the shift factor which the closest to 1
|
|
64
|
+
_shift_val = np.arange(0.3, 3.05, .05) ** .5
|
|
65
|
+
# _shift_val = np.arange(0.9, 1.805, .005)**.5
|
|
66
|
+
|
|
67
|
+
_shift_factor = DataFrame(columns=range(_shift_val.size), index=_aps_shift_x.index)
|
|
68
|
+
_shift_factor.loc[:, :] = _shift_val
|
|
69
|
+
|
|
70
|
+
# _dropna_idx = _shift_factor.dropna(how='all').index.copy()
|
|
71
|
+
_dropna_idx = _aps_shift_x.dropna(how='all').index.copy()
|
|
72
|
+
|
|
73
|
+
## use the target function to get the similar aps and smps bin
|
|
74
|
+
## S2 = sum( (smps_fit_line(dia) - aps(dia*shift_factor) )**2 )
|
|
75
|
+
## assumption : the same diameter between smps and aps should get the same conc.
|
|
76
|
+
|
|
77
|
+
## be sure they art in log value
|
|
78
|
+
_S2 = DataFrame(index=_aps_shift_x.index)
|
|
79
|
+
_dia_table = DataFrame(np.full(_aps_shift_x.shape, _aps_shift_x.keys()),
|
|
80
|
+
columns=_aps_shift_x.keys(), index=_aps_shift_x.index)
|
|
81
|
+
|
|
82
|
+
pool = Pool(cpu_count())
|
|
83
|
+
|
|
84
|
+
_S2 = pool.starmap(partial(_powerlaw_fit, _coeA, _coeB, _aps), list(enumerate(_shift_val)))
|
|
85
|
+
|
|
86
|
+
pool.close()
|
|
87
|
+
pool.join()
|
|
88
|
+
|
|
89
|
+
S2 = concat(_S2, axis=1)[np.arange(_shift_val.size)]
|
|
90
|
+
# S2 /= S2.max(axis=1).to_frame().values
|
|
91
|
+
|
|
92
|
+
shift_factor_dN = DataFrame(
|
|
93
|
+
_shift_factor.loc[_dropna_idx].values[range(len(_dropna_idx)), S2.loc[_dropna_idx].idxmin(axis=1).values],
|
|
94
|
+
index=_dropna_idx).reindex(_dt_indx).astype(float)
|
|
95
|
+
|
|
96
|
+
shift_factor_dN = shift_factor_dN.mask((shift_factor_dN ** 2 < 0.6) | (shift_factor_dN ** 2 > 2.6))
|
|
97
|
+
|
|
98
|
+
return shift_factor_dN
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _corr_fc(_aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth, _idx, _sh):
|
|
102
|
+
ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
|
|
103
|
+
dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
|
|
104
|
+
|
|
105
|
+
_aps_sh = _aps_dia / _sh
|
|
106
|
+
_aps_sh_inp = _aps_sh.where((_aps_sh >= 500) & (_aps_sh <= 1500.)).copy()
|
|
107
|
+
_aps_sh_corr = _aps_sh.where((_aps_sh >= _smps_dia[-1]) & (_aps_sh <= 1500.)).copy()
|
|
108
|
+
|
|
109
|
+
corr_x = np.append(_smps_dia, _aps_sh_corr.dropna())
|
|
110
|
+
|
|
111
|
+
input_x = np.append(_smps_dia, _aps_sh_inp.dropna())
|
|
112
|
+
input_y = concat([_smps_dn, _aps_dn.iloc[:, ~np.isnan(_aps_sh_inp)]], axis=1)
|
|
113
|
+
input_y.columns = input_x
|
|
114
|
+
|
|
115
|
+
input_x.sort()
|
|
116
|
+
input_y = input_y[input_x]
|
|
117
|
+
corr_y = input_y[corr_x]
|
|
118
|
+
|
|
119
|
+
S2_lst = []
|
|
120
|
+
for (_tm, _inp_y_dn), (_tm, _cor_y_dn) in zip(input_y.dropna(how='all').iterrows(),
|
|
121
|
+
corr_y.dropna(how='all').iterrows()):
|
|
122
|
+
## corr(spec_data, spec_spline)
|
|
123
|
+
_spl_dt = [unvpline(input_x, _inp_y, s=_smooth)(corr_x) for _inp_y in
|
|
124
|
+
[_inp_y_dn, ds_fc(_inp_y_dn), dv_fc(_inp_y_dn)]]
|
|
125
|
+
_cor_dt = [_cor_y_dn, ds_fc(_cor_y_dn), dv_fc(_cor_y_dn)]
|
|
126
|
+
|
|
127
|
+
_cor_all = sum([np.corrcoef(_cor, _spl)[0, 1] for _cor, _spl in zip(_cor_dt, _spl_dt)])
|
|
128
|
+
|
|
129
|
+
S2_lst.append((3 - _cor_all) / 3)
|
|
130
|
+
|
|
131
|
+
return DataFrame(S2_lst, columns=[_idx])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# def _S2_calculate_dSdV(_smps, _aps, _shft_dn, _S2, smps_ori, aps_ori):
|
|
135
|
+
# def _S2_calculate_dSdV(_smps, _aps, smps_ori=None):
|
|
136
|
+
def _corr_with_dNdSdV(_smps, _aps, _alg_type):
|
|
137
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92moverlap range correlation : {_alg_type}\033[0m")
|
|
138
|
+
|
|
139
|
+
_smps_dia = _smps.keys().astype(float)
|
|
140
|
+
_aps_dia = _aps.keys().astype(float)
|
|
141
|
+
|
|
142
|
+
all_index = _smps.index.copy()
|
|
143
|
+
qc_index = DatetimeIndex(set(_smps.dropna(how='all').index) & set(_aps.dropna(how='all').index)).sort_values()
|
|
144
|
+
|
|
145
|
+
_smps_dn = _smps.loc[qc_index].copy()
|
|
146
|
+
_aps_dn = _aps.loc[qc_index].copy()
|
|
147
|
+
|
|
148
|
+
ds_fc = lambda _dt: _dt * _dt.index ** 2 * np.pi
|
|
149
|
+
dv_fc = lambda _dt: _dt * _dt.index ** 3 * np.pi / 6
|
|
150
|
+
|
|
151
|
+
_std_bin = np.geomspace(11.8, 19810, 230)
|
|
152
|
+
_merge_bin = _std_bin[(_std_bin >= _smps_dia[-1]) & (_std_bin < 1500)].copy()
|
|
153
|
+
|
|
154
|
+
_smooth = 50
|
|
155
|
+
|
|
156
|
+
_shift_val = np.arange(0.5, 2.605, .005) ** .5
|
|
157
|
+
_shift_val = np.arange(0.9, 2.01, .01) ** .5
|
|
158
|
+
_shift_val = np.arange(0.9, 2.65, .05) ** .5
|
|
159
|
+
|
|
160
|
+
## spline fitting with shift aps and smps
|
|
161
|
+
pool = Pool(cpu_count())
|
|
162
|
+
|
|
163
|
+
S2_lst = pool.starmap(partial(_corr_fc, _aps_dia, _smps_dia, _smps_dn, _aps_dn, _smooth),
|
|
164
|
+
list(enumerate(_shift_val)))
|
|
165
|
+
|
|
166
|
+
pool.close()
|
|
167
|
+
pool.join()
|
|
168
|
+
|
|
169
|
+
S2_table = concat(S2_lst, axis=1).set_index(qc_index)[np.arange(_shift_val.size)].astype(float).dropna()
|
|
170
|
+
min_shft = S2_table.idxmin(axis=1).values
|
|
171
|
+
|
|
172
|
+
return DataFrame(_shift_val[min_shft.astype(int)], index=S2_table.index).astype(float).reindex(_smps.index)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## Create merge data
|
|
176
|
+
## shift all smps bin and remove the aps bin which smaller than the latest old smps bin
|
|
177
|
+
## Return : merge bins, merge data, density
|
|
178
|
+
def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _shift_mode, _alg_type):
|
|
179
|
+
print(f"\t\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mcreate merge data : {_shift_mode} and {_alg_type}\033[0m")
|
|
180
|
+
|
|
181
|
+
_ori_idx = _smps_ori.index.copy()
|
|
182
|
+
# _merge_idx = _smps_ori.loc[_aps_ori.dropna(how='all').index].dropna(how='all').index
|
|
183
|
+
|
|
184
|
+
_corr_aps_cond = _aps_ori.keys() < 700
|
|
185
|
+
_corr_aps_ky = _aps_ori.keys()[_corr_aps_cond]
|
|
186
|
+
|
|
187
|
+
_merge_idx = DatetimeIndex(set(_smps_ori.dropna(how='all').index) & set(_aps_ori.dropna(how='all').index) &
|
|
188
|
+
set(_shift_ori.dropna(how='all').index)).sort_values()
|
|
189
|
+
|
|
190
|
+
_smps, _aps, _shift = _smps_ori.loc[_merge_idx], _aps_ori.loc[_merge_idx], _shift_ori.loc[_merge_idx].values
|
|
191
|
+
|
|
192
|
+
## parameter
|
|
193
|
+
_smps_key, _aps_key = _smps.keys()._data.astype(float), _aps.keys()._data.astype(float)
|
|
194
|
+
|
|
195
|
+
_cntr = 1000
|
|
196
|
+
_bin_lb = _smps_key[-1]
|
|
197
|
+
|
|
198
|
+
## make shift bins
|
|
199
|
+
_smps_bin = np.full(_smps.shape, _smps_key)
|
|
200
|
+
_aps_bin = np.full(_aps.shape, _aps_key)
|
|
201
|
+
|
|
202
|
+
_std_bin = np.geomspace(_smps_key[0], _aps_key[-1], 230)
|
|
203
|
+
_std_bin_merge = _std_bin[(_std_bin < _cntr) & (_std_bin > _bin_lb)]
|
|
204
|
+
_std_bin_inte1 = _std_bin[_std_bin <= _bin_lb]
|
|
205
|
+
_std_bin_inte2 = _std_bin[_std_bin >= _cntr]
|
|
206
|
+
|
|
207
|
+
if _shift_mode == 'mobility':
|
|
208
|
+
_aps_bin /= _shift
|
|
209
|
+
|
|
210
|
+
elif _shift_mode == 'aerodynamic':
|
|
211
|
+
_smps_bin *= _shift
|
|
212
|
+
|
|
213
|
+
## merge
|
|
214
|
+
_merge_lst, _corr_lst = [], []
|
|
215
|
+
for _bin_smps, _bin_aps, _dt_smps, _dt_aps, _sh in zip(_smps_bin, _aps_bin, _smps.values, _aps.values, _shift):
|
|
216
|
+
## keep complete smps bins and data
|
|
217
|
+
## remove the aps bin data lower than smps bin
|
|
218
|
+
_condi = _bin_aps >= _bin_smps[-1]
|
|
219
|
+
|
|
220
|
+
_merge_bin = np.hstack((_bin_smps, _bin_aps[_condi]))
|
|
221
|
+
_merge_dt = np.hstack((_dt_smps, _dt_aps[_condi]))
|
|
222
|
+
|
|
223
|
+
_merge_fit_loc = (_merge_bin < 1500) & (_merge_bin > _smps_lb)
|
|
224
|
+
|
|
225
|
+
## coeA and coeB
|
|
226
|
+
_unvpl_fc = unvpline(np.log(_merge_bin[_merge_fit_loc]), np.log(_merge_dt[_merge_fit_loc]), s=50)
|
|
227
|
+
_inte_fc = interp1d(_merge_bin, _merge_dt, kind='linear', fill_value='extrapolate')
|
|
228
|
+
|
|
229
|
+
_merge_dt_fit = np.hstack((_inte_fc(_std_bin_inte1), np.exp(_unvpl_fc(np.log(_std_bin_merge))),
|
|
230
|
+
_inte_fc(_std_bin_inte2)))
|
|
231
|
+
|
|
232
|
+
_merge_lst.append(_merge_dt_fit)
|
|
233
|
+
_corr_lst.append(interp1d(_std_bin, _merge_dt_fit)(_bin_aps[_corr_aps_cond]))
|
|
234
|
+
|
|
235
|
+
_df_merge = DataFrame(_merge_lst, columns=_std_bin, index=_merge_idx)
|
|
236
|
+
_df_merge = _df_merge.mask(_df_merge < 0)
|
|
237
|
+
|
|
238
|
+
_df_corr = DataFrame(_corr_lst, columns=_corr_aps_ky, index=_merge_idx) / _aps_ori.loc[_merge_idx, _corr_aps_ky]
|
|
239
|
+
|
|
240
|
+
## process output df
|
|
241
|
+
## average, align with index
|
|
242
|
+
def _out_df(*_df_arg, **_df_kwarg):
|
|
243
|
+
_df = DataFrame(*_df_arg, **_df_kwarg).reindex(_ori_idx)
|
|
244
|
+
_df.index.name = 'time'
|
|
245
|
+
return _df
|
|
246
|
+
|
|
247
|
+
return _out_df(_df_merge), _out_df(_shift_ori ** 2), _out_df(_df_corr)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _fitness_func(psd, rho, pm25):
|
|
251
|
+
psd_pm25 = psd[psd.keys()[psd.keys().values <= 2500]] * np.diff(np.log10(psd.keys())).mean()
|
|
252
|
+
rho_pm25 = pm25 / (psd_pm25 * np.pi * psd_pm25.keys().values ** 3 / 6 * 1e-9).sum(axis=1, min_count=1)
|
|
253
|
+
|
|
254
|
+
return (rho['density'] - rho_pm25) ** 2
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def merge_SMPS_APS(df_smps, df_aps, df_pm25, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000,
|
|
258
|
+
dndsdv_alg=True, times_range=(0.8, 1.25, .05)):
|
|
259
|
+
# merge_data, merge_data_dn, merge_data_dsdv, merge_data_cor_dn, density, density_dn, density_dsdv, density_cor_dn = [DataFrame([np.nan])] * 8
|
|
260
|
+
|
|
261
|
+
## set to the same units
|
|
262
|
+
smps, aps = df_smps.copy(), df_aps.copy()
|
|
263
|
+
smps.columns = smps.keys().to_numpy(float)
|
|
264
|
+
aps.columns = aps.keys().to_numpy(float)
|
|
265
|
+
|
|
266
|
+
if aps_unit == 'um':
|
|
267
|
+
aps.columns = aps.keys() * 1e3
|
|
268
|
+
|
|
269
|
+
fitness_typ = dict(dn=[], cor_dn=[], dndsdv=[], cor_dndsdv=[])
|
|
270
|
+
shift_typ = dict(dn=[], cor_dn=[], dndsdv=[], cor_dndsdv=[])
|
|
271
|
+
oth_typ = dict()
|
|
272
|
+
|
|
273
|
+
times_ary = np.arange(*times_range).round(4)
|
|
274
|
+
# times_ary = np.arange(*(0.8, 0.9, .05)).round(4)
|
|
275
|
+
|
|
276
|
+
for times in times_ary:
|
|
277
|
+
|
|
278
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mSMPS times value : {times}\033[0m")
|
|
279
|
+
|
|
280
|
+
aps_input = aps.copy()
|
|
281
|
+
aps_over = aps_input.loc[:, (aps.keys() > 700) & (aps.keys() < 1000)].copy()
|
|
282
|
+
|
|
283
|
+
smps_input = (smps * times).copy()
|
|
284
|
+
smps_over = smps_input[smps.keys()[smps.keys() > 500]].copy()
|
|
285
|
+
|
|
286
|
+
for _count in range(2):
|
|
287
|
+
|
|
288
|
+
## shift data calculate
|
|
289
|
+
## original
|
|
290
|
+
if _count == 0:
|
|
291
|
+
alg_type = 'dn'
|
|
292
|
+
shift = _powerlaw_fit_dN(smps_over, aps_over, alg_type)
|
|
293
|
+
|
|
294
|
+
if dndsdv_alg:
|
|
295
|
+
shift_dsdv = _corr_with_dNdSdV(smps_over, aps_over, 'dndsdv').mask(shift.isna())
|
|
296
|
+
|
|
297
|
+
## aps correct
|
|
298
|
+
else:
|
|
299
|
+
alg_type = 'cor_dndsdv'
|
|
300
|
+
shift_cor = _powerlaw_fit_dN(smps_over, aps_over, 'cor_dn')
|
|
301
|
+
|
|
302
|
+
if dndsdv_alg:
|
|
303
|
+
shift = _corr_with_dNdSdV(smps_over, aps_over, alg_type).mask(shift_cor.isna())
|
|
304
|
+
|
|
305
|
+
## merge aps and smps
|
|
306
|
+
## 1. power law fit (dn) -> return dn data and aps correct factor
|
|
307
|
+
## 2. correaltion with dn, ds, dv -> return corrected dn_ds_dv data
|
|
308
|
+
if (alg_type == 'dn') | dndsdv_alg:
|
|
309
|
+
merge_arg = (smps_input, aps_input, shift, smps_overlap_lowbound, aps_fit_highbound)
|
|
310
|
+
|
|
311
|
+
merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
312
|
+
density.columns = ['density']
|
|
313
|
+
|
|
314
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data, density, df_pm25))
|
|
315
|
+
shift_typ[alg_type].append(shift[0])
|
|
316
|
+
|
|
317
|
+
## without aps correct
|
|
318
|
+
if _count == 0:
|
|
319
|
+
## merge aps and smps
|
|
320
|
+
## dn_ds_dv data
|
|
321
|
+
if dndsdv_alg:
|
|
322
|
+
alg_type = 'dndsdv'
|
|
323
|
+
merge_arg = (smps_input, aps_input, shift_dsdv, smps_overlap_lowbound, aps_fit_highbound)
|
|
324
|
+
|
|
325
|
+
merge_data_dsdv, density_dsdv, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
326
|
+
density_dsdv.columns = ['density']
|
|
327
|
+
|
|
328
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data_dsdv, density_dsdv, df_pm25))
|
|
329
|
+
shift_typ[alg_type].append(shift_dsdv[0])
|
|
330
|
+
|
|
331
|
+
## dn data
|
|
332
|
+
merge_data_dn, density_dn = merge_data.copy(), density.copy()
|
|
333
|
+
|
|
334
|
+
## correct aps data
|
|
335
|
+
corr = _corr.resample('1d').mean().reindex(smps.index).ffill()
|
|
336
|
+
corr = corr.mask(corr < 1, 1)
|
|
337
|
+
|
|
338
|
+
aps_input.loc[:, corr.keys()] *= corr
|
|
339
|
+
aps_over = aps_input.copy()
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
## with aps correct
|
|
343
|
+
else:
|
|
344
|
+
## merge aps and smps
|
|
345
|
+
## dn data
|
|
346
|
+
alg_type = 'cor_dn'
|
|
347
|
+
merge_arg = (smps_input, aps_input, shift_cor, smps_overlap_lowbound, aps_fit_highbound)
|
|
348
|
+
|
|
349
|
+
merge_data_cor_dn, density_cor_dn, _ = _merge_data(*merge_arg, 'mobility', _alg_type=alg_type)
|
|
350
|
+
density_cor_dn.columns = ['density']
|
|
351
|
+
|
|
352
|
+
fitness_typ[alg_type].append(_fitness_func(merge_data_cor_dn, density_cor_dn, df_pm25))
|
|
353
|
+
shift_typ[alg_type].append(shift_cor[0])
|
|
354
|
+
|
|
355
|
+
## get times value and shift value
|
|
356
|
+
out_dic = {}
|
|
357
|
+
for (_typ, _lst), (_typ, _shft) in zip(fitness_typ.items(), shift_typ.items()):
|
|
358
|
+
oth_typ[_typ] = None
|
|
359
|
+
if len(_lst) == 0: continue
|
|
360
|
+
|
|
361
|
+
df_times_min = concat(_lst, axis=1, keys=range(len(_lst))).idxmin(axis=1).dropna().astype(int)
|
|
362
|
+
df_shift = concat(_shft, axis=1, keys=times_ary.tolist()).loc[df_times_min.index].values[
|
|
363
|
+
range(len(df_times_min.index)), df_times_min.values]
|
|
364
|
+
|
|
365
|
+
oth_typ[_typ] = DataFrame(np.array([df_shift, times_ary[df_times_min.values]]).T,
|
|
366
|
+
index=df_times_min.index, columns=['shift', 'times']).reindex(smps.index)
|
|
367
|
+
|
|
368
|
+
## re-calculate merge_data
|
|
369
|
+
alg_type = ['dn', 'cor_dn', 'dndsdv', 'cor_dndsdv'] if dndsdv_alg else ['dn', 'cor_dn']
|
|
370
|
+
|
|
371
|
+
out_dic = {}
|
|
372
|
+
den_lst, times_lst = [], []
|
|
373
|
+
for _typ in alg_type:
|
|
374
|
+
print(f"\t\t{dtm.now().strftime('%m/%d %X')} : \033[92mre-caculate merge data with times: {_typ}\033[0m")
|
|
375
|
+
typ = oth_typ[_typ]
|
|
376
|
+
smps_input = smps.copy() * typ['times'].to_frame().values
|
|
377
|
+
|
|
378
|
+
corr_typ = corr if 'cor' in _typ else 1
|
|
379
|
+
aps_input = aps.copy()
|
|
380
|
+
aps_input.loc[:, corr.keys()] *= corr_typ
|
|
381
|
+
|
|
382
|
+
merge_arg = (smps_input, aps_input, typ['shift'].to_frame(), smps_overlap_lowbound, aps_fit_highbound)
|
|
383
|
+
|
|
384
|
+
merge_data, density, _corr = _merge_data(*merge_arg, 'mobility', _alg_type=_typ)
|
|
385
|
+
density.columns = ['density']
|
|
386
|
+
|
|
387
|
+
out_dic[f'data_{_typ}'] = merge_data
|
|
388
|
+
|
|
389
|
+
den_lst.append(density)
|
|
390
|
+
times_lst.append(typ['times'])
|
|
391
|
+
|
|
392
|
+
out_rho = concat(den_lst, axis=1)
|
|
393
|
+
out_times = concat(times_lst, axis=1)
|
|
394
|
+
out_rho.columns = alg_type
|
|
395
|
+
out_times.columns = alg_type
|
|
396
|
+
|
|
397
|
+
# breakpoint()
|
|
398
|
+
|
|
399
|
+
## out
|
|
400
|
+
out_dic.update(dict(density=out_rho, times=out_times))
|
|
401
|
+
|
|
402
|
+
# out_dic = {
|
|
403
|
+
# 'data_cor_dndsdv' : merge_data,
|
|
404
|
+
# 'data_dn' : merge_data_dn,
|
|
405
|
+
# 'data_dndsdv' : merge_data_dsdv,
|
|
406
|
+
# 'data_cor_dn' : merge_data_cor_dn,
|
|
407
|
+
|
|
408
|
+
# 'density' : out_rho,
|
|
409
|
+
|
|
410
|
+
# 'data_all_aer' : merge_data_aer,
|
|
411
|
+
|
|
412
|
+
# 'density_cor_dndsdv' : density,
|
|
413
|
+
# 'density_dn' : density_dn,
|
|
414
|
+
# 'density_dndsdv' : density_dsdv,
|
|
415
|
+
# 'density_cor_dn' : density_cor_dn,
|
|
416
|
+
# }
|
|
417
|
+
|
|
418
|
+
## process data
|
|
419
|
+
for _nam, _df in out_dic.items():
|
|
420
|
+
out_dic[_nam] = _df.reindex(smps.index).copy()
|
|
421
|
+
|
|
422
|
+
return out_dic
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy import exp, log
|
|
3
|
+
from scipy.signal import find_peaks
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def geometric(dp: np.ndarray,
|
|
7
|
+
dist: np.ndarray
|
|
8
|
+
) -> tuple[float, float]:
|
|
9
|
+
""" Calculate the geometric mean and standard deviation. """
|
|
10
|
+
|
|
11
|
+
_gmd = (((dist * log(dp)).sum()) / dist.sum())
|
|
12
|
+
|
|
13
|
+
logdp_mesh, gmd_mesh = np.meshgrid(log(dp), _gmd)
|
|
14
|
+
_gsd = ((((logdp_mesh - gmd_mesh) ** 2) * dist).sum() / dist.sum()) ** .5
|
|
15
|
+
|
|
16
|
+
return exp(_gmd), exp(_gsd)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def contribution(dp: np.ndarray,
|
|
20
|
+
dist: np.ndarray
|
|
21
|
+
) -> tuple[float, float, float]:
|
|
22
|
+
""" Calculate the relative contribution of each mode. """
|
|
23
|
+
|
|
24
|
+
ultra = dist[(dp >= 11.8) & (dp < 100)].sum() / dist.sum()
|
|
25
|
+
accum = dist[(dp >= 100) & (dp < 1000)].sum() / dist.sum()
|
|
26
|
+
coars = dist[(dp >= 1000) & (dp < 2500)].sum() / dist.sum()
|
|
27
|
+
|
|
28
|
+
return ultra, accum, coars
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def mode(dp: np.ndarray,
|
|
32
|
+
dist: np.ndarray
|
|
33
|
+
) -> np.ndarray:
|
|
34
|
+
""" Find three peak mode in distribution. """
|
|
35
|
+
|
|
36
|
+
min_value = np.array([dist.min()])
|
|
37
|
+
mode, _ = find_peaks(np.concatenate([min_value, dist, min_value]), distance=len(dist) - 1)
|
|
38
|
+
|
|
39
|
+
return dp[mode - 1]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def properties(dist,
|
|
43
|
+
dp: np.ndarray,
|
|
44
|
+
dlogdp: np.ndarray,
|
|
45
|
+
weighting: str
|
|
46
|
+
) -> dict:
|
|
47
|
+
""" for apply """
|
|
48
|
+
dist = np.array(dist)
|
|
49
|
+
|
|
50
|
+
gmd, gsd = geometric(dp, dist)
|
|
51
|
+
ultra, accum, coarse = contribution(dp, dist)
|
|
52
|
+
peak = mode(dp, dist)
|
|
53
|
+
|
|
54
|
+
return {key: round(value, 3) for key, value in
|
|
55
|
+
{f'total_{weighting}': (dist * dlogdp).sum(),
|
|
56
|
+
f'GMD_{weighting}': gmd,
|
|
57
|
+
f'GSD_{weighting}': gsd,
|
|
58
|
+
f'mode_{weighting}': peak[0],
|
|
59
|
+
f'ultra_{weighting}': ultra,
|
|
60
|
+
f'accum_{weighting}': accum,
|
|
61
|
+
f'coarse_{weighting}': coarse}
|
|
62
|
+
.items()}
|
|
Binary file
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from pandas import DataFrame, read_json, concat
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _basic(_df_voc):
|
|
7
|
+
with (Path(__file__).parent / 'support_voc.json').open('r', encoding='utf-8', errors='ignore') as f:
|
|
8
|
+
_par = read_json(f)
|
|
9
|
+
|
|
10
|
+
# parameter
|
|
11
|
+
_keys = _df_voc.keys()
|
|
12
|
+
|
|
13
|
+
invalid_keys = [key for key in _df_voc.keys() if key not in set(_par.keys())]
|
|
14
|
+
|
|
15
|
+
if invalid_keys:
|
|
16
|
+
raise KeyError(f'\n\t\t{invalid_keys} are not supported keys.'
|
|
17
|
+
f'\n\t\tPlease check the\033[91m AeroViz/docs/instruments/voc.md\033[0m file to use the correct name.')
|
|
18
|
+
|
|
19
|
+
_MW, _MIR, _SOAP, _KOH = _par.loc['MW', :], _par.loc['MIR', :], _par.loc['SOAP', :], _par.loc['KOH', :]
|
|
20
|
+
|
|
21
|
+
_voc_classify = {
|
|
22
|
+
'alkane_total': ['Ethane', 'Propane', 'Isobutane', 'n-Butane', 'Isopentane', 'n-Pentane', 'n-Hexane',
|
|
23
|
+
'n-Heptane', 'n-Octane', 'n-Nonane', 'n-Decane', 'n-Undecane', 'n-Dodecane',
|
|
24
|
+
|
|
25
|
+
'Cyclopentane', 'Methylcyclopentane', 'Cyclohexane', 'Methylcyclohexane',
|
|
26
|
+
|
|
27
|
+
'2,2-Dimethylbutane', '2,3-Dimethylbutane', '2-Methylpentane', '3-Methylpentane',
|
|
28
|
+
'2,4-Dimethylpentane', '2-Methylhexane', '3-Methylhexane',
|
|
29
|
+
'2,2,4-Trimethylpentane', '2,3,4-Trimethylpentane', '2-Methylheptane', '3-Methylheptane'],
|
|
30
|
+
|
|
31
|
+
'alkene_total': ['Ethylene', 'Propylene', '1-Butene', 't-2-Butene', 'cis-2-Butene', '1-Pentene', 't-2-Pentene',
|
|
32
|
+
'cis-2-Pentene', '1-Hexene', 'Isoprene', '1.3-Butadiene', '1-Octene'],
|
|
33
|
+
|
|
34
|
+
'aromatic_total': ['Benzene', 'Toluene', 'Ethylbenzene', 'm/p-Xylene', 'o-Xylene', 'Styrene',
|
|
35
|
+
'Isopropylbenzene',
|
|
36
|
+
'n-Propylbenzene', 'm-Ethyltoluene', 'p-Ethyltoluene', 'o-Ethyltoluene', 'm-Diethylbenzene',
|
|
37
|
+
'p-Diethylbenzene', '1,2,4-Trimethylbenzene', '1,2,3-Trimethylbenzene',
|
|
38
|
+
'1,3,5-Trimethylbenzene', ],
|
|
39
|
+
|
|
40
|
+
'alkyne_total': ['Acetylene'],
|
|
41
|
+
|
|
42
|
+
'OVOC': ['Acetaldehyde', 'Ethanol', 'Acetone', 'IPA', 'Ethyl Acetate', 'Butyl Acetate'],
|
|
43
|
+
|
|
44
|
+
'ClVOC': ['VCM', 'TCE', 'PCE', '1.4-DCB', '1.2-DCB'],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_df_MW = (_df_voc * _MW).copy()
|
|
48
|
+
_df_dic = {
|
|
49
|
+
'Conc': _df_voc.copy(),
|
|
50
|
+
'OFP': _df_MW / 48 * _MIR,
|
|
51
|
+
'SOAP': _df_MW / 24.5 * _SOAP / 100 * 0.054,
|
|
52
|
+
'LOH': _df_MW / 24.5 / _MW * 0.602 * _KOH,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# calculate
|
|
56
|
+
_out = {}
|
|
57
|
+
for _nam, _df in _df_dic.items():
|
|
58
|
+
|
|
59
|
+
_df_out = DataFrame(index=_df_voc.index)
|
|
60
|
+
|
|
61
|
+
for _voc_nam, _voc_lst in _voc_classify.items():
|
|
62
|
+
_lst = list(set(_keys) & set(_voc_lst))
|
|
63
|
+
if len(_lst) == 0:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
_df_out = concat([_df[_lst], _df_out], axis=1)
|
|
67
|
+
|
|
68
|
+
_df_out[_voc_nam] = _df[_lst].sum(axis=1, min_count=1)
|
|
69
|
+
|
|
70
|
+
_df_out['Total'] = _df.sum(axis=1, min_count=1)
|
|
71
|
+
|
|
72
|
+
_out[_nam] = _df_out
|
|
73
|
+
|
|
74
|
+
return _out
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def markdown_table_to_dataframe():
|
|
78
|
+
import pandas as pd
|
|
79
|
+
from pathlib import Path
|
|
80
|
+
|
|
81
|
+
# support_voc.md
|
|
82
|
+
with open(Path(__file__).parent / 'support_voc.md', 'r', encoding='utf-8') as file:
|
|
83
|
+
markdown_content = file.read()
|
|
84
|
+
|
|
85
|
+
# 將內容分割成行
|
|
86
|
+
lines = markdown_content.strip().split('\n')
|
|
87
|
+
|
|
88
|
+
# 提取表頭
|
|
89
|
+
headers = [col.strip() for col in lines[0].split('|')[1:-1]]
|
|
90
|
+
|
|
91
|
+
# 解析數據行
|
|
92
|
+
data = []
|
|
93
|
+
for line in lines[2:]: # 跳過表頭和分隔行
|
|
94
|
+
columns = [col.strip() for col in line.split('|')[1:-1]]
|
|
95
|
+
data.append(columns)
|
|
96
|
+
|
|
97
|
+
# 創建 DataFrame
|
|
98
|
+
df = pd.DataFrame(data, columns=headers)
|
|
99
|
+
|
|
100
|
+
# 轉換數據類型
|
|
101
|
+
numeric_columns = ['MIR', 'MW', 'SOAP', 'KOH']
|
|
102
|
+
for col in numeric_columns:
|
|
103
|
+
df[col] = pd.to_numeric(df[col], errors='coerce')
|
|
104
|
+
df = df.set_index('Species').T
|
|
105
|
+
|
|
106
|
+
df = df.iloc[:, :-7]
|
|
107
|
+
|
|
108
|
+
df.to_json(Path(__file__).parent / 'support_voc.json', indent=4)
|