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,388 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import numpy as np
|
|
6
|
+
from matplotlib.pyplot import Figure, Axes
|
|
7
|
+
|
|
8
|
+
from AeroViz.dataProcess.Optical.PyMieScatt_update import ScatteringFunction
|
|
9
|
+
from AeroViz.dataProcess.Optical.mie_theory import Mie_Q, Mie_MEE, Mie_PESD
|
|
10
|
+
from AeroViz.plot.utils import *
|
|
11
|
+
|
|
12
|
+
__all__ = ['Q_plot',
|
|
13
|
+
'RI_couple',
|
|
14
|
+
'RRI_2D',
|
|
15
|
+
'scattering_phase',
|
|
16
|
+
'response_surface',
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
mapping_dic = {'AS': {'m': 1.53 + 0j, 'density': 1.73, 'label': fr'$NH_{4}NO_{3}$', 'color': '#A65E58'},
|
|
20
|
+
'AN': {'m': 1.55 + 0j, 'density': 1.77, 'label': fr'$(NH_{4})_{2}SO_{4}$', 'color': '#A5BF6B'},
|
|
21
|
+
'OM': {'m': 1.54 + 0j, 'density': 1.40, 'label': 'OM', 'color': '#F2BF5E'},
|
|
22
|
+
'Soil': {'m': 1.56 + 0.01j, 'density': 2.60, 'label': 'Soil', 'color': '#3F83BF'},
|
|
23
|
+
'SS': {'m': 1.54 + 0j, 'density': 1.90, 'label': 'SS', 'color': '#B777C2'},
|
|
24
|
+
'BC': {'m': 1.80 + 0.54j, 'density': 1.50, 'label': 'BC', 'color': '#D1CFCB'},
|
|
25
|
+
'Water': {'m': 1.333 + 0j, 'density': 1.00, 'label': 'Water', 'color': '#96c8e6'}}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@set_figure
|
|
29
|
+
def Q_plot(species: Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"] | list[
|
|
30
|
+
Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]],
|
|
31
|
+
x: Literal["dp", "sp"] = 'dp',
|
|
32
|
+
y: Literal["Q", "MEE"] = "Q",
|
|
33
|
+
mode: Literal["ext", "sca", "abs"] = 'ext',
|
|
34
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
35
|
+
"""
|
|
36
|
+
Generate a plot showing optical efficiency or mass optical efficiency for different particle species.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
species : Union[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"], list[Literal["AS", "AN", "OM", "Soil", "SS", "BC", "Water"]]]
|
|
41
|
+
The particle species or list of particle species to plot. Valid species include 'AS' (Ammonium Sulfate),
|
|
42
|
+
'AN' (Ammonium Nitrate), 'OM' (Organic Matter), 'Soil', 'SS' (Sea Salt), 'BC' (Black Carbon), and 'Water'.
|
|
43
|
+
|
|
44
|
+
x : Literal["dp", "sp"], optional
|
|
45
|
+
The x-axis parameter. 'dp' represents particle diameter, and 'sp' represents size parameter (alpha).
|
|
46
|
+
Default is 'dp'.
|
|
47
|
+
|
|
48
|
+
y : Literal["Q", "MEE"], optional
|
|
49
|
+
The y-axis parameter. 'Q' represents optical efficiency (Q_ext, Q_sca, Q_abs), and 'MEE' represents
|
|
50
|
+
mass optical efficiency (MEE, MSE, MAE). Default is 'Q'.
|
|
51
|
+
|
|
52
|
+
mode : Literal["ext", "sca", "abs"], optional
|
|
53
|
+
The mode of efficiency to plot. 'ext' for extinction efficiency, 'sca' for scattering efficiency,
|
|
54
|
+
and 'abs' for absorption efficiency. Default is 'ext'.
|
|
55
|
+
|
|
56
|
+
**kwargs
|
|
57
|
+
Additional keyword arguments to pass to the plot function.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
ax : Axes
|
|
62
|
+
Matplotlib Axes object containing the generated plot.
|
|
63
|
+
|
|
64
|
+
Examples
|
|
65
|
+
--------
|
|
66
|
+
Example usage of the Q_plot function:
|
|
67
|
+
|
|
68
|
+
>>> Q_plot('AS', x='dp', y='Q', mode='ext')
|
|
69
|
+
>>> Q_plot(['AS', 'AN'], x='sp', y='MEE')
|
|
70
|
+
"""
|
|
71
|
+
dp = np.geomspace(10, 10000, 2000)
|
|
72
|
+
|
|
73
|
+
mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
|
|
74
|
+
|
|
75
|
+
xlabel_mapping = {'dp': 'Particle Diameter (nm)',
|
|
76
|
+
'sp': 'Size parameter (\\alpha)'}
|
|
77
|
+
|
|
78
|
+
ylabel_mapping = {'Q': {'ext': r'$Extinction\ efficiency\ (Q_{ext})$',
|
|
79
|
+
'sca': r'$Scattering\ efficiency\ (Q_{sca})$',
|
|
80
|
+
'abs': r'$Absorption\ efficiency\ (Q_{abs})$'},
|
|
81
|
+
'MEE': {'ext': r'$MEE\ (m^{2}/g)$',
|
|
82
|
+
'sca': r'$MSE\ (m^{2}/g)$',
|
|
83
|
+
'abs': r'$MAE\ (m^{2}/g)$'}}
|
|
84
|
+
|
|
85
|
+
typ = mode_mapping.get(mode, None)
|
|
86
|
+
xlabel = xlabel_mapping.get(x, None)
|
|
87
|
+
ylabel = ylabel_mapping.get(y, None).get(mode, None)
|
|
88
|
+
|
|
89
|
+
fig, ax = plt.subplots()
|
|
90
|
+
|
|
91
|
+
if x == "sp":
|
|
92
|
+
size_para = math.pi * dp.copy() / 550
|
|
93
|
+
dp_ = size_para
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
plt.semilogx()
|
|
97
|
+
dp_ = dp.copy()
|
|
98
|
+
|
|
99
|
+
if isinstance(species, list):
|
|
100
|
+
for i, specie in enumerate(species):
|
|
101
|
+
label = mapping_dic[specie].get('label', None)
|
|
102
|
+
color = mapping_dic[specie].get('color', None)
|
|
103
|
+
|
|
104
|
+
mapping_dic[specie]['Q'] = Mie_Q(mapping_dic[specie]['m'], 550, dp)
|
|
105
|
+
mapping_dic[specie]['MEE'] = Mie_MEE(mapping_dic[specie]['m'], 550, dp, mapping_dic[specie]['density'])
|
|
106
|
+
|
|
107
|
+
plt.plot(dp_, mapping_dic[specie][f'{y}'][typ], color=color, label=label, alpha=1, lw=2)
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
legend_label = {'Q': [r'$\bf Q_{{ext}}$', r'$\bf Q_{{scat}}$', r'$\bf Q_{{abs}}$'],
|
|
111
|
+
'MEE': [r'$\bf MEE$', r'$\bf MSE$', r'$\bf MAE$']}
|
|
112
|
+
|
|
113
|
+
ylabel_mapping = {'Q': r'$\bf Optical\ efficiency\ (Q_{{ext, sca, abs}})$',
|
|
114
|
+
'MEE': r'$\bf Mass\ Optical\ Efficiency\ (m^2/g)$'}
|
|
115
|
+
|
|
116
|
+
legend = legend_label.get(y, None)
|
|
117
|
+
ylabel = ylabel_mapping.get(y, None)
|
|
118
|
+
|
|
119
|
+
mapping_dic[species]['Q'] = Mie_Q(mapping_dic[species]['m'], 550, dp)
|
|
120
|
+
mapping_dic[species]['MEE'] = Mie_MEE(mapping_dic[species]['m'], 550, dp, mapping_dic[species]['density'])
|
|
121
|
+
|
|
122
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][0], color='b', label=legend[0])
|
|
123
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][1], color='g', label=legend[1])
|
|
124
|
+
plt.plot(dp_, mapping_dic[species][f'{y}'][2], color='r', label=legend[2])
|
|
125
|
+
plt.text(0.04, 0.92, mapping_dic[species]['label'], transform=ax.transAxes, weight='bold')
|
|
126
|
+
|
|
127
|
+
ax.set(xlim=(dp.min(), dp.max()), ylim=(0, None), xlabel=xlabel, ylabel=ylabel)
|
|
128
|
+
ax.grid(color='k', axis='x', which='major', linestyle='dashdot', linewidth=0.4, alpha=0.4)
|
|
129
|
+
ax.legend(loc='best', prop={'weight': 'bold'})
|
|
130
|
+
|
|
131
|
+
# fig.savefig(PATH_MAIN/f'Q_{species}')
|
|
132
|
+
plt.show()
|
|
133
|
+
|
|
134
|
+
return fig, ax
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@set_figure(figsize=(9, 4))
|
|
138
|
+
def RI_couple(**kwargs) -> tuple[Figure, Axes]:
|
|
139
|
+
"""
|
|
140
|
+
Generate a plot to test the influence of imaginary parts on scattering and absorption efficiencies.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
**kwargs
|
|
145
|
+
Additional keyword arguments to pass to the plot function.
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
ax : Axes
|
|
150
|
+
Matplotlib Axes object containing the generated plot.
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
Example usage of the IJ_couple function:
|
|
155
|
+
|
|
156
|
+
>>> ax = RI_couple()
|
|
157
|
+
"""
|
|
158
|
+
dp = np.geomspace(10, 10000, 5000)
|
|
159
|
+
|
|
160
|
+
a = Mie_Q(1.50 + 0.01j, 550, dp)
|
|
161
|
+
b = Mie_Q(1.50 + 0.1j, 550, dp)
|
|
162
|
+
c = Mie_Q(1.50 + 0.5j, 550, dp)
|
|
163
|
+
|
|
164
|
+
fig, ax = plt.subplots(1, 2)
|
|
165
|
+
plt.subplots_adjust(right=0.9, wspace=0.4)
|
|
166
|
+
(ax1, ax2) = ax
|
|
167
|
+
size_para = math.pi * dp / 550
|
|
168
|
+
|
|
169
|
+
ax1.plot(size_para, a[1], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
|
|
170
|
+
ax1.plot(size_para, b[1], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
|
|
171
|
+
ax1.plot(size_para, c[1], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
|
|
172
|
+
ax1.legend()
|
|
173
|
+
|
|
174
|
+
ax1.set_xlim(0, size_para[-1])
|
|
175
|
+
ax1.set_ylim(0, None)
|
|
176
|
+
ax1.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
|
|
177
|
+
ax1.set_ylabel(r'$\bf Scattering\ efficiency\ (Q_{{scat}})$')
|
|
178
|
+
|
|
179
|
+
ax2.plot(size_para, a[2], 'k-', alpha=1, label=r'$\bf\ k\ =\ 0.01$')
|
|
180
|
+
ax2.plot(size_para, b[2], 'b-', alpha=1, label=r'$\bf\ k\ =\ 0.10$')
|
|
181
|
+
ax2.plot(size_para, c[2], 'g-', alpha=1, label=r'$\bf\ k\ =\ 0.50$')
|
|
182
|
+
ax2.legend()
|
|
183
|
+
|
|
184
|
+
ax2.set_xlim(0, size_para[-1])
|
|
185
|
+
ax2.set_ylim(0, None)
|
|
186
|
+
ax2.set_xlabel(r'$\bf Size\ parameter\ (\alpha)$')
|
|
187
|
+
ax2.set_ylabel(r'$\bf Absorption\ efficiency\ (Q_{{abs}})$')
|
|
188
|
+
|
|
189
|
+
fig.suptitle(r'$\bf n\ =\ 1.50 $')
|
|
190
|
+
# fig.savefig(PATH_MAIN/f'IJ_couple')
|
|
191
|
+
|
|
192
|
+
plt.show()
|
|
193
|
+
|
|
194
|
+
return fig, ax
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@set_figure
|
|
198
|
+
def RRI_2D(mode: Literal["ext", "sca", "abs"] = 'ext',
|
|
199
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
200
|
+
"""
|
|
201
|
+
Generate a 2D plot of scattering efficiency (Q) against real and imaginary parts of the refractive index.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
mode : {'ext', 'sca', 'abs'}, optional
|
|
206
|
+
The mode of scattering efficiency to plot:
|
|
207
|
+
- 'ext' for extinction efficiency (Q_ext)
|
|
208
|
+
- 'sca' for scattering efficiency (Q_sca)
|
|
209
|
+
- 'abs' for absorption efficiency (Q_abs)
|
|
210
|
+
Default is 'ext'.
|
|
211
|
+
|
|
212
|
+
**kwargs
|
|
213
|
+
Additional keyword arguments to pass to the plot function.
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
ax : Axes
|
|
218
|
+
Matplotlib Axes object containing the generated 2D plot.
|
|
219
|
+
|
|
220
|
+
Examples
|
|
221
|
+
--------
|
|
222
|
+
Example usage of the RRI_2D function:
|
|
223
|
+
|
|
224
|
+
>>> RRI_2D(mode='sca', xlabel='Real Part (n)', ylabel='Imaginary Part (k)', title='Scattering Efficiency 2D Plot')
|
|
225
|
+
"""
|
|
226
|
+
mode_mapping = {'ext': 0, 'sca': 1, 'abs': 2}
|
|
227
|
+
|
|
228
|
+
typ = mode_mapping.get(mode, None)
|
|
229
|
+
|
|
230
|
+
for dp in [400, 550, 700]:
|
|
231
|
+
RRI = np.linspace(1.3, 2, 100)
|
|
232
|
+
IRI = np.linspace(0, 0.7, 100)
|
|
233
|
+
arr = np.zeros((RRI.size, IRI.size))
|
|
234
|
+
|
|
235
|
+
for i, I_RI in enumerate(IRI):
|
|
236
|
+
for j, R_RI in enumerate(RRI):
|
|
237
|
+
arr[i, j] = Mie_Q(R_RI + 1j * I_RI, 550, dp)[typ]
|
|
238
|
+
|
|
239
|
+
fig, ax = plt.subplots()
|
|
240
|
+
plt.title(fr'$\bf dp\ = {dp}\ nm$', )
|
|
241
|
+
plt.xlabel(r'$\bf Real\ part\ (n)$', )
|
|
242
|
+
plt.ylabel(r'$\bf Imaginary\ part\ (k)$', )
|
|
243
|
+
|
|
244
|
+
im = plt.imshow(arr, extent=(1.3, 2, 0, 0.7), cmap='jet', origin='lower')
|
|
245
|
+
color_bar = plt.colorbar(im, extend='both')
|
|
246
|
+
color_bar.set_label(label=fr'$\bf Q_{{{mode}}}$')
|
|
247
|
+
|
|
248
|
+
# fig.savefig(PATH_MAIN/f'RRI_{mode}_{dp}')
|
|
249
|
+
|
|
250
|
+
plt.show()
|
|
251
|
+
|
|
252
|
+
return fig, ax
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@set_figure
|
|
256
|
+
def scattering_phase(m: complex = 1.55 + 0.01j,
|
|
257
|
+
wave: float = 600,
|
|
258
|
+
dp: float = 200) -> tuple[Figure, Axes]:
|
|
259
|
+
"""
|
|
260
|
+
Generate a polar plot to visualize the scattering phase function.
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
m : complex, optional
|
|
265
|
+
The complex refractive index of the scattering medium. Default is 1.55 + 0.01j.
|
|
266
|
+
wave : float, optional
|
|
267
|
+
The wavelength of the incident light in nanometers. Default is 600 nm.
|
|
268
|
+
dp : float, optional
|
|
269
|
+
The particle diameter in nanometers. Default is 200 nm.
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
ax : Axes
|
|
274
|
+
Matplotlib Axes object containing the generated polar plot.
|
|
275
|
+
|
|
276
|
+
Examples
|
|
277
|
+
--------
|
|
278
|
+
Example usage of the scattering_phase function:
|
|
279
|
+
|
|
280
|
+
>>> ax = scattering_phase(m=1.55 + 0.01j, wave=600, dp=200)
|
|
281
|
+
"""
|
|
282
|
+
theta, _SL, _SR, _SU = ScatteringFunction(m, wave, dp)
|
|
283
|
+
|
|
284
|
+
SL = np.append(_SL, _SL[::-1])
|
|
285
|
+
SR = np.append(_SR, _SR[::-1])
|
|
286
|
+
SU = np.append(_SU, _SU[::-1])
|
|
287
|
+
|
|
288
|
+
angles = ['0', '60', '120', '180', '240', '300']
|
|
289
|
+
|
|
290
|
+
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
|
|
291
|
+
|
|
292
|
+
theta = np.linspace(0, 2 * np.pi, len(SL))
|
|
293
|
+
|
|
294
|
+
plt.thetagrids(range(0, 360, int(360 / len(angles))), angles)
|
|
295
|
+
|
|
296
|
+
plt.plot(theta, SL, '-', linewidth=2, color='#115162', label='SL')
|
|
297
|
+
plt.fill(theta, SL, '#afe0f5', alpha=0.5)
|
|
298
|
+
plt.plot(theta, SR, '-', linewidth=2, color='#7FAE80', label='SR')
|
|
299
|
+
plt.fill(theta, SR, '#b5e6c5', alpha=0.5)
|
|
300
|
+
plt.plot(theta, SU, '-', linewidth=2, color='#621129', label='SU')
|
|
301
|
+
plt.fill(theta, SU, '#f5afbd', alpha=0.5)
|
|
302
|
+
|
|
303
|
+
plt.legend(loc='best', bbox_to_anchor=(1, 0, 0.2, 1), prop={'weight': 'bold'})
|
|
304
|
+
plt.title(r'$\bf Scattering\ phase\ function$')
|
|
305
|
+
|
|
306
|
+
plt.show()
|
|
307
|
+
return fig, ax
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@set_figure
|
|
311
|
+
def response_surface(real_range=(1.33, 1.7),
|
|
312
|
+
gmd_range=(10, 500),
|
|
313
|
+
num=50,
|
|
314
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
315
|
+
"""
|
|
316
|
+
Generate a response surface plot for sensitivity tests of extinction based on Mie scattering.
|
|
317
|
+
|
|
318
|
+
Parameters
|
|
319
|
+
----------
|
|
320
|
+
real_range : tuple, optional
|
|
321
|
+
The range of real part (refractive index) values for sensitivity testing. Default is (1.33, 1.7).
|
|
322
|
+
|
|
323
|
+
gmd_range : tuple, optional
|
|
324
|
+
The range of geometric mean diameter (GMD) values for sensitivity testing. Default is (60, 400).
|
|
325
|
+
|
|
326
|
+
num : int, optional
|
|
327
|
+
The number of points to generate within the specified ranges. Default is 50.
|
|
328
|
+
|
|
329
|
+
**kwargs
|
|
330
|
+
Additional keyword arguments to pass to the plot function.
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
ax : Axes
|
|
335
|
+
Matplotlib Axes object containing the generated response surface plot.
|
|
336
|
+
|
|
337
|
+
Examples
|
|
338
|
+
--------
|
|
339
|
+
Example usage of the response_surface function:
|
|
340
|
+
|
|
341
|
+
>>> response_surface(real_range=(1.4, 1.6), gmd_range=(100, 300), num=30, xlabel='Real Part (n)',
|
|
342
|
+
... ylabel='GMD (nm)', zlabel='Extinction (1/Mm)', title='Sensitivity Tests of Extinction')
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def function(RI, GMD):
|
|
346
|
+
Z = np.zeros_like(RI) # 使用 np.zeros_like 可以確保 Z 和 RI 具有相同的形狀
|
|
347
|
+
|
|
348
|
+
for i in range(RI.shape[0]):
|
|
349
|
+
for j in range(RI.shape[1]):
|
|
350
|
+
_RI, _GMD = RI[i, j], GMD[i, j]
|
|
351
|
+
Bext, Bsca, Babs = Mie_PESD(m=_RI, lognormal=True, geoMean=_GMD, geoStdDev=2.)
|
|
352
|
+
Z[i, j] = np.sum(Bext)
|
|
353
|
+
|
|
354
|
+
return Z
|
|
355
|
+
|
|
356
|
+
# 假設 RI、GSD、GMD
|
|
357
|
+
RI = np.linspace(real_range[0], real_range[1], num)
|
|
358
|
+
GMD = np.linspace(gmd_range[0], gmd_range[1], num)
|
|
359
|
+
|
|
360
|
+
# 建立三維 meshgrid
|
|
361
|
+
real, gmd = np.meshgrid(RI, GMD, indexing='xy')
|
|
362
|
+
|
|
363
|
+
# Result
|
|
364
|
+
ext = function(real, gmd)
|
|
365
|
+
|
|
366
|
+
# plot
|
|
367
|
+
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
|
|
368
|
+
ax.plot_surface(real, gmd, ext, rstride=1, cstride=1, cmap=plt.get_cmap('jet'), edgecolor='none')
|
|
369
|
+
|
|
370
|
+
ax.set(xlabel='Real part (n)', ylabel='GMD (nm)', zlabel=Unit('Extinction'),
|
|
371
|
+
title='Sensitive tests of extinction')
|
|
372
|
+
|
|
373
|
+
ax.zaxis.get_offset_text().set_visible(False)
|
|
374
|
+
exponent = math.floor(math.log10(np.max(ext)))
|
|
375
|
+
ax.text(ax.get_xlim()[1] * 1.01, ax.get_ylim()[1], ax.get_zlim()[1] * 1.1, s=fr'${{\times}}\ 10^{exponent}$')
|
|
376
|
+
ax.ticklabel_format(style='sci', axis='z', scilimits=(0, 0), useOffset=False)
|
|
377
|
+
|
|
378
|
+
plt.show()
|
|
379
|
+
|
|
380
|
+
return fig, ax
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
if __name__ == '__main__':
|
|
384
|
+
Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='MEE')
|
|
385
|
+
Q_plot(['AS', 'AN', 'OM', 'Soil', 'SS', 'BC'], x='dp', y='Q')
|
|
386
|
+
|
|
387
|
+
RI_couple()
|
|
388
|
+
response_surface()
|
AeroViz/plot/pie.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from matplotlib.pyplot import Figure, Axes
|
|
7
|
+
from pandas import DataFrame
|
|
8
|
+
|
|
9
|
+
from AeroViz.plot.utils import *
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'pie',
|
|
13
|
+
'donuts'
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@set_figure(fw='bold')
|
|
18
|
+
def pie(data_set: DataFrame | dict,
|
|
19
|
+
labels: list[str],
|
|
20
|
+
unit: str,
|
|
21
|
+
style: Literal["pie", 'donut'],
|
|
22
|
+
ax: Axes | None = None,
|
|
23
|
+
symbol: bool = True,
|
|
24
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
25
|
+
"""
|
|
26
|
+
Create a pie or donut chart based on the provided data.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
data_set : pd.DataFrame | dict
|
|
31
|
+
A pandas DataFrame or dictionary mapping category names to a list of species.
|
|
32
|
+
If a DataFrame is provided, the index represents the categories, and each column contains species data.
|
|
33
|
+
If a dictionary is provided, it maps category names to lists of species data.
|
|
34
|
+
It is assumed that all lists or DataFrame columns contain the same number of entries as the *labels* list.
|
|
35
|
+
labels : list of str
|
|
36
|
+
The labels for each category.
|
|
37
|
+
unit : str
|
|
38
|
+
The unit to display in the center of the donut chart.
|
|
39
|
+
style : Literal["pie", 'donut']
|
|
40
|
+
The style of the chart, either 'pie' for a standard pie chart or 'donut' for a donut chart.
|
|
41
|
+
ax : plt.Axes or None, optional
|
|
42
|
+
The Axes object to plot the chart onto. If None, a new figure and Axes will be created.
|
|
43
|
+
symbol : bool, optional
|
|
44
|
+
Whether to display values for each species in the chart.
|
|
45
|
+
**kwargs
|
|
46
|
+
Additional keyword arguments to be passed to the plotting function.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
matplotlib.axes.Axes
|
|
51
|
+
The Axes object containing the violin plot.
|
|
52
|
+
|
|
53
|
+
Notes
|
|
54
|
+
-----
|
|
55
|
+
- If *data_set* is a dictionary, it should contain lists of species that correspond to each category in *labels*.
|
|
56
|
+
- The length of each list in *data_set* or the number of columns in the DataFrame should match the length of the *labels* list.
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
>>> data_set = {'Category 1': [10, 20, 30], 'Category 2': [15, 25, 35]}
|
|
61
|
+
>>> labels = ['Species 1', 'Species 2', 'Species 3']
|
|
62
|
+
>>> pie(data_set, labels, unit='kg', style='pie', symbol=True)
|
|
63
|
+
"""
|
|
64
|
+
if isinstance(data_set, DataFrame):
|
|
65
|
+
category_names = list(data_set.index)
|
|
66
|
+
data = data_set.to_numpy()
|
|
67
|
+
|
|
68
|
+
pies, species = data.shape
|
|
69
|
+
|
|
70
|
+
elif isinstance(data_set, dict):
|
|
71
|
+
category_names = list(data_set.keys())
|
|
72
|
+
data = np.array(list(data_set.values()))
|
|
73
|
+
|
|
74
|
+
pies, species = data.shape
|
|
75
|
+
|
|
76
|
+
else:
|
|
77
|
+
raise ValueError('data_set must be a DataFrame or a dictionary.')
|
|
78
|
+
|
|
79
|
+
colors = kwargs.get('colors') or (Color.colors1 if species == 6 else Color.getColor(num=species))
|
|
80
|
+
|
|
81
|
+
radius = 4
|
|
82
|
+
width = 4 if style == 'pie' else 1
|
|
83
|
+
|
|
84
|
+
text = [''] * pies if style == 'pie' else [Unit(unit) + '\n\n' +
|
|
85
|
+
'{:.2f} ± {:.2f}'.format(x, s)
|
|
86
|
+
for x, s in zip(data.sum(axis=1), data.std(axis=1))]
|
|
87
|
+
pct_distance = 0.6 if style == 'pie' else 0.88
|
|
88
|
+
|
|
89
|
+
fig, ax = plt.subplots(1, pies, figsize=((pies * 2) + 1, 2)) if ax is None else (ax.get_figure(), ax)
|
|
90
|
+
|
|
91
|
+
if pies == 1:
|
|
92
|
+
ax = [ax]
|
|
93
|
+
|
|
94
|
+
for i in range(pies):
|
|
95
|
+
ax[i].pie(data[i], labels=None, colors=colors, textprops=None,
|
|
96
|
+
autopct=lambda pct: auto_label_pct(pct, symbol=symbol, include_pct=True),
|
|
97
|
+
pctdistance=pct_distance, radius=radius, wedgeprops=dict(width=width, edgecolor='w'))
|
|
98
|
+
|
|
99
|
+
ax[i].pie(data[i], labels=None, colors=colors, textprops=None,
|
|
100
|
+
autopct=lambda pct: auto_label_pct(pct, symbol=symbol, ignore='outer', include_pct=True),
|
|
101
|
+
pctdistance=1.3, radius=radius, wedgeprops=dict(width=width, edgecolor='w'))
|
|
102
|
+
ax[i].axis('equal')
|
|
103
|
+
ax[i].text(0, 0, text[i], ha='center', va='center')
|
|
104
|
+
|
|
105
|
+
if kwargs.get('title') is None:
|
|
106
|
+
ax[i].set_title(category_names[i])
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
if len(kwargs.get('title')) == pies:
|
|
110
|
+
title = kwargs.get('title')
|
|
111
|
+
else:
|
|
112
|
+
raise ValueError('The length of the title list must match the number of pies.')
|
|
113
|
+
|
|
114
|
+
ax[i].set_title(title[i])
|
|
115
|
+
|
|
116
|
+
ax[-1].legend(labels, loc='center left', prop={'size': 8, 'weight': 'normal'}, bbox_to_anchor=(1, 0, 1.15, 1))
|
|
117
|
+
|
|
118
|
+
# fig.savefig(f"pie_{style}_{title}")
|
|
119
|
+
|
|
120
|
+
plt.show()
|
|
121
|
+
|
|
122
|
+
return fig, ax
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@set_figure(fw='bold')
|
|
126
|
+
def donuts(data_set: DataFrame | dict,
|
|
127
|
+
labels: list[str],
|
|
128
|
+
unit: str,
|
|
129
|
+
ax: Axes | None = None,
|
|
130
|
+
symbol=True,
|
|
131
|
+
**kwargs) -> tuple[Figure, Axes]:
|
|
132
|
+
"""
|
|
133
|
+
Plot a donut chart based on the data set.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
data_set : pd.DataFrame | dict
|
|
138
|
+
A pandas DataFrame or a dictionary mapping category names to a list of species.
|
|
139
|
+
If a DataFrame is provided, the index represents the categories, and each column contains species data.
|
|
140
|
+
If a dictionary is provided, it maps category names to lists of species data.
|
|
141
|
+
It is assumed that all lists or DataFrame columns contain the same number of entries as the *labels* list.
|
|
142
|
+
labels : list of str
|
|
143
|
+
The category labels.
|
|
144
|
+
unit : str
|
|
145
|
+
The unit to be displayed in the center of the donut chart.
|
|
146
|
+
ax : matplotlib.axes.Axes, optional
|
|
147
|
+
The axes to plot on. If None, the current axes will be used (default).
|
|
148
|
+
symbol : bool, optional
|
|
149
|
+
Whether to display values for each species (default is True).
|
|
150
|
+
**kwargs : dict, optional
|
|
151
|
+
Additional keyword arguments to pass to the matplotlib pie chart function.
|
|
152
|
+
|
|
153
|
+
Returns
|
|
154
|
+
-------
|
|
155
|
+
matplotlib.axes.Axes
|
|
156
|
+
The axes containing the donut chart.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
if isinstance(data_set, DataFrame):
|
|
160
|
+
category_names = list(data_set.index)
|
|
161
|
+
data = data_set.to_numpy()
|
|
162
|
+
|
|
163
|
+
pies, species = data.shape
|
|
164
|
+
|
|
165
|
+
elif isinstance(data_set, dict):
|
|
166
|
+
category_names = list(data_set.keys())
|
|
167
|
+
data = np.array(list(data_set.values()))
|
|
168
|
+
|
|
169
|
+
pies, species = data.shape
|
|
170
|
+
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError('data_set must be a DataFrame or a dictionary.')
|
|
173
|
+
|
|
174
|
+
colors1 = kwargs.get('colors') or (Color.colors1 if species == 6 else Color.getColor(num=species))
|
|
175
|
+
colors2 = Color.adjust_opacity(colors1, 0.8)
|
|
176
|
+
colors3 = Color.adjust_opacity(colors1, 0.6)
|
|
177
|
+
|
|
178
|
+
fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
|
|
179
|
+
|
|
180
|
+
ax.pie(data[2], labels=None, colors=colors1, textprops=None,
|
|
181
|
+
autopct=lambda pct: auto_label_pct(pct, symbol=symbol, include_pct=True),
|
|
182
|
+
pctdistance=0.9, radius=14, wedgeprops=dict(width=3, edgecolor='w'))
|
|
183
|
+
|
|
184
|
+
ax.pie(data[1], labels=None, colors=colors2, textprops=None,
|
|
185
|
+
autopct=lambda pct: auto_label_pct(pct, symbol=symbol, include_pct=True),
|
|
186
|
+
pctdistance=0.85, radius=11, wedgeprops=dict(width=3, edgecolor='w'))
|
|
187
|
+
|
|
188
|
+
ax.pie(data[0], labels=None, colors=colors3, textprops=None,
|
|
189
|
+
autopct=lambda pct: auto_label_pct(pct, symbol=symbol, include_pct=True),
|
|
190
|
+
pctdistance=0.80, radius=8, wedgeprops=dict(width=3, edgecolor='w'))
|
|
191
|
+
|
|
192
|
+
text = (Unit(f'{unit}') + '\n\n' +
|
|
193
|
+
'Event : ' + "{:.2f}".format(np.sum(data[2])) + '\n' +
|
|
194
|
+
'Transition : ' + "{:.2f}".format(np.sum(data[1])) + '\n' +
|
|
195
|
+
'Clean : ' + "{:.2f}".format(np.sum(data[0])))
|
|
196
|
+
|
|
197
|
+
ax.text(0, 0, text, ha='center', va='center')
|
|
198
|
+
ax.axis('equal')
|
|
199
|
+
|
|
200
|
+
ax.set_title(kwargs.get('title', ''))
|
|
201
|
+
|
|
202
|
+
ax.legend(labels, loc='center', prop={'size': 8}, title_fontproperties={'weight': 'bold'},
|
|
203
|
+
title=f'Outer : {category_names[2]}' + '\n' + f'Middle : {category_names[1]}' + '\n' + f'Inner : {category_names[0]}',
|
|
204
|
+
bbox_to_anchor=(0.8, 0, 0.5, 1))
|
|
205
|
+
|
|
206
|
+
# fig.savefig(f"donuts_{title}")
|
|
207
|
+
|
|
208
|
+
plt.show()
|
|
209
|
+
|
|
210
|
+
return fig, ax
|