AeroViz 0.1.3b0__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 (81) hide show
  1. AeroViz/__init__.py +5 -3
  2. AeroViz/{config → data}/DEFAULT_DATA.csv +1 -1
  3. AeroViz/dataProcess/Chemistry/__init__.py +7 -7
  4. AeroViz/dataProcess/Chemistry/_isoropia.py +5 -2
  5. AeroViz/dataProcess/Chemistry/_mass_volume.py +15 -18
  6. AeroViz/dataProcess/Chemistry/_ocec.py +2 -2
  7. AeroViz/dataProcess/Chemistry/_teom.py +2 -1
  8. AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
  9. AeroViz/dataProcess/Optical/Angstrom_exponent.py +20 -0
  10. AeroViz/dataProcess/Optical/_IMPROVE.py +13 -15
  11. AeroViz/dataProcess/Optical/__init__.py +15 -30
  12. AeroViz/dataProcess/Optical/_absorption.py +21 -47
  13. AeroViz/dataProcess/Optical/_extinction.py +20 -15
  14. AeroViz/dataProcess/Optical/_mie.py +0 -1
  15. AeroViz/dataProcess/Optical/_scattering.py +19 -20
  16. AeroViz/dataProcess/SizeDistr/__init__.py +7 -7
  17. AeroViz/dataProcess/SizeDistr/_merge.py +2 -2
  18. AeroViz/dataProcess/SizeDistr/_merge_v1.py +2 -2
  19. AeroViz/dataProcess/SizeDistr/_merge_v2.py +2 -2
  20. AeroViz/dataProcess/SizeDistr/_merge_v3.py +1 -1
  21. AeroViz/dataProcess/SizeDistr/_merge_v4.py +1 -1
  22. AeroViz/dataProcess/VOC/__init__.py +3 -3
  23. AeroViz/dataProcess/__init__.py +28 -6
  24. AeroViz/dataProcess/core/__init__.py +10 -17
  25. AeroViz/plot/__init__.py +1 -1
  26. AeroViz/plot/box.py +2 -1
  27. AeroViz/plot/optical/optical.py +4 -4
  28. AeroViz/plot/regression.py +25 -39
  29. AeroViz/plot/scatter.py +68 -2
  30. AeroViz/plot/templates/__init__.py +2 -1
  31. AeroViz/plot/templates/ammonium_rich.py +34 -0
  32. AeroViz/plot/templates/diurnal_pattern.py +11 -9
  33. AeroViz/plot/templates/koschmieder.py +51 -115
  34. AeroViz/plot/templates/metal_heatmap.py +115 -17
  35. AeroViz/plot/timeseries/__init__.py +1 -0
  36. AeroViz/plot/timeseries/template.py +47 -0
  37. AeroViz/plot/timeseries/timeseries.py +275 -208
  38. AeroViz/plot/utils/plt_utils.py +2 -2
  39. AeroViz/plot/utils/units.json +5 -0
  40. AeroViz/plot/violin.py +9 -8
  41. AeroViz/process/__init__.py +2 -2
  42. AeroViz/process/script/AbstractDistCalc.py +1 -1
  43. AeroViz/process/script/Chemical.py +5 -4
  44. AeroViz/process/script/Others.py +1 -1
  45. AeroViz/rawDataReader/__init__.py +17 -22
  46. AeroViz/rawDataReader/{utils/config.py → config/supported_instruments.py} +38 -52
  47. AeroViz/rawDataReader/core/__init__.py +104 -229
  48. AeroViz/rawDataReader/script/AE33.py +10 -11
  49. AeroViz/rawDataReader/script/AE43.py +8 -11
  50. AeroViz/rawDataReader/script/APS_3321.py +6 -6
  51. AeroViz/rawDataReader/script/Aurora.py +18 -19
  52. AeroViz/rawDataReader/script/BC1054.py +11 -15
  53. AeroViz/rawDataReader/script/EPA_vertical.py +35 -7
  54. AeroViz/rawDataReader/script/GRIMM.py +2 -9
  55. AeroViz/rawDataReader/script/{IGAC_ZM.py → IGAC.py} +17 -17
  56. AeroViz/rawDataReader/script/MA350.py +7 -14
  57. AeroViz/rawDataReader/script/Minion.py +103 -0
  58. AeroViz/rawDataReader/script/NEPH.py +24 -29
  59. AeroViz/rawDataReader/script/SMPS_TH.py +4 -4
  60. AeroViz/rawDataReader/script/SMPS_aim11.py +6 -6
  61. AeroViz/rawDataReader/script/SMPS_genr.py +6 -6
  62. AeroViz/rawDataReader/script/Sunset_OCEC.py +60 -0
  63. AeroViz/rawDataReader/script/TEOM.py +8 -6
  64. AeroViz/rawDataReader/script/Table.py +7 -8
  65. AeroViz/rawDataReader/script/VOC.py +26 -0
  66. AeroViz/rawDataReader/script/__init__.py +10 -12
  67. AeroViz/tools/database.py +7 -9
  68. AeroViz/tools/datareader.py +3 -3
  69. {AeroViz-0.1.3b0.dist-info → AeroViz-0.1.4.dist-info}/METADATA +1 -1
  70. AeroViz-0.1.4.dist-info/RECORD +112 -0
  71. AeroViz/rawDataReader/script/IGAC_TH.py +0 -104
  72. AeroViz/rawDataReader/script/OCEC_LCRES.py +0 -34
  73. AeroViz/rawDataReader/script/OCEC_RES.py +0 -28
  74. AeroViz/rawDataReader/script/VOC_TH.py +0 -30
  75. AeroViz/rawDataReader/script/VOC_ZM.py +0 -37
  76. AeroViz-0.1.3b0.dist-info/RECORD +0 -110
  77. /AeroViz/{config → data}/DEFAULT_PNSD_DATA.csv +0 -0
  78. /AeroViz/rawDataReader/{utils → config}/__init__.py +0 -0
  79. {AeroViz-0.1.3b0.dist-info → AeroViz-0.1.4.dist-info}/LICENSE +0 -0
  80. {AeroViz-0.1.3b0.dist-info → AeroViz-0.1.4.dist-info}/WHEEL +0 -0
  81. {AeroViz-0.1.3b0.dist-info → AeroViz-0.1.4.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- from ..core import _writter, _run_process
1
+ from ..core import Writer, run_process
2
2
 
3
3
  __all__ = [
4
4
 
@@ -7,10 +7,10 @@ __all__ = [
7
7
  ]
8
8
 
9
9
 
10
- class SizeDistr(_writter):
10
+ class SizeDistr(Writer):
11
11
 
12
12
  ## basic
13
- @_run_process('SizeDistr - basic', 'distr_basic')
13
+ @run_process('SizeDistr - basic', 'distr_basic')
14
14
  def basic(self, df, hybrid_bin_start_loc=None, unit='nm', bin_range=(0, 20000), input_type='norm'):
15
15
  from ._size_distr import _basic
16
16
 
@@ -19,7 +19,7 @@ class SizeDistr(_writter):
19
19
  return self, out
20
20
 
21
21
  ## merge
22
- @_run_process('SizeDistr - merge_SMPS_APS_v4', 'distr_merge')
22
+ @run_process('SizeDistr - merge_SMPS_APS_v4', 'distr_merge')
23
23
  def merge_SMPS_APS_v4(self, df_smps, df_aps, df_pm25, aps_unit='um',
24
24
  smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True,
25
25
  times_range=(0.8, 1.25, .05)):
@@ -31,7 +31,7 @@ class SizeDistr(_writter):
31
31
  return self, out
32
32
 
33
33
  ## merge
34
- @_run_process('SizeDistr - merge_SMPS_APS_v3', 'distr_merge')
34
+ @run_process('SizeDistr - merge_SMPS_APS_v3', 'distr_merge')
35
35
  def merge_SMPS_APS_v3(self, df_smps, df_aps, aps_unit='um',
36
36
  smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True):
37
37
  from ._merge_v3 import merge_SMPS_APS
@@ -41,7 +41,7 @@ class SizeDistr(_writter):
41
41
  return self, out
42
42
 
43
43
  ## merge
44
- @_run_process('SizeDistr - merge_SMPS_APS_v2', 'distr_merge')
44
+ @run_process('SizeDistr - merge_SMPS_APS_v2', 'distr_merge')
45
45
  def merge_SMPS_APS_v2(self, df_smps, df_aps, aps_unit='um',
46
46
  smps_overlap_lowbound=500, aps_fit_highbound=1000):
47
47
  from ._merge_v2 import merge_SMPS_APS
@@ -51,7 +51,7 @@ class SizeDistr(_writter):
51
51
  return self, out
52
52
 
53
53
  ## merge
54
- @_run_process('SizeDistr - merge_SMPS_APS_v1', 'distr_merge')
54
+ @run_process('SizeDistr - merge_SMPS_APS_v1', 'distr_merge')
55
55
  def merge_SMPS_APS(self, df_smps, df_aps, aps_unit='um', shift_mode='mobility',
56
56
  smps_overlap_lowbound=523, aps_fit_highbound=800):
57
57
  from ._merge_v1 import _merge_SMPS_APS
@@ -5,7 +5,7 @@ from pandas import DataFrame, to_datetime
5
5
  # from scipy.interpolate import interp1d
6
6
  from scipy.interpolate import UnivariateSpline as unvpline, interp1d
7
7
 
8
- from AeroViz.dataProcess.core import _union_index
8
+ from AeroViz.dataProcess.core import union_index
9
9
 
10
10
  __all__ = ['merge_SMPS_APS']
11
11
 
@@ -195,7 +195,7 @@ def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _coe, _shift
195
195
 
196
196
 
197
197
  def merge_SMPS_APS(df_smps, df_aps, aps_unit='um', smps_overlap_lowbound=500, aps_fit_highbound=1000):
198
- df_smps, df_aps = _union_index(df_smps, df_aps)
198
+ df_smps, df_aps = union_index(df_smps, df_aps)
199
199
 
200
200
  ## set to the same units
201
201
  smps, aps_ori = df_smps.copy(), df_aps.copy()
@@ -5,7 +5,7 @@ from pandas import DataFrame, to_datetime
5
5
  # from scipy.interpolate import interp1d
6
6
  from scipy.interpolate import UnivariateSpline as unvpline, interp1d
7
7
 
8
- from AeroViz.dataProcess.core import _union_index
8
+ from AeroViz.dataProcess.core import union_index
9
9
 
10
10
  __all__ = ['_merge_SMPS_APS']
11
11
 
@@ -203,7 +203,7 @@ def _merge_data(_smps_ori, _aps_ori, _shift_ori, _shift_mode, _smps_lb, _aps_hb,
203
203
 
204
204
  ## aps_fit_highbound : the diameter I choose randomly
205
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)
206
+ df_smps, df_aps = union_index(df_smps, df_aps)
207
207
 
208
208
  # print(f'\nMerge data :')
209
209
  # print(f' APS fittint higher diameter : {aps_fit_highbound:4d} nm')
@@ -5,7 +5,7 @@ from pandas import DataFrame, to_datetime
5
5
  # from scipy.interpolate import interp1d
6
6
  from scipy.interpolate import UnivariateSpline as unvpline, interp1d
7
7
 
8
- from AeroViz.dataProcess.core import _union_index
8
+ from AeroViz.dataProcess.core import union_index
9
9
 
10
10
  __all__ = ['_merge_SMPS_APS']
11
11
 
@@ -196,7 +196,7 @@ def _merge_data(_smps_ori, _aps_ori, _shift_ori, _smps_lb, _aps_hb, _coe, _shift
196
196
 
197
197
 
198
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)
199
+ df_smps, df_aps = union_index(df_smps, df_aps)
200
200
 
201
201
  ## set to the same units
202
202
  smps, aps_ori = df_smps.copy(), df_aps.copy()
@@ -1,4 +1,4 @@
1
- # from ContainerHandle.dataProcess.utils import _union_index
1
+ # from ContainerHandle.dataProcess.config import _union_index
2
2
 
3
3
  from datetime import datetime as dtm
4
4
 
@@ -1,4 +1,4 @@
1
- # from ContainerHandle.dataProcess.utils import _union_index
1
+ # from ContainerHandle.dataProcess.config import _union_index
2
2
 
3
3
  import warnings
4
4
  from datetime import datetime as dtm
@@ -1,4 +1,4 @@
1
- from ..core import _writter, _run_process
1
+ from ..core import Writer, run_process
2
2
 
3
3
  __all__ = [
4
4
 
@@ -7,10 +7,10 @@ __all__ = [
7
7
  ]
8
8
 
9
9
 
10
- class VOC(_writter):
10
+ class VOC(Writer):
11
11
 
12
12
  ## Reconstruction
13
- @_run_process('VOC - basic', 'voc_basic')
13
+ @run_process('VOC - basic', 'voc_basic')
14
14
  def VOC_basic(self, _df_voc):
15
15
  from ._potential_par import _basic
16
16
 
@@ -1,11 +1,33 @@
1
+ from pathlib import Path
2
+
1
3
  from .Chemistry import Chemistry
2
4
  from .Optical import Optical
3
5
  from .SizeDistr import SizeDistr
4
6
  from .VOC import VOC
5
7
 
6
- __all__ = [
7
- 'Optical',
8
- 'SizeDistr',
9
- 'Chemistry',
10
- 'VOC',
11
- ]
8
+ __all__ = ['DataProcess']
9
+
10
+
11
+ def DataProcess(method: str,
12
+ path_out: Path,
13
+ excel: bool = False,
14
+ csv: bool = True,
15
+ ):
16
+ # Mapping of method names to their respective classes
17
+ method_class_map = {
18
+ 'Chemistry': Chemistry,
19
+ 'Optical': Optical,
20
+ 'SizeDistr': SizeDistr,
21
+ 'VOC': VOC
22
+ }
23
+
24
+ if method not in method_class_map.keys():
25
+ raise ValueError(f"Method name '{method}' is not valid. \nMust be one of: {list(method_class_map.keys())}")
26
+
27
+ writer_module = method_class_map[method](
28
+ path_out=path_out,
29
+ excel=excel,
30
+ csv=csv
31
+ )
32
+
33
+ return writer_module
@@ -5,17 +5,16 @@ from pathlib import Path
5
5
  from pandas import concat
6
6
 
7
7
 
8
- class _writter:
8
+ class Writer:
9
9
 
10
10
  def __init__(self, path_out=None, excel=True, csv=False):
11
-
12
11
  self.path_out = Path(path_out) if path_out is not None else path_out
13
12
  self.excel = excel
14
13
  self.csv = csv
15
14
 
16
- def _pre_process(self, _out):
17
-
18
- if type(_out) == dict:
15
+ @staticmethod
16
+ def pre_process(_out):
17
+ if isinstance(_out, dict):
19
18
  for _ky, _df in _out.items():
20
19
  _df.index.name = 'time'
21
20
  else:
@@ -23,8 +22,7 @@ class _writter:
23
22
 
24
23
  return _out
25
24
 
26
- def _save_out(self, _nam, _out):
27
-
25
+ def save_out(self, _nam, _out):
28
26
  _check = True
29
27
  while _check:
30
28
 
@@ -44,7 +42,7 @@ class _writter:
44
42
  _out.to_excel(f, sheet_name=f'{_nam}')
45
43
 
46
44
  if self.csv:
47
- if type(_out) == dict:
45
+ if isinstance(_out, dict):
48
46
  _path_out = self.path_out / _nam
49
47
  _path_out.mkdir(exist_ok=True, parents=True)
50
48
 
@@ -60,7 +58,7 @@ class _writter:
60
58
  input('\t\t\33[41m Please Close The File And Press "Enter" \33[0m\n')
61
59
 
62
60
 
63
- def _run_process(*_ini_set):
61
+ def run_process(*_ini_set):
64
62
  def _decorator(_prcs_fc):
65
63
  def _wrap(*arg, **kwarg):
66
64
  _fc_name, _nam = _ini_set
@@ -71,9 +69,9 @@ def _run_process(*_ini_set):
71
69
  print(f"\n\t{dtm.now().strftime('%m/%d %X')} : Process \033[92m{_fc_name}\033[0m -> {_nam}")
72
70
 
73
71
  _class, _out = _prcs_fc(*arg, **kwarg)
74
- _out = _class._pre_process(_out)
72
+ _out = _class.pre_process(_out)
75
73
 
76
- _class._save_out(_nam, _out)
74
+ _class.save_out(_nam, _out)
77
75
 
78
76
  return _out
79
77
 
@@ -82,12 +80,7 @@ def _run_process(*_ini_set):
82
80
  return _decorator
83
81
 
84
82
 
85
- def _union_index(*_df_arg):
83
+ def union_index(*_df_arg):
86
84
  _idx = concat(_df_arg, axis=1).index
87
85
 
88
- # _idx = DatetimeIndex([])
89
-
90
- # for _df in _df_arg:
91
- # _idx = _idx.union(DataFrame(_df).index)
92
-
93
86
  return [_df.reindex(_idx) if _df is not None else None for _df in _df_arg]
AeroViz/plot/__init__.py CHANGED
@@ -1,12 +1,12 @@
1
1
  from . import distribution
2
2
  from . import meteorology
3
3
  from . import optical
4
- from . import timeseries
5
4
  from .bar import bar
6
5
  from .box import box
7
6
  from .pie import pie, donuts
8
7
  from .regression import linear_regression, multiple_linear_regression
9
8
  from .scatter import scatter
10
9
  from .templates import *
10
+ from .timeseries import timeseries, timeseries_template, timeseries_stacked
11
11
  from .utils import *
12
12
  from .violin import violin
AeroViz/plot/box.py CHANGED
@@ -15,7 +15,8 @@ def box(df: pd.DataFrame,
15
15
  x_bins: list | np.ndarray = None,
16
16
  add_scatter: bool = True,
17
17
  ax: Axes | None = None,
18
- **kwargs) -> tuple[Figure, Axes]:
18
+ **kwargs
19
+ ) -> tuple[Figure, Axes]:
19
20
  fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
20
21
 
21
22
  df = df.dropna(subset=[x, y]).copy()
@@ -381,8 +381,8 @@ def response_surface(real_range=(1.33, 1.7),
381
381
 
382
382
 
383
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')
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
386
 
387
- # RI_couple()
388
- # response_surface()
387
+ # RI_couple()
388
+ response_surface()
@@ -23,53 +23,47 @@ def linear_regression(df: pd.DataFrame,
23
23
  **kwargs
24
24
  ) -> tuple[Figure, Axes]:
25
25
  """
26
- Create a scatter plot with multiple regression lines for the given data.
26
+ Create a scatter plot with regression lines for the given data.
27
27
 
28
28
  Parameters
29
29
  ----------
30
- df : DataFrame
30
+ df : pd.DataFrame
31
31
  Input DataFrame containing the data.
32
-
33
32
  x : str or list of str
34
- Column name(s) for the x-axis variable(s).
35
-
33
+ Column name(s) for the x-axis variable(s). If a list, only the first element is used.
36
34
  y : str or list of str
37
35
  Column name(s) for the y-axis variable(s).
38
-
39
36
  labels : str or list of str, optional
40
37
  Labels for the y-axis variable(s). If None, column names are used as labels. Default is None.
41
-
42
- ax : AxesSubplot, optional
43
- Matplotlib AxesSubplot to use for the plot. If None, a new subplot is created. Default is None.
44
-
38
+ ax : Axes, optional
39
+ Matplotlib Axes object to use for the plot. If None, a new subplot is created. Default is None.
45
40
  diagonal : bool, optional
46
41
  If True, a diagonal line (1:1 line) is added to the plot. Default is False.
47
-
48
42
  positive : bool, optional
49
- Whether to let coefficient positive. Default is True.
50
-
43
+ Whether to constrain the regression coefficients to be positive. Default is True.
51
44
  fit_intercept: bool, optional
52
- Whether to fit intercept. Default is True.
53
-
45
+ Whether to calculate the intercept for this model. Default is True.
54
46
  **kwargs
55
- Additional keyword arguments to customize the plot.
47
+ Additional keyword arguments for plot customization.
56
48
 
57
49
  Returns
58
50
  -------
59
- AxesSubplot
60
- Matplotlib AxesSubplot containing the scatter plot.
51
+ fig : Figure
52
+ The matplotlib Figure object.
53
+ ax : Axes
54
+ The matplotlib Axes object with the scatter plot.
61
55
 
62
56
  Notes
63
57
  -----
64
- - The function creates a scatter plot with the option to include multiple regression lines.
65
- - If regression is True, regression lines are fitted for each y variable.
66
- - Additional customization can be done using the **kwargs.
58
+ - The function creates a scatter plot with optional regression lines.
59
+ - The regression line is fitted for each y variable.
60
+ - Customization options are provided via **kwargs.
67
61
 
68
62
  Example
69
63
  -------
70
64
  >>> linear_regression(df, x='X', y=['Y1', 'Y2'], labels=['Label1', 'Label2'],
71
- ... regression=True, diagonal=True, xlim=(0, 10), ylim=(0, 20),
72
- ... xlabel="X-axis", ylabel="Y-axis", title="Scatter Plot with Regressions")
65
+ ... diagonal=True, xlim=(0, 10), ylim=(0, 20),
66
+ ... xlabel="X-axis", ylabel="Y-axis", title="Scatter Plot with Regressions")
73
67
  """
74
68
  fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
75
69
 
@@ -140,37 +134,29 @@ def multiple_linear_regression(df: pd.DataFrame,
140
134
 
141
135
  Parameters
142
136
  ----------
143
- df : pandas.DataFrame
137
+ df : pd.DataFrame
144
138
  Input DataFrame containing the data.
145
-
146
139
  x : str or list of str
147
140
  Column name(s) for the independent variable(s). Can be a single string or a list of strings.
148
-
149
141
  y : str or list of str
150
142
  Column name(s) for the dependent variable(s). Can be a single string or a list of strings.
151
-
152
143
  labels : str or list of str, optional
153
144
  Labels for the dependent variable(s). If None, column names are used as labels. Default is None.
154
-
155
- ax : matplotlib.axes.Axes or None, optional
145
+ ax : Axes, optional
156
146
  Matplotlib Axes object to use for the plot. If None, a new subplot is created. Default is None.
157
-
158
147
  diagonal : bool, optional
159
148
  Whether to include a diagonal line (1:1 line) in the plot. Default is False.
160
-
161
149
  positive : bool, optional
162
- Whether to let coefficient positive. Default is True.
163
-
150
+ Whether to constrain the regression coefficients to be positive. Default is True.
164
151
  fit_intercept: bool, optional
165
- Whether to fit intercept. Default is True.
166
-
152
+ Whether to calculate the intercept for this model. Default is True.
167
153
  **kwargs
168
- Additional keyword arguments to customize the plot.
154
+ Additional keyword arguments for plot customization.
169
155
 
170
156
  Returns
171
157
  -------
172
- matplotlib.axes.Axes
173
- Matplotlib Axes object containing the regression plot.
158
+ tuple[Figure, Axes]
159
+ The Figure and Axes containing the regression plot.
174
160
 
175
161
  Notes
176
162
  -----
@@ -180,7 +166,7 @@ def multiple_linear_regression(df: pd.DataFrame,
180
166
  Example
181
167
  -------
182
168
  >>> multiple_linear_regression(df, x=['X1', 'X2'], y='Y', labels=['Y1', 'Y2'],
183
- ... diagonal=True, add_constant=True,
169
+ ... diagonal=True, fit_intercept=True,
184
170
  ... xlabel="X-axis", ylabel="Y-axis", title="Multiple Linear Regression Plot")
185
171
  """
186
172
  fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
AeroViz/plot/scatter.py CHANGED
@@ -11,7 +11,13 @@ from AeroViz.plot.utils import *
11
11
  __all__ = ['scatter']
12
12
 
13
13
 
14
- @set_figure(figsize=(5, 4))
14
+ def check_empty(*arrays):
15
+ for i, arr in enumerate(arrays):
16
+ if arr.size == 0:
17
+ raise ValueError(f"Array is empty!")
18
+
19
+
20
+ @set_figure
15
21
  def scatter(df: pd.DataFrame,
16
22
  x: str,
17
23
  y: str,
@@ -21,12 +27,69 @@ def scatter(df: pd.DataFrame,
21
27
  regression=False,
22
28
  diagonal=False,
23
29
  ax: Axes | None = None,
24
- **kwargs) -> tuple[Figure, Axes]:
30
+ **kwargs
31
+ ) -> tuple[Figure, Axes]:
32
+ """
33
+ Creates a scatter plot with optional color and size encoding.
34
+
35
+ Parameters
36
+ ----------
37
+ df : pd.DataFrame
38
+ The DataFrame containing the data to plot.
39
+ x : str
40
+ The column name for the x-axis values.
41
+ y : str
42
+ The column name for the y-axis values.
43
+ c : str, optional
44
+ The column name for color encoding. Default is None.
45
+ s : str, optional
46
+ The column name for size encoding. Default is None.
47
+ cmap : str, optional
48
+ The colormap to use for the color encoding. Default is 'jet'.
49
+ regression : bool, optional
50
+ If True, fits and plots a linear regression line. Default is False.
51
+ diagonal : bool, optional
52
+ If True, plots a 1:1 diagonal line. Default is False.
53
+ ax : Axes, optional
54
+ The matplotlib Axes to plot on. If not provided, a new figure and axes are created.
55
+ **kwargs : Any
56
+ Additional keyword arguments passed to customize the plot, such as `fig_kws` for figure creation and `xlabel`,
57
+ `ylabel`, `xlim`, `ylim`, `title` for axis labeling and limits.
58
+
59
+ Returns
60
+ -------
61
+ fig : Figure
62
+ The matplotlib Figure object.
63
+ ax : Axes
64
+ The matplotlib Axes object with the scatter plot.
65
+
66
+ Notes
67
+ -----
68
+ - If both `c` and `s` are provided, the scatter plot will encode data points using both color and size.
69
+ - If only `c` is provided, data points will be color-coded according to the values in the `c` column.
70
+ - If only `s` is provided, data points will be sized according to the values in the `s` column.
71
+ - If neither `c` nor `s` is provided, a basic scatter plot is created.
72
+ - The `regression` option will add a linear regression line and display the equation on the plot.
73
+ - The `diagonal` option will add a 1:1 reference line to the plot.
74
+
75
+ Examples
76
+ --------
77
+ >>> import pandas as pd
78
+ >>> from AeroViz.plot import scatter
79
+ >>> df = pd.DataFrame({
80
+ >>> 'x': [1, 2, 3, 4],
81
+ >>> 'y': [1.1, 2.0, 2.9, 4.1],
82
+ >>> 'color': [10, 20, 30, 40],
83
+ >>> 'size': [100, 200, 300, 400]
84
+ >>> })
85
+ >>> fig, ax = scatter(df, x='x', y='y', c='color', s='size', regression=True, diagonal=True)
86
+ """
25
87
  fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
26
88
 
27
89
  if c is not None and s is not None:
28
90
  df_ = df.dropna(subset=[x, y, c, s]).copy()
29
91
  x_data, y_data, c_data, s_data = df_[x].to_numpy(), df_[y].to_numpy(), df_[c].to_numpy(), df_[s].to_numpy()
92
+ check_empty(x_data, y_data, c_data, s_data)
30
93
 
31
94
  scatter = ax.scatter(x_data, y_data, c=c_data,
32
95
  norm=Normalize(vmin=np.percentile(c_data, 10), vmax=np.percentile(c_data, 90)),
@@ -43,6 +106,7 @@ def scatter(df: pd.DataFrame,
43
106
  elif c is not None:
44
107
  df_ = df.dropna(subset=[x, y, c]).copy()
45
108
  x_data, y_data, c_data = df_[x].to_numpy(), df_[y].to_numpy(), df_[c].to_numpy()
109
+ check_empty(x_data, y_data, c_data)
46
110
 
47
111
  scatter = ax.scatter(x_data, y_data, c=c_data, vmin=c_data.min(), vmax=np.percentile(c_data, 90), cmap=cmap,
48
112
  alpha=0.7,
@@ -52,6 +116,7 @@ def scatter(df: pd.DataFrame,
52
116
  elif s is not None:
53
117
  df_ = df.dropna(subset=[x, y, s]).copy()
54
118
  x_data, y_data, s_data = df_[x].to_numpy(), df_[y].to_numpy(), df_[s].to_numpy()
119
+ check_empty(x_data, y_data, s_data)
55
120
 
56
121
  scatter = ax.scatter(x_data, y_data, s=50 * (s_data / s_data.max()) ** 1.5, color='#7a97c9', alpha=0.7,
57
122
  edgecolors='white')
@@ -68,6 +133,7 @@ def scatter(df: pd.DataFrame,
68
133
  else:
69
134
  df_ = df.dropna(subset=[x, y]).copy()
70
135
  x_data, y_data = df_[x].to_numpy(), df_[y].to_numpy()
136
+ check_empty(x_data, y_data)
71
137
 
72
138
  scatter = ax.scatter(x_data, y_data, s=30, color='#7a97c9', alpha=0.7, edgecolors='white')
73
139
  colorbar = False
@@ -1,5 +1,6 @@
1
+ from .ammonium_rich import ammonium_rich
1
2
  from .contour import *
2
3
  from .corr_matrix import corr_matrix
3
4
  from .diurnal_pattern import *
4
5
  from .koschmieder import *
5
- from .metal_heatmap import metal_heatmaps, process_data
6
+ from .metal_heatmap import metal_heatmaps, process_data_with_two_df
@@ -0,0 +1,34 @@
1
+ import matplotlib.pyplot as plt
2
+ from matplotlib.pyplot import Figure, Axes
3
+ from pandas import DataFrame
4
+
5
+ from AeroViz.plot.utils import set_figure, Unit
6
+
7
+
8
+ @set_figure(figsize=(5, 4))
9
+ def ammonium_rich(df: DataFrame,
10
+ **kwargs
11
+ ) -> tuple[Figure, Axes]:
12
+ df = df[['NH4+', 'SO42-', 'NO3-', 'PM2.5']].dropna().copy().div([18, 96, 62, 1])
13
+ df['required_ammonium'] = df['NO3-'] + 2 * df['SO42-']
14
+
15
+ fig, ax = plt.subplots()
16
+
17
+ scatter = ax.scatter(df['required_ammonium'].to_numpy(), df['NH4+'].to_numpy(), c=df['PM2.5'].to_numpy(),
18
+ vmin=0, vmax=70, cmap='jet', marker='o', s=10, alpha=1)
19
+
20
+ ax.axline((0, 0), slope=1., color='k', lw=2, ls='--', alpha=0.5, label='1:1')
21
+ plt.text(0.97, 0.97, r'$\bf 1:1\ Line$', color='k', ha='right', va='top', transform=ax.transAxes)
22
+
23
+ ax.set(xlim=(0, 1.2),
24
+ ylim=(0, 1.2),
25
+ xlabel=r'$\bf NO_{3}^{-}\ +\ 2\ \times\ SO_{4}^{2-}\ (mole\ m^{-3})$',
26
+ ylabel=r'$\bf NH_{4}^{+}\ (mole\ m^{-3})$',
27
+ title=kwargs.get('title', ''))
28
+
29
+ color_bar = plt.colorbar(scatter, label=Unit('PM2.5'), extend='both')
30
+
31
+ # fig.savefig(f'Ammonium_rich_{title}')
32
+ plt.show()
33
+
34
+ return fig, ax
@@ -1,7 +1,7 @@
1
1
  import matplotlib.pyplot as plt
2
- import pandas as pd
3
2
  from matplotlib.pyplot import Figure, Axes
4
3
  from matplotlib.ticker import AutoMinorLocator
4
+ from pandas import DataFrame
5
5
 
6
6
  from AeroViz.plot.utils import *
7
7
 
@@ -9,22 +9,24 @@ __all__ = ['diurnal_pattern']
9
9
 
10
10
 
11
11
  @set_figure
12
- def diurnal_pattern(data_set: pd.DataFrame,
13
- data_std: pd.DataFrame,
12
+ def diurnal_pattern(df: DataFrame,
14
13
  y: str | list[str],
15
- std_area=0.5,
14
+ std_area: float = 0.5,
16
15
  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)
16
+ **kwargs
17
+ ) -> tuple[Figure, Axes]:
18
+ if 'hour' or 'Hour' not in df.columns:
19
+ df['Hour'] = df.index.hour
19
20
 
20
21
  Hour = range(0, 24)
22
+ mean = df.groupby('Hour')[y].mean()
23
+ std = df.groupby('Hour')[y].std() * std_area
21
24
 
22
- mean = data_set[y]
23
- std = data_std[y] * std_area
25
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
24
26
 
25
27
  # Plot Diurnal pattern
26
28
  ax.plot(Hour, mean, 'blue')
27
- ax.fill_between(Hour, y1=mean + std, y2=mean - std, alpha=0.5, color='blue', edgecolor=None)
29
+ ax.fill_between(Hour, y1=mean + std, y2=mean - std, alpha=0.2, color='blue', edgecolor=None)
28
30
 
29
31
  ax.set(xlabel=kwargs.get('xlabel', 'Hours'),
30
32
  ylabel=kwargs.get('ylabel', Unit(y)),