AeroViz 0.1.5__py3-none-any.whl → 0.1.7__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 (59) hide show
  1. AeroViz/dataProcess/Chemistry/_mass_volume.py +4 -3
  2. AeroViz/dataProcess/Chemistry/_ocec.py +20 -7
  3. AeroViz/dataProcess/Optical/_IMPROVE.py +2 -3
  4. AeroViz/dataProcess/SizeDistr/__init__.py +6 -10
  5. AeroViz/plot/__init__.py +1 -0
  6. AeroViz/plot/meteorology/meteorology.py +2 -0
  7. AeroViz/plot/optical/optical.py +1 -1
  8. AeroViz/plot/pie.py +14 -2
  9. AeroViz/plot/radar.py +184 -0
  10. AeroViz/plot/scatter.py +16 -7
  11. AeroViz/plot/templates/koschmieder.py +11 -8
  12. AeroViz/plot/timeseries/timeseries.py +0 -1
  13. AeroViz/rawDataReader/__init__.py +75 -70
  14. AeroViz/rawDataReader/config/supported_instruments.py +70 -38
  15. AeroViz/rawDataReader/core/__init__.py +208 -178
  16. AeroViz/rawDataReader/script/AE33.py +1 -1
  17. AeroViz/rawDataReader/script/AE43.py +1 -1
  18. AeroViz/rawDataReader/script/APS_3321.py +2 -2
  19. AeroViz/rawDataReader/script/Aurora.py +1 -1
  20. AeroViz/rawDataReader/script/BC1054.py +1 -1
  21. AeroViz/rawDataReader/script/EPA.py +39 -0
  22. AeroViz/rawDataReader/script/GRIMM.py +1 -1
  23. AeroViz/rawDataReader/script/IGAC.py +6 -23
  24. AeroViz/rawDataReader/script/MA350.py +1 -1
  25. AeroViz/rawDataReader/script/Minion.py +102 -30
  26. AeroViz/rawDataReader/script/NEPH.py +1 -1
  27. AeroViz/rawDataReader/script/{Sunset_OCEC.py → OCEC.py} +2 -2
  28. AeroViz/rawDataReader/script/SMPS.py +77 -0
  29. AeroViz/rawDataReader/script/TEOM.py +2 -2
  30. AeroViz/rawDataReader/script/VOC.py +2 -2
  31. AeroViz/rawDataReader/script/XRF.py +11 -0
  32. AeroViz/rawDataReader/script/__init__.py +4 -6
  33. {AeroViz-0.1.5.dist-info → AeroViz-0.1.7.dist-info}/METADATA +57 -32
  34. {AeroViz-0.1.5.dist-info → AeroViz-0.1.7.dist-info}/RECORD +37 -55
  35. AeroViz/process/__init__.py +0 -31
  36. AeroViz/process/core/DataProc.py +0 -19
  37. AeroViz/process/core/SizeDist.py +0 -90
  38. AeroViz/process/core/__init__.py +0 -4
  39. AeroViz/process/method/PyMieScatt_update.py +0 -567
  40. AeroViz/process/method/__init__.py +0 -2
  41. AeroViz/process/method/mie_theory.py +0 -260
  42. AeroViz/process/method/prop.py +0 -62
  43. AeroViz/process/script/AbstractDistCalc.py +0 -143
  44. AeroViz/process/script/Chemical.py +0 -177
  45. AeroViz/process/script/IMPACT.py +0 -49
  46. AeroViz/process/script/IMPROVE.py +0 -161
  47. AeroViz/process/script/Others.py +0 -65
  48. AeroViz/process/script/PSD.py +0 -103
  49. AeroViz/process/script/PSD_dry.py +0 -93
  50. AeroViz/process/script/__init__.py +0 -5
  51. AeroViz/process/script/retrieve_RI.py +0 -69
  52. AeroViz/rawDataReader/script/EPA_vertical.py +0 -46
  53. AeroViz/rawDataReader/script/SMPS_TH.py +0 -41
  54. AeroViz/rawDataReader/script/SMPS_aim11.py +0 -51
  55. AeroViz/rawDataReader/script/SMPS_genr.py +0 -51
  56. AeroViz/rawDataReader/script/Table.py +0 -27
  57. {AeroViz-0.1.5.dist-info → AeroViz-0.1.7.dist-info}/LICENSE +0 -0
  58. {AeroViz-0.1.5.dist-info → AeroViz-0.1.7.dist-info}/WHEEL +0 -0
  59. {AeroViz-0.1.5.dist-info → AeroViz-0.1.7.dist-info}/top_level.txt +0 -0
@@ -1,38 +1,78 @@
1
+ from typing import Literal
2
+
1
3
  import numpy as np
2
- from pandas import read_csv, to_datetime, to_numeric
4
+ import pandas
5
+ from pandas import read_excel, to_numeric
3
6
 
4
7
  from AeroViz.rawDataReader.core import AbstractReader
5
8
 
9
+ pandas.set_option("future.no_silent_downcasting", True)
10
+
11
+ desired_order1 = ['SO2', 'NO', 'NOx', 'NO2', 'CO', 'O3', 'THC', 'NMHC',
12
+ 'CH4', 'PM10', 'PM2.5', 'WS', 'WD', 'AT', 'RH']
13
+
14
+ desired_order2 = ['Benzene', 'Toluene', 'EthylBenzene', 'm/p-Xylene', 'o-Xylene']
15
+
16
+ desired_order3 = ['Al', 'Si', 'P', 'S', 'Cl', 'K', 'Ca', 'Ti', 'V', 'Cr', 'Mn', 'Fe',
17
+ 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Rb', 'Sr',
18
+ 'Y', 'Zr', 'Nb', 'Mo', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te',
19
+ 'Cs', 'Ba', 'La', 'Ce', 'W', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi']
20
+
21
+ desired_order4 = ['NH3', 'HF', 'HCl', 'HNO2', 'HNO3', 'G-SO2',
22
+ 'Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+',
23
+ 'F-', 'Cl-', 'NO2-', 'NO3-', 'PO43-', 'SO42-']
24
+
6
25
 
7
26
  class Reader(AbstractReader):
8
27
  nam = 'Minion'
9
28
 
10
29
  def _raw_reader(self, file):
11
- with file.open('r', encoding='utf-8-sig', errors='ignore') as f:
12
- _df = read_csv(f, low_memory=False, index_col=0)
30
+ # 讀取 Excel 文件
31
+ df = read_excel(file, index_col=0, parse_dates=True)
32
+
33
+ # 重命名列,去除空白
34
+ df = df.rename(columns=lambda x: x.strip())
35
+
36
+ # 保存單位行並給它一個名稱
37
+ units = df.iloc[0].copy()
13
38
 
14
- _df.index = to_datetime(_df.index, errors='coerce')
15
- _df.index.name = 'time'
39
+ # 刪除原始數據中的單位行
40
+ df = df.iloc[1:]
16
41
 
17
- _df.columns = _df.keys().str.strip(' ')
42
+ # 替換特定值
43
+ df = df.replace({'維護校正': '*', np.nan: '-', '0L': '_', 'Nodata': '-'}, inplace=False)
44
+ df = df.replace(to_replace=r'\d*[#]\b', value='#', regex=True)
45
+ df = df.replace(to_replace=r'\d*[L]\b', value='_', regex=True)
18
46
 
19
- return _df.loc[~_df.index.duplicated() & _df.index.notna()]
47
+ # 處理除了'WD'列的 0
48
+ non_wd_columns = [col for col in df.columns if col != 'WD']
49
+ df.loc[:, non_wd_columns] = df.loc[:, non_wd_columns].replace({0: '_'})
50
+
51
+ # 重新排序列
52
+ df = self.reorder_dataframe_columns(df, [desired_order1, desired_order2, desired_order3, desired_order4])
53
+
54
+ # 將單位行添加回 DataFrame
55
+ # df = concat([units.to_frame().T, df])
56
+
57
+ df.index.name = 'Time'
58
+
59
+ return df.loc[~df.index.duplicated() & df.index.notna()]
20
60
 
21
61
  def _QC(self, _df):
62
+ # remove negative value
63
+ _df = _df.mask((_df < 0).copy())
64
+
22
65
  # XRF QAQC
23
66
  _df = self.XRF_QAQC(_df)
24
67
 
25
68
  # ions balance
26
- _df = self.ions_balance(_df)
27
-
28
- # remove negative value
29
- _df = _df.mask((_df < 0).copy())
69
+ _df = self.IGAC_QAQC(_df)
30
70
 
31
71
  # QC data in 6h
32
- return _df.resample('6h').apply(self.basic_QC).resample(self.meta.get("freq")).mean()
72
+ return _df.resample('6h').apply(self.n_sigma_QC).resample(self.meta.get("freq")).mean()
33
73
 
34
74
  # base on Xact 625i Minimum Decision Limit (MDL) for XRF in ng/m3, 60 min sample time
35
- def XRF_QAQC(self, df):
75
+ def XRF_QAQC(self, df, MDL_replace: Literal['nan', '0.5 * MDL'] = 'nan'):
36
76
  MDL = {
37
77
  'Al': 100, 'Si': 18, 'P': 5.2, 'S': 3.2,
38
78
  'Cl': 1.7, 'K': 1.2, 'Ca': 0.3, 'Ti': 1.6,
@@ -40,34 +80,68 @@ class Reader(AbstractReader):
40
80
  'Co': 0.14, 'Ni': 0.096, 'Cu': 0.079, 'Zn': 0.067,
41
81
  'Ga': 0.059, 'Ge': 0.056, 'As': 0.063, 'Se': 0.081,
42
82
  'Br': 0.1, 'Rb': 0.19, 'Sr': 0.22, 'Y': 0.28,
43
- 'Zr': 0.33, 'Nb': 0.41, 'Mo': 0.48, 'Ag': 1.9,
44
- 'Cd': 2.5, 'In': 3.1, 'Sn': 4.1, 'Sb': 5.2,
45
- 'Te': 0.6, 'I': 0.49, 'Cs': 0.37, 'Ba': 0.39,
46
- 'La': 0.36, 'Ce': 0.3, 'Pt': 0.12, 'Au': 0.1,
47
- 'Hg': 0.12, 'Tl': 0.12, 'Pb': 0.13, 'Bi': 0.13
83
+ 'Zr': 0.33, 'Nb': 0.41, 'Mo': 0.48, 'Pd': 2.2,
84
+ 'Ag': 1.9, 'Cd': 2.5, 'In': 3.1, 'Sn': 4.1,
85
+ 'Sb': 5.2, 'Te': 0.6, 'Cs': 0.37, 'Ba': 0.39,
86
+ 'La': 0.36, 'Ce': 0.3, 'W': 0.0001, 'Pt': 0.12,
87
+ 'Au': 0.1, 'Hg': 0.12, 'Tl': 0.12, 'Pb': 0.13,
88
+ 'Bi': 0.13
48
89
  }
49
- # 將小於 MDL 值的數據替換為 NaN
90
+ # 將小於 MDL 值的數據替換為 nan or 5/6 MDL
50
91
  for element, threshold in MDL.items():
51
92
  if element in df.columns:
52
- df[element] = df[element].where(df[element] >= threshold, np.nan)
93
+ rep = np.nan if MDL_replace == 'nan' else 0.5 * threshold
94
+ df[element] = df[element].where(df[element] >= threshold, rep)
53
95
 
54
96
  self.logger.info(f"{'=' * 60}")
55
97
  self.logger.info(f"XRF QAQC summary:")
56
98
  self.logger.info("\t\ttransform values below MDL to NaN")
57
99
  self.logger.info(f"{'=' * 60}")
58
100
 
101
+ # 轉換單位 ng/m3 -> ug/m3
102
+ if df.Al.max() > 10 and df.Fe.max() > 10:
103
+ # 確保 MDL.keys() 中的所有列都存在於 _df 中
104
+ columns_to_convert = [col for col in MDL.keys() if col in df.columns]
105
+
106
+ df[columns_to_convert] = df[columns_to_convert].div(1000)
107
+
59
108
  return df
60
109
 
61
- def ions_balance(self, df, tolerance=0.3):
110
+ def IGAC_QAQC(self, df, tolerance=1):
62
111
  """
63
112
  Calculate the balance of ions in the system
64
113
  """
114
+ # https://www.yangyao-env.com/web/product/product_in2.jsp?pd_id=PD1640151884502
115
+ MDL = {
116
+ 'HF': 0.08, 'HCl': 0.05, 'HNO2': 0.01, 'HNO3': 0.05, 'G-SO2': 0.05, 'NH3': 0.1,
117
+ 'Na+': 0.05, 'NH4+': 0.08, 'K+': 0.08, 'Mg2+': 0.05, 'Ca2+': 0.05,
118
+ 'F-': 0.08, 'Cl-': 0.05, 'NO2-': 0.05, 'NO3-': 0.01, 'PO43-': None, 'SO42-': 0.05,
119
+ }
120
+
121
+ MR = {
122
+ 'HF': 200, 'HCl': 200, 'HNO2': 200, 'HNO3': 200, 'G-SO2': 200, 'NH3': 300,
123
+ 'Na+': 300, 'NH4+': 300, 'K+': 300, 'Mg2+': 300, 'Ca2+': 300,
124
+ 'F-': 300, 'Cl-': 300, 'NO2-': 300, 'NO3-': 300, 'PO43-': None, 'SO42-': 300,
125
+ }
126
+
127
+ _cation, _anion, _main = (['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+'],
128
+ ['Cl-', 'NO2-', 'NO3-', 'SO42-'],
129
+ ['SO42-', 'NO3-', 'NH4+'])
130
+ # QC: replace values below MDL with 0.5 * MDL -> ions balance -> PM2.5 > main salt
131
+ # mass tolerance = 0.3, ions balance tolerance = 0.3
132
+
133
+ # # conc. of main salt should be present at the same time (NH4+, SO42-, NO3-)
134
+ # _df_salt = df.mask(df.sum(axis=1, min_count=1) > df.PM25).dropna(subset=_main).copy()
135
+
65
136
  # Define the ions
66
- item = ['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+', 'F-', 'Cl-', 'NO2-', 'NO3-', 'PO43-', 'SO42-']
137
+ item = ['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+', 'Cl-', 'NO2-', 'NO3-', 'SO42-']
67
138
 
68
139
  # Calculate the balance
69
- _df = df[item].copy()
70
- _df = _df.apply(lambda x: to_numeric(x, errors='coerce'))
140
+ _df = df[item].apply(lambda x: to_numeric(x, errors='coerce'))
141
+
142
+ # for (_key, _df_col) in _df.items():
143
+ # _df[_key] = _df_col.mask(_df_col < MDL[_key], MDL[_key] / 2)
144
+
71
145
  _df['+_mole'] = _df[['Na+', 'NH4+', 'K+', 'Mg2+', 'Ca2+']].div([23, 18, 39, (24 / 2), (40 / 2)]).sum(axis=1,
72
146
  skipna=True)
73
147
  _df['-_mole'] = _df[['Cl-', 'NO2-', 'NO3-', 'SO42-']].div([35.5, 46, 62, (96 / 2)]).sum(axis=1, skipna=True)
@@ -79,12 +153,8 @@ class Reader(AbstractReader):
79
153
  lower_bound, upper_bound = 1 - tolerance, 1 + tolerance
80
154
 
81
155
  # 根据ratio决定是否保留原始数据
82
- valid_mask = (
83
- (_df['ratio'] <= upper_bound) &
84
- (_df['ratio'] >= lower_bound) &
85
- ~np.isnan(_df['+_mole']) &
86
- ~np.isnan(_df['-_mole'])
87
- )
156
+ valid_mask = ((_df['ratio'] <= upper_bound) & (_df['ratio'] >= lower_bound) &
157
+ ~np.isnan(_df['+_mole']) & ~np.isnan(_df['-_mole']))
88
158
 
89
159
  # 保留数据或将不符合条件的行设为NaN
90
160
  df.loc[~valid_mask, item] = np.nan
@@ -100,4 +170,6 @@ class Reader(AbstractReader):
100
170
  if retained_percentage < 70:
101
171
  self.logger.warning("Warning: The percentage of retained data is less than 70%")
102
172
 
173
+ # print(f"\tretain {retained_percentage.__round__(0)}% data within tolerance {tolerance}")
174
+
103
175
  return df
@@ -67,4 +67,4 @@ class Reader(AbstractReader):
67
67
  _df = _df[(_df['BB'] < _df['B']) & (_df['BG'] < _df['G']) & (_df['BR'] < _df['R'])]
68
68
 
69
69
  # QC data in 1h
70
- return _df.resample('1h').apply(self.basic_QC).resample(self.meta.get("freq")).mean()
70
+ return _df.resample('1h').apply(self.n_sigma_QC).resample(self.meta.get("freq")).mean()
@@ -4,7 +4,7 @@ from AeroViz.rawDataReader.core import AbstractReader
4
4
 
5
5
 
6
6
  class Reader(AbstractReader):
7
- nam = 'Sunset_OCEC'
7
+ nam = 'OCEC'
8
8
 
9
9
  def _raw_reader(self, file):
10
10
  with open(file, 'r', encoding='utf-8', errors='ignore') as f:
@@ -51,7 +51,7 @@ class Reader(AbstractReader):
51
51
  def _QC(self, _df):
52
52
  import numpy as np
53
53
 
54
- _df = _df.where(_df > 0)
54
+ _df = _df.mask((_df <= 0) | (_df > 100)).copy()
55
55
 
56
56
  thresholds = {
57
57
  'Thermal_OC': 0.3,
@@ -0,0 +1,77 @@
1
+ import csv
2
+
3
+ import numpy as np
4
+ from pandas import to_datetime, to_numeric, read_csv, isna
5
+
6
+ from AeroViz.rawDataReader.core import AbstractReader
7
+
8
+
9
+ def find_header_row(file_obj, delimiter):
10
+ csv_reader = csv.reader(file_obj, delimiter=delimiter)
11
+ for skip, row in enumerate(csv_reader):
12
+ if row and (row[0] in ['Sample #', 'Scan Number']):
13
+ return skip
14
+ raise ValueError("Header row not found")
15
+
16
+
17
+ def parse_date(df, date_format):
18
+ if 'Date' in df.columns and 'Start Time' in df.columns:
19
+ return to_datetime(df['Date'] + ' ' + df['Start Time'], format=date_format, errors='coerce')
20
+ elif 'DateTime Sample Start' in df.columns:
21
+ return to_datetime(df['DateTime Sample Start'], format=date_format, errors='coerce')
22
+ else:
23
+ raise ValueError("Expected date columns not found")
24
+
25
+
26
+ class Reader(AbstractReader):
27
+ nam = 'SMPS'
28
+
29
+ def _raw_reader(self, file):
30
+ with open(file, 'r', encoding='utf-8', errors='ignore') as f:
31
+ if file.suffix.lower() == '.txt':
32
+ delimiter, date_formats = '\t', ['%m/%d/%y %X', '%m/%d/%Y %X']
33
+ else: # csv
34
+ delimiter, date_formats = ',', ['%d/%m/%Y %X']
35
+
36
+ skip = find_header_row(f, delimiter)
37
+ f.seek(0)
38
+
39
+ _df = read_csv(f, sep=delimiter, skiprows=skip)
40
+
41
+ for date_format in date_formats:
42
+ _time_index = parse_date(_df, date_format)
43
+ if not isna(_time_index).all():
44
+ break
45
+ else:
46
+ raise ValueError("Unable to parse dates with given formats")
47
+
48
+ # sequence the data
49
+ numeric_cols = [col for col in _df.columns if col.strip().replace('.', '').isdigit()]
50
+ numeric_cols.sort(key=lambda x: float(x.strip()))
51
+
52
+ _df.index = _time_index
53
+ _df.index.name = 'time'
54
+
55
+ _df_smps = _df[numeric_cols]
56
+ _df_smps.columns = _df_smps.columns.astype(float)
57
+ _df_smps = _df_smps.loc[_df_smps.index.dropna().copy()]
58
+
59
+ return _df_smps.apply(to_numeric, errors='coerce')
60
+
61
+ # QC data
62
+ def _QC(self, _df):
63
+
64
+ # mask out the data size lower than 7
65
+ _df['total'] = _df.sum(axis=1, min_count=1) * (np.diff(np.log(_df.keys().to_numpy(float)))).mean()
66
+ _df_size = _df['total'].dropna().resample('1h').size().resample(_df.index.freq).ffill()
67
+ _df = _df.mask(_df_size < 7)
68
+
69
+ # remove total conc. lower than 2000
70
+ _df = _df.mask(_df['total'] < 2000)
71
+
72
+ # remove the bin over 400 nm which num. conc. larger than 4000
73
+ _df_remv_ky = _df.keys()[:-2][_df.keys()[:-2] >= 400.]
74
+
75
+ _df[_df_remv_ky] = _df[_df_remv_ky].copy().mask(_df[_df_remv_ky] > 4000.)
76
+
77
+ return _df[_df.keys()[:-1]]
@@ -25,9 +25,9 @@ class Reader(AbstractReader):
25
25
 
26
26
  _df = _df.set_index(to_datetime(_tm_idx, errors='coerce', format='%d - %m - %Y %X'))
27
27
 
28
- _df = _df.where(_df['status'] < 1e-7)
28
+ _df = _df.where(_df['status'] < 1)
29
29
 
30
- _df = _df[['PM_NV', 'PM_Total', 'noise', ]]
30
+ _df = _df[['PM_NV', 'PM_Total', 'noise']]
31
31
 
32
32
  return _df.loc[~_df.index.duplicated() & _df.index.notna()]
33
33
 
@@ -20,8 +20,8 @@ class Reader(AbstractReader):
20
20
 
21
21
  if invalid_keys:
22
22
  self.logger.warning(f'{invalid_keys} are not supported keys.')
23
- print(f'\n\t\t{invalid_keys} are not supported keys.'
24
- f'\n\t\tPlease check the\033[91m support_voc.md\033[0m file to use the correct name.')
23
+ print(f'\n\t{invalid_keys} are not supported keys.'
24
+ f'\n\tPlease check the\033[91m support_voc.md\033[0m file to use the correct name.')
25
25
 
26
26
  if valid_keys:
27
27
  return _df[valid_keys].loc[~_df.index.duplicated() & _df.index.notna()]
@@ -0,0 +1,11 @@
1
+ from AeroViz.rawDataReader.core import AbstractReader
2
+
3
+
4
+ class Reader(AbstractReader):
5
+ nam = 'XRF'
6
+
7
+ def _raw_reader(self, file):
8
+ pass
9
+
10
+ def _QC(self, _df):
11
+ pass
@@ -1,9 +1,7 @@
1
1
  __all__ = [
2
2
  'NEPH',
3
3
  'Aurora',
4
- 'SMPS_TH',
5
- 'SMPS_genr',
6
- 'SMPS_aim11',
4
+ 'SMPS',
7
5
  'APS_3321',
8
6
  'GRIMM',
9
7
  'AE33',
@@ -11,10 +9,10 @@ __all__ = [
11
9
  'BC1054',
12
10
  'MA350',
13
11
  'TEOM',
14
- 'Sunset_OCEC',
12
+ 'OCEC',
15
13
  'IGAC',
14
+ 'XRF',
16
15
  'VOC',
17
- 'Table',
18
- 'EPA_vertical',
16
+ 'EPA',
19
17
  'Minion'
20
18
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: AeroViz
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Aerosol science
5
5
  Home-page: https://github.com/Alex870521/AeroViz
6
6
  Author: alex
@@ -11,14 +11,15 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.12
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: pandas
15
- Requires-Dist: numpy
16
- Requires-Dist: matplotlib
17
- Requires-Dist: seaborn
18
- Requires-Dist: scipy
19
- Requires-Dist: scikit-learn
20
- Requires-Dist: windrose
21
- Requires-Dist: tabulate
14
+ Requires-Dist: pandas ==2.2.2
15
+ Requires-Dist: numpy ==1.26.4
16
+ Requires-Dist: matplotlib ==3.8.4
17
+ Requires-Dist: scipy ==1.14.0
18
+ Requires-Dist: seaborn ==0.13.2
19
+ Requires-Dist: scikit-learn ==1.5.1
20
+ Requires-Dist: windrose ==1.9.2
21
+ Requires-Dist: tabulate ==0.9.0
22
+ Requires-Dist: rich ~=13.7.1
22
23
 
23
24
  ## <div align="center">AeroViz for Aerosol Science Visualization</div>
24
25
 
@@ -27,7 +28,7 @@ Requires-Dist: tabulate
27
28
  <img alt="Static Badge" src="https://img.shields.io/badge/python-3.12-blue?logo=python">
28
29
  <img alt="Static Badge" src="https://img.shields.io/badge/License-MIT-yellow">
29
30
  <img alt="Static Badge" src="https://img.shields.io/badge/github-updating-red?logo=github">
30
- <img src="https://img.shields.io/badge/testing-green?logo=Pytest&logoColor=blue">
31
+ <img alt="Static Badge" src="https://img.shields.io/badge/testing-green?logo=Pytest&logoColor=blue">
31
32
 
32
33
  </p>
33
34
 
@@ -52,42 +53,66 @@ Requires-Dist: tabulate
52
53
  ## <div align="center">Installation</div>
53
54
 
54
55
  ```bash
55
- pip install AeroViz # ensure the python version is >= 3.12
56
+ pip install AeroViz
56
57
  ```
57
58
 
58
- ## <div align="center">Usage</div>
59
+ ## <div align="center">Quick Start</div>
59
60
 
60
61
  ```python
61
62
  import AeroViz
63
+ from AeroViz import RawDataReader, DataProcess, plot
64
+
65
+ # Read data from a supported instrument
66
+ data = RawDataReader('NEPH', '/path/to/data', start='2024-01-01', end='2024-01-31')
67
+
68
+ # Create a visualization
69
+ plot.timeseries(data, y='scattering_coefficient')
62
70
  ```
63
71
 
64
- ## <div align="center">RawDataReader Supported Instruments</div>
72
+ For more detailed usage instructions, please refer to our [User Guide]().
73
+
74
+ ## RawDataReader
75
+
76
+ RawDataReader supports a wide range of aerosol instruments, including NEPH, SMPS, AE33, and many more. It handles
77
+ various file types and time resolutions, making data processing efficient and standardized.
78
+
79
+ For a detailed list of supported instruments, file types, and data columns, please refer to
80
+ our [RawDataReader Usage Guide](docs/RawDataReader_Usage_Guide.md) in the `docs` folder.
81
+
82
+ ### Key Features:
83
+
84
+ - Supports multiple aerosol instruments
85
+ - Applies customizable quality control measures
86
+ - Offers flexible data filtering and resampling options
87
+ - Enables easy data export to CSV format
65
88
 
66
- > [!NOTE]\
67
- > We are continuously working to support more instruments. Please check back for updates or contribute to our project on
68
- > GitHub.
89
+ ### Supported Instruments
69
90
 
70
91
  The AeroViz project currently supports data from the following instruments:
71
92
 
72
- - **SMPS (Scanning Mobility Particle Sizer)**
73
- - **APS (Aerodynamic Particle Sizer)**
74
- - **GRIMM (GRIMM Aerosol Technik)**
75
- - **TEOM (Continuous Ambient Particulate Monitor)**
76
- - **NEPH (Nephelometer)**
77
- - **Aurora (Nephelometer)**
78
- - **AE33 (Aethalometer Model 33)**
79
- - **AE43 (Aethalometer Model 43)**
80
- - **BC1054 (Black Carbon Monitor 1054)**
81
- - **MA350 (MicroAeth MA350)**
82
- - **OCEC (Organic Carbon Elemental Carbon Analyzer)**
83
- - **IGAC (In-situ Gas and Aerosol Compositions monitor)**
84
- - **VOC (Volatile Organic Compounds Monitor)**
93
+ - SMPS (Scanning Mobility Particle Sizer)
94
+ - APS (Aerodynamic Particle Sizer)
95
+ - GRIMM (GRIMM Aerosol Technik)
96
+ - TEOM (Continuous Ambient Particulate Monitor)
97
+ - NEPH (Nephelometer)
98
+ - Aurora (Nephelometer)
99
+ - AE33 (Aethalometer Model 33)
100
+ - AE43 (Aethalometer Model 43)
101
+ - BC1054 (Black Carbon Monitor 1054)
102
+ - MA350 (MicroAeth MA350)
103
+ - OCEC (Organic Carbon Elemental Carbon Analyzer)
104
+ - IGAC (In-situ Gas and Aerosol Compositions monitor)
105
+ - XRF (X-ray Fluorescence Spectrometer)
106
+ - VOC (Volatile Organic Compounds Monitor)
107
+
108
+ > **Note:** We are continuously working to support more instruments. Please check back for updates or contribute to our
109
+ > project on GitHub.
85
110
 
86
111
  ## <div align="center">DataProcess Supported Method</div>
87
112
 
88
113
  The AeroViz project currently supports the following processing methods:
89
114
 
90
- - **Chemistry**
115
+ - **Chemistry**:
91
116
  - **Optical**
92
117
  - **SizeDistr**
93
118
  - **VOC**
@@ -100,7 +125,7 @@ For detailed documentation, please refer to the `docs` folder, which includes:
100
125
 
101
126
  | Documentation | Description |
102
127
  |--------------------------------------------|----------------------------|
103
- | [User Guide](docs/user_guide.md) | Basic usage instructions |
128
+ | [User Guide](docs/user_guide) | Basic usage instructions |
104
129
  | [Developer Guide](docs/developer_guide.md) | Developer guidelines |
105
130
  | [API Reference](docs/api_reference.md) | API documentation |
106
131
  | [FAQ](docs/faq.md) | Frequently Asked Questions |
@@ -108,7 +133,7 @@ For detailed documentation, please refer to the `docs` folder, which includes:
108
133
 
109
134
  </div>
110
135
 
111
- ## <div align="center">Related Dependencies</div>
136
+ ## <div align="center">Related Source</div>
112
137
 
113
138
  * #### [PyMieScatt](https://github.com/bsumlin/PyMieScatt.git)
114
139
  * #### [py-smps](https://github.com/quant-aq/py-smps.git)