AeroViz 0.1.3__py3-none-any.whl → 0.1.4__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 (121) hide show
  1. AeroViz/__init__.py +7 -5
  2. AeroViz/{config → data}/DEFAULT_DATA.csv +1 -1
  3. AeroViz/dataProcess/Chemistry/__init__.py +40 -40
  4. AeroViz/dataProcess/Chemistry/_calculate.py +15 -15
  5. AeroViz/dataProcess/Chemistry/_isoropia.py +72 -68
  6. AeroViz/dataProcess/Chemistry/_mass_volume.py +158 -161
  7. AeroViz/dataProcess/Chemistry/_ocec.py +109 -109
  8. AeroViz/dataProcess/Chemistry/_partition.py +19 -18
  9. AeroViz/dataProcess/Chemistry/_teom.py +9 -11
  10. AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
  11. AeroViz/dataProcess/Optical/Angstrom_exponent.py +20 -0
  12. AeroViz/dataProcess/Optical/_IMPROVE.py +40 -41
  13. AeroViz/dataProcess/Optical/__init__.py +29 -44
  14. AeroViz/dataProcess/Optical/_absorption.py +21 -47
  15. AeroViz/dataProcess/Optical/_extinction.py +31 -25
  16. AeroViz/dataProcess/Optical/_mie.py +5 -7
  17. AeroViz/dataProcess/Optical/_mie_sd.py +89 -90
  18. AeroViz/dataProcess/Optical/_scattering.py +19 -20
  19. AeroViz/dataProcess/SizeDistr/__init__.py +39 -39
  20. AeroViz/dataProcess/SizeDistr/__merge.py +159 -158
  21. AeroViz/dataProcess/SizeDistr/_merge.py +155 -154
  22. AeroViz/dataProcess/SizeDistr/_merge_v1.py +162 -161
  23. AeroViz/dataProcess/SizeDistr/_merge_v2.py +153 -152
  24. AeroViz/dataProcess/SizeDistr/_merge_v3.py +327 -327
  25. AeroViz/dataProcess/SizeDistr/_merge_v4.py +273 -275
  26. AeroViz/dataProcess/SizeDistr/_size_distr.py +51 -51
  27. AeroViz/dataProcess/VOC/__init__.py +9 -9
  28. AeroViz/dataProcess/VOC/_potential_par.py +53 -55
  29. AeroViz/dataProcess/__init__.py +28 -6
  30. AeroViz/dataProcess/core/__init__.py +59 -65
  31. AeroViz/plot/__init__.py +7 -2
  32. AeroViz/plot/bar.py +126 -0
  33. AeroViz/plot/box.py +69 -0
  34. AeroViz/plot/distribution/distribution.py +421 -427
  35. AeroViz/plot/meteorology/meteorology.py +240 -292
  36. AeroViz/plot/optical/__init__.py +0 -1
  37. AeroViz/plot/optical/optical.py +230 -230
  38. AeroViz/plot/pie.py +198 -0
  39. AeroViz/plot/regression.py +196 -0
  40. AeroViz/plot/scatter.py +165 -0
  41. AeroViz/plot/templates/__init__.py +2 -4
  42. AeroViz/plot/templates/ammonium_rich.py +34 -0
  43. AeroViz/plot/templates/contour.py +25 -25
  44. AeroViz/plot/templates/corr_matrix.py +86 -93
  45. AeroViz/plot/templates/diurnal_pattern.py +28 -26
  46. AeroViz/plot/templates/koschmieder.py +59 -123
  47. AeroViz/plot/templates/metal_heatmap.py +135 -37
  48. AeroViz/plot/timeseries/__init__.py +1 -0
  49. AeroViz/plot/timeseries/template.py +47 -0
  50. AeroViz/plot/timeseries/timeseries.py +324 -264
  51. AeroViz/plot/utils/__init__.py +2 -1
  52. AeroViz/plot/utils/_color.py +57 -57
  53. AeroViz/plot/utils/_unit.py +48 -48
  54. AeroViz/plot/utils/plt_utils.py +92 -0
  55. AeroViz/plot/utils/sklearn_utils.py +49 -0
  56. AeroViz/plot/utils/units.json +5 -0
  57. AeroViz/plot/violin.py +80 -0
  58. AeroViz/process/__init__.py +17 -17
  59. AeroViz/process/core/DataProc.py +9 -9
  60. AeroViz/process/core/SizeDist.py +81 -81
  61. AeroViz/process/method/PyMieScatt_update.py +488 -488
  62. AeroViz/process/method/mie_theory.py +231 -229
  63. AeroViz/process/method/prop.py +40 -40
  64. AeroViz/process/script/AbstractDistCalc.py +103 -103
  65. AeroViz/process/script/Chemical.py +168 -167
  66. AeroViz/process/script/IMPACT.py +40 -40
  67. AeroViz/process/script/IMPROVE.py +152 -152
  68. AeroViz/process/script/Others.py +45 -45
  69. AeroViz/process/script/PSD.py +26 -26
  70. AeroViz/process/script/PSD_dry.py +69 -70
  71. AeroViz/process/script/retrieve_RI.py +50 -51
  72. AeroViz/rawDataReader/__init__.py +53 -58
  73. AeroViz/rawDataReader/config/supported_instruments.py +155 -0
  74. AeroViz/rawDataReader/core/__init__.py +233 -356
  75. AeroViz/rawDataReader/script/AE33.py +17 -18
  76. AeroViz/rawDataReader/script/AE43.py +18 -21
  77. AeroViz/rawDataReader/script/APS_3321.py +30 -30
  78. AeroViz/rawDataReader/script/Aurora.py +23 -24
  79. AeroViz/rawDataReader/script/BC1054.py +36 -40
  80. AeroViz/rawDataReader/script/EPA_vertical.py +37 -9
  81. AeroViz/rawDataReader/script/GRIMM.py +16 -23
  82. AeroViz/rawDataReader/script/IGAC.py +90 -0
  83. AeroViz/rawDataReader/script/MA350.py +32 -39
  84. AeroViz/rawDataReader/script/Minion.py +103 -0
  85. AeroViz/rawDataReader/script/NEPH.py +69 -74
  86. AeroViz/rawDataReader/script/SMPS_TH.py +25 -25
  87. AeroViz/rawDataReader/script/SMPS_aim11.py +32 -32
  88. AeroViz/rawDataReader/script/SMPS_genr.py +31 -31
  89. AeroViz/rawDataReader/script/Sunset_OCEC.py +60 -0
  90. AeroViz/rawDataReader/script/TEOM.py +30 -28
  91. AeroViz/rawDataReader/script/Table.py +13 -14
  92. AeroViz/rawDataReader/script/VOC.py +26 -0
  93. AeroViz/rawDataReader/script/__init__.py +18 -20
  94. AeroViz/tools/database.py +64 -66
  95. AeroViz/tools/dataclassifier.py +106 -106
  96. AeroViz/tools/dataprinter.py +51 -51
  97. AeroViz/tools/datareader.py +38 -38
  98. {AeroViz-0.1.3.dist-info → AeroViz-0.1.4.dist-info}/METADATA +5 -4
  99. AeroViz-0.1.4.dist-info/RECORD +112 -0
  100. AeroViz/plot/improve/__init__.py +0 -1
  101. AeroViz/plot/improve/improve.py +0 -240
  102. AeroViz/plot/optical/aethalometer.py +0 -77
  103. AeroViz/plot/templates/event_evolution.py +0 -65
  104. AeroViz/plot/templates/regression.py +0 -256
  105. AeroViz/plot/templates/scatter.py +0 -130
  106. AeroViz/plot/templates/templates.py +0 -398
  107. AeroViz/plot/utils/_decorator.py +0 -74
  108. AeroViz/rawDataReader/script/IGAC_TH.py +0 -104
  109. AeroViz/rawDataReader/script/IGAC_ZM.py +0 -90
  110. AeroViz/rawDataReader/script/OCEC_LCRES.py +0 -34
  111. AeroViz/rawDataReader/script/OCEC_RES.py +0 -28
  112. AeroViz/rawDataReader/script/VOC_TH.py +0 -30
  113. AeroViz/rawDataReader/script/VOC_ZM.py +0 -37
  114. AeroViz/rawDataReader/utils/__init__.py +0 -0
  115. AeroViz/rawDataReader/utils/config.py +0 -169
  116. AeroViz-0.1.3.dist-info/RECORD +0 -111
  117. /AeroViz/{config → data}/DEFAULT_PNSD_DATA.csv +0 -0
  118. /AeroViz/{config → rawDataReader/config}/__init__.py +0 -0
  119. {AeroViz-0.1.3.dist-info → AeroViz-0.1.4.dist-info}/LICENSE +0 -0
  120. {AeroViz-0.1.3.dist-info → AeroViz-0.1.4.dist-info}/WHEEL +0 -0
  121. {AeroViz-0.1.3.dist-info → AeroViz-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,398 +0,0 @@
1
- from typing import Literal
2
-
3
- import matplotlib.pyplot as plt
4
- import numpy as np
5
- import pandas as pd
6
- import seaborn as sns
7
- from matplotlib.pyplot import Figure, Axes
8
- from pandas import DataFrame
9
-
10
- from AeroViz.plot.utils import *
11
-
12
- __all__ = [
13
- 'pie',
14
- 'donuts',
15
- 'violin',
16
- 'bar',
17
- ]
18
-
19
-
20
- def _auto_label_pct(pct,
21
- symbol: bool = True,
22
- include_pct: bool = False,
23
- ignore: Literal["inner", "outer"] = 'inner',
24
- value: float = 2):
25
- if not symbol:
26
- return ''
27
- cond = pct <= value if ignore == 'inner' else pct > value
28
- label = '' if cond else '{:.1f}'.format(pct)
29
- return '' if label == '' else label + '%' if include_pct else label
30
-
31
-
32
- @set_figure(fs=8, fw='bold')
33
- def pie(data_set: DataFrame | dict,
34
- labels: list[str],
35
- unit: str,
36
- style: Literal["pie", 'donut'],
37
- ax: Axes | None = None,
38
- symbol: bool = True,
39
- **kwargs) -> tuple[Figure, Axes]:
40
- """
41
- Create a pie or donut chart based on the provided data.
42
-
43
- Parameters
44
- ----------
45
- data_set : pd.DataFrame | dict
46
- A pandas DataFrame or dictionary mapping category names to a list of species.
47
- If a DataFrame is provided, the index represents the categories, and each column contains species data.
48
- If a dictionary is provided, it maps category names to lists of species data.
49
- It is assumed that all lists or DataFrame columns contain the same number of entries as the *labels* list.
50
- labels : list of str
51
- The labels for each category.
52
- unit : str
53
- The unit to display in the center of the donut chart.
54
- style : Literal["pie", 'donut']
55
- The style of the chart, either 'pie' for a standard pie chart or 'donut' for a donut chart.
56
- ax : plt.Axes or None, optional
57
- The Axes object to plot the chart onto. If None, a new figure and Axes will be created.
58
- symbol : bool, optional
59
- Whether to display values for each species in the chart.
60
- **kwargs
61
- Additional keyword arguments to be passed to the plotting function.
62
-
63
- Returns
64
- -------
65
- matplotlib.axes.Axes
66
- The Axes object containing the violin plot.
67
-
68
- Notes
69
- -----
70
- - If *data_set* is a dictionary, it should contain lists of species that correspond to each category in *labels*.
71
- - The length of each list in *data_set* or the number of columns in the DataFrame should match the length of the *labels* list.
72
-
73
- Examples
74
- --------
75
- >>> data_set = {'Category 1': [10, 20, 30], 'Category 2': [15, 25, 35]}
76
- >>> labels = ['Species 1', 'Species 2', 'Species 3']
77
- >>> pie(data_set, labels, unit='kg', style='pie', symbol=True)
78
- """
79
- if isinstance(data_set, DataFrame):
80
- category_names = list(data_set.index)
81
- data = data_set.to_numpy()
82
-
83
- pies, species = data.shape
84
-
85
- elif isinstance(data_set, dict):
86
- category_names = list(data_set.keys())
87
- data = np.array(list(data_set.values()))
88
-
89
- pies, species = data.shape
90
-
91
- else:
92
- raise ValueError('data_set must be a DataFrame or a dictionary.')
93
-
94
- colors = kwargs.get('colors') or (Color.colors1 if species == 6 else Color.getColor(num=species))
95
-
96
- radius = 4
97
- width = 4 if style == 'pie' else 1
98
-
99
- text = [''] * pies if style == 'pie' else [Unit(unit) + '\n\n' + '{:.2f}'.format(x) for x in data.sum(axis=1)]
100
- pct_distance = 0.6 if style == 'pie' else 0.88
101
-
102
- fig, ax = plt.subplots(1, pies, figsize=((pies * 2) + 1, 2)) if ax is None else (ax.get_figure(), ax)
103
-
104
- if pies == 1:
105
- ax = [ax]
106
-
107
- for i in range(pies):
108
- ax[i].pie(data[i], labels=None, colors=colors, textprops=None,
109
- autopct=lambda pct: _auto_label_pct(pct, symbol=symbol, include_pct=True),
110
- pctdistance=pct_distance, radius=radius, wedgeprops=dict(width=width, edgecolor='w'))
111
-
112
- ax[i].pie(data[i], labels=None, colors=colors, textprops=None,
113
- autopct=lambda pct: _auto_label_pct(pct, symbol=symbol, ignore='outer', include_pct=True),
114
- pctdistance=1.3, radius=radius, wedgeprops=dict(width=width, edgecolor='w'))
115
- ax[i].axis('equal')
116
- ax[i].text(0, 0, text[i], ha='center', va='center')
117
- ax[i].set_title(category_names[i])
118
-
119
- ax[-1].legend(labels, loc='center left', prop={'weight': 'bold'}, bbox_to_anchor=(1, 0, 1.15, 1))
120
-
121
- # fig.savefig(f"pie_{style}_{title}")
122
-
123
- plt.show()
124
-
125
- return fig, ax
126
-
127
-
128
- @set_figure(fs=8, fw='bold')
129
- def donuts(data_set: DataFrame | dict,
130
- labels: list[str],
131
- unit: str,
132
- ax: Axes | None = None,
133
- symbol=True,
134
- **kwargs) -> tuple[Figure, Axes]:
135
- """
136
- Plot a donut chart based on the data set.
137
-
138
- Parameters
139
- ----------
140
- data_set : pd.DataFrame | dict
141
- A pandas DataFrame or a dictionary mapping category names to a list of species.
142
- If a DataFrame is provided, the index represents the categories, and each column contains species data.
143
- If a dictionary is provided, it maps category names to lists of species data.
144
- It is assumed that all lists or DataFrame columns contain the same number of entries as the *labels* list.
145
- labels : list of str
146
- The category labels.
147
- unit : str
148
- The unit to be displayed in the center of the donut chart.
149
- ax : matplotlib.axes.Axes, optional
150
- The axes to plot on. If None, the current axes will be used (default).
151
- symbol : bool, optional
152
- Whether to display values for each species (default is True).
153
- **kwargs : dict, optional
154
- Additional keyword arguments to pass to the matplotlib pie chart function.
155
-
156
- Returns
157
- -------
158
- matplotlib.axes.Axes
159
- The axes containing the donut chart.
160
- """
161
-
162
- if isinstance(data_set, DataFrame):
163
- category_names = list(data_set.index)
164
- data = data_set.to_numpy()
165
-
166
- pies, species = data.shape
167
-
168
- elif isinstance(data_set, dict):
169
- category_names = list(data_set.keys())
170
- data = np.array(list(data_set.values()))
171
-
172
- pies, species = data.shape
173
-
174
- else:
175
- raise ValueError('data_set must be a DataFrame or a dictionary.')
176
-
177
- colors1 = kwargs.get('colors') or (Color.colors1 if species == 6 else Color.getColor(num=species))
178
- colors2 = Color.adjust_opacity(colors1, 0.8)
179
- colors3 = Color.adjust_opacity(colors1, 0.6)
180
-
181
- fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
182
-
183
- ax.pie(data[2], labels=None, colors=colors1, textprops=None,
184
- autopct=lambda pct: _auto_label_pct(pct, symbol=symbol, include_pct=True),
185
- pctdistance=0.9, radius=14, wedgeprops=dict(width=3, edgecolor='w'))
186
-
187
- ax.pie(data[1], labels=None, colors=colors2, textprops=None,
188
- autopct=lambda pct: _auto_label_pct(pct, symbol=symbol, include_pct=True),
189
- pctdistance=0.85, radius=11, wedgeprops=dict(width=3, edgecolor='w'))
190
-
191
- ax.pie(data[0], labels=None, colors=colors3, textprops=None,
192
- autopct=lambda pct: _auto_label_pct(pct, symbol=symbol, include_pct=True),
193
- pctdistance=0.80, radius=8, wedgeprops=dict(width=3, edgecolor='w'))
194
-
195
- text = (Unit(f'{unit}') + '\n\n' +
196
- 'Event : ' + "{:.2f}".format(np.sum(data[2])) + '\n' +
197
- 'Transition : ' + "{:.2f}".format(np.sum(data[1])) + '\n' +
198
- 'Clean : ' + "{:.2f}".format(np.sum(data[0])))
199
-
200
- ax.text(0, 0, text, ha='center', va='center')
201
- ax.axis('equal')
202
-
203
- ax.set_title(kwargs.get('title', ''))
204
-
205
- ax.legend(labels, loc='center', prop={'weight': 'bold'}, title_fontproperties={'weight': 'bold'},
206
- title=f'Outer : {category_names[2]}' + '\n' + f'Middle : {category_names[1]}' + '\n' + f'Inner : {category_names[0]}',
207
- bbox_to_anchor=(0.8, 0, 0.5, 1))
208
-
209
- # fig.savefig(f"donuts_{title}")
210
-
211
- plt.show()
212
-
213
- return fig, ax
214
-
215
-
216
- @set_figure(figsize=(5, 4))
217
- def bar(data_set: DataFrame | dict,
218
- data_std: DataFrame | None,
219
- labels: list[str],
220
- unit: str,
221
- style: Literal["stacked", "dispersed"] = "dispersed",
222
- orientation: Literal["va", "ha"] = 'va',
223
- ax: Axes | None = None,
224
- symbol=True,
225
- **kwargs
226
- ) -> tuple[Figure, Axes]:
227
- """
228
- Parameters
229
- ----------
230
- data_set : pd.DataFrame or dict
231
- A mapping from category names to a list of species mean or a DataFrame with columns as categories and values as means.
232
- data_std : pd.DataFrame or None
233
- A DataFrame with standard deviations corresponding to data_set, or None if standard deviations are not provided.
234
- labels : list of str
235
- The species names.
236
- unit : str
237
- The unit for the values.
238
- style : {'stacked', 'dispersed'}, default 'dispersed'
239
- Whether to display the bars stacked or dispersed.
240
- orientation : {'va', 'ha'}, default 'va'
241
- The orientation of the bars, 'va' for vertical and 'ha' for horizontal.
242
- ax : plt.Axes or None, default None
243
- The Axes object to plot on. If None, a new figure and Axes are created.
244
- symbol : bool, default True
245
- Whether to display values for each bar.
246
- kwargs : dict
247
- Additional keyword arguments passed to the barplot function.
248
-
249
- Returns
250
- -------
251
- matplotlib.Axes
252
- The Axes object containing the plot.
253
-
254
- """
255
- # data process
256
- data = data_set.values
257
-
258
- if data_std is None:
259
- data_std = np.zeros(data.shape)
260
- else:
261
- data_std = data_std.values
262
-
263
- groups, species = data.shape
264
- groups_arr = np.arange(groups)
265
- species_arr = np.arange(species)
266
-
267
- total = np.array([data.sum(axis=1), ] * species).T
268
-
269
- pct_data = data / total * 100
270
- data_cum = pct_data.cumsum(axis=1)
271
-
272
- # figure info
273
- category_names = kwargs.get('ticks') or list(data_set.index)
274
- title = kwargs.get('title', '')
275
- colors = kwargs.get('colors') or (Color.colors1 if species == 6 else Color.getColor(num=species))
276
-
277
- fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
278
-
279
- if style == "stacked":
280
- for i in range(species):
281
- widths = pct_data[:, i]
282
- starts = data_cum[:, i] - pct_data[:, i]
283
-
284
- if orientation == 'va':
285
- _ = ax.bar(groups_arr, widths, bottom=starts, width=0.7, color=colors[i], label=labels[i],
286
- edgecolor=None, capsize=None)
287
- if orientation == 'ha':
288
- _ = ax.barh(groups_arr, widths, left=starts, height=0.7, color=colors[i], label=labels[i],
289
- edgecolor=None, capsize=None)
290
- if symbol:
291
- ax.bar_label(_, fmt=_auto_label_pct, label_type='center', padding=0, fontsize=10, weight='bold')
292
-
293
- if style == "dispersed":
294
- width = 0.1
295
- block = width / 4
296
-
297
- for i in range(species):
298
- val = data[:, i]
299
- std = (0,) * groups, data_std[:, i]
300
- if orientation == 'va':
301
- _ = ax.bar(groups_arr + (i + 1) * (width + block), val, yerr=std, width=width, color=colors[i],
302
- edgecolor=None, capsize=None)
303
- if orientation == 'ha':
304
- _ = ax.barh(groups_arr + (i + 1) * (width + block), val, xerr=std, height=width, color=colors[i],
305
- edgecolor=None, capsize=None)
306
- if symbol:
307
- ax.bar_label(_, fmt=_auto_label_pct, label_type='center', padding=0, fontsize=8, weight='bold')
308
-
309
- if orientation == 'va':
310
- xticks = groups_arr + (species / 2 + 0.5) * (width + block) if style == "dispersed" else groups_arr
311
- ax.set_xticks(xticks, category_names, weight='bold')
312
- ax.set_ylabel(Unit(unit) if style == "dispersed" else '$Contribution (\\%)$')
313
- ax.set_ylim(0, None if style == "dispersed" else 100)
314
- ax.legend(labels, bbox_to_anchor=(1, 1), loc='upper left', prop={'size': 12})
315
-
316
- if orientation == 'ha':
317
- ax.invert_yaxis()
318
- yticks = groups_arr + 3.5 * (width + block) if style == "dispersed" else groups_arr
319
- ax.set_yticks(yticks, category_names, weight='bold')
320
- ax.set_xlabel(Unit(unit) if style == "dispersed" else '$Contribution (\\%)$')
321
- ax.set_xlim(0, None if style == "dispersed" else 100)
322
- ax.legend(labels, bbox_to_anchor=(1, 1), loc='upper left', prop={'size': 12})
323
-
324
- # fig.savefig(f"Barplot_{title}")
325
-
326
- plt.show()
327
-
328
- return fig, ax
329
-
330
-
331
- @set_figure
332
- def violin(data_set: DataFrame | dict,
333
- unit: str,
334
- ax: Axes | None = None,
335
- **kwargs
336
- ) -> tuple[Figure, Axes]:
337
- """
338
- Generate a violin plot for multiple data sets.
339
-
340
- Parameters
341
- ----------
342
- data_set : pd.DataFrame or dict
343
- A mapping from category names to pandas DataFrames containing the data.
344
- unit : str
345
- The unit for the data being plotted.
346
- ax : matplotlib.axes.Axes, optional
347
- The Axes object to draw the plot onto. If not provided, a new figure will be created.
348
- **kwargs : dict
349
- Additional keyword arguments to be passed to the violinplot function.
350
-
351
- Returns
352
- -------
353
- matplotlib.axes.Axes
354
- The Axes object containing the violin plot.
355
-
356
- """
357
- fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
358
-
359
- data = data_set.to_numpy()
360
-
361
- data = data[~np.isnan(data).any(axis=1)]
362
-
363
- grps = data.shape[1]
364
-
365
- width = 0.6
366
- block = width / 2
367
- x_position = np.arange(grps)
368
-
369
- plt.boxplot(data, positions=x_position, widths=0.15,
370
- showfliers=False, showmeans=True, meanline=False, patch_artist=True,
371
- capprops=dict(linewidth=0),
372
- whiskerprops=dict(linewidth=1.5, color='k', alpha=1),
373
- boxprops=dict(linewidth=1.5, color='k', facecolor='#4778D3', alpha=1),
374
- meanprops=dict(marker='o', markeredgecolor='black', markerfacecolor='white', markersize=6),
375
- medianprops=dict(linewidth=1.5, ls='-', color='k', alpha=1))
376
-
377
- sns.violinplot(data=data, density_norm='area', color='#4778D3', inner=None)
378
-
379
- for violin, alpha in zip(ax.collections[:], [0.5] * len(ax.collections[:])):
380
- violin.set_alpha(alpha)
381
- violin.set_edgecolor(None)
382
-
383
- plt.scatter(x_position, data.mean(), marker='o', facecolor='white', edgecolor='k', s=10)
384
-
385
- xlim = kwargs.get('xlim') or (x_position[0] - (width / 2 + block), x_position[-1] + (width / 2 + block))
386
- ylim = kwargs.get('ylim') or (0, None)
387
- xlabel = kwargs.get('xlabel') or ''
388
- ylabel = kwargs.get('ylabel') or Unit(unit)
389
- xticks = kwargs.get('xticks') or [x.replace('-', '\n') for x in list(data_set.keys())]
390
-
391
- ax.set(xlim=xlim, ylim=ylim, xlabel=xlabel, ylabel=ylabel, title=kwargs.get('title'))
392
- ax.set_xticks(x_position, xticks, fontweight='bold', fontsize=12)
393
-
394
- # fig.savefig(f'Violin_{unit}')
395
-
396
- plt.show()
397
-
398
- return fig, ax
@@ -1,74 +0,0 @@
1
- from functools import wraps
2
-
3
- import matplotlib.pyplot as plt
4
-
5
- __all__ = ['set_figure']
6
-
7
-
8
- # For more details please see https://matplotlib.org/stable/users/explain/customizing.html
9
-
10
-
11
- def set_figure(func=None,
12
- *,
13
- figsize: tuple | None = None,
14
- fs: int | None = None,
15
- fw: str = None,
16
- autolayout: bool = True
17
- ):
18
- def decorator(_func):
19
- @wraps(_func)
20
- def wrapper(*args, **kwargs):
21
- print(f'\t\t Plot: \033[96m{_func.__name__}\033[0m')
22
-
23
- plt.rcParams['mathtext.fontset'] = 'custom'
24
- plt.rcParams['mathtext.rm'] = 'Times New Roman'
25
- plt.rcParams['mathtext.it'] = 'Times New Roman: italic'
26
- plt.rcParams['mathtext.bf'] = 'Times New Roman: bold'
27
- plt.rcParams['mathtext.default'] = 'regular'
28
-
29
- # The font properties used by `text.Text`.
30
- # The text, annotate, label, title, ticks, are used to create text
31
- plt.rcParams['font.family'] = 'Times New Roman'
32
- plt.rcParams['font.weight'] = fw or 'normal'
33
- plt.rcParams['font.size'] = fs or 8
34
-
35
- plt.rcParams['axes.titlesize'] = 'large'
36
- plt.rcParams['axes.titleweight'] = 'bold'
37
- plt.rcParams['axes.labelweight'] = 'bold'
38
-
39
- # color
40
- plt.rcParams['axes.prop_cycle'] = plt.cycler(color=['b', 'g', 'r', 'c', 'm', 'y', 'k'])
41
-
42
- plt.rcParams['xtick.labelsize'] = 'medium'
43
- plt.rcParams['ytick.labelsize'] = 'medium'
44
-
45
- # matplotlib.font_manager.FontProperties ---> matplotlib.rcParams
46
- plt.rcParams['legend.loc'] = 'best'
47
- plt.rcParams['legend.frameon'] = False
48
- plt.rcParams['legend.fontsize'] = 'small'
49
- plt.rcParams['legend.title_fontsize'] = 'medium'
50
- plt.rcParams['legend.handlelength'] = 1.5
51
- plt.rcParams['legend.labelspacing'] = 0.7
52
-
53
- plt.rcParams['figure.figsize'] = figsize or (5, 4)
54
- plt.rcParams['figure.dpi'] = 200
55
- plt.rcParams['figure.autolayout'] = autolayout
56
-
57
- if ~autolayout:
58
- plt.rcParams['figure.subplot.left'] = 0.1
59
- plt.rcParams['figure.subplot.right'] = 0.875
60
- plt.rcParams['figure.subplot.top'] = 0.875
61
- plt.rcParams['figure.subplot.bottom'] = 0.125
62
-
63
- # plt.rcParams['figure.constrained_layout.use'] = True
64
-
65
- plt.rcParams['savefig.transparent'] = True
66
-
67
- return _func(*args, **kwargs)
68
-
69
- return wrapper
70
-
71
- if func is None:
72
- return decorator
73
-
74
- return decorator(func)
@@ -1,104 +0,0 @@
1
- # read meteorological data from google sheet
2
-
3
-
4
- import numpy as np
5
- from pandas import read_csv, concat, to_datetime
6
-
7
- from AeroViz.rawDataReader.core import AbstractReader
8
-
9
-
10
- class Reader(AbstractReader):
11
- nam = 'IGAC_TH'
12
-
13
- def _raw_reader(self, _file):
14
-
15
- self.meta['freq'] = self._oth_set.get('data_freq') or self.meta['freq']
16
-
17
- with (_file).open('r', encoding='utf-8-sig', errors='ignore') as f:
18
- _df = read_csv(f, low_memory=False, index_col=0)
19
-
20
- _df.index = to_datetime(_df.index, errors='coerce', format=self._oth_set.get('date_format') or 'mixed')
21
- _df.index.name = 'time'
22
-
23
- _df.columns = _df.keys().str.strip(' ')
24
-
25
- _df = _df.loc[_df.index.dropna()].copy()
26
-
27
- return _df.loc[~_df.index.duplicated()]
28
-
29
- ## QC data
30
- def _QC(self, _df):
31
-
32
- ## QC parameter, function (MDL SE LE)
33
- _mdl = {
34
- 'Na+': 0.05,
35
- 'NH4+': 0.05,
36
- 'K+': 0.05,
37
- 'Mg2+': 0.05,
38
- 'Ca2+': 0.05,
39
- 'Cl-': 0.05,
40
- 'NO2-': 0.05,
41
- 'NO3-': 0.05,
42
- 'SO42-': 0.05,
43
- }
44
-
45
- def _se_le(_df_, _log=False):
46
- _df_ = np.log10(_df_) if _log else _df_
47
-
48
- _df_qua = _df_.quantile([.25, .75])
49
- _df_q1, _df_q3 = _df_qua.loc[.25].copy(), _df_qua.loc[.75].copy()
50
- _df_iqr = _df_q3 - _df_q1
51
-
52
- _se = concat([_df_q1 - 1.5 * _df_iqr] * len(_df_), axis=1).T.set_index(_df_.index)
53
- _le = concat([_df_q3 + 1.5 * _df_iqr] * len(_df_), axis=1).T.set_index(_df_.index)
54
-
55
- if _log:
56
- return 10 ** _se, 10 ** _le
57
- return _se, _le
58
-
59
- _cation, _anion, _main = ['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+'], ['Cl-', 'NO2-', 'NO3-', 'SO42-', ], ['SO42-',
60
- 'NO3-',
61
- 'NH4+']
62
-
63
- _df_salt = _df[_mdl.keys()].copy()
64
- _df_pm = _df['PM2.5'].copy()
65
-
66
- ## lower than PM2.5
67
- ## conc. of main salt should be present at the same time (NH4+, SO42-, NO3-)
68
- _df_salt = _df_salt.mask(_df_salt.sum(axis=1, min_count=1) > _df_pm).dropna(subset=_main).copy()
69
-
70
- ## mdl
71
- for (_key, _df_col), _mdl_val in zip(_df_salt.items(), _mdl.values()):
72
- _df_salt[_key] = _df_col.mask(_df_col < _mdl_val, _mdl_val / 2)
73
-
74
- ## group by time (per month)
75
- _df_salt['tm'] = _df_salt.index.strftime('%Y-%m')
76
-
77
- _df_lst = []
78
- for _ky, _df_grp in _df_salt.groupby('tm'):
79
- _df_grp = _df_grp[_mdl.keys()].copy()
80
-
81
- ## calculate SE LE
82
- ## salt < LE
83
- _se, _le = _se_le(_df_grp, _log=True)
84
- _df_grp = _df_grp.mask(_df_grp > _le).copy()
85
-
86
- ## C/A, A/C
87
- _rat_CA = (_df_grp[_cation].sum(axis=1) / _df_grp[_anion].sum(axis=1)).to_frame()
88
- _rat_AC = (1 / _rat_CA).copy()
89
-
90
- _se, _le = _se_le(_rat_CA, )
91
- _cond_CA = (_rat_CA < _le) & (_rat_CA > 0)
92
-
93
- _se, _le = _se_le(_rat_AC, )
94
- _cond_AC = (_rat_AC < _le) & (_rat_AC > 0)
95
-
96
- _df_grp = _df_grp.where((_cond_CA * _cond_AC)[0]).copy()
97
-
98
- ## conc. of main salt > SE
99
- _se, _le = _se_le(_df_grp[_main], _log=True)
100
- _df_grp[_main] = _df_grp[_main].mask(_df_grp[_main] < _se).copy()
101
-
102
- _df_lst.append(_df_grp)
103
-
104
- return concat(_df_lst).reindex(_df.index)
@@ -1,90 +0,0 @@
1
- # read meteorological data from google sheet
2
-
3
-
4
- import numpy as np
5
- from pandas import read_csv, concat, to_numeric
6
-
7
- from AeroViz.rawDataReader.core import AbstractReader
8
-
9
-
10
- class Reader(AbstractReader):
11
- nam = 'IGAC_ZM'
12
-
13
- def _raw_reader(self, _file):
14
-
15
- with (_file).open('r', encoding='utf-8-sig', errors='ignore') as f:
16
- _df = read_csv(f, parse_dates=[0], index_col=[0], na_values=['-']).apply(to_numeric, errors='coerce')
17
-
18
- _df.columns = _df.keys().str.strip(' ')
19
- _df.index.name = 'time'
20
-
21
- return _df.loc[_df.index.dropna()].loc[~_df.index.duplicated()]
22
-
23
- ## QC data
24
- def _QC(self, _df):
25
-
26
- ## QC parameter, function (MDL SE LE)
27
- _mdl = {
28
- 'Na+': 0.06,
29
- 'NH4+': 0.05,
30
- 'K+': 0.05,
31
- 'Mg2+': 0.12,
32
- 'Ca2+': 0.07,
33
- 'Cl-': 0.07,
34
- 'NO2-': 0.05,
35
- 'NO3-': 0.11,
36
- 'SO42-': 0.08,
37
- }
38
- _mdl.update(self._oth_set.get('mdl', {}))
39
-
40
- def _se_le(_df_, _log=False):
41
- _df_ = np.log10(_df_) if _log else _df_
42
-
43
- _df_qua = _df_.quantile([.25, .75])
44
- _df_q1, _df_q3 = _df_qua.loc[.25].copy(), _df_qua.loc[.75].copy()
45
- _df_iqr = _df_q3 - _df_q1
46
-
47
- _se = concat([_df_q1 - 1.5 * _df_iqr] * len(_df_), axis=1).T.set_index(_df_.index)
48
- _le = concat([_df_q3 + 1.5 * _df_iqr] * len(_df_), axis=1).T.set_index(_df_.index)
49
-
50
- if _log:
51
- return 10 ** _se, 10 ** _le
52
- return _se, _le
53
-
54
- _cation, _anion, _main = ['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+'], ['Cl-', 'NO2-', 'NO3-', 'SO42-', ], ['SO42-',
55
- 'NO3-',
56
- 'NH4+']
57
-
58
- _df_salt = _df[_mdl.keys()].copy()
59
- _df_pm = _df['PM2.5'].copy()
60
-
61
- ## lower than PM2.5
62
- ## conc. of main salt should be present at the same time (NH4+, SO42-, NO3-)
63
- _df_salt = _df_salt.mask(_df_salt.sum(axis=1, min_count=1) > _df_pm).dropna(subset=_main).copy()
64
-
65
- ## mdl
66
- for (_key, _df_col), _mdl_val in zip(_df_salt.items(), _mdl.values()):
67
- _df_salt[_key] = _df_col.mask(_df_col < _mdl_val, _mdl_val / 2)
68
-
69
- ## calculate SE LE
70
- ## salt < LE
71
- _se, _le = _se_le(_df_salt, _log=True)
72
- _df_salt = _df_salt.mask(_df_salt > _le).copy()
73
-
74
- ## C/A, A/C
75
- _rat_CA = (_df_salt[_cation].sum(axis=1) / _df_salt[_anion].sum(axis=1)).to_frame()
76
- _rat_AC = (1 / _rat_CA).copy()
77
-
78
- _se, _le = _se_le(_rat_CA, )
79
- _cond_CA = (_rat_CA < _le) & (_rat_CA > 0)
80
-
81
- _se, _le = _se_le(_rat_AC, )
82
- _cond_AC = (_rat_AC < _le) & (_rat_AC > 0)
83
-
84
- _df_salt = _df_salt.where((_cond_CA * _cond_AC)[0]).copy()
85
-
86
- ## conc. of main salt > SE
87
- _se, _le = _se_le(_df_salt[_main], _log=True)
88
- _df_salt[_main] = _df_salt[_main].mask(_df_salt[_main] < _se).copy()
89
-
90
- return _df_salt.reindex(_df.index)
@@ -1,34 +0,0 @@
1
- from pandas import to_datetime, read_csv
2
-
3
- from AeroViz.rawDataReader.core import AbstractReader
4
-
5
-
6
- class Reader(AbstractReader):
7
- nam = 'OCEC_LCRES'
8
-
9
- def _raw_reader(self, _file):
10
- with open(_file, 'r', encoding='utf-8', errors='ignore') as f:
11
- _df = read_csv(f, skiprows=3)
12
-
13
- _col = {'Thermal/Optical OC (ugC/LCm^3)': 'Thermal_OC',
14
- 'Thermal/Optical EC (ugC/LCm^3)': 'Thermal_EC',
15
- 'OC=TC-BC (ugC/LCm^3)': 'Optical_OC',
16
- 'BC (ugC/LCm^3)': 'Optical_EC',
17
- 'Sample Volume Local Condition Actual m^3': 'Sample_Volume',
18
- 'TC (ugC/LCm^3)': 'TC', }
19
-
20
- _tm_idx = to_datetime(_df['Start Date/Time'], errors='coerce')
21
- _df['time'] = _tm_idx
22
-
23
- _df = _df.dropna(subset='time').loc[~_tm_idx.duplicated()].set_index('time')
24
-
25
- return _df[_col.keys()].rename(columns=_col)
26
-
27
- ## QC data
28
- def _QC(self, _df):
29
- _df[['Thermal_OC', 'Optical_OC']] = _df[['Thermal_OC', 'Optical_OC']].where(
30
- _df[['Thermal_OC', 'Optical_OC']] > 0.3).copy()
31
- _df[['Thermal_EC', 'Optical_EC']] = _df[['Thermal_EC', 'Optical_EC']].where(
32
- _df[['Thermal_EC', 'Optical_EC']] > .015).copy()
33
-
34
- return _df