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
@@ -12,97 +12,90 @@ from AeroViz.plot.utils import *
12
12
  __all__ = ['corr_matrix']
13
13
 
14
14
 
15
- @set_figure(fs=8)
15
+ @set_figure
16
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
17
+ cmap: str = "RdBu",
18
+ ax: Axes | None = None,
19
+ **kwargs
20
+ ) -> tuple[Figure, Axes]:
21
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
22
+
23
+ _corr = data.corr()
24
+ corr = pd.melt(_corr.reset_index(), id_vars='index')
25
+ corr.columns = ['x', 'y', 'value']
26
+
27
+ p_values = _corr.apply(lambda col1: _corr.apply(lambda col2: pearsonr(col1, col2)[1]))
28
+ p_values = p_values.mask(p_values > 0.05)
29
+ p_values = pd.melt(p_values.reset_index(), id_vars='index').dropna()
30
+ p_values.columns = ['x', 'y', 'value']
31
+
32
+ # Mapping from column names to integer coordinates
33
+ x_labels = [v for v in sorted(corr['x'].unique())]
34
+ y_labels = [v for v in sorted(corr['y'].unique())]
35
+ x_to_num = {p[1]: p[0] for p in enumerate(x_labels)}
36
+ y_to_num = {p[1]: p[0] for p in enumerate(y_labels)}
37
+
38
+ # Show column labels on the axes
39
+ ax.set_xticks([x_to_num[v] for v in x_labels])
40
+ ax.set_xticklabels(x_labels, rotation=90, horizontalalignment='center')
41
+ ax.set_yticks([y_to_num[v] for v in y_labels])
42
+ ax.set_yticklabels(y_labels)
43
+
44
+ # ax.tick_params(axis='both', which='major', direction='out', top=True, left=True)
45
+
46
+ ax.grid(False, 'major')
47
+ ax.grid(True, 'minor')
48
+ ax.set_xticks([t + 0.5 for t in ax.get_xticks()], minor=True)
49
+ ax.set_yticks([t + 0.5 for t in ax.get_yticks()], minor=True)
50
+
51
+ ax.set_xlim([-0.5, max([v for v in x_to_num.values()]) + 0.5])
52
+ ax.set_ylim([-0.5, max([v for v in y_to_num.values()]) + 0.5])
53
+
54
+ n_colors = 256 # Use 256 colors for the diverging color palette
55
+ palette = sns.color_palette(cmap, n_colors=n_colors) # Create the palette
56
+
57
+ # Range of values that will be mapped to the palette, i.e. min and max possible correlation
58
+ color_min, color_max = [-1, 1]
59
+
60
+ def value_to_color(val):
61
+ val_position = float((val - color_min)) / (color_max - color_min)
62
+ ind = int(val_position * (n_colors - 1)) # target index in the color palette
63
+ return palette[ind]
64
+
65
+ point = ax.scatter(
66
+ x=corr['x'].map(x_to_num),
67
+ y=corr['y'].map(y_to_num),
68
+ s=corr['value'].abs() * 70,
69
+ c=corr['value'].apply(value_to_color), # Vector of square color values, mapped to color palette
70
+ marker='s',
71
+ label='$R^{2}$'
72
+ )
73
+
74
+ axes_image = plt.cm.ScalarMappable(cmap=colormaps[cmap])
75
+
76
+ cax = inset_axes(ax, width="5%",
77
+ height="100%",
78
+ loc='lower left',
79
+ bbox_to_anchor=(1.02, 0., 1, 1),
80
+ bbox_transform=ax.transAxes,
81
+ borderpad=0)
82
+
83
+ cbar = plt.colorbar(mappable=axes_image, cax=cax, label=r'$R^{2}$')
84
+
85
+ cbar.set_ticks([0, 0.25, 0.5, 0.75, 1])
86
+ cbar.set_ticklabels(np.linspace(-1, 1, 5))
87
+
88
+ point2 = ax.scatter(
89
+ x=p_values['x'].map(x_to_num),
90
+ y=p_values['y'].map(y_to_num),
91
+ s=10,
92
+ marker='*',
93
+ color='k',
94
+ label='p < 0.05'
95
+ )
96
+
97
+ ax.legend(handles=[point2], labels=['p < 0.05'], bbox_to_anchor=(0.05, 1, 0.1, 0.05))
98
+
99
+ plt.show()
100
+
101
+ return fig, ax
@@ -1,42 +1,44 @@
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
 
8
8
  __all__ = ['diurnal_pattern']
9
9
 
10
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)
11
+ @set_figure
12
+ def diurnal_pattern(df: DataFrame,
13
+ y: str | list[str],
14
+ std_area: float = 0.5,
15
+ ax: Axes | None = None,
16
+ **kwargs
17
+ ) -> tuple[Figure, Axes]:
18
+ if 'hour' or 'Hour' not in df.columns:
19
+ df['Hour'] = df.index.hour
19
20
 
20
- Hour = range(0, 24)
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
- # 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)
27
+ # Plot Diurnal pattern
28
+ ax.plot(Hour, mean, 'blue')
29
+ ax.fill_between(Hour, y1=mean + std, y2=mean - std, alpha=0.2, color='blue', edgecolor=None)
28
30
 
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]))
31
+ ax.set(xlabel=kwargs.get('xlabel', 'Hours'),
32
+ ylabel=kwargs.get('ylabel', Unit(y)),
33
+ xlim=kwargs.get('xlim', (0, 23)),
34
+ ylim=kwargs.get('ylim', (None, None)),
35
+ xticks=kwargs.get('xticks', [0, 4, 8, 12, 16, 20]))
34
36
 
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)
37
+ ax.tick_params(axis='both', which='major')
38
+ ax.tick_params(axis='x', which='minor')
39
+ ax.xaxis.set_minor_locator(AutoMinorLocator())
40
+ ax.ticklabel_format(axis='y', style='sci', scilimits=(-2, 3), useMathText=True)
39
41
 
40
- plt.show()
42
+ plt.show()
41
43
 
42
- return fig, ax
44
+ return fig, ax
@@ -1,4 +1,3 @@
1
- from typing import Literal
2
1
 
3
2
  import matplotlib.pyplot as plt
4
3
  import numpy as np
@@ -11,146 +10,83 @@ from AeroViz.plot.utils import *
11
10
  __all__ = ['koschmieder']
12
11
 
13
12
 
14
- @set_figure(fs=12)
13
+ @set_figure
15
14
  def koschmieder(df: pd.DataFrame,
16
- y: Literal['Vis_Naked', 'Vis_LPV'],
17
- function: Literal['log', 'reciprocal'] = 'log',
18
- ax: Axes | None = None,
19
- **kwargs) -> tuple[Figure, Axes]:
20
- # x = Visibility, y = Extinction, log-log fit!!
21
- def _log_fit(x, y, func=lambda x, a: -x + a):
22
- x_log = np.log(x)
23
- y_log = np.log(y)
24
-
25
- popt, pcov = curve_fit(func, x_log, y_log)
15
+ vis: str,
16
+ ext: list[str],
17
+ ax: Axes | None = None,
18
+ **kwargs
19
+ ) -> tuple[Figure, Axes]:
20
+ """
21
+ Plot Koschmieder relationship between Visibility and Extinction.
26
22
 
27
- residuals = y_log - func(x_log, *popt)
28
- ss_res = np.sum(residuals ** 2)
29
- ss_total = np.sum((y_log - np.mean(y_log)) ** 2)
30
- r_squared = 1 - (ss_res / ss_total)
31
- print(f'Const_Log = {popt[0].round(3)}')
32
- print(f'Const = {np.exp(popt)[0].round(3)}')
33
- print(f'R^2 = {r_squared.round(3)}')
34
- return np.exp(popt)[0], pcov
35
-
36
- def _reciprocal_fit(x, y, func=lambda x, a, b: a / (x ** b)):
37
- popt, pcov = curve_fit(func, x, y)
23
+ x = Visibility, y = Extinction, log-log fit!!
24
+ """
25
+ def _log_fit(x, y, func=lambda x, a: -x + a):
26
+ x_log, y_log = np.log(x), np.log(y)
27
+ popt, pcov = curve_fit(func, x_log, y_log)
38
28
 
39
- residuals = y - func(x, *popt)
40
- ss_res = np.sum(residuals ** 2)
41
- ss_total = np.sum((y - np.mean(y)) ** 2)
42
- r_squared = 1 - (ss_res / ss_total)
43
- print(f'Const = {popt.round(3)}')
44
- print(f' R^2 = {r_squared.round(3)}')
45
- return popt, pcov
29
+ return np.exp(popt)[0], pcov
46
30
 
47
- fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
31
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
48
32
 
49
- _df1 = df[['Extinction', 'ExtinctionByGas', y]].dropna().copy()
50
- _df2 = df[['total_ext_dry', 'ExtinctionByGas', y]].dropna().copy()
33
+ boxcolors = ['#3f83bf', '#a5bf6b']
34
+ scattercolor = ['blue', 'green']
35
+ arts = []
36
+ labels = []
51
37
 
52
- x_data1 = _df1[y]
53
- y_data1 = _df1['Extinction'] + _df1['ExtinctionByGas']
38
+ for i, ext_col in enumerate(ext):
39
+ _df = df[[ext_col, vis]].dropna().copy()
40
+ x_data = _df[vis]
41
+ y_data = _df[ext_col]
54
42
 
55
- x_data2 = _df2[y]
56
- y_data2 = _df2['total_ext_dry'] + _df2['ExtinctionByGas']
43
+ bins = np.linspace(0, 50, 25)
44
+ wid = (bins + (bins[1] - bins[0]) / 2)[0:-1]
57
45
 
58
- para_coeff = []
59
- boxcolors = ['#3f83bf', '#a5bf6b']
46
+ _df[f'{vis}_bins'] = pd.cut(x_data, bins=bins, labels=wid)
60
47
 
61
- for i, (df_, x_data, y_data) in enumerate(zip([_df1, _df2], [x_data1, x_data2], [y_data1, y_data2])):
62
- df_['Total_Ext'] = y_data
48
+ grouped = _df.groupby(f'{vis}_bins', observed=False)
63
49
 
64
- if y == 'Vis_Naked':
65
- df_grp = df_.groupby(f'{y}')
50
+ vis_labels, vals, median_vals = [], [], []
51
+ for _, subdf in grouped:
52
+ if len(subdf[ext_col].dropna()) > 3:
53
+ vis_labels.append(subdf[vis].mean())
54
+ vals.append(subdf[ext_col].dropna().values)
55
+ median_vals.append(subdf[ext_col].mean())
66
56
 
67
- vals, median_vals, vis = [], [], []
68
- for j, (name, subdf) in enumerate(df_grp):
69
- if len(subdf['Total_Ext'].dropna()) > 20:
70
- vis.append('{:.0f}'.format(name))
71
- vals.append(subdf['Total_Ext'].dropna().values)
72
- median_vals.append(subdf['Total_Ext'].dropna().median())
57
+ plt.boxplot(vals, labels=vis_labels, positions=np.array(vis_labels, dtype='float'),
58
+ widths=(bins[1] - bins[0]) / 2.5,
59
+ showfliers=False, showmeans=True, meanline=False, patch_artist=True,
60
+ boxprops=dict(facecolor=boxcolors[i], alpha=.7),
61
+ meanprops=dict(marker='o', markerfacecolor='white', markeredgecolor='k', markersize=4),
62
+ medianprops=dict(color='#000000', ls='-'))
73
63
 
74
- plt.boxplot(vals, labels=vis, positions=np.array(vis, dtype='int'), widths=0.4,
75
- showfliers=False, showmeans=True, meanline=False, patch_artist=True,
76
- boxprops=dict(facecolor=boxcolors[i], alpha=.7),
77
- meanprops=dict(marker='o', markerfacecolor='white', markeredgecolor='k', markersize=4),
78
- medianprops=dict(color='#000000', ls='-'))
64
+ plt.scatter(x_data, y_data, marker='.', s=10, facecolor='white', edgecolor=boxcolors[i], alpha=0.1)
79
65
 
80
- plt.scatter(x_data, y_data, marker='.', s=10, facecolor='white', edgecolor=boxcolors[i], alpha=0.1)
66
+ # fit curve
67
+ coeff, _ = _log_fit(np.array(vis_labels, dtype='float'), np.array(median_vals, dtype='float'))
81
68
 
82
- if y == 'Vis_LPV':
83
- bins = np.linspace(0, 70, 36)
84
- wid = (bins + (bins[1] - bins[0]) / 2)[0:-1]
69
+ # Plot lines (ref & Measurement)
70
+ x_fit = np.linspace(0.1, 50, 1000)
85
71
 
86
- df_[f'{x_data.name}' + '_bins'] = pd.cut(x=x_data, bins=bins, labels=wid)
72
+ func = lambda x, a: a / x
73
+ line, = ax.plot(x_fit, func(x_fit, coeff), c=scattercolor[i], lw=3,
74
+ label=f'Vis (km) = {round(coeff)} / Ext')
87
75
 
88
- grouped = df_.groupby(f'{x_data.name}' + '_bins', observed=False)
76
+ arts.append(line)
77
+ labels.append(f'Vis (km) = {round(coeff)} / Ext')
89
78
 
90
- vals, median_vals, vis = [], [], []
91
- for j, (name, subdf) in enumerate(grouped):
92
- if len(subdf['Total_Ext'].dropna()) > 20:
93
- vis.append('{:.1f}'.format(name))
94
- vals.append(subdf['Total_Ext'].dropna().values)
95
- median_vals.append(subdf['Total_Ext'].dropna().mean())
79
+ ax.legend(handles=arts, labels=labels, loc='upper right', prop=dict(weight='bold'), bbox_to_anchor=(0.99, 0.99))
96
80
 
97
- plt.boxplot(vals, labels=vis, positions=np.array(vis, dtype='float'), widths=(bins[1] - bins[0]) / 2.5,
98
- showfliers=False, showmeans=True, meanline=False, patch_artist=True,
99
- boxprops=dict(facecolor=boxcolors[i], alpha=.7),
100
- meanprops=dict(marker='o', markerfacecolor='white', markeredgecolor='k', markersize=4),
101
- medianprops=dict(color='#000000', ls='-'))
81
+ ax.set(xlabel=kwargs.get('xlim', 'Visibility (km)'),
82
+ ylabel=kwargs.get('xlim', 'Extinction (1/Mm)'),
83
+ title=kwargs.get('ylim', 'Koschmieder relationship'),
84
+ xlim=kwargs.get('xlim', (0, 30)),
85
+ ylim=kwargs.get('ylim', (0, 500))
86
+ )
102
87
 
103
- plt.scatter(x_data, y_data, marker='.', s=10, facecolor='white', edgecolor=boxcolors[i], alpha=0.1)
88
+ plt.xticks(ticks=np.array(range(0, 31, 5)), labels=np.array(range(0, 31, 5)))
104
89
 
105
- # fit curve
106
- _x = np.array(vis, dtype='float')
107
- _y = np.array(median_vals, dtype='float')
90
+ plt.show()
108
91
 
109
- if function == 'log':
110
- func = lambda x, a: a / x
111
- coeff, pcov = _log_fit(_x, _y)
112
-
113
- else:
114
- func = lambda x, a, b: a / (x ** b)
115
- coeff, pcov = _reciprocal_fit(_x, _y)
116
-
117
- para_coeff.append(coeff)
118
-
119
- # Plot lines (ref & Measurement)
120
- x_fit = np.linspace(0.1, 70, 1000)
121
-
122
- if function == 'log':
123
- line1, = ax.plot(x_fit, func(x_fit, para_coeff[0]), c='b', lw=3)
124
- line2, = ax.plot(x_fit, func(x_fit, para_coeff[1]), c='g', lw=3)
125
-
126
- labels = ['Vis (km) = ' + f'{round(para_coeff[0])}' + ' / Ext (Dry Extinction)',
127
- 'Vis (km) = ' + f'{round(para_coeff[1])}' + ' / Ext (Amb Extinction)']
128
-
129
- else:
130
- x_fit = np.linspace(0.1, 70, 1000)
131
- line1, = ax.plot(x_fit, func(x_fit, *para_coeff[0]), c='b', lw=3)
132
- line2, = ax.plot(x_fit, func(x_fit, *para_coeff[1]), c='g', lw=3)
133
-
134
- labels = [f'Ext = ' + '{:.0f} / Vis ^ {:.3f}'.format(*para_coeff[0]) + ' (Dry Extinction)',
135
- f'Ext = ' + '{:.0f} / Vis ^ {:.3f}'.format(*para_coeff[1]) + ' (Amb Extinction)']
136
-
137
- plt.legend(handles=[line1, line2], labels=labels, loc='upper right', prop=dict(size=10, weight='bold'),
138
- bbox_to_anchor=(0.99, 0.99))
139
-
140
- plt.xticks(ticks=np.array(range(0, 51, 5)), labels=np.array(range(0, 51, 5)))
141
- plt.xlim(0, 50)
142
- plt.ylim(0, 700)
143
- plt.title(r'$\bf Koschmieder\ relationship$')
144
- plt.xlabel(f'{y} (km)')
145
- plt.ylabel(r'$\bf Extinction\ coefficient\ (1/Mm)$')
146
-
147
- plt.show()
148
-
149
- return fig, ax
150
-
151
-
152
- if __name__ == '__main__':
153
- from AeroViz.tools import DataBase
154
-
155
- koschmieder(DataBase(), 'Vis_LPV', 'log')
156
- # koschmieder(DataBase, 'Vis_Naked', 'reciprocal')
92
+ return fig, ax