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
@@ -9,63 +9,63 @@ __all__ = ['Color']
9
9
 
10
10
 
11
11
  class Color:
12
- color_cycle = cycler(color=['b', 'g', 'r', 'c', 'm', 'y', 'k'])
13
-
14
- linecolor = [{'line': '#1a56db', 'edge': '#0F50A6', 'face': '#5983D9'},
15
- {'line': '#046c4e', 'edge': '#1B591F', 'face': '#538C4A'},
16
- {'line': '#c81e1e', 'edge': '#f05252', 'face': '#f98080'}]
17
-
18
- # colors = ['#FF3333', '#33FF33', '#FFFF33', '#5555FF', '#B94FFF', '#AAAAAA', '#748690'] # the last one is "unknown"
19
-
20
- colors1 = ['#A65E58', '#A5BF6B', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB']
21
- colors2 = ['#A65E58', '#A5BF6B', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB', '#96c8e6']
22
- colors3 = ['#A65E58', '#A5BF6B', '#a6710d', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB', '#96c8e6'] # POC SOC
23
-
24
- colors_mutiWater = ['#A65E58', '#c18e8a', '#A5BF6B', '#c5d6a0', '#F2BF5E', '#3F83BF', '#c089ca', '#d3acda',
25
- '#D1CFCB']
26
- colors_mutiWater2 = ['#A65E58', '#96c8e6', '#A5BF6B', '#96c8e6', '#F2BF5E', '#3F83BF', '#c089ca', '#96c8e6',
27
- '#D1CFCB'] # water
28
-
29
- color_choose = {'Clean': ['#1d4a9f', '#84a7e9'],
30
- 'Transition': ['#4a9f1d', '#a7e984'],
31
- 'Event': ['#9f1d4a', '#e984a7']}
32
-
33
- paired = [plt.get_cmap('Paired')(i) for i in range(4)]
34
-
35
- @staticmethod
36
- def getColor(num: int = 6, cmap: str = 'jet_r'):
37
- category_colors = plt.colormaps[cmap](np.linspace(0.1, 0.9, num))
38
- return [plc.to_hex(category_colors[i]) for i in range(num)]
39
-
40
- @staticmethod
41
- def palplot(*args, **kwargs):
42
- sns.palplot(*args, **kwargs)
43
-
44
- @staticmethod
45
- def adjust_opacity(colors: str | list[str], alpha: float):
46
- if isinstance(colors, str):
47
- colors = [colors]
48
-
49
- adjusted_colors = []
50
- for color in colors:
51
- # 將顏色轉換為RGB表示
52
- r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
53
- # 調整透明度
54
- r_new = int(alpha * r + (1 - alpha) * 255)
55
- g_new = int(alpha * g + (1 - alpha) * 255)
56
- b_new = int(alpha * b + (1 - alpha) * 255)
57
- # 轉換為新的色碼
58
- new_color = '#{:02X}{:02X}{:02X}'.format(r_new, g_new, b_new)
59
- adjusted_colors.append(new_color)
60
- return adjusted_colors
61
-
62
- @staticmethod
63
- def color_maker(obj, cmap='Blues'):
64
- colors = np.nan_to_num(obj, nan=0)
65
- scalar_map = plt.cm.ScalarMappable(cmap=colormaps[cmap]) # create a scalar map for the colorbar
66
- scalar_map.set_array(colors)
67
- return scalar_map, colors
12
+ color_cycle = cycler(color=['b', 'g', 'r', 'c', 'm', 'y', 'k'])
13
+
14
+ linecolor = [{'line': '#1a56db', 'edge': '#0F50A6', 'face': '#5983D9'},
15
+ {'line': '#046c4e', 'edge': '#1B591F', 'face': '#538C4A'},
16
+ {'line': '#c81e1e', 'edge': '#f05252', 'face': '#f98080'}]
17
+
18
+ # colors = ['#FF3333', '#33FF33', '#FFFF33', '#5555FF', '#B94FFF', '#AAAAAA', '#748690'] # the last one is "unknown"
19
+
20
+ colors1 = ['#A65E58', '#A5BF6B', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB']
21
+ colors2 = ['#A65E58', '#A5BF6B', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB', '#96c8e6']
22
+ colors3 = ['#A65E58', '#A5BF6B', '#a6710d', '#F2BF5E', '#3F83BF', '#B777C2', '#D1CFCB', '#96c8e6'] # POC SOC
23
+
24
+ colors_mutiWater = ['#A65E58', '#c18e8a', '#A5BF6B', '#c5d6a0', '#F2BF5E', '#3F83BF', '#c089ca', '#d3acda',
25
+ '#D1CFCB']
26
+ colors_mutiWater2 = ['#A65E58', '#96c8e6', '#A5BF6B', '#96c8e6', '#F2BF5E', '#3F83BF', '#c089ca', '#96c8e6',
27
+ '#D1CFCB'] # water
28
+
29
+ color_choose = {'Clean': ['#1d4a9f', '#84a7e9'],
30
+ 'Transition': ['#4a9f1d', '#a7e984'],
31
+ 'Event': ['#9f1d4a', '#e984a7']}
32
+
33
+ paired = [plt.get_cmap('Paired')(i) for i in range(4)]
34
+
35
+ @staticmethod
36
+ def getColor(num: int = 6, cmap: str = 'jet_r'):
37
+ category_colors = plt.colormaps[cmap](np.linspace(0.1, 0.9, num))
38
+ return [plc.to_hex(category_colors[i]) for i in range(num)]
39
+
40
+ @staticmethod
41
+ def palplot(*args, **kwargs):
42
+ sns.palplot(*args, **kwargs)
43
+
44
+ @staticmethod
45
+ def adjust_opacity(colors: str | list[str], alpha: float):
46
+ if isinstance(colors, str):
47
+ colors = [colors]
48
+
49
+ adjusted_colors = []
50
+ for color in colors:
51
+ # 將顏色轉換為RGB表示
52
+ r, g, b = int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)
53
+ # 調整透明度
54
+ r_new = int(alpha * r + (1 - alpha) * 255)
55
+ g_new = int(alpha * g + (1 - alpha) * 255)
56
+ b_new = int(alpha * b + (1 - alpha) * 255)
57
+ # 轉換為新的色碼
58
+ new_color = '#{:02X}{:02X}{:02X}'.format(r_new, g_new, b_new)
59
+ adjusted_colors.append(new_color)
60
+ return adjusted_colors
61
+
62
+ @staticmethod
63
+ def color_maker(obj, cmap='Blues'):
64
+ colors = np.nan_to_num(obj, nan=0)
65
+ scalar_map = plt.cm.ScalarMappable(cmap=colormaps[cmap]) # create a scalar map for the colorbar
66
+ scalar_map.set_array(colors)
67
+ return scalar_map, colors
68
68
 
69
69
 
70
70
  if __name__ == '__main__':
71
- Color.palplot(Color.colors2)
71
+ Color.palplot(Color.colors2)
@@ -5,51 +5,51 @@ __all__ = ['Unit']
5
5
 
6
6
 
7
7
  class Unit:
8
- file_path = Path(__file__).parent / 'units.json'
9
- data = None
10
-
11
- def __new__(cls, unit: str):
12
- cls.data = cls.load_jsonfile()
13
- try:
14
- value = cls.data[unit]
15
- return r'${}$'.format(value.replace(' ', r'\ '))
16
- except KeyError:
17
- print(f"Attribute '{unit}' not found. Using default value.")
18
- return r'${}$'.format(unit.replace(' ', r'\ ')) if unit is not None else 'None'
19
-
20
- @classmethod
21
- def load_jsonfile(cls):
22
- """ 讀取 JSON 檔中數據并將其變成屬性 """
23
- try:
24
- with open(cls.file_path, 'r', encoding='utf-8') as f:
25
- return json.load(f)
26
-
27
- except FileNotFoundError:
28
- print(f"JSON file '{cls.file_path}' not found.")
29
- except json.JSONDecodeError:
30
- print(f"Invalid JSON format in '{cls.file_path}'.")
31
-
32
- @classmethod
33
- def update_jsonfile(cls, key, value):
34
- """ 更新JSON檔 """
35
- with open(cls.file_path, 'r', encoding='utf-8') as f:
36
- old_data = json.load(f)
37
-
38
- old_data[key] = value
39
-
40
- with open(cls.file_path, 'w', encoding='utf-8') as f:
41
- json.dump(old_data, f, indent=4)
42
-
43
- @classmethod
44
- def del_jsonfile(cls, key):
45
- """ 更新JSON檔 """
46
- with open(cls.file_path, 'r', encoding='utf-8') as f:
47
- old_data = json.load(f)
48
-
49
- if key in old_data:
50
- del old_data[key]
51
-
52
- with open(cls.file_path, 'w', encoding='utf-8') as f:
53
- json.dump(old_data, f, indent=4)
54
- else:
55
- print(f"Key '{key}' not found.")
8
+ file_path = Path(__file__).parent / 'units.json'
9
+ data = None
10
+
11
+ def __new__(cls, unit: str):
12
+ cls.data = cls.load_jsonfile()
13
+ try:
14
+ value = cls.data[unit]
15
+ return r'${}$'.format(value.replace(' ', r'\ '))
16
+ except KeyError:
17
+ print(f"Attribute '{unit}' not found. Using default value.")
18
+ return r'${}$'.format(unit.replace(' ', r'\ ')) if unit is not None else 'None'
19
+
20
+ @classmethod
21
+ def load_jsonfile(cls):
22
+ """ 讀取 JSON 檔中數據并將其變成屬性 """
23
+ try:
24
+ with open(cls.file_path, 'r', encoding='utf-8') as f:
25
+ return json.load(f)
26
+
27
+ except FileNotFoundError:
28
+ print(f"JSON file '{cls.file_path}' not found.")
29
+ except json.JSONDecodeError:
30
+ print(f"Invalid JSON format in '{cls.file_path}'.")
31
+
32
+ @classmethod
33
+ def update_jsonfile(cls, key, value):
34
+ """ 更新JSON檔 """
35
+ with open(cls.file_path, 'r', encoding='utf-8') as f:
36
+ old_data = json.load(f)
37
+
38
+ old_data[key] = value
39
+
40
+ with open(cls.file_path, 'w', encoding='utf-8') as f:
41
+ json.dump(old_data, f, indent=4)
42
+
43
+ @classmethod
44
+ def del_jsonfile(cls, key):
45
+ """ 更新JSON檔 """
46
+ with open(cls.file_path, 'r', encoding='utf-8') as f:
47
+ old_data = json.load(f)
48
+
49
+ if key in old_data:
50
+ del old_data[key]
51
+
52
+ with open(cls.file_path, 'w', encoding='utf-8') as f:
53
+ json.dump(old_data, f, indent=4)
54
+ else:
55
+ print(f"Key '{key}' not found.")
@@ -0,0 +1,92 @@
1
+ from functools import wraps
2
+ from typing import Literal
3
+
4
+ import matplotlib.pyplot as plt
5
+ from matplotlib.pyplot import Axes
6
+
7
+ __all__ = ['set_figure', 'combine_legends', 'auto_label_pct']
8
+
9
+
10
+ def set_figure(func=None,
11
+ *,
12
+ figsize: tuple | None = None,
13
+ fs: int | None = None,
14
+ fw: str = None,
15
+ autolayout: bool = True):
16
+ # For more details please see https://matplotlib.org/stable/users/explain/customizing.html
17
+ def decorator(_func):
18
+ @wraps(_func)
19
+ def wrapper(*args, **kwargs):
20
+ print(f'\n\tPlot:\033[96m {_func.__name__}\033[0m')
21
+
22
+ plt.rcParams['mathtext.fontset'] = 'custom'
23
+ plt.rcParams['mathtext.rm'] = 'Times New Roman'
24
+ plt.rcParams['mathtext.it'] = 'Times New Roman: italic'
25
+ plt.rcParams['mathtext.bf'] = 'Times New Roman: bold'
26
+ plt.rcParams['mathtext.default'] = 'regular'
27
+
28
+ # The font properties used by `text.Text`.
29
+ # The text, annotate, label, title, ticks, are used to create text
30
+ plt.rcParams['font.family'] = 'Times New Roman'
31
+ plt.rcParams['font.weight'] = fw or 'normal'
32
+ plt.rcParams['font.size'] = fs or 8
33
+
34
+ plt.rcParams['axes.titlesize'] = 'large'
35
+ plt.rcParams['axes.titleweight'] = 'bold'
36
+ plt.rcParams['axes.labelweight'] = 'bold'
37
+
38
+ # color
39
+ plt.rcParams['axes.prop_cycle'] = plt.cycler(color=['b', 'g', 'r', 'c', 'm', 'y', 'k'])
40
+
41
+ plt.rcParams['xtick.labelsize'] = 'medium'
42
+ plt.rcParams['ytick.labelsize'] = 'medium'
43
+
44
+ # matplotlib.font_manager.FontProperties ---> matplotlib.rcParams
45
+ plt.rcParams['legend.loc'] = 'best'
46
+ plt.rcParams['legend.frameon'] = False
47
+ plt.rcParams['legend.fontsize'] = 'small'
48
+ plt.rcParams['legend.title_fontsize'] = 'medium'
49
+ plt.rcParams['legend.handlelength'] = 1.5
50
+ plt.rcParams['legend.labelspacing'] = 0.7
51
+
52
+ plt.rcParams['figure.figsize'] = figsize or (4, 4)
53
+ plt.rcParams['figure.dpi'] = 200
54
+ plt.rcParams['figure.autolayout'] = autolayout
55
+
56
+ if not autolayout:
57
+ plt.rcParams['figure.subplot.left'] = 0.1
58
+ plt.rcParams['figure.subplot.right'] = 0.875
59
+ plt.rcParams['figure.subplot.top'] = 0.875
60
+ plt.rcParams['figure.subplot.bottom'] = 0.125
61
+
62
+ # plt.rcParams['figure.constrained_layout.use'] = True
63
+
64
+ plt.rcParams['savefig.transparent'] = True
65
+
66
+ return _func(*args, **kwargs)
67
+
68
+ return wrapper
69
+
70
+ if func is None:
71
+ return decorator
72
+
73
+ return decorator(func)
74
+
75
+
76
+ def combine_legends(axes_list: list[Axes]) -> tuple[list, list]:
77
+ return (
78
+ [legend for axes in axes_list for legend in axes.get_legend_handles_labels()[0]],
79
+ [label for axes in axes_list for label in axes.get_legend_handles_labels()[1]]
80
+ )
81
+
82
+
83
+ def auto_label_pct(pct,
84
+ symbol: bool = True,
85
+ include_pct: bool = False,
86
+ ignore: Literal["inner", "outer"] = 'inner',
87
+ value: float = 2):
88
+ if not symbol:
89
+ return ''
90
+ cond = pct <= value if ignore == 'inner' else pct > value
91
+ label = '' if cond else '{:.1f}'.format(pct)
92
+ return '' if label == '' else label + '%' if include_pct else label
@@ -0,0 +1,49 @@
1
+ import numpy as np
2
+ from sklearn.linear_model import LinearRegression
3
+ from tabulate import tabulate
4
+
5
+ __all__ = ['linear_regression_base']
6
+
7
+
8
+ def linear_regression_base(x_array: np.ndarray,
9
+ y_array: np.ndarray,
10
+ columns: str | list[str] | None = None,
11
+ positive: bool = True,
12
+ fit_intercept: bool = True):
13
+ if len(x_array.shape) > 1 and x_array.shape[1] >= 2:
14
+ model = LinearRegression(positive=positive, fit_intercept=fit_intercept).fit(x_array, y_array)
15
+
16
+ coefficients = model.coef_[0].round(3)
17
+ intercept = model.intercept_[0].round(3) if fit_intercept else 'None'
18
+ r_square = model.score(x_array, y_array).__round__(3)
19
+ y_predict = model.predict(x_array)
20
+
21
+ equation = ' + '.join([f'{coeff:.3f} * {col}' for coeff, col in zip(coefficients, columns)])
22
+ equation = equation.replace(' + 0.000 * Const', '') # Remove terms with coefficient 0
23
+
24
+ text = 'y = ' + str(equation) + '\n' + r'$\bf R^2 = $' + str(r_square)
25
+ tab = tabulate([[*coefficients, intercept, r_square]], headers=[*columns, 'intercept', 'R^2'], floatfmt=".3f",
26
+ tablefmt="fancy_grid")
27
+ print('\n' + tab)
28
+
29
+ return text, y_predict, coefficients
30
+
31
+ else:
32
+ x_array = x_array.reshape(-1, 1)
33
+ y_array = y_array.reshape(-1, 1)
34
+
35
+ model = LinearRegression(positive=positive, fit_intercept=fit_intercept).fit(x_array, y_array)
36
+
37
+ slope = model.coef_[0][0].round(3)
38
+ intercept = model.intercept_[0].round(3) if fit_intercept else 'None'
39
+ r_square = model.score(x_array, y_array).__round__(3)
40
+ y_predict = model.predict(x_array)
41
+
42
+ text = np.poly1d([slope, intercept])
43
+ text = 'y = ' + str(text).replace('\n', "") + '\n' + r'$\bf R^2 = $' + str(r_square)
44
+
45
+ tab = tabulate([[slope, intercept, r_square]], headers=['slope', 'intercept', 'R^2'], floatfmt=".3f",
46
+ tablefmt="fancy_grid")
47
+ print('\n' + tab)
48
+
49
+ return text, y_predict, slope
@@ -6,6 +6,8 @@
6
6
  "T_OC": "OC (\u00b5g/m^3)",
7
7
  "PM1": "PM_{1} (\u00b5g/m^3)",
8
8
  "PM25": "PM_{2.5} (\u00b5g/m^3)",
9
+ "PM2.5": "PM_{2.5} (\u00b5g/m^3)",
10
+ "PM10": "PM_{10} (\u00b5g/m^3)",
9
11
  "SIA": "SIA (\u00b5g/m^3)",
10
12
  "POC": "POC (\u00b5g/m^3)",
11
13
  "SOC": "SOC (\u00b5g/m^3)",
@@ -20,12 +22,15 @@
20
22
  "Babs": "Mie Amb Absorption (1/Mm)",
21
23
  "Babs_dry": "Mie Dry Absorption (1/Mm)",
22
24
  "Absorption": "Absorption (1/Mm)",
25
+ "abs": "Absorption (1/Mm)",
23
26
  "Bext": "Mie Amb Extinction (1/Mm)",
24
27
  "Bext_dry": "Mie Dry Extinction (1/Mm)",
25
28
  "Extinction": "Extinction (1/Mm)",
29
+ "ext": "Extinction (1/Mm)",
26
30
  "Bsca": "Mie Amb Scattering (1/Mm)",
27
31
  "Bsca_dry": "Mie Dry Scattering (1/Mm)",
28
32
  "Scattering": "Scattering (1/Mm)",
33
+ "sca": "Scattering (1/Mm)",
29
34
  "Diurnal": "Hour",
30
35
  "PBLH": "PBLH (m)",
31
36
  "VC": "VC (m²/s)",
AeroViz/plot/violin.py ADDED
@@ -0,0 +1,80 @@
1
+ import matplotlib.pyplot as plt
2
+ import numpy as np
3
+ import pandas as pd
4
+ import seaborn as sns
5
+ from matplotlib.pyplot import Figure, Axes
6
+ from pandas import DataFrame
7
+
8
+ from AeroViz.plot.utils import *
9
+
10
+ __all__ = ['violin']
11
+
12
+
13
+ @set_figure(fw='bold')
14
+ def violin(df: DataFrame | dict,
15
+ unit: str,
16
+ ax: Axes | None = None,
17
+ **kwargs
18
+ ) -> tuple[Figure, Axes]:
19
+ """
20
+ Generate a violin plot for multiple data sets.
21
+
22
+ Parameters
23
+ ----------
24
+ df : pd.DataFrame or dict
25
+ A mapping from category names to pandas DataFrames containing the data.
26
+ unit : str
27
+ The unit for the data being plotted.
28
+ ax : matplotlib.axes.Axes, optional
29
+ The Axes object to draw the plot onto. If not provided, a new figure will be created.
30
+ **kwargs : dict
31
+ Additional keyword arguments to be passed to the violinplot function.
32
+
33
+ Returns
34
+ -------
35
+ fig : Figure
36
+ The matplotlib Figure object.
37
+ ax : Axes
38
+ The matplotlib Axes object with the scatter plot.
39
+
40
+ """
41
+ fig, ax = plt.subplots(**kwargs.get('fig_kws', {})) if ax is None else (ax.get_figure(), ax)
42
+
43
+ data = df.to_numpy()
44
+
45
+ data = data[~np.isnan(data).any(axis=1)]
46
+
47
+ grps = data.shape[1]
48
+
49
+ width = 0.6
50
+ block = width / 2
51
+ x_position = np.arange(grps)
52
+
53
+ plt.boxplot(data, positions=x_position, widths=0.15,
54
+ showfliers=False, showmeans=True, meanline=False, patch_artist=True,
55
+ capprops=dict(linewidth=0),
56
+ whiskerprops=dict(linewidth=1.5, color='k', alpha=1),
57
+ boxprops=dict(linewidth=1.5, color='k', facecolor='#4778D3', alpha=1),
58
+ meanprops=dict(marker='o', markeredgecolor='black', markerfacecolor='white', markersize=6),
59
+ medianprops=dict(linewidth=1.5, ls='-', color='k', alpha=1))
60
+
61
+ sns.violinplot(data=data, density_norm='area', color='#4778D3', inner=None)
62
+
63
+ for violin, alpha in zip(ax.collections[:], [0.5] * len(ax.collections[:])):
64
+ violin.set_alpha(alpha)
65
+ violin.set_edgecolor(None)
66
+
67
+ plt.scatter(x_position, data.mean(), marker='o', facecolor='white', edgecolor='k', s=10)
68
+
69
+ xlim = kwargs.get('xlim') or (x_position[0] - (width / 2 + block), x_position[-1] + (width / 2 + block))
70
+ ylim = kwargs.get('ylim') or (0, None)
71
+ xlabel = kwargs.get('xlabel') or ''
72
+ ylabel = kwargs.get('ylabel') or Unit(unit)
73
+ xticks = kwargs.get('xticks') or [x.replace('-', '\n') for x in list(df.keys())]
74
+
75
+ ax.set(xlim=xlim, ylim=ylim, xlabel=xlabel, ylabel=ylabel, title=kwargs.get('title'))
76
+ ax.set_xticks(x_position, xticks, fontweight='bold', fontsize=12)
77
+
78
+ plt.show()
79
+
80
+ return fig, ax
@@ -3,29 +3,29 @@ from pathlib import Path
3
3
  from pandas import read_csv, concat
4
4
 
5
5
  from AeroViz.process.script import (ImpactProc, ImproveProc, ChemicalProc, ParticleSizeDistProc,
6
- ExtinctionDistProc, OthersProc)
6
+ ExtinctionDistProc, OthersProc)
7
7
 
8
- __all__ = ['DataProcess']
8
+ __all__ = ['DataProcessor', 'ImpactProc', 'ImproveProc', 'ChemicalProc', 'ParticleSizeDistProc', 'ExtinctionDistProc', ]
9
9
 
10
10
 
11
- class DataProcess:
12
- def __new__(cls, file_path, reset: bool = False, save_file: Path | str = 'All_data.csv'):
13
- file_path = Path(file_path)
11
+ class DataProcessor:
12
+ def __new__(cls, file_path, reset: bool = False, save_file: Path | str = 'All_data.csv'):
13
+ file_path = Path(file_path)
14
14
 
15
- print(f'\t\t \033[96m --- Processing Data --- \033[0m')
15
+ print(f'\t\t \033[96m --- Processing Data --- \033[0m')
16
16
 
17
- if file_path.exists() and not reset:
18
- return read_csv(file_path, parse_dates=['Time'], index_col='Time',
19
- na_values=('-', 'E', 'F'), low_memory=False)
17
+ if file_path.exists() and not reset:
18
+ return read_csv(file_path, parse_dates=['Time'], index_col='Time',
19
+ na_values=('-', 'E', 'F'), low_memory=False)
20
20
 
21
- processor = [ImpactProc, ChemicalProc, ImproveProc, ParticleSizeDistProc, ExtinctionDistProc, OthersProc]
22
- reset = [False, False, False, False, False, False]
23
- save_filename = ['IMPACT.csv', 'chemical.csv', 'revised_IMPROVE.csv', 'PSD.csv', 'PESD.csv', 'Others.csv']
21
+ processor = [ImpactProc, ChemicalProc, ImproveProc, ParticleSizeDistProc, ExtinctionDistProc, OthersProc]
22
+ reset = [False, False, False, False, False, False]
23
+ save_filename = ['IMPACT.csv', 'chemical.csv', 'revised_IMPROVE.csv', 'PSD.csv', 'PESD.csv', 'Others.csv']
24
24
 
25
- _df = concat([processor().process_data(reset, save_filename) for processor, reset, save_filename in
26
- zip(processor, reset, save_filename)], axis=1)
25
+ _df = concat([processor().process_data(reset, save_filename) for processor, reset, save_filename in
26
+ zip(processor, reset, save_filename)], axis=1)
27
27
 
28
- # 7. save result
29
- _df.to_csv(file_path)
28
+ # 7. save result
29
+ _df.to_csv(file_path)
30
30
 
31
- return _df
31
+ return _df
@@ -7,13 +7,13 @@ __all__ = ['DataProc']
7
7
 
8
8
 
9
9
  class DataProc(ABC):
10
- def __init__(self):
11
- pass
10
+ def __init__(self):
11
+ pass
12
12
 
13
- @abstractmethod
14
- def process_data(self,
15
- reset: bool = False,
16
- save_filename: str | Path = None
17
- ) -> DataFrame:
18
- """ Implementation of processing data """
19
- pass
13
+ @abstractmethod
14
+ def process_data(self,
15
+ reset: bool = False,
16
+ save_filename: str | Path = None
17
+ ) -> DataFrame:
18
+ """ Implementation of processing data """
19
+ pass