AeroViz 0.1.21__py3-none-any.whl

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