AeroViz 0.1.0__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.

Potentially problematic release.


This version of AeroViz might be problematic. Click here for more details.

Files changed (102) hide show
  1. AeroViz/__init__.py +15 -0
  2. AeroViz/dataProcess/Chemistry/__init__.py +63 -0
  3. AeroViz/dataProcess/Chemistry/_calculate.py +27 -0
  4. AeroViz/dataProcess/Chemistry/_isoropia.py +99 -0
  5. AeroViz/dataProcess/Chemistry/_mass_volume.py +175 -0
  6. AeroViz/dataProcess/Chemistry/_ocec.py +184 -0
  7. AeroViz/dataProcess/Chemistry/_partition.py +29 -0
  8. AeroViz/dataProcess/Chemistry/_teom.py +16 -0
  9. AeroViz/dataProcess/Optical/_IMPROVE.py +61 -0
  10. AeroViz/dataProcess/Optical/__init__.py +62 -0
  11. AeroViz/dataProcess/Optical/_absorption.py +54 -0
  12. AeroViz/dataProcess/Optical/_extinction.py +36 -0
  13. AeroViz/dataProcess/Optical/_mie.py +16 -0
  14. AeroViz/dataProcess/Optical/_mie_sd.py +143 -0
  15. AeroViz/dataProcess/Optical/_scattering.py +30 -0
  16. AeroViz/dataProcess/SizeDistr/__init__.py +61 -0
  17. AeroViz/dataProcess/SizeDistr/__merge.py +250 -0
  18. AeroViz/dataProcess/SizeDistr/_merge.py +245 -0
  19. AeroViz/dataProcess/SizeDistr/_merge_v1.py +254 -0
  20. AeroViz/dataProcess/SizeDistr/_merge_v2.py +243 -0
  21. AeroViz/dataProcess/SizeDistr/_merge_v3.py +518 -0
  22. AeroViz/dataProcess/SizeDistr/_merge_v4.py +424 -0
  23. AeroViz/dataProcess/SizeDistr/_size_distr.py +93 -0
  24. AeroViz/dataProcess/VOC/__init__.py +19 -0
  25. AeroViz/dataProcess/VOC/_potential_par.py +76 -0
  26. AeroViz/dataProcess/__init__.py +11 -0
  27. AeroViz/dataProcess/core/__init__.py +92 -0
  28. AeroViz/plot/__init__.py +7 -0
  29. AeroViz/plot/distribution/__init__.py +1 -0
  30. AeroViz/plot/distribution/distribution.py +582 -0
  31. AeroViz/plot/improve/__init__.py +1 -0
  32. AeroViz/plot/improve/improve.py +240 -0
  33. AeroViz/plot/meteorology/__init__.py +1 -0
  34. AeroViz/plot/meteorology/meteorology.py +317 -0
  35. AeroViz/plot/optical/__init__.py +2 -0
  36. AeroViz/plot/optical/aethalometer.py +77 -0
  37. AeroViz/plot/optical/optical.py +388 -0
  38. AeroViz/plot/templates/__init__.py +8 -0
  39. AeroViz/plot/templates/contour.py +47 -0
  40. AeroViz/plot/templates/corr_matrix.py +108 -0
  41. AeroViz/plot/templates/diurnal_pattern.py +42 -0
  42. AeroViz/plot/templates/event_evolution.py +65 -0
  43. AeroViz/plot/templates/koschmieder.py +156 -0
  44. AeroViz/plot/templates/metal_heatmap.py +57 -0
  45. AeroViz/plot/templates/regression.py +256 -0
  46. AeroViz/plot/templates/scatter.py +130 -0
  47. AeroViz/plot/templates/templates.py +398 -0
  48. AeroViz/plot/timeseries/__init__.py +1 -0
  49. AeroViz/plot/timeseries/timeseries.py +317 -0
  50. AeroViz/plot/utils/__init__.py +3 -0
  51. AeroViz/plot/utils/_color.py +71 -0
  52. AeroViz/plot/utils/_decorator.py +74 -0
  53. AeroViz/plot/utils/_unit.py +55 -0
  54. AeroViz/process/__init__.py +31 -0
  55. AeroViz/process/core/DataProc.py +19 -0
  56. AeroViz/process/core/SizeDist.py +90 -0
  57. AeroViz/process/core/__init__.py +4 -0
  58. AeroViz/process/method/PyMieScatt_update.py +567 -0
  59. AeroViz/process/method/__init__.py +2 -0
  60. AeroViz/process/method/mie_theory.py +258 -0
  61. AeroViz/process/method/prop.py +62 -0
  62. AeroViz/process/script/AbstractDistCalc.py +143 -0
  63. AeroViz/process/script/Chemical.py +176 -0
  64. AeroViz/process/script/IMPACT.py +49 -0
  65. AeroViz/process/script/IMPROVE.py +161 -0
  66. AeroViz/process/script/Others.py +65 -0
  67. AeroViz/process/script/PSD.py +103 -0
  68. AeroViz/process/script/PSD_dry.py +94 -0
  69. AeroViz/process/script/__init__.py +5 -0
  70. AeroViz/process/script/retrieve_RI.py +70 -0
  71. AeroViz/rawDataReader/__init__.py +68 -0
  72. AeroViz/rawDataReader/core/__init__.py +397 -0
  73. AeroViz/rawDataReader/script/AE33.py +31 -0
  74. AeroViz/rawDataReader/script/AE43.py +34 -0
  75. AeroViz/rawDataReader/script/APS_3321.py +47 -0
  76. AeroViz/rawDataReader/script/Aurora.py +38 -0
  77. AeroViz/rawDataReader/script/BC1054.py +46 -0
  78. AeroViz/rawDataReader/script/EPA_vertical.py +18 -0
  79. AeroViz/rawDataReader/script/GRIMM.py +35 -0
  80. AeroViz/rawDataReader/script/IGAC_TH.py +104 -0
  81. AeroViz/rawDataReader/script/IGAC_ZM.py +90 -0
  82. AeroViz/rawDataReader/script/MA350.py +45 -0
  83. AeroViz/rawDataReader/script/NEPH.py +57 -0
  84. AeroViz/rawDataReader/script/OCEC_LCRES.py +34 -0
  85. AeroViz/rawDataReader/script/OCEC_RES.py +28 -0
  86. AeroViz/rawDataReader/script/SMPS_TH.py +41 -0
  87. AeroViz/rawDataReader/script/SMPS_aim11.py +51 -0
  88. AeroViz/rawDataReader/script/SMPS_genr.py +51 -0
  89. AeroViz/rawDataReader/script/TEOM.py +46 -0
  90. AeroViz/rawDataReader/script/Table.py +28 -0
  91. AeroViz/rawDataReader/script/VOC_TH.py +30 -0
  92. AeroViz/rawDataReader/script/VOC_ZM.py +37 -0
  93. AeroViz/rawDataReader/script/__init__.py +22 -0
  94. AeroViz/tools/__init__.py +3 -0
  95. AeroViz/tools/database.py +94 -0
  96. AeroViz/tools/dataclassifier.py +117 -0
  97. AeroViz/tools/datareader.py +66 -0
  98. AeroViz-0.1.0.dist-info/LICENSE +21 -0
  99. AeroViz-0.1.0.dist-info/METADATA +117 -0
  100. AeroViz-0.1.0.dist-info/RECORD +102 -0
  101. AeroViz-0.1.0.dist-info/WHEEL +5 -0
  102. AeroViz-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,388 @@
1
+ import math
2
+ from typing import Literal
3
+
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ # from PyMieScatt import ScatteringFunction
7
+ from matplotlib.pyplot import Figure, Axes
8
+
9
+ from AeroViz.plot.utils import *
10
+ from AeroViz.process.method.mie_theory import Mie_Q, Mie_MEE, Mie_PESD
11
+
12
+ __all__ = ['Q_plot',
13
+ 'RI_couple',
14
+ 'RRI_2D',
15
+ # 'scattering_phase',
16
+ 'response_surface',
17
+ ]
18
+
19
+ mapping_dic = {'AS': {'m': 1.53 + 0j, 'density': 1.73, 'label': fr'$NH_{4}NO_{3}$', 'color': '#A65E58'},
20
+ 'AN': {'m': 1.55 + 0j, 'density': 1.77, 'label': fr'$(NH_{4})_{2}SO_{4}$', 'color': '#A5BF6B'},
21
+ 'OM': {'m': 1.54 + 0j, 'density': 1.40, 'label': 'OM', 'color': '#F2BF5E'},
22
+ 'Soil': {'m': 1.56 + 0.01j, 'density': 2.60, 'label': 'Soil', 'color': '#3F83BF'},
23
+ 'SS': {'m': 1.54 + 0j, 'density': 1.90, 'label': 'SS', 'color': '#B777C2'},
24
+ 'BC': {'m': 1.80 + 0.54j, 'density': 1.50, 'label': 'BC', 'color': '#D1CFCB'},
25
+ 'Water': {'m': 1.333 + 0j, 'density': 1.00, 'label': 'Water', 'color': '#96c8e6'}}
26
+
27
+
28
+ @set_figure
29
+ def Q_plot(species: Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"] | list[
30
+ Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]],
31
+ x: Literal["dp", "sp"] = 'dp',
32
+ y: Literal["Q", "MEE"] = "Q",
33
+ mode: Literal["ext", "sca", "abs"] = 'ext',
34
+ **kwargs) -> tuple[Figure, Axes]:
35
+ """
36
+ Generate a plot showing optical efficiency or mass optical efficiency for different particle species.
37
+
38
+ Parameters
39
+ ----------
40
+ species : Union[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"], list[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]]]
41
+ The particle species or list of particle species to plot. Valid species include 'AS' (Ammonium Sulfate),
42
+ 'AN' (Ammonium Nitrate), 'OM' (Organic Matter), 'Soil', 'SS' (Sea Salt), 'BC' (Black Carbon), and 'Water'.
43
+
44
+ x : Literal["dp", "sp"], optional
45
+ The x-axis parameter. 'dp' represents particle diameter, and 'sp' represents size parameter (alpha).
46
+ Default is 'dp'.
47
+
48
+ y : Literal["Q", "MEE"], optional
49
+ The y-axis parameter. 'Q' represents optical efficiency (Q_ext, Q_sca, Q_abs), and 'MEE' represents
50
+ mass optical efficiency (MEE, MSE, MAE). Default is 'Q'.
51
+
52
+ mode : Literal["ext", "sca", "abs"], optional
53
+ The mode of efficiency to plot. 'ext' for extinction efficiency, 'sca' for scattering efficiency,
54
+ and 'abs' for absorption efficiency. Default is 'ext'.
55
+
56
+ **kwargs
57
+ Additional keyword arguments to pass to the plot function.
58
+
59
+ Returns
60
+ -------
61
+ ax : Axes
62
+ Matplotlib Axes object containing the generated plot.
63
+
64
+ Examples
65
+ --------
66
+ Example usage of the Q_plot function:
67
+
68
+ >>> Q_plot('AS', x='dp', y='Q', mode='ext')
69
+ >>> Q_plot(['AS', 'AN'], x='sp', y='MEE')
70
+ """
71
+ dp = np.geomspace(10, 10000, 2000)
72
+
73
+ mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
74
+
75
+ xlabel_mapping = {'dp': 'Particle Diameter (nm)',
76
+ 'sp': 'Size parameter (\\alpha)'}
77
+
78
+ ylabel_mapping = {'Q': {'ext': r'$Extinction\ efficiency\ (Q_{ext})$',
79
+ 'sca': r'$Scattering\ efficiency\ (Q_{sca})$',
80
+ 'abs': r'$Absorption\ efficiency\ (Q_{abs})$'},
81
+ 'MEE': {'ext': r'$MEE\ (m^{2}/g)$',
82
+ 'sca': r'$MSE\ (m^{2}/g)$',
83
+ 'abs': r'$MAE\ (m^{2}/g)$'}}
84
+
85
+ typ = mode_mapping.get(mode, None)
86
+ xlabel = xlabel_mapping.get(x, None)
87
+ ylabel = ylabel_mapping.get(y, None).get(mode, None)
88
+
89
+ fig, ax = plt.subplots()
90
+
91
+ if x == "sp":
92
+ size_para = math.pi * dp.copy() / 550
93
+ dp_ = size_para
94
+
95
+ else:
96
+ plt.semilogx()
97
+ dp_ = dp.copy()
98
+
99
+ if isinstance(species, list):
100
+ for i, specie in enumerate(species):
101
+ label = mapping_dic[specie].get('label', None)
102
+ color = mapping_dic[specie].get('color', None)
103
+
104
+ mapping_dic[specie]['Q'] = Mie_Q(mapping_dic[specie]['m'], 550, dp)
105
+ mapping_dic[specie]['MEE'] = Mie_MEE(mapping_dic[specie]['m'], 550, dp, mapping_dic[specie]['density'])
106
+
107
+ plt.plot(dp_, mapping_dic[specie][f'{y}'][typ], color=color, label=label, alpha=1, lw=2)
108
+
109
+ else:
110
+ legend_label = {'Q': [r'$\bf Q_{{ext}}$', r'$\bf Q_{{scat}}$', r'$\bf Q_{{abs}}$'],
111
+ 'MEE': [r'$\bf MEE$', r'$\bf MSE$', r'$\bf MAE$']}
112
+
113
+ ylabel_mapping = {'Q': r'$\bf Optical\ efficiency\ (Q_{{ext, sca, abs}})$',
114
+ 'MEE': r'$\bf Mass\ Optical\ Efficiency\ (m^2/g)$'}
115
+
116
+ legend = legend_label.get(y, None)
117
+ ylabel = ylabel_mapping.get(y, None)
118
+
119
+ mapping_dic[species]['Q'] = Mie_Q(mapping_dic[species]['m'], 550, dp)
120
+ mapping_dic[species]['MEE'] = Mie_MEE(mapping_dic[species]['m'], 550, dp, mapping_dic[species]['density'])
121
+
122
+ plt.plot(dp_, mapping_dic[species][f'{y}'][0], color='b', label=legend[0])
123
+ plt.plot(dp_, mapping_dic[species][f'{y}'][1], color='g', label=legend[1])
124
+ plt.plot(dp_, mapping_dic[species][f'{y}'][2], color='r', label=legend[2])
125
+ plt.text(0.04, 0.92, mapping_dic[species]['label'], transform=ax.transAxes, weight='bold')
126
+
127
+ ax.set(xlim=(dp.min(), dp.max()), ylim=(0, None), xlabel=xlabel, ylabel=ylabel)
128
+ ax.grid(color='k', axis='x', which='major', linestyle='dashdot', linewidth=0.4, alpha=0.4)
129
+ ax.legend(loc='best', prop={'weight': 'bold'})
130
+
131
+ # fig.savefig(PATH_MAIN/f'Q_{species}')
132
+ plt.show()
133
+
134
+ return fig, ax
135
+
136
+
137
+ @set_figure(figsize=(9, 4))
138
+ def RI_couple(**kwargs) -> tuple[Figure, Axes]:
139
+ """
140
+ Generate a plot to test the influence of imaginary parts on scattering and absorption efficiencies.
141
+
142
+ Parameters
143
+ ----------
144
+ **kwargs
145
+ Additional keyword arguments to pass to the plot function.
146
+
147
+ Returns
148
+ -------
149
+ ax : Axes
150
+ Matplotlib Axes object containing the generated plot.
151
+
152
+ Examples
153
+ --------
154
+ Example usage of the IJ_couple function:
155
+
156
+ >>> ax = RI_couple()
157
+ """
158
+ dp = np.geomspace(10, 10000, 5000)
159
+
160
+ a = Mie_Q(1.50 + 0.01j, 550, dp)
161
+ b = Mie_Q(1.50 + 0.1j, 550, dp)
162
+ c = Mie_Q(1.50 + 0.5j, 550, dp)
163
+
164
+ fig, ax = plt.subplots(1, 2)
165
+ plt.subplots_adjust(right=0.9, wspace=0.4)
166
+ (ax1, ax2) = ax
167
+ size_para = math.pi * dp / 550
168
+
169
+ ax1.plot(size_para, a[1], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
170
+ ax1.plot(size_para, b[1], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
171
+ ax1.plot(size_para, c[1], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
172
+ ax1.legend()
173
+
174
+ ax1.set_xlim(0, size_para[-1])
175
+ ax1.set_ylim(0, None)
176
+ ax1.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
177
+ ax1.set_ylabel(r'$\bf Scattering\ efficiency\ (Q_{{scat}})$')
178
+
179
+ ax2.plot(size_para, a[2], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
180
+ ax2.plot(size_para, b[2], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
181
+ ax2.plot(size_para, c[2], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
182
+ ax2.legend()
183
+
184
+ ax2.set_xlim(0, size_para[-1])
185
+ ax2.set_ylim(0, None)
186
+ ax2.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
187
+ ax2.set_ylabel(r'$\bf Absorption\ efficiency\ (Q_{{abs}})$')
188
+
189
+ fig.suptitle(r'$\bf n\ =\ 1.50 $')
190
+ # fig.savefig(PATH_MAIN/f'IJ_couple')
191
+
192
+ plt.show()
193
+
194
+ return fig, ax
195
+
196
+
197
+ @set_figure
198
+ def RRI_2D(mode: Literal["ext", "sca", "abs"] = 'ext',
199
+ **kwargs) -> tuple[Figure, Axes]:
200
+ """
201
+ Generate a 2D plot of scattering efficiency (Q) against real and imaginary parts of the refractive index.
202
+
203
+ Parameters
204
+ ----------
205
+ mode : {'ext', 'sca', 'abs'}, optional
206
+ The mode of scattering efficiency to plot:
207
+ - 'ext' for extinction efficiency (Q_ext)
208
+ - 'sca' for scattering efficiency (Q_sca)
209
+ - 'abs' for absorption efficiency (Q_abs)
210
+ Default is 'ext'.
211
+
212
+ **kwargs
213
+ Additional keyword arguments to pass to the plot function.
214
+
215
+ Returns
216
+ -------
217
+ ax : Axes
218
+ Matplotlib Axes object containing the generated 2D plot.
219
+
220
+ Examples
221
+ --------
222
+ Example usage of the RRI_2D function:
223
+
224
+ >>> RRI_2D(mode='sca', xlabel='Real Part (n)', ylabel='Imaginary Part (k)', title='Scattering Efficiency 2D Plot')
225
+ """
226
+ mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
227
+
228
+ typ = mode_mapping.get(mode, None)
229
+
230
+ for dp in [400, 550, 700]:
231
+ RRI = np.linspace(1.3, 2, 100)
232
+ IRI = np.linspace(0, 0.7, 100)
233
+ arr = np.zeros((RRI.size, IRI.size))
234
+
235
+ for i, I_RI in enumerate(IRI):
236
+ for j, R_RI in enumerate(RRI):
237
+ arr[i, j] = Mie_Q(R_RI + 1j * I_RI, 550, dp)[typ]
238
+
239
+ fig, ax = plt.subplots()
240
+ plt.title(fr'$\bf dp\ = {dp}\ nm$', )
241
+ plt.xlabel(r'$\bf Real\ part\ (n)$', )
242
+ plt.ylabel(r'$\bf Imaginary\ part\ (k)$', )
243
+
244
+ im = plt.imshow(arr, extent=(1.3, 2, 0, 0.7), cmap='jet', origin='lower')
245
+ color_bar = plt.colorbar(im, extend='both')
246
+ color_bar.set_label(label=fr'$\bf Q_{{{mode}}}$')
247
+
248
+ # fig.savefig(PATH_MAIN/f'RRI_{mode}_{dp}')
249
+
250
+ plt.show()
251
+
252
+ return fig, ax
253
+
254
+
255
+ # @set_figure
256
+ # def scattering_phase(m: complex = 1.55 + 0.01j,
257
+ # wave: float = 600,
258
+ # dp: float = 200) -> tuple[Figure, Axes]:
259
+ # """
260
+ # Generate a polar plot to visualize the scattering phase function.
261
+ #
262
+ # Parameters
263
+ # ----------
264
+ # m : complex, optional
265
+ # The complex refractive index of the scattering medium. Default is 1.55 + 0.01j.
266
+ # wave : float, optional
267
+ # The wavelength of the incident light in nanometers. Default is 600 nm.
268
+ # dp : float, optional
269
+ # The particle diameter in nanometers. Default is 200 nm.
270
+ #
271
+ # Returns
272
+ # -------
273
+ # ax : Axes
274
+ # Matplotlib Axes object containing the generated polar plot.
275
+ #
276
+ # Examples
277
+ # --------
278
+ # Example usage of the scattering_phase function:
279
+ #
280
+ # >>> ax = scattering_phase(m=1.55 + 0.01j, wave=600, dp=200)
281
+ # """
282
+ # theta, _SL, _SR, _SU = ScatteringFunction(m, wave, dp)
283
+ #
284
+ # SL = np.append(_SL, _SL[::-1])
285
+ # SR = np.append(_SR, _SR[::-1])
286
+ # SU = np.append(_SU, _SU[::-1])
287
+ #
288
+ # angles = ['0', '60', '120', '180', '240', '300']
289
+ #
290
+ # fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
291
+ #
292
+ # theta = np.linspace(0, 2 * np.pi, len(SL))
293
+ #
294
+ # plt.thetagrids(range(0, 360, int(360 / len(angles))), angles)
295
+ #
296
+ # plt.plot(theta, SL, '-', linewidth=2, color='#115162', label='SL')
297
+ # plt.fill(theta, SL, '#afe0f5', alpha=0.5)
298
+ # plt.plot(theta, SR, '-', linewidth=2, color='#7FAE80', label='SR')
299
+ # plt.fill(theta, SR, '#b5e6c5', alpha=0.5)
300
+ # plt.plot(theta, SU, '-', linewidth=2, color='#621129', label='SU')
301
+ # plt.fill(theta, SU, '#f5afbd', alpha=0.5)
302
+ #
303
+ # plt.legend(loc='best', bbox_to_anchor=(1, 0, 0.2, 1), prop={'weight': 'bold'})
304
+ # plt.title(r'$\bf Scattering\ phase\ function$')
305
+ #
306
+ # plt.show()
307
+ # return fig, ax
308
+ #
309
+
310
+ @set_figure
311
+ def response_surface(real_range=(1.33, 1.7),
312
+ gmd_range=(10, 500),
313
+ num=50,
314
+ **kwargs) -> tuple[Figure, Axes]:
315
+ """
316
+ Generate a response surface plot for sensitivity tests of extinction based on Mie scattering.
317
+
318
+ Parameters
319
+ ----------
320
+ real_range : tuple, optional
321
+ The range of real part (refractive index) values for sensitivity testing. Default is (1.33, 1.7).
322
+
323
+ gmd_range : tuple, optional
324
+ The range of geometric mean diameter (GMD) values for sensitivity testing. Default is (60, 400).
325
+
326
+ num : int, optional
327
+ The number of points to generate within the specified ranges. Default is 50.
328
+
329
+ **kwargs
330
+ Additional keyword arguments to pass to the plot function.
331
+
332
+ Returns
333
+ -------
334
+ ax : Axes
335
+ Matplotlib Axes object containing the generated response surface plot.
336
+
337
+ Examples
338
+ --------
339
+ Example usage of the response_surface function:
340
+
341
+ >>> response_surface(real_range=(1.4, 1.6), gmd_range=(100, 300), num=30, xlabel='Real Part (n)',
342
+ ... ylabel='GMD (nm)', zlabel='Extinction (1/Mm)', title='Sensitivity Tests of Extinction')
343
+ """
344
+
345
+ def function(RI, GMD):
346
+ Z = np.zeros_like(RI) # 使用 np.zeros_like 可以確保 Z 和 RI 具有相同的形狀
347
+
348
+ for i in range(RI.shape[0]):
349
+ for j in range(RI.shape[1]):
350
+ _RI, _GMD = RI[i, j], GMD[i, j]
351
+ Bext, Bsca, Babs = Mie_PESD(m=_RI, lognormal=True, geoMean=_GMD, geoStdDev=2.)
352
+ Z[i, j] = np.sum(Bext)
353
+
354
+ return Z
355
+
356
+ # 假設 RI、GSD、GMD
357
+ RI = np.linspace(real_range[0], real_range[1], num)
358
+ GMD = np.linspace(gmd_range[0], gmd_range[1], num)
359
+
360
+ # 建立三維 meshgrid
361
+ real, gmd = np.meshgrid(RI, GMD, indexing='xy')
362
+
363
+ # Result
364
+ ext = function(real, gmd)
365
+
366
+ # plot
367
+ fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
368
+ ax.plot_surface(real, gmd, ext, rstride=1, cstride=1, cmap=plt.get_cmap('jet'), edgecolor='none')
369
+
370
+ ax.set(xlabel='Real part (n)', ylabel='GMD (nm)', zlabel=Unit('Extinction'),
371
+ title='Sensitive tests of Extinction')
372
+
373
+ ax.zaxis.get_offset_text().set_visible(False)
374
+ exponent = math.floor(math.log10(np.max(ext)))
375
+ ax.text(ax.get_xlim()[1] * 1.01, ax.get_ylim()[1], ax.get_zlim()[1] * 1.1, s=fr'${{\times}}\ 10^{exponent}$')
376
+ ax.ticklabel_format(style='sci', axis='z', scilimits=(0, 0), useOffset=False)
377
+
378
+ plt.show()
379
+
380
+ return fig, ax
381
+
382
+
383
+ if __name__ == '__main__':
384
+ Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='MEE')
385
+ Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='Q')
386
+
387
+ # RI_couple()
388
+ # response_surface()
@@ -0,0 +1,8 @@
1
+ from .contour import *
2
+ from .corr_matrix import corr_matrix
3
+ from .diurnal_pattern import *
4
+ from .koschmieder import *
5
+ from .metal_heatmap import metal_heatmaps, process_data
6
+ from .regression import *
7
+ from .scatter import *
8
+ from .templates import *
@@ -0,0 +1,47 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+ from matplotlib.pyplot import Figure, Axes
4
+ from scipy.optimize import curve_fit
5
+
6
+ from AeroViz.plot.utils import *
7
+
8
+ __all__ = ['contour']
9
+
10
+
11
+ @set_figure
12
+ def contour(df, ax: Axes | None = None, **kwargs) -> tuple[Figure, Axes]:
13
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
14
+
15
+ npoints = 1000
16
+ xreg = np.linspace(df.PM25.min(), df.PM25.max(), 83)
17
+ yreg = np.linspace(df.gRH.min(), df.gRH.max(), 34)
18
+ X, Y = np.meshgrid(xreg, yreg)
19
+
20
+ d_f = df.copy()
21
+ df['gRH'] = d_f['gRH'].round(2)
22
+ df['PM25'] = d_f['PM25'].round(2)
23
+
24
+ def func(data, *params):
25
+ return params[0] * data ** (params[1])
26
+
27
+ initial_guess = [1.0, 1.0]
28
+
29
+ fit_df = df[['PM25', 'gRH', 'Extinction']].dropna()
30
+ popt, pcov = curve_fit(func, xdata=(fit_df['PM25'] * fit_df['gRH']), ydata=fit_df['Extinction'], p0=initial_guess,
31
+ maxfev=2000000, method='trf')
32
+
33
+ x, y = df.PM25, df.gRH
34
+
35
+ # pcolor = ax.pcolormesh(X, Y, (X * 4.5 * Y ** (1 / 3)), cmap='jet', shading='auto', vmin=0, vmax=843, alpha=0.8)
36
+ Z = func(X * Y, *popt)
37
+ cont = ax.contour(X, Y, Z, colors='black', levels=5, vmin=0, vmax=Z.max())
38
+ conf = ax.contourf(X, Y, Z, cmap='YlGnBu', levels=100, vmin=0, vmax=Z.max())
39
+ ax.clabel(cont, colors=['black'], fmt=lambda s: f"{s:.0f} 1/Mm")
40
+ ax.set(xlabel=Unit('PM25'), ylabel=Unit('gRH'), xlim=(x.min(), x.max()), ylim=(y.min(), y.max()))
41
+
42
+ color_bar = plt.colorbar(conf, pad=0.02, fraction=0.05, label='Extinction (1/Mm)')
43
+ color_bar.ax.set_xticklabels(color_bar.ax.get_xticks().astype(int))
44
+
45
+ plt.show()
46
+
47
+ return fig, ax
@@ -0,0 +1,108 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+ import pandas as pd
4
+ import seaborn as sns
5
+ from matplotlib import colormaps
6
+ from matplotlib.pyplot import Figure, Axes
7
+ from mpl_toolkits.axes_grid1.inset_locator import inset_axes
8
+ from scipy.stats import pearsonr
9
+
10
+ from AeroViz.plot.utils import *
11
+
12
+ __all__ = ['corr_matrix']
13
+
14
+
15
+ @set_figure(fs=8)
16
+ def corr_matrix(data: pd.DataFrame,
17
+ cmap: str = "RdBu",
18
+ ax: Axes | None = None,
19
+ **kwargs) -> tuple[Figure, Axes]:
20
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
21
+
22
+ columns = ['Extinction', 'Scattering', 'Absorption', 'PM1', 'PM25', 'PM10', 'PBLH', 'VC',
23
+ 'AT', 'RH', 'WS', 'NO', 'NO2', 'NOx', 'O3', 'Benzene', 'Toluene',
24
+ 'SO2', 'CO', 'THC', 'CH4', 'NMHC', 'NH3', 'HCl', 'HNO2', 'HNO3',
25
+ 'Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+', 'Cl-', 'NO2-', 'NO3-', 'SO42-', ]
26
+
27
+ df = data[columns]
28
+
29
+ _corr = df.corr()
30
+ corr = pd.melt(_corr.reset_index(),
31
+ id_vars='index') # Unpivot the dataframe, so we can get pair of arrays for x and y
32
+ corr.columns = ['x', 'y', 'value']
33
+
34
+ p_values = _corr.apply(lambda col1: _corr.apply(lambda col2: pearsonr(col1, col2)[1]))
35
+ p_values = p_values.mask(p_values > 0.05)
36
+ p_values = pd.melt(p_values.reset_index(), id_vars='index').dropna()
37
+ p_values.columns = ['x', 'y', 'value']
38
+
39
+ # Mapping from column names to integer coordinates
40
+ x_labels = [v for v in sorted(corr['x'].unique())]
41
+ y_labels = [v for v in sorted(corr['y'].unique())]
42
+ x_to_num = {p[1]: p[0] for p in enumerate(x_labels)}
43
+ y_to_num = {p[1]: p[0] for p in enumerate(y_labels)}
44
+
45
+ # Show column labels on the axes
46
+ ax.set_xticks([x_to_num[v] for v in x_labels])
47
+ ax.set_xticklabels(x_labels, rotation=90, horizontalalignment='center')
48
+ ax.set_yticks([y_to_num[v] for v in y_labels])
49
+ ax.set_yticklabels(y_labels)
50
+
51
+ # ax.tick_params(axis='both', which='major', direction='out', top=True, left=True)
52
+
53
+ ax.grid(False, 'major')
54
+ ax.grid(True, 'minor')
55
+ ax.set_xticks([t + 0.5 for t in ax.get_xticks()], minor=True)
56
+ ax.set_yticks([t + 0.5 for t in ax.get_yticks()], minor=True)
57
+
58
+ ax.set_xlim([-0.5, max([v for v in x_to_num.values()]) + 0.5])
59
+ ax.set_ylim([-0.5, max([v for v in y_to_num.values()]) + 0.5])
60
+
61
+ n_colors = 256 # Use 256 colors for the diverging color palette
62
+ palette = sns.color_palette(cmap, n_colors=n_colors) # Create the palette
63
+
64
+ # Range of values that will be mapped to the palette, i.e. min and max possible correlation
65
+ color_min, color_max = [-1, 1]
66
+
67
+ def value_to_color(val):
68
+ val_position = float((val - color_min)) / (color_max - color_min)
69
+ ind = int(val_position * (n_colors - 1)) # target index in the color palette
70
+ return palette[ind]
71
+
72
+ point = ax.scatter(
73
+ x=corr['x'].map(x_to_num),
74
+ y=corr['y'].map(y_to_num),
75
+ s=corr['value'].abs() * 70,
76
+ c=corr['value'].apply(value_to_color), # Vector of square color values, mapped to color palette
77
+ marker='s',
78
+ label='$R^{2}$'
79
+ )
80
+
81
+ axes_image = plt.cm.ScalarMappable(cmap=colormaps[cmap])
82
+
83
+ cax = inset_axes(ax, width="5%",
84
+ height="100%",
85
+ loc='lower left',
86
+ bbox_to_anchor=(1.02, 0., 1, 1),
87
+ bbox_transform=ax.transAxes,
88
+ borderpad=0)
89
+
90
+ cbar = plt.colorbar(mappable=axes_image, cax=cax, label=r'$R^{2}$')
91
+
92
+ cbar.set_ticks([0, 0.25, 0.5, 0.75, 1])
93
+ cbar.set_ticklabels(np.linspace(-1, 1, 5))
94
+
95
+ point2 = ax.scatter(
96
+ x=p_values['x'].map(x_to_num),
97
+ y=p_values['y'].map(y_to_num),
98
+ s=10,
99
+ marker='*',
100
+ color='k',
101
+ label='p < 0.05'
102
+ )
103
+
104
+ ax.legend(handles=[point2], labels=['p < 0.05'], bbox_to_anchor=(0.05, 1, 0.1, 0.05))
105
+
106
+ plt.show()
107
+
108
+ return fig, ax
@@ -0,0 +1,42 @@
1
+ import matplotlib.pyplot as plt
2
+ import pandas as pd
3
+ from matplotlib.pyplot import Figure, Axes
4
+ from matplotlib.ticker import AutoMinorLocator
5
+
6
+ from AeroViz.plot.utils import *
7
+
8
+ __all__ = ['diurnal_pattern']
9
+
10
+
11
+ @set_figure(figsize=(4, 4), fs=8)
12
+ def diurnal_pattern(data_set: pd.DataFrame,
13
+ data_std: pd.DataFrame,
14
+ y: str | list[str],
15
+ std_area=0.5,
16
+ ax: Axes | None = None,
17
+ **kwargs) -> tuple[Figure, Axes]:
18
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
19
+
20
+ Hour = range(0, 24)
21
+
22
+ mean = data_set[y]
23
+ std = data_std[y] * std_area
24
+
25
+ # Plot Diurnal pattern
26
+ ax.plot(Hour, mean, 'blue')
27
+ ax.fill_between(Hour, y1=mean + std, y2=mean - std, alpha=0.5, color='blue', edgecolor=None)
28
+
29
+ ax.set(xlabel=kwargs.get('xlabel', 'Hours'),
30
+ ylabel=kwargs.get('ylabel', Unit(y)),
31
+ xlim=kwargs.get('xlim', (0, 23)),
32
+ ylim=kwargs.get('ylim', (None, None)),
33
+ xticks=kwargs.get('xticks', [0, 4, 8, 12, 16, 20]))
34
+
35
+ ax.tick_params(axis='both', which='major')
36
+ ax.tick_params(axis='x', which='minor')
37
+ ax.xaxis.set_minor_locator(AutoMinorLocator())
38
+ ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 3), useMathText=True)
39
+
40
+ plt.show()
41
+
42
+ return fig, ax
@@ -0,0 +1,65 @@
1
+ from os.path import join as pth
2
+
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ from pandas import date_range, read_csv
6
+
7
+ from AeroViz.plot.utils import *
8
+
9
+ # TODO:
10
+
11
+ # read csv file
12
+ blh = read_csv(pth('事件分析.csv'), parse_dates=['Time'], index_col='Time')
13
+
14
+
15
+ @set_figure(figsize=(12, 5))
16
+ def event_evolution(_df, **kwargs):
17
+ print(f'Plot : {_df.month[0]}')
18
+
19
+ st_tm, fn_tm = _df.index[0], _df.index[-1]
20
+ tick_time = date_range(st_tm, fn_tm, freq='1d') # set tick
21
+
22
+ # seperate day and night
23
+ _df_day = _df.between_time('6:00', '17:00').reindex(date_range(st_tm, fn_tm, freq='1h'))
24
+ _df_night = _df.between_time('18:00', '5:00').reindex(date_range(st_tm, fn_tm, freq='1h'))
25
+
26
+ ## plot
27
+ fig, ax = plt.subplots()
28
+
29
+ ## plot background
30
+ shade_value, _ = np.meshgrid(_df['PM2.5'], np.arange((1., 2500), 100))
31
+ ax.pcolormesh(_df.index, np.arange((1., 2500), 100), shade_value, cmap='binary', vmin=0, vmax=300,
32
+ shading='auto')
33
+
34
+ ## plot day and night
35
+ ld = ax.scatter(_df.index[0:], _df_day['Ext'], s=50, c='#73b9ff', label='Day Ext', marker='o', alpha=.7)
36
+ ln = ax.scatter(_df.index[0:], _df_night['Ext'], s=50, c='#00238c', label='Night Ext', marker='o', alpha=.7)
37
+
38
+ ax2 = ax.twinx()
39
+ # ld, = ax2.plot(_df_day['VC'],c='#FF9797',label='day 06:00~18:00')
40
+ # ln, = ax2.plot(_df_night['VC'],c='#FF0000',label='night 18:00~06:00')
41
+ ld2 = ax2.scatter(_df.index, _df_day['VC'], s=50, c='#FF9797', label='Day VC', marker='o', alpha=.5)
42
+ ln2 = ax2.scatter(_df.index, _df_night['VC'], s=50, c='#FF0000', label='Night VC', marker='o', alpha=.5)
43
+
44
+ # add legend on the first axes
45
+ ax.legend(handles=[ld, ln, ld2, ln2], framealpha=0, prop={'weight': 'bold'}, loc='upper left')
46
+
47
+ # add xlabel, ylabel, suptitle
48
+ ax.set(xlabel='Date',
49
+ ylabel='Ext (1/Mm)',
50
+ xlim=(st_tm, fn_tm),
51
+ ylim=(1., 600),
52
+ xticks=tick_time,
53
+ xticklabels=[_tm.strftime("%F %H:00") for _tm in tick_time])
54
+
55
+ ax2.set(ylabel=r'$VC (m^{2}/s)$',
56
+ ylim=(1., 2500))
57
+
58
+ fig.suptitle(f'Event evolution ({st_tm.strftime("%F")}_{fn_tm.strftime("%F")})')
59
+
60
+ # save figure
61
+ fig.savefig(pth(f"event_evolution_{st_tm.strftime("%F")}_{fn_tm.strftime("%F")}"))
62
+
63
+
64
+ if __name__ == '__main__':
65
+ event_evolution(blh)