AeroViz 0.1.21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AeroViz/__init__.py +13 -0
- AeroViz/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/data/DEFAULT_DATA.csv +1417 -0
- AeroViz/data/DEFAULT_PNSD_DATA.csv +1417 -0
- AeroViz/data/hysplit_example_data.txt +101 -0
- AeroViz/dataProcess/Chemistry/__init__.py +149 -0
- AeroViz/dataProcess/Chemistry/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Chemistry/_calculate.py +557 -0
- AeroViz/dataProcess/Chemistry/_isoropia.py +150 -0
- AeroViz/dataProcess/Chemistry/_mass_volume.py +487 -0
- AeroViz/dataProcess/Chemistry/_ocec.py +172 -0
- AeroViz/dataProcess/Chemistry/isrpia.cnf +21 -0
- AeroViz/dataProcess/Chemistry/isrpia2.exe +0 -0
- AeroViz/dataProcess/Optical/PyMieScatt_update.py +577 -0
- AeroViz/dataProcess/Optical/_IMPROVE.py +452 -0
- AeroViz/dataProcess/Optical/__init__.py +281 -0
- AeroViz/dataProcess/Optical/__pycache__/PyMieScatt_update.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/__pycache__/mie_theory.cpython-312.pyc +0 -0
- AeroViz/dataProcess/Optical/_derived.py +518 -0
- AeroViz/dataProcess/Optical/_extinction.py +123 -0
- AeroViz/dataProcess/Optical/_mie_sd.py +912 -0
- AeroViz/dataProcess/Optical/_retrieve_RI.py +243 -0
- AeroViz/dataProcess/Optical/coefficient.py +72 -0
- AeroViz/dataProcess/Optical/fRH.pkl +0 -0
- AeroViz/dataProcess/Optical/mie_theory.py +260 -0
- AeroViz/dataProcess/README.md +271 -0
- AeroViz/dataProcess/SizeDistr/__init__.py +245 -0
- AeroViz/dataProcess/SizeDistr/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/SizeDistr/__pycache__/_size_dist.cpython-312.pyc +0 -0
- AeroViz/dataProcess/SizeDistr/_size_dist.py +810 -0
- AeroViz/dataProcess/SizeDistr/merge/README.md +93 -0
- AeroViz/dataProcess/SizeDistr/merge/__init__.py +20 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v0.py +251 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v0_1.py +246 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v1.py +255 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v2.py +244 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v3.py +518 -0
- AeroViz/dataProcess/SizeDistr/merge/_merge_v4.py +422 -0
- AeroViz/dataProcess/SizeDistr/prop.py +62 -0
- AeroViz/dataProcess/VOC/__init__.py +14 -0
- AeroViz/dataProcess/VOC/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/VOC/_potential_par.py +108 -0
- AeroViz/dataProcess/VOC/support_voc.json +446 -0
- AeroViz/dataProcess/__init__.py +66 -0
- AeroViz/dataProcess/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/dataProcess/core/__init__.py +272 -0
- AeroViz/dataProcess/core/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/mcp_server.py +352 -0
- AeroViz/plot/__init__.py +13 -0
- AeroViz/plot/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/bar.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/box.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/pie.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/radar.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/regression.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/scatter.cpython-312.pyc +0 -0
- AeroViz/plot/__pycache__/violin.cpython-312.pyc +0 -0
- AeroViz/plot/bar.py +126 -0
- AeroViz/plot/box.py +69 -0
- AeroViz/plot/distribution/__init__.py +1 -0
- AeroViz/plot/distribution/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/distribution/__pycache__/distribution.cpython-312.pyc +0 -0
- AeroViz/plot/distribution/distribution.py +576 -0
- AeroViz/plot/meteorology/CBPF.py +295 -0
- AeroViz/plot/meteorology/__init__.py +3 -0
- AeroViz/plot/meteorology/__pycache__/CBPF.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/hysplit.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/__pycache__/wind_rose.cpython-312.pyc +0 -0
- AeroViz/plot/meteorology/hysplit.py +93 -0
- AeroViz/plot/meteorology/wind_rose.py +77 -0
- AeroViz/plot/optical/__init__.py +1 -0
- AeroViz/plot/optical/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/optical/__pycache__/optical.cpython-312.pyc +0 -0
- AeroViz/plot/optical/optical.py +388 -0
- AeroViz/plot/pie.py +210 -0
- AeroViz/plot/radar.py +184 -0
- AeroViz/plot/regression.py +200 -0
- AeroViz/plot/scatter.py +174 -0
- AeroViz/plot/templates/__init__.py +6 -0
- AeroViz/plot/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/ammonium_rich.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/contour.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/corr_matrix.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/diurnal_pattern.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/koschmieder.cpython-312.pyc +0 -0
- AeroViz/plot/templates/__pycache__/metal_heatmap.cpython-312.pyc +0 -0
- AeroViz/plot/templates/ammonium_rich.py +34 -0
- AeroViz/plot/templates/contour.py +47 -0
- AeroViz/plot/templates/corr_matrix.py +267 -0
- AeroViz/plot/templates/diurnal_pattern.py +61 -0
- AeroViz/plot/templates/koschmieder.py +95 -0
- AeroViz/plot/templates/metal_heatmap.py +164 -0
- AeroViz/plot/timeseries/__init__.py +2 -0
- AeroViz/plot/timeseries/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/__pycache__/template.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/__pycache__/timeseries.cpython-312.pyc +0 -0
- AeroViz/plot/timeseries/template.py +47 -0
- AeroViz/plot/timeseries/timeseries.py +446 -0
- AeroViz/plot/utils/__init__.py +4 -0
- AeroViz/plot/utils/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/_color.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/_unit.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/plt_utils.cpython-312.pyc +0 -0
- AeroViz/plot/utils/__pycache__/sklearn_utils.cpython-312.pyc +0 -0
- AeroViz/plot/utils/_color.py +71 -0
- AeroViz/plot/utils/_unit.py +55 -0
- AeroViz/plot/utils/fRH.json +390 -0
- AeroViz/plot/utils/plt_utils.py +92 -0
- AeroViz/plot/utils/sklearn_utils.py +49 -0
- AeroViz/plot/utils/units.json +89 -0
- AeroViz/plot/violin.py +80 -0
- AeroViz/rawDataReader/FLOW.md +138 -0
- AeroViz/rawDataReader/__init__.py +220 -0
- AeroViz/rawDataReader/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/__init__.py +0 -0
- AeroViz/rawDataReader/config/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/__pycache__/supported_instruments.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/config/supported_instruments.py +135 -0
- AeroViz/rawDataReader/core/__init__.py +658 -0
- AeroViz/rawDataReader/core/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/logger.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/pre_process.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/qc.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/__pycache__/report.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/core/logger.py +171 -0
- AeroViz/rawDataReader/core/pre_process.py +308 -0
- AeroViz/rawDataReader/core/qc.py +961 -0
- AeroViz/rawDataReader/core/report.py +579 -0
- AeroViz/rawDataReader/script/AE33.py +173 -0
- AeroViz/rawDataReader/script/AE43.py +151 -0
- AeroViz/rawDataReader/script/APS.py +339 -0
- AeroViz/rawDataReader/script/Aurora.py +191 -0
- AeroViz/rawDataReader/script/BAM1020.py +90 -0
- AeroViz/rawDataReader/script/BC1054.py +161 -0
- AeroViz/rawDataReader/script/EPA.py +79 -0
- AeroViz/rawDataReader/script/GRIMM.py +68 -0
- AeroViz/rawDataReader/script/IGAC.py +140 -0
- AeroViz/rawDataReader/script/MA350.py +179 -0
- AeroViz/rawDataReader/script/Minion.py +218 -0
- AeroViz/rawDataReader/script/NEPH.py +199 -0
- AeroViz/rawDataReader/script/OCEC.py +173 -0
- AeroViz/rawDataReader/script/Q-ACSM.py +12 -0
- AeroViz/rawDataReader/script/SMPS.py +389 -0
- AeroViz/rawDataReader/script/TEOM.py +181 -0
- AeroViz/rawDataReader/script/VOC.py +106 -0
- AeroViz/rawDataReader/script/Xact.py +244 -0
- AeroViz/rawDataReader/script/__init__.py +28 -0
- AeroViz/rawDataReader/script/__pycache__/AE33.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/AE43.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/APS.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Aurora.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/BAM1020.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/BC1054.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/EPA.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/GRIMM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/IGAC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/MA350.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Minion.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/NEPH.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/OCEC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Q-ACSM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/SMPS.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/TEOM.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/VOC.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/Xact.cpython-312.pyc +0 -0
- AeroViz/rawDataReader/script/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/tools/__init__.py +2 -0
- AeroViz/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- AeroViz/tools/__pycache__/database.cpython-312.pyc +0 -0
- AeroViz/tools/__pycache__/dataclassifier.cpython-312.pyc +0 -0
- AeroViz/tools/database.py +95 -0
- AeroViz/tools/dataclassifier.py +117 -0
- AeroViz/tools/dataprinter.py +58 -0
- aeroviz-0.1.21.dist-info/METADATA +294 -0
- aeroviz-0.1.21.dist-info/RECORD +180 -0
- aeroviz-0.1.21.dist-info/WHEEL +5 -0
- aeroviz-0.1.21.dist-info/licenses/LICENSE +21 -0
- aeroviz-0.1.21.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import matplotlib.colors as colors
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import numpy as np
|
|
6
|
+
from matplotlib.collections import PolyCollection
|
|
7
|
+
from matplotlib.pyplot import Figure, Axes
|
|
8
|
+
from matplotlib.ticker import FuncFormatter
|
|
9
|
+
from numpy import log, exp, sqrt, pi
|
|
10
|
+
from pandas import DataFrame, Series, date_range
|
|
11
|
+
from scipy.optimize import curve_fit
|
|
12
|
+
from scipy.signal import find_peaks
|
|
13
|
+
from scipy.stats import norm, lognorm
|
|
14
|
+
from tabulate import tabulate
|
|
15
|
+
|
|
16
|
+
from AeroViz.plot.utils import *
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'plot_dist',
|
|
20
|
+
'heatmap',
|
|
21
|
+
'heatmap_tms',
|
|
22
|
+
'three_dimension',
|
|
23
|
+
'curve_fitting'
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@set_figure
|
|
28
|
+
def plot_dist(data: DataFrame | np.ndarray,
|
|
29
|
+
data_std: DataFrame | None = None,
|
|
30
|
+
std_scale: float | None = 1,
|
|
31
|
+
unit: Literal["Number", "Surface", "Volume", "Extinction"] = 'Number',
|
|
32
|
+
additional: Literal["Std", "Enhancement", "Error"] = None,
|
|
33
|
+
fig: Figure | None = None,
|
|
34
|
+
ax: Axes | None = None,
|
|
35
|
+
**kwargs
|
|
36
|
+
) -> tuple[Figure, Axes]:
|
|
37
|
+
"""
|
|
38
|
+
Plot particle size distribution curves and optionally show enhancements.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
data : dict or list
|
|
43
|
+
If dict, keys are labels and values are arrays of distribution values.
|
|
44
|
+
If listed, it should contain three arrays for different curves.
|
|
45
|
+
data_std : dict
|
|
46
|
+
Dictionary containing standard deviation data for ambient extinction distribution.
|
|
47
|
+
std_scale : float
|
|
48
|
+
The width of standard deviation.
|
|
49
|
+
unit : {'Number', 'Surface', 'Volume', 'Extinction'}
|
|
50
|
+
Unit of measurement for the data.
|
|
51
|
+
additional : {'std', 'enhancement', 'error'}
|
|
52
|
+
Whether to show enhancement curves.
|
|
53
|
+
fig : Figure, optional
|
|
54
|
+
Matplotlib Figure object to use.
|
|
55
|
+
ax : AxesSubplot, optional
|
|
56
|
+
Matplotlib AxesSubplot object to use. If not provided, a new subplot will be created.
|
|
57
|
+
**kwargs : dict
|
|
58
|
+
Additional keyword arguments.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
ax : AxesSubplot
|
|
63
|
+
Matplotlib AxesSubplot.
|
|
64
|
+
|
|
65
|
+
Examples
|
|
66
|
+
--------
|
|
67
|
+
>>> plot_dist(DataFrame(...), additional="Enhancement")
|
|
68
|
+
"""
|
|
69
|
+
fig, ax = plt.subplots(**{**{'figsize': (6, 2)}, **kwargs.get('fig_kws', {})}) if ax is None else (
|
|
70
|
+
ax.get_figure(), ax)
|
|
71
|
+
|
|
72
|
+
# plot_kws
|
|
73
|
+
plot_kws = dict(ls='solid', lw=2, alpha=0.8, **kwargs.get('plot_kws', {}))
|
|
74
|
+
|
|
75
|
+
# Receive input data
|
|
76
|
+
dp = np.array(data.columns, dtype=float)
|
|
77
|
+
states = np.array(data.index)
|
|
78
|
+
|
|
79
|
+
for state in states:
|
|
80
|
+
mean = data.loc[state].to_numpy()
|
|
81
|
+
ax.plot(dp, mean, label=state, color=Color.color_choose[state][0], **plot_kws)
|
|
82
|
+
|
|
83
|
+
if additional == 'Std':
|
|
84
|
+
std = data_std.loc[state].to_numpy() * std_scale
|
|
85
|
+
ax.fill_between(dp, y1=mean - std, y2=mean + std, alpha=0.4, color=Color.color_choose[state][1],
|
|
86
|
+
edgecolor=None, label='__nolegend__')
|
|
87
|
+
|
|
88
|
+
# figure_set
|
|
89
|
+
ax.set(xlim=(dp.min(), dp.max()), ylim=(0, None), xscale='log',
|
|
90
|
+
xlabel=r'$D_{p} (nm)$', ylabel=Unit(f'{unit}_dist'), title=kwargs.get('title', unit))
|
|
91
|
+
|
|
92
|
+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 3), useMathText=True)
|
|
93
|
+
ax.grid(axis='x', which='major', color='k', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
94
|
+
|
|
95
|
+
Clean = data.loc['Clean'].to_numpy()
|
|
96
|
+
Transition = data.loc['Transition'].to_numpy()
|
|
97
|
+
Event = data.loc['Event'].to_numpy()
|
|
98
|
+
|
|
99
|
+
if additional == "Enhancement":
|
|
100
|
+
ax2 = ax.twinx()
|
|
101
|
+
ax2.plot(dp, Transition / Clean, ls='dashed', color='k', label=f'{additional} ratio 1')
|
|
102
|
+
ax2.plot(dp, Event / Transition, ls='dashed', color='gray', label=f'{additional} ratio 2')
|
|
103
|
+
ax2.set(ylabel='Enhancement ratio')
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
ax2 = ax.twinx()
|
|
107
|
+
error1 = np.where(Transition != 0, np.abs(Clean - Transition) / Clean * 100, 0)
|
|
108
|
+
error2 = np.where(Event != 0, np.abs(Transition - Event) / Transition * 100, 0)
|
|
109
|
+
|
|
110
|
+
ax2.plot(dp, error1, ls='--', color='k', label='Error 1 ')
|
|
111
|
+
ax2.plot(dp, error2, ls='--', color='gray', label='Error 2')
|
|
112
|
+
ax2.set(ylabel='Error (%)')
|
|
113
|
+
|
|
114
|
+
ax.legend(*combine_legends(fig.get_axes()), prop={'weight': 'bold'})
|
|
115
|
+
|
|
116
|
+
plt.show()
|
|
117
|
+
|
|
118
|
+
return fig, ax
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@set_figure
|
|
122
|
+
def heatmap(data: DataFrame,
|
|
123
|
+
unit: Literal["Number", "Surface", "Volume", "Extinction"],
|
|
124
|
+
cmap: str = 'Blues',
|
|
125
|
+
colorbar: bool = False,
|
|
126
|
+
magic_number: int = 11,
|
|
127
|
+
ax: Axes | None = None,
|
|
128
|
+
**kwargs
|
|
129
|
+
) -> tuple[Figure, Axes]:
|
|
130
|
+
"""
|
|
131
|
+
Plot a heatmap of particle size distribution.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
data : pandas.DataFrame
|
|
136
|
+
The data containing particle size distribution values. Each column corresponds to a size bin,
|
|
137
|
+
and each row corresponds to a different distribution.
|
|
138
|
+
|
|
139
|
+
unit : {'Number', 'Surface', 'Volume', 'Extinction'}, optional
|
|
140
|
+
The unit of measurement for the data.
|
|
141
|
+
|
|
142
|
+
cmap : str, default='Blues'
|
|
143
|
+
The colormap to use for the heatmap.
|
|
144
|
+
|
|
145
|
+
colorbar : bool, default=False
|
|
146
|
+
Whether to show the colorbar.
|
|
147
|
+
|
|
148
|
+
magic_number : int, default=11
|
|
149
|
+
The number of bins to use for the histogram.
|
|
150
|
+
|
|
151
|
+
ax : matplotlib.axes.Axes, optional
|
|
152
|
+
The axes to plot the heatmap on. If not provided, a new subplot will be created.
|
|
153
|
+
|
|
154
|
+
**kwargs
|
|
155
|
+
Additional keyword arguments to pass to matplotlib functions.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
matplotlib.axes.Axes
|
|
160
|
+
The Axes object containing the heatmap.
|
|
161
|
+
|
|
162
|
+
Examples
|
|
163
|
+
--------
|
|
164
|
+
>>> heatmap(DataFrame(...), unit='Number')
|
|
165
|
+
|
|
166
|
+
Notes
|
|
167
|
+
-----
|
|
168
|
+
This function calculates a 2D histogram of the log-transformed particle sizes and the distribution values.
|
|
169
|
+
It then plots the heatmap using a logarithmic color scale.
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
fig, ax = plt.subplots(**{**{'figsize': (3, 3)}, **kwargs.get('fig_kws', {})}) if ax is None else (
|
|
173
|
+
ax.get_figure(), ax)
|
|
174
|
+
|
|
175
|
+
min_value = 1e-8
|
|
176
|
+
dp = np.array(data.columns, dtype=float)
|
|
177
|
+
x = np.append(np.tile(dp, data.to_numpy().shape[0]), np.log(dp).max())
|
|
178
|
+
y = np.append(data.to_numpy().flatten(), min_value)
|
|
179
|
+
|
|
180
|
+
# mask NaN
|
|
181
|
+
x = x[~np.isnan(y)]
|
|
182
|
+
y = y[~np.isnan(y)]
|
|
183
|
+
|
|
184
|
+
# using log(x)
|
|
185
|
+
histogram, xedges, yedges = np.histogram2d(np.log(x), y, bins=len(dp) + magic_number)
|
|
186
|
+
histogram[histogram == 0] = min_value # Avoid log(0)
|
|
187
|
+
|
|
188
|
+
plot_kws = dict(norm=colors.LogNorm(vmin=1, vmax=histogram.max()), cmap=cmap, **kwargs.get('plot_kws', {}))
|
|
189
|
+
|
|
190
|
+
pco = ax.pcolormesh(xedges[:-1], yedges[:-1], histogram.T, shading='gouraud', **plot_kws)
|
|
191
|
+
|
|
192
|
+
ax.plot(np.log(dp), data.mean() + data.std(), ls='dashed', color='r', label='pollutant')
|
|
193
|
+
ax.plot(np.log(dp), data.mean(), ls='dashed', color='k', alpha=0.5, label='mean')
|
|
194
|
+
ax.plot(np.log(dp), data.mean() - data.std(), ls='dashed', color='b', label='clean')
|
|
195
|
+
|
|
196
|
+
ax.set(xlim=(np.log(dp).min(), np.log(dp).max()), ylim=(0, None),
|
|
197
|
+
xlabel=r'$D_{p} (nm)$', ylabel=Unit(f'{unit}_dist'), title=kwargs.get('title', unit))
|
|
198
|
+
|
|
199
|
+
major_ticks = np.power(10, np.arange(np.ceil(np.log10(dp.min())), np.floor(np.log10(dp.max())) + 1))
|
|
200
|
+
minor_ticks = [v for v in np.concatenate([_ * np.arange(2, 10) for _ in major_ticks]) if min(dp) <= v <= max(dp)]
|
|
201
|
+
|
|
202
|
+
ax.set_xticks(np.log(major_ticks))
|
|
203
|
+
ax.set_xticks(np.log(minor_ticks), minor=True)
|
|
204
|
+
ax.xaxis.set_major_formatter(FuncFormatter(lambda tick, pos: "{:.0f}".format(np.exp(tick))))
|
|
205
|
+
|
|
206
|
+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 3), useMathText=True)
|
|
207
|
+
ax.grid(axis='x', which='major', color='k', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
208
|
+
ax.legend(prop={'weight': 'bold'})
|
|
209
|
+
|
|
210
|
+
if colorbar:
|
|
211
|
+
plt.colorbar(pco, pad=0.02, fraction=0.05, label='Counts', **kwargs.get('cbar_kws', {}))
|
|
212
|
+
|
|
213
|
+
plt.show()
|
|
214
|
+
|
|
215
|
+
return fig, ax
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@set_figure
|
|
219
|
+
def heatmap_tms(data: DataFrame,
|
|
220
|
+
unit: Literal["Number", "Surface", "Volume", "Extinction"],
|
|
221
|
+
cmap: str = 'jet',
|
|
222
|
+
ax: Axes | None = None,
|
|
223
|
+
**kwargs
|
|
224
|
+
) -> tuple[Figure, Axes]:
|
|
225
|
+
""" Plot the size distribution over time.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
data : DataFrame
|
|
230
|
+
A DataFrame of particle concentrations to plot the heatmap.
|
|
231
|
+
|
|
232
|
+
ax : matplotlib.axis.Axis
|
|
233
|
+
An axis object to plot on. If none is provided, one will be created.
|
|
234
|
+
|
|
235
|
+
unit : Literal["Number", "Surface", "Volume", "Extinction"]
|
|
236
|
+
default='Number'
|
|
237
|
+
|
|
238
|
+
cmap : matplotlib.colormap, default='viridis'
|
|
239
|
+
The colormap to use. Can be anything other that 'jet'.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
ax : matplotlib.axis.Axis
|
|
244
|
+
|
|
245
|
+
Notes
|
|
246
|
+
-----
|
|
247
|
+
Do not dropna when using this code.
|
|
248
|
+
|
|
249
|
+
Examples
|
|
250
|
+
--------
|
|
251
|
+
Plot a SPMS + APS data:
|
|
252
|
+
>>> heatmap_tms(DataFrame(...), cmap='jet')
|
|
253
|
+
"""
|
|
254
|
+
fig, ax = plt.subplots(
|
|
255
|
+
**{**{'figsize': (len(data.index) * 0.01, 2)}, **kwargs.get('fig_kws', {})}) if ax is None else (
|
|
256
|
+
ax.get_figure(), ax)
|
|
257
|
+
|
|
258
|
+
time = data.index
|
|
259
|
+
dp = np.array(data.columns, dtype=float)
|
|
260
|
+
|
|
261
|
+
# data = data.interpolate(method='linear', axis=0)
|
|
262
|
+
data = np.nan_to_num(data.to_numpy())
|
|
263
|
+
|
|
264
|
+
vmin_mapping = {'Number': 1e2, 'Surface': 1e8, 'Volume': 1e9, 'Extinction': 1}
|
|
265
|
+
|
|
266
|
+
# Set the colorbar min and max based on the min and max of the values
|
|
267
|
+
cbar_min = kwargs.get('cbar_kws', {}).pop('cbar_min', vmin_mapping[unit])
|
|
268
|
+
cbar_max = kwargs.get('cbar_kws', {}).pop('cbar_max', np.nanmax(data))
|
|
269
|
+
|
|
270
|
+
# Set the plot_kws
|
|
271
|
+
plot_kws = dict(norm=colors.LogNorm(vmin=cbar_min, vmax=cbar_max), cmap=cmap, **kwargs.get('plot_kws', {}))
|
|
272
|
+
|
|
273
|
+
# main plot
|
|
274
|
+
pco = ax.pcolormesh(time, dp, data.T, shading='auto', **plot_kws)
|
|
275
|
+
|
|
276
|
+
# Set ax
|
|
277
|
+
st_tm, fn_tm = time[0], time[-1]
|
|
278
|
+
tick_time = date_range(st_tm, fn_tm, freq=kwargs.get('freq', '10d')).strftime("%F")
|
|
279
|
+
|
|
280
|
+
ax.set(xlim=(st_tm, fn_tm),
|
|
281
|
+
ylim=(dp.min(), dp.max()),
|
|
282
|
+
ylabel='$D_p (nm)$',
|
|
283
|
+
xticks=tick_time,
|
|
284
|
+
xticklabels=tick_time,
|
|
285
|
+
yscale='log',
|
|
286
|
+
title=kwargs.get('title', f'{st_tm.strftime("%F")} - {fn_tm.strftime("%F")}'))
|
|
287
|
+
|
|
288
|
+
plt.colorbar(pco, pad=0.02, fraction=0.02, label=Unit(f'{unit}_dist'), **kwargs.get('cbar_kws', {}))
|
|
289
|
+
|
|
290
|
+
plt.show()
|
|
291
|
+
|
|
292
|
+
return fig, ax
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@set_figure
|
|
296
|
+
def three_dimension(data: DataFrame | np.ndarray,
|
|
297
|
+
unit: Literal["Number", "Surface", "Volume", "Extinction"],
|
|
298
|
+
cmap: str = 'Blues',
|
|
299
|
+
ax: Axes | None = None,
|
|
300
|
+
**kwargs
|
|
301
|
+
) -> tuple[Figure, Axes]:
|
|
302
|
+
"""
|
|
303
|
+
Create a 3D plot with data from a pandas DataFrame or numpy array.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
data : DataFrame or ndarray
|
|
308
|
+
Input data containing the values to be plotted.
|
|
309
|
+
|
|
310
|
+
unit : {'Number', 'Surface', 'Volume', 'Extinction'}
|
|
311
|
+
Unit of measurement for the data.
|
|
312
|
+
|
|
313
|
+
cmap : str, default='Blues'
|
|
314
|
+
The colormap to use for the facecolors.
|
|
315
|
+
|
|
316
|
+
ax : AxesSubplot, optional
|
|
317
|
+
Matplotlib AxesSubplot. If not provided, a new subplot will be created.
|
|
318
|
+
**kwargs
|
|
319
|
+
Additional keyword arguments to customize the plot.
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
Axes
|
|
324
|
+
Matplotlib Axes object representing the 3D plot.
|
|
325
|
+
|
|
326
|
+
Notes
|
|
327
|
+
-----
|
|
328
|
+
- The function creates a 3D plot with data provided in a pandas DataFrame or numpy array.
|
|
329
|
+
- The x-axis is logarithmically scaled, and ticks and labels are formatted accordingly.
|
|
330
|
+
- Additional customization can be done using the **kwargs.
|
|
331
|
+
|
|
332
|
+
Example
|
|
333
|
+
-------
|
|
334
|
+
>>> three_dimension(DataFrame(...), unit='Number', cmap='Blues')
|
|
335
|
+
"""
|
|
336
|
+
fig, ax = plt.subplots(figsize=(4, 4), subplot_kw={"projection": "3d"},
|
|
337
|
+
**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
338
|
+
|
|
339
|
+
dp = np.array(['11.7', *data.columns, '2437.4'], dtype=float)
|
|
340
|
+
lines = data.shape[0]
|
|
341
|
+
|
|
342
|
+
_X, _Y = np.meshgrid(np.log(dp), np.arange(lines))
|
|
343
|
+
_Z = np.pad(data, ((0, 0), (1, 1)), 'constant')
|
|
344
|
+
|
|
345
|
+
verts = []
|
|
346
|
+
for i in range(_X.shape[0]):
|
|
347
|
+
verts.append(list(zip(_X[i, :], _Z[i, :])))
|
|
348
|
+
|
|
349
|
+
facecolors = plt.colormaps[cmap](np.linspace(0, 1, len(verts)))
|
|
350
|
+
poly = PolyCollection(verts, facecolors=facecolors, edgecolors='k', lw=0.5, alpha=.7)
|
|
351
|
+
ax.add_collection3d(poly, zs=range(1, lines + 1), zdir='y')
|
|
352
|
+
|
|
353
|
+
ax.set(xlim=(np.log(11.7), np.log(2437.4)), ylim=(1, lines), zlim=(0, np.nanmax(_Z)),
|
|
354
|
+
xlabel='$D_{p} (nm)$', ylabel='Class', zlabel=Unit(f'{unit}_dist'))
|
|
355
|
+
|
|
356
|
+
ax.set_xticks(np.log([10, 100, 1000]))
|
|
357
|
+
ax.set_xticks(np.log([20, 30, 40, 50, 60, 70, 80, 90, 200, 300, 400, 500, 600, 700, 800, 900, 2000]), minor=True)
|
|
358
|
+
ax.xaxis.set_major_formatter(FuncFormatter((lambda tick, pos: "{:.0f}".format(np.exp(tick)))))
|
|
359
|
+
ax.ticklabel_format(axis='z', style='sci', scilimits=(0, 3), useMathText=True)
|
|
360
|
+
|
|
361
|
+
ax.zaxis.get_offset_text().set_visible(False)
|
|
362
|
+
exponent = np.floor(np.log10(np.nanmax(data))).astype(int)
|
|
363
|
+
ax.text(ax.get_xlim()[1] * 1.05, ax.get_ylim()[1], ax.get_zlim()[1] * 1.1, s=fr'${{\times}}\ 10^{exponent}$')
|
|
364
|
+
|
|
365
|
+
plt.show()
|
|
366
|
+
|
|
367
|
+
return fig, ax
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@set_figure
|
|
371
|
+
def curve_fitting(dp: np.ndarray,
|
|
372
|
+
dist: np.ndarray | Series | DataFrame,
|
|
373
|
+
mode: int = None,
|
|
374
|
+
unit: Literal["Number", "Surface", "Volume", "Extinction"] = None,
|
|
375
|
+
ax: Axes | None = None,
|
|
376
|
+
**kwargs
|
|
377
|
+
) -> tuple[Figure, Axes]:
|
|
378
|
+
"""
|
|
379
|
+
Fit a log-normal distribution to the given data and plot the result.
|
|
380
|
+
|
|
381
|
+
Parameters
|
|
382
|
+
----------
|
|
383
|
+
- dp (array): Array of diameter values.
|
|
384
|
+
- dist (array): Array of distribution values corresponding to each diameter.
|
|
385
|
+
- mode (int, optional): Number of log-normal distribution to fit (default is None).
|
|
386
|
+
- **kwargs: Additional keyword arguments to be passed to the plot_function.
|
|
387
|
+
|
|
388
|
+
Returns
|
|
389
|
+
-------
|
|
390
|
+
None
|
|
391
|
+
|
|
392
|
+
Notes
|
|
393
|
+
-----
|
|
394
|
+
- The function fits a sum of log-normal distribution to the input data.
|
|
395
|
+
- The number of distribution is determined by the 'mode' parameter.
|
|
396
|
+
- Additional plotting customization can be done using the **kwargs.
|
|
397
|
+
|
|
398
|
+
Example
|
|
399
|
+
-------
|
|
400
|
+
>>> curve_fitting(dp, dist, mode=2, xlabel="Diameter (nm)", ylabel="Distribution")
|
|
401
|
+
"""
|
|
402
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
403
|
+
|
|
404
|
+
# Calculate total number concentration and normalize distribution
|
|
405
|
+
total_num = np.sum(dist * log(dp))
|
|
406
|
+
norm_data = dist / total_num
|
|
407
|
+
|
|
408
|
+
def lognorm_func(x, *params):
|
|
409
|
+
num_distributions = len(params) // 3
|
|
410
|
+
result = np.zeros_like(x)
|
|
411
|
+
|
|
412
|
+
for i in range(num_distributions):
|
|
413
|
+
offset = i * 3
|
|
414
|
+
_number, _geomean, _geostd = params[offset: offset + 3]
|
|
415
|
+
|
|
416
|
+
result += (_number / (log(_geostd) * sqrt(2 * pi)) *
|
|
417
|
+
exp(-(log(x) - log(_geomean)) ** 2 / (2 * log(_geostd) ** 2)))
|
|
418
|
+
|
|
419
|
+
return result
|
|
420
|
+
|
|
421
|
+
# initial gauss
|
|
422
|
+
min_value = np.array([min(dist)])
|
|
423
|
+
extend_ser = np.concatenate([min_value, dist, min_value])
|
|
424
|
+
_mode, _ = find_peaks(extend_ser, distance=20)
|
|
425
|
+
peak = dp[_mode - 1]
|
|
426
|
+
mode = mode or len(peak)
|
|
427
|
+
|
|
428
|
+
# 初始參數猜測
|
|
429
|
+
initial_guess = [0.05, 20., 2.] * mode
|
|
430
|
+
|
|
431
|
+
# 設定參數範圍
|
|
432
|
+
bounds = ([1e-6, 10, 1] * mode, [1, 3000, 8] * mode)
|
|
433
|
+
|
|
434
|
+
# 使用 curve_fit 函數進行擬合
|
|
435
|
+
result = curve_fit(lognorm_func, dp, norm_data, p0=initial_guess, bounds=bounds)
|
|
436
|
+
|
|
437
|
+
# 獲取擬合的參數
|
|
438
|
+
params = result[0].tolist()
|
|
439
|
+
|
|
440
|
+
print('\n' + "Fitting Results:")
|
|
441
|
+
table = []
|
|
442
|
+
|
|
443
|
+
for i in range(mode):
|
|
444
|
+
offset = i * 3
|
|
445
|
+
num, mu, sigma = params[offset:offset + 3]
|
|
446
|
+
table.append([f'log-{i + 1}', f"{num * total_num:.3f}", f"{mu:.3f}", f"{sigma:.3f}"])
|
|
447
|
+
|
|
448
|
+
# 使用 tabulate 來建立表格並印出
|
|
449
|
+
print(tabulate(table, headers=["log-", "number", "mu", "sigma"], floatfmt=".3f", tablefmt="fancy_grid"))
|
|
450
|
+
|
|
451
|
+
fit_curve = total_num * lognorm_func(dp, *params)
|
|
452
|
+
|
|
453
|
+
plt.plot(dp, fit_curve, color='#c41b1b', label='Fitting curve', lw=2.5)
|
|
454
|
+
plt.plot(dp, dist, color='b', label='Observed curve', lw=2.5)
|
|
455
|
+
|
|
456
|
+
ax.set(xlim=(dp.min(), dp.max()), ylim=(0, None), xscale='log',
|
|
457
|
+
xlabel=r'$\bf D_{p}\ (nm)$', ylabel=Unit(f'{unit}_dist'), title=kwargs.get('title'))
|
|
458
|
+
|
|
459
|
+
plt.grid(color='k', axis='x', which='major', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
460
|
+
ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 3), useMathText=True)
|
|
461
|
+
ax.legend(prop={'weight': 'bold'})
|
|
462
|
+
|
|
463
|
+
plt.show(block=True)
|
|
464
|
+
|
|
465
|
+
return fig, ax
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
@set_figure
|
|
469
|
+
def ls_mode(**kwargs) -> tuple[Figure, Axes]:
|
|
470
|
+
"""
|
|
471
|
+
Plot log-normal mass size distribution for small mode, large mode, and sea salt particles.
|
|
472
|
+
|
|
473
|
+
Parameters
|
|
474
|
+
----------
|
|
475
|
+
**kwargs : dict
|
|
476
|
+
Additional keyword arguments.
|
|
477
|
+
|
|
478
|
+
Examples
|
|
479
|
+
--------
|
|
480
|
+
Example : Plot log-normal mass size distribution with default settings
|
|
481
|
+
>>> ls_mode()
|
|
482
|
+
"""
|
|
483
|
+
|
|
484
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {}))
|
|
485
|
+
|
|
486
|
+
geoMean = [0.2, 0.5, 2.5]
|
|
487
|
+
geoStdv = [2.2, 1.5, 2.0]
|
|
488
|
+
color = ['g', 'r', 'b']
|
|
489
|
+
label = [r'$\bf Small\ mode\ :D_{g}\ =\ 0.2\ \mu m,\ \sigma_{{g}}\ =\ 2.2$',
|
|
490
|
+
r'$\bf Large\ mode\ :D_{g}\ =\ 0.5\ \mu m,\ \sigma_{{g}}\ =\ 1.5$',
|
|
491
|
+
r'$\bf Sea\ salt\ :D_{g}\ =\ 2.5\ \mu m,\ \sigma_{{g}}\ =\ 2.0$']
|
|
492
|
+
|
|
493
|
+
x = np.geomspace(0.001, 20, 10000)
|
|
494
|
+
for _gmd, _gsd, _color, _label in zip(geoMean, geoStdv, color, label):
|
|
495
|
+
lognorm = 1 / (log(_gsd) * sqrt(2 * pi)) * (exp(-(log(x) - log(_gmd)) ** 2 / (2 * log(_gsd) ** 2)))
|
|
496
|
+
|
|
497
|
+
ax.semilogx(x, lognorm, color=_color, label=_label)
|
|
498
|
+
ax.fill_between(x, lognorm, 0, where=(lognorm > 0), color=_color, alpha=0.3, label='__nolegend__')
|
|
499
|
+
|
|
500
|
+
ax.set(xlim=(0.001, 20), ylim=(0, None), xscale='log', xlabel=r'$\bf D_{p}\ (nm)$',
|
|
501
|
+
ylabel=r'$\bf Probability\ (dM/dlogdp)$', title=r'Log-normal Mass Size Distribution')
|
|
502
|
+
|
|
503
|
+
ax.grid(color='k', axis='x', which='major', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
504
|
+
ax.legend(prop={'weight': 'bold'})
|
|
505
|
+
|
|
506
|
+
plt.show()
|
|
507
|
+
|
|
508
|
+
return fig, ax
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@set_figure
|
|
512
|
+
def lognorm_dist(**kwargs) -> tuple[Figure, Axes]:
|
|
513
|
+
#
|
|
514
|
+
"""
|
|
515
|
+
Plot various particle size distribution to illustrate log-normal distribution and transformations.
|
|
516
|
+
|
|
517
|
+
Parameters
|
|
518
|
+
----------
|
|
519
|
+
**kwargs : dict
|
|
520
|
+
Additional keyword arguments.
|
|
521
|
+
|
|
522
|
+
Examples
|
|
523
|
+
--------
|
|
524
|
+
Example : Plot default particle size distribution
|
|
525
|
+
>>> lognorm_dist()
|
|
526
|
+
"""
|
|
527
|
+
|
|
528
|
+
fig, ax = plt.subplots(2, 2, **kwargs.get('fig_kws', {}))
|
|
529
|
+
([ax1, ax2], [ax3, ax4]) = ax
|
|
530
|
+
fig.suptitle('Particle Size Distribution', fontweight='bold')
|
|
531
|
+
plt.subplots_adjust(left=0.125, right=0.925, bottom=0.1, top=0.93, wspace=0.4, hspace=0.4)
|
|
532
|
+
|
|
533
|
+
# pdf
|
|
534
|
+
normpdf = lambda x, mu, sigma: (1 / (sigma * sqrt(2 * pi))) * exp(-(x - mu) ** 2 / (2 * sigma ** 2))
|
|
535
|
+
lognormpdf = lambda x, gmean, gstd: (1 / (log(gstd) * sqrt(2 * pi))) * exp(
|
|
536
|
+
-(log(x) - log(gmean)) ** 2 / (2 * log(gstd) ** 2))
|
|
537
|
+
lognormpdf2 = lambda x, gmean, gstd: (1 / (x * log(gstd) * sqrt(2 * pi))) * exp(
|
|
538
|
+
-(log(x) - log(gmean)) ** 2 / (2 * log(gstd) ** 2))
|
|
539
|
+
|
|
540
|
+
# 生成x
|
|
541
|
+
x = np.linspace(-10, 10, 1000)
|
|
542
|
+
x2 = np.geomspace(0.01, 100, 1000)
|
|
543
|
+
|
|
544
|
+
# Question 1
|
|
545
|
+
# 若對數常態分布x有gmd=3, gstd=2,ln(x) ~ 常態分佈,試問其分布的平均值與標準差?? Y ~ N(mu=log(gmean), sigma=log(gstd))
|
|
546
|
+
data1 = lognorm(scale=3, s=log(2)).rvs(size=5000)
|
|
547
|
+
|
|
548
|
+
# Question 2
|
|
549
|
+
# 若常態分布x有平均值3 標準差1,exp(x)則為一對數常態分佈? 由對數常態分佈的定義 若隨機變數ln(Z)是常態分布 則Z為對數常態分布
|
|
550
|
+
# 因此已知Z = exp(x), so ln(Z)=x,Z ~ 對數常態分佈,試問其分布的幾何平均值與幾何標準差是?? Z ~ LN(geoMean=exp(mu), geoStd=exp(sigma))
|
|
551
|
+
data2 = norm(loc=3, scale=1).rvs(size=5000)
|
|
552
|
+
|
|
553
|
+
def plot_distribution(ax, x, pdf, color='k-', xscale='linear'):
|
|
554
|
+
ax.plot(x, pdf, color)
|
|
555
|
+
ax.set(xlabel='Particle Size (micron)', ylabel='Probability Density', xlim=(x.min(), x.max()), xscale=xscale)
|
|
556
|
+
|
|
557
|
+
# 繪製粒徑分布
|
|
558
|
+
plot_distribution(ax1, x, normpdf(x, mu=0, sigma=2))
|
|
559
|
+
|
|
560
|
+
plot_distribution(ax2, x2, lognormpdf(x2, gmean=0.8, gstd=1.5), 'g-', xscale='log')
|
|
561
|
+
plot_distribution(ax2, x2, lognormpdf2(x2, gmean=0.8, gstd=1.5), 'r--', xscale='log')
|
|
562
|
+
plot_distribution(ax2, x2, lognorm(scale=0.8, s=log(1.5)).pdf(x2), 'b--', xscale='log')
|
|
563
|
+
|
|
564
|
+
plot_distribution(ax3, x, normpdf(x, mu=log(3), sigma=log(2)), 'k-')
|
|
565
|
+
ax3.hist(log(data1), bins=100, density=True, alpha=0.6, color='g')
|
|
566
|
+
|
|
567
|
+
plot_distribution(ax4, x2, lognormpdf2(x2, gmean=exp(3), gstd=exp(1)), 'r-', xscale='log')
|
|
568
|
+
ax4.hist(exp(data2), bins=100, density=True, alpha=0.6, color='g')
|
|
569
|
+
|
|
570
|
+
plt.show()
|
|
571
|
+
|
|
572
|
+
return fig, ax
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
if __name__ == '__main__':
|
|
576
|
+
lognorm_dist()
|