AeroViz 0.1.5__tar.gz → 0.1.6__tar.gz

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 (124) hide show
  1. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_mass_volume.py +4 -3
  2. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_IMPROVE.py +2 -3
  3. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/__init__.py +6 -10
  4. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/__init__.py +3 -5
  5. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/config/supported_instruments.py +30 -31
  6. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/core/__init__.py +108 -103
  7. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/APS_3321.py +2 -2
  8. aeroviz-0.1.5/AeroViz/rawDataReader/script/Sunset_OCEC.py → aeroviz-0.1.6/AeroViz/rawDataReader/script/OCEC.py +1 -1
  9. aeroviz-0.1.6/AeroViz/rawDataReader/script/SMPS.py +76 -0
  10. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/VOC.py +2 -2
  11. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/__init__.py +2 -4
  12. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz.egg-info/PKG-INFO +13 -10
  13. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz.egg-info/SOURCES.txt +2 -4
  14. aeroviz-0.1.6/AeroViz.egg-info/requires.txt +9 -0
  15. {aeroviz-0.1.5 → aeroviz-0.1.6}/PKG-INFO +13 -10
  16. {aeroviz-0.1.5 → aeroviz-0.1.6}/README.md +3 -1
  17. {aeroviz-0.1.5 → aeroviz-0.1.6}/requirements.txt +2 -1
  18. aeroviz-0.1.6/setup.py +28 -0
  19. aeroviz-0.1.5/AeroViz/rawDataReader/script/SMPS_TH.py +0 -41
  20. aeroviz-0.1.5/AeroViz/rawDataReader/script/SMPS_aim11.py +0 -51
  21. aeroviz-0.1.5/AeroViz/rawDataReader/script/SMPS_genr.py +0 -51
  22. aeroviz-0.1.5/AeroViz.egg-info/requires.txt +0 -8
  23. aeroviz-0.1.5/setup.py +0 -35
  24. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/__init__.py +0 -0
  25. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/data/DEFAULT_DATA.csv +0 -0
  26. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/data/DEFAULT_PNSD_DATA.csv +0 -0
  27. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/__init__.py +0 -0
  28. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_calculate.py +0 -0
  29. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_isoropia.py +0 -0
  30. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_ocec.py +0 -0
  31. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_partition.py +0 -0
  32. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/_teom.py +0 -0
  33. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/isrpia.cnf +0 -0
  34. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Chemistry/isrpia2.exe +0 -0
  35. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/Angstrom_exponent.py +0 -0
  36. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/__init__.py +0 -0
  37. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_absorption.py +0 -0
  38. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_extinction.py +0 -0
  39. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_mie.py +0 -0
  40. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_mie_sd.py +0 -0
  41. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/_scattering.py +0 -0
  42. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/Optical/fRH.pkl +0 -0
  43. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/__merge.py +0 -0
  44. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_merge.py +0 -0
  45. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_merge_v1.py +0 -0
  46. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_merge_v2.py +0 -0
  47. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_merge_v3.py +0 -0
  48. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_merge_v4.py +0 -0
  49. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/SizeDistr/_size_distr.py +0 -0
  50. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/VOC/__init__.py +0 -0
  51. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/VOC/_potential_par.py +0 -0
  52. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/VOC/support_voc.json +0 -0
  53. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/__init__.py +0 -0
  54. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/dataProcess/core/__init__.py +0 -0
  55. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/__init__.py +0 -0
  56. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/bar.py +0 -0
  57. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/box.py +0 -0
  58. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/distribution/__init__.py +0 -0
  59. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/distribution/distribution.py +0 -0
  60. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/meteorology/__init__.py +0 -0
  61. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/meteorology/meteorology.py +0 -0
  62. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/optical/__init__.py +0 -0
  63. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/optical/optical.py +0 -0
  64. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/pie.py +0 -0
  65. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/regression.py +0 -0
  66. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/scatter.py +0 -0
  67. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/__init__.py +0 -0
  68. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/ammonium_rich.py +0 -0
  69. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/contour.py +0 -0
  70. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/corr_matrix.py +0 -0
  71. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/diurnal_pattern.py +0 -0
  72. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/koschmieder.py +0 -0
  73. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/templates/metal_heatmap.py +0 -0
  74. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/timeseries/__init__.py +0 -0
  75. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/timeseries/template.py +0 -0
  76. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/timeseries/timeseries.py +0 -0
  77. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/__init__.py +0 -0
  78. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/_color.py +0 -0
  79. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/_unit.py +0 -0
  80. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/fRH.json +0 -0
  81. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/plt_utils.py +0 -0
  82. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/sklearn_utils.py +0 -0
  83. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/utils/units.json +0 -0
  84. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/plot/violin.py +0 -0
  85. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/__init__.py +0 -0
  86. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/core/DataProc.py +0 -0
  87. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/core/SizeDist.py +0 -0
  88. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/core/__init__.py +0 -0
  89. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/method/PyMieScatt_update.py +0 -0
  90. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/method/__init__.py +0 -0
  91. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/method/mie_theory.py +0 -0
  92. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/method/prop.py +0 -0
  93. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/AbstractDistCalc.py +0 -0
  94. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/Chemical.py +0 -0
  95. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/IMPACT.py +0 -0
  96. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/IMPROVE.py +0 -0
  97. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/Others.py +0 -0
  98. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/PSD.py +0 -0
  99. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/PSD_dry.py +0 -0
  100. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/__init__.py +0 -0
  101. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/process/script/retrieve_RI.py +0 -0
  102. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/config/__init__.py +0 -0
  103. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/AE33.py +0 -0
  104. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/AE43.py +0 -0
  105. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/Aurora.py +0 -0
  106. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/BC1054.py +0 -0
  107. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/EPA_vertical.py +0 -0
  108. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/GRIMM.py +0 -0
  109. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/IGAC.py +0 -0
  110. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/MA350.py +0 -0
  111. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/Minion.py +0 -0
  112. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/NEPH.py +0 -0
  113. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/TEOM.py +0 -0
  114. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/rawDataReader/script/Table.py +0 -0
  115. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/tools/__init__.py +0 -0
  116. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/tools/database.py +0 -0
  117. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/tools/dataclassifier.py +0 -0
  118. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/tools/dataprinter.py +0 -0
  119. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz/tools/datareader.py +0 -0
  120. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz.egg-info/dependency_links.txt +0 -0
  121. {aeroviz-0.1.5 → aeroviz-0.1.6}/AeroViz.egg-info/top_level.txt +0 -0
  122. {aeroviz-0.1.5 → aeroviz-0.1.6}/LICENSE +0 -0
  123. {aeroviz-0.1.5 → aeroviz-0.1.6}/MANIFEST.in +0 -0
  124. {aeroviz-0.1.5 → aeroviz-0.1.6}/setup.cfg +0 -0
@@ -2,13 +2,14 @@ from pandas import concat, DataFrame
2
2
 
3
3
 
4
4
  def _basic(df_che, df_ref, df_water, df_density, nam_lst):
5
+
5
6
  df_all = concat(df_che, axis=1)
6
7
  index = df_all.index.copy()
7
8
  df_all.columns = nam_lst
8
9
 
9
10
  # parameter
10
11
  mol_A, mol_S, mol_N = df_all['NH4+'] / 18, df_all['SO42-'] / 96, df_all['NO3-'] / 62
11
- df_all['status'] = (mol_A) / (2 * mol_S + mol_N)
12
+ df_all['status'] = mol_A / (2 * mol_S + mol_N)
12
13
 
13
14
  convert_nam = {'AS': 'SO42-',
14
15
  'AN': 'NO3-',
@@ -92,7 +93,7 @@ def _basic(df_che, df_ref, df_water, df_density, nam_lst):
92
93
  df_mass['total'] = df_mass.sum(axis=1, min_count=6)
93
94
 
94
95
  qc_ratio = df_mass['total'] / df_ref
95
- qc_cond = (qc_ratio >= 0.7) & (qc_ratio <= 1.3)
96
+ qc_cond = (qc_ratio >= 0.5) & (qc_ratio <= 1.5)
96
97
 
97
98
  # volume
98
99
  df_vol = DataFrame()
@@ -153,7 +154,7 @@ def _basic(df_che, df_ref, df_water, df_density, nam_lst):
153
154
  out.update(ri_dic)
154
155
 
155
156
  for _ky, _df in out.items():
156
- out[_ky] = _df.reindex(index).where(qc_cond)
157
+ out[_ky] = _df.reindex(index)
157
158
 
158
159
  return out
159
160
 
@@ -1,8 +1,7 @@
1
- import pickle as pkl
2
1
  from pathlib import Path
3
2
 
4
3
  import numpy as np
5
- from pandas import DataFrame
4
+ from pandas import DataFrame, read_pickle
6
5
 
7
6
  from AeroViz.dataProcess.core import union_index
8
7
 
@@ -12,7 +11,7 @@ def _revised(_df_mass, _df_RH):
12
11
 
13
12
  # fRH
14
13
  with (Path(__file__).parent / 'fRH.pkl').open('rb') as f:
15
- _fRH = pkl.load(f)
14
+ _fRH = read_pickle(f)
16
15
  _fRH.loc[np.nan] = np.nan
17
16
 
18
17
  def fRH(_RH):
@@ -1,15 +1,11 @@
1
1
  from ..core import Writer, run_process
2
2
 
3
- __all__ = [
4
-
5
- 'SizeDistr',
6
-
7
- ]
3
+ __all__ = ['SizeDistr']
8
4
 
9
5
 
10
6
  class SizeDistr(Writer):
11
7
 
12
- ## basic
8
+ # basic
13
9
  @run_process('SizeDistr - basic', 'distr_basic')
14
10
  def basic(self, df, hybrid_bin_start_loc=None, unit='nm', bin_range=(0, 20000), input_type='norm'):
15
11
  from ._size_distr import _basic
@@ -18,7 +14,7 @@ class SizeDistr(Writer):
18
14
 
19
15
  return self, out
20
16
 
21
- ## merge
17
+ # merge
22
18
  @run_process('SizeDistr - merge_SMPS_APS_v4', 'distr_merge')
23
19
  def merge_SMPS_APS_v4(self, df_smps, df_aps, df_pm25, aps_unit='um',
24
20
  smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True,
@@ -30,7 +26,7 @@ class SizeDistr(Writer):
30
26
 
31
27
  return self, out
32
28
 
33
- ## merge
29
+ # merge
34
30
  @run_process('SizeDistr - merge_SMPS_APS_v3', 'distr_merge')
35
31
  def merge_SMPS_APS_v3(self, df_smps, df_aps, aps_unit='um',
36
32
  smps_overlap_lowbound=500, aps_fit_highbound=1000, dndsdv_alg=True):
@@ -40,7 +36,7 @@ class SizeDistr(Writer):
40
36
 
41
37
  return self, out
42
38
 
43
- ## merge
39
+ # merge
44
40
  @run_process('SizeDistr - merge_SMPS_APS_v2', 'distr_merge')
45
41
  def merge_SMPS_APS_v2(self, df_smps, df_aps, aps_unit='um',
46
42
  smps_overlap_lowbound=500, aps_fit_highbound=1000):
@@ -50,7 +46,7 @@ class SizeDistr(Writer):
50
46
 
51
47
  return self, out
52
48
 
53
- ## merge
49
+ # merge
54
50
  @run_process('SizeDistr - merge_SMPS_APS_v1', 'distr_merge')
55
51
  def merge_SMPS_APS(self, df_smps, df_aps, aps_unit='um', shift_mode='mobility',
56
52
  smps_overlap_lowbound=523, aps_fit_highbound=800):
@@ -12,7 +12,7 @@ def RawDataReader(instrument_name: str,
12
12
  qc: bool = True,
13
13
  csv_raw: bool = True,
14
14
  reset: bool = False,
15
- rate: bool = False,
15
+ rate: bool = True,
16
16
  append_data: bool = False,
17
17
  start: datetime | None = None,
18
18
  end: datetime | None = None,
@@ -72,9 +72,7 @@ def RawDataReader(instrument_name: str,
72
72
  instrument_class_map = {
73
73
  'NEPH': NEPH,
74
74
  'Aurora': Aurora,
75
- 'SMPS_genr': SMPS_genr,
76
- 'SMPS_aim11': SMPS_aim11,
77
- 'SMPS_TH': SMPS_TH,
75
+ 'SMPS': SMPS,
78
76
  'GRIMM': GRIMM,
79
77
  'APS_3321': APS_3321,
80
78
  'AE33': AE33,
@@ -82,7 +80,7 @@ def RawDataReader(instrument_name: str,
82
80
  'BC1054': BC1054,
83
81
  'MA350': MA350,
84
82
  'TEOM': TEOM,
85
- 'Sunset_OCEC': Sunset_OCEC,
83
+ 'OCEC': OCEC,
86
84
  'IGAC': IGAC,
87
85
  'VOC': VOC,
88
86
  'Table': Table,
@@ -2,76 +2,64 @@
2
2
 
3
3
  meta = {
4
4
  "NEPH": {
5
- "pattern": "*.dat",
5
+ "pattern": ["*.dat"],
6
6
  "freq": "5min",
7
7
  "deter_key": {"Scatter Coe. (550 nm)": ["G"]},
8
8
  },
9
9
 
10
10
  "Aurora": {
11
- "pattern": "*.csv",
11
+ "pattern": ["*.csv"],
12
12
  "freq": "1min",
13
13
  "deter_key": {"Scatter Coe. (550 nm)": ["G"]},
14
14
  },
15
15
 
16
- "SMPS_TH": {
17
- "pattern": "*.txt",
18
- "freq": "6min",
19
- "deter_key": {"Bins": ["all"]},
20
- },
21
-
22
- "SMPS_genr": {
23
- "pattern": "*.txt",
24
- "freq": "6min",
25
- "deter_key": {"Bins": ["all"]},
26
- },
27
-
28
- "SMPS_aim11": {
29
- "pattern": "*.csv",
16
+ "SMPS": {
17
+ "pattern": ["*.txt", "*.csv"],
30
18
  "freq": "6min",
31
19
  "deter_key": {"Bins": ["all"]},
32
20
  },
33
21
 
34
22
  "GRIMM": {
35
- "pattern": "*.dat",
23
+ "pattern": ["*.dat"],
36
24
  "freq": "6min",
37
25
  "deter_key": {"Bins": ["all"]},
38
26
  },
39
27
 
40
28
  "APS_3321": {
41
- "pattern": "*.TXT",
29
+ "pattern": ["*.txt"],
42
30
  "freq": "6min",
43
31
  "deter_key": {"Bins": ["all"]},
44
32
  },
45
33
 
46
34
  "AE33": {
47
- "pattern": "[!ST|!CT|!FV]*[!log]_AE33*.dat",
35
+ "pattern": ["[!ST|!CT|!FV]*[!log]_AE33*.dat"],
48
36
  "freq": "1min",
49
37
  "deter_key": {"BC Mass Conc. (880 nm)": ["BC6"]},
50
38
  "error_state": [],
51
39
  },
52
40
 
53
41
  "AE43": {
54
- "pattern": "[!ST|!CT|!FV]*[!log]_AE43*.dat",
42
+ "pattern": ["[!ST|!CT|!FV]*[!log]_AE43*.dat"],
55
43
  "freq": "1min",
56
44
  "deter_key": {"BC Mass Conc. (880 nm)": ["BC6"]},
57
45
  "error_state": [],
58
46
  },
59
47
 
60
48
  "BC1054": {
61
- "pattern": "*.csv",
49
+ "pattern": ["*.csv"],
62
50
  "freq": "1min",
63
51
  "deter_key": {"BC Mass Conc. (880 nm)": ["BC9"]},
64
52
  "error_state": [1, 2, 4, 8, 16, 32, 65536],
65
53
  },
66
54
 
67
55
  "MA350": {
68
- "pattern": "*.csv",
56
+ "pattern": ["*.csv"],
69
57
  "freq": "1min",
70
58
  "deter_key": {"BC Mass Conc. (880 nm)": ["BC5"]},
71
59
  },
72
60
 
73
61
  "TEOM": {
74
- "pattern": "*.csv",
62
+ "pattern": ["*.csv"],
75
63
  "freq": "6min",
76
64
  "deter_key": {
77
65
  "PM1.0 Mass Conc.": ["PM_Total"],
@@ -79,8 +67,8 @@ meta = {
79
67
  },
80
68
  },
81
69
 
82
- "Sunset_OCEC": {
83
- "pattern": "*LCRes.csv",
70
+ "OCEC": {
71
+ "pattern": ["*LCRes.csv"],
84
72
  "freq": "1h",
85
73
  "deter_key": {
86
74
  "Thermal OC": ["Thermal_OC"],
@@ -91,7 +79,7 @@ meta = {
91
79
  },
92
80
 
93
81
  "IGAC": {
94
- "pattern": "*.csv",
82
+ "pattern": ["*.csv"],
95
83
  "freq": "1h",
96
84
  "deter_key": {
97
85
  "Na+": ["Na+"],
@@ -108,7 +96,7 @@ meta = {
108
96
  },
109
97
 
110
98
  "VOC": {
111
- "pattern": "*.csv",
99
+ "pattern": ["*.csv"],
112
100
  "freq": "1h",
113
101
  "key": [
114
102
  'Benzene', 'Toluene', 'Ethylbenzene', 'm/p-Xylene', 'o-Xylene', 'Ethane', 'Propane', 'Isobutane',
@@ -129,20 +117,31 @@ meta = {
129
117
  },
130
118
 
131
119
  "Table": {
132
- "pattern": "*.csv",
120
+ "pattern": ["*.csv"],
133
121
  "freq": "1h",
134
122
  "deter_key": None,
135
123
  },
136
124
 
137
125
  "EPA_vertical": {
138
- "pattern": "*.csv",
126
+ "pattern": ["*.csv"],
139
127
  "freq": "1h",
140
128
  "deter_key": None,
141
129
  },
142
130
 
143
131
  "Minion": {
144
- "pattern": "*.csv",
132
+ "pattern": ["*.csv"],
145
133
  "freq": "1h",
146
- "deter_key": None,
134
+ "deter_key": {
135
+ "Na+": ["Na+"],
136
+ "NH4+": ["NH4+"],
137
+ "K+": ["K+"],
138
+ "Mg2+": ["Mg2+"],
139
+ "Ca2+": ["Ca2+"],
140
+ "Cl-": ["Cl-"],
141
+ "NO2-": ["NO2-"],
142
+ "NO3-": ["NO3-"],
143
+ "SO42-": ["SO42-"],
144
+ "Main Salt (NH4+, NO3-, SO42-)": ["NO3-", "SO42-", "NH4+"],
145
+ },
147
146
  },
148
147
  }
@@ -1,4 +1,4 @@
1
- import json as jsn
1
+ import json
2
2
  import logging
3
3
  import pickle as pkl
4
4
  from abc import ABC, abstractmethod
@@ -9,28 +9,35 @@ from typing import Any
9
9
  import numpy as np
10
10
  import pandas as pd
11
11
  from pandas import DataFrame, date_range, concat, to_numeric, to_datetime
12
+ from rich.console import Console
13
+ from rich.progress import Progress, TextColumn, BarColumn, TimeRemainingColumn, TaskProgressColumn
12
14
 
13
15
  from ..config.supported_instruments import meta
14
16
 
15
17
  __all__ = ['AbstractReader']
16
18
 
17
19
 
20
+ console = Console(force_terminal=True, color_system="auto")
21
+
22
+
18
23
  class AbstractReader(ABC):
19
- nam = 'AbstractReader'
24
+ """
25
+ Abstract class for reading raw data from different instruments. Each instrument should have a separate class that
26
+ inherits from this class and implements the abstract methods. The abstract methods are `_raw_reader` and `_QC`.
20
27
 
21
- # initial data
22
- # input : file path, reset switch
28
+ List the file in the path and read pickle file if it exists, else read raw data and dump the pickle file the
29
+ pickle file will be generated after read raw data first time, if you want to re-read the rawdata, please set
30
+ 'reset=True'
31
+ """
23
32
 
24
- # list the file in the path and read pickle file if it exists, else read raw data and dump the pickle file the
25
- # pickle file will be generated after read raw data first time, if you want to re-read the rawdata, please set
26
- # 'reset=True'
33
+ nam = 'AbstractReader'
27
34
 
28
35
  def __init__(self,
29
36
  path: Path | str,
30
37
  qc: bool = True,
31
38
  csv_raw: bool = True,
32
39
  reset: bool = False,
33
- rate: bool = False,
40
+ rate: bool = True,
34
41
  append_data: bool = False):
35
42
 
36
43
  self.path = Path(path)
@@ -41,7 +48,7 @@ class AbstractReader(ABC):
41
48
  self.rate = rate
42
49
  self.qc = qc
43
50
  self.csv = csv_raw
44
- self.append = append_data & reset
51
+ self.append = append_data and reset
45
52
 
46
53
  self.pkl_nam = self.path / f'_read_{self.nam.lower()}.pkl'
47
54
  self.csv_nam = self.path / f'_read_{self.nam.lower()}.csv'
@@ -49,21 +56,12 @@ class AbstractReader(ABC):
49
56
  self.csv_nam_raw = self.path / f'_read_{self.nam.lower()}_raw.csv'
50
57
  self.csv_out = self.path / f'output_{self.nam.lower()}.csv'
51
58
 
52
- # dependency injection function, customize each instrument
53
- @abstractmethod
54
- def _raw_reader(self, file):
55
- pass
56
-
57
- @abstractmethod
58
- def _QC(self, df: DataFrame):
59
- return df
60
-
61
59
  def __call__(self,
62
60
  start: dtm | None = None,
63
61
  end: dtm | None = None,
64
62
  mean_freq: str = '1h',
65
63
  csv_out: bool = True,
66
- ) -> DataFrame | None:
64
+ ) -> DataFrame:
67
65
 
68
66
  if start and end and end <= start:
69
67
  raise ValueError(f"Invalid time range: start {start} is after end {end}")
@@ -78,6 +76,14 @@ class AbstractReader(ABC):
78
76
 
79
77
  return data
80
78
 
79
+ @abstractmethod
80
+ def _raw_reader(self, file):
81
+ pass
82
+
83
+ @abstractmethod
84
+ def _QC(self, df: DataFrame):
85
+ return df
86
+
81
87
  @staticmethod
82
88
  def basic_QC(df: DataFrame):
83
89
  df_ave, df_std = df.mean(), df.std()
@@ -85,40 +91,25 @@ class AbstractReader(ABC):
85
91
 
86
92
  return df.mask(df_lowb | df_highb).copy()
87
93
 
88
- # set each to true datetime(18:30:01 -> 18:30:00) and rindex data
89
- def _raw_process(self, _df):
90
- # get time from df and set time to whole time to create time index
91
- _st, _ed = _df.index.sort_values()[[0, -1]]
92
- _tm_index = date_range(_st.strftime('%Y%m%d %H00'), _ed.floor('h').strftime('%Y%m%d %H00'),
93
- freq=self.meta['freq'])
94
- _tm_index.name = 'time'
95
-
96
- return _df.apply(to_numeric, errors='coerce').resample(self.meta['freq']).mean().reindex(_tm_index)
97
-
98
94
  def _setup_logger(self) -> logging.Logger:
99
95
  logger = logging.getLogger(self.nam)
100
96
  logger.setLevel(logging.INFO)
97
+
98
+ for handler in logger.handlers[:]:
99
+ logger.removeHandler(handler)
100
+
101
101
  handler = logging.FileHandler(self.path / f'{self.nam}.log')
102
102
  handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
103
103
  logger.addHandler(handler)
104
104
  return logger
105
105
 
106
- # acquisition rate and yield rate
107
- def _rate_calculate(self, _fout_raw, _fout_qc, _st_raw, _ed_raw):
106
+ def _rate_calculate(self, _fout_raw, _fout_qc, _st_raw, _ed_raw) -> None:
108
107
  if self.meta['deter_key'] is not None:
109
108
  _start, _end = _fout_qc.index[[0, -1]]
110
109
 
111
110
  _drop_how = 'any'
112
111
  _the_size = len(_fout_raw.resample('1h').mean().index)
113
112
 
114
- self.logger.info(f"{'=' * 60}")
115
- self.logger.info(
116
- f"Raw data time : {_st_raw.strftime('%Y-%m-%d %H:%M:%S')} to {_ed_raw.strftime('%Y-%m-%d %H:%M:%S')}")
117
- self.logger.info(
118
- f"Output time : {_start.strftime('%Y-%m-%d %H:%M:%S')} to {_end.strftime('%Y-%m-%d %H:%M:%S')}")
119
- self.logger.info(f"{'-' * 60}")
120
- print(f"\n\n\t\tfrom {_start.strftime('%Y-%m-%d %H:%M:%S')} to {_end.strftime('%Y-%m-%d %H:%M:%S')}\n")
121
-
122
113
  for _nam, _key in self.meta['deter_key'].items():
123
114
  if _key == ['all']:
124
115
  _key, _drop_how = _fout_qc.keys(), 'all'
@@ -137,9 +128,19 @@ class AbstractReader(ABC):
137
128
  self.logger.info(f'\tYield rate: {_yid_rate}%')
138
129
  self.logger.info(f"{'=' * 60}")
139
130
 
140
- print(f'\t\t{_nam} : ')
141
- print(f'\t\t\tacquisition rate : \033[91m{_acq_rate}%\033[0m')
142
- print(f'\t\t\tyield rate : \033[91m{_yid_rate}%\033[0m')
131
+ print(f'\n\t{_nam} : ')
132
+ print(f'\t\tacquisition rate : \033[91m{_acq_rate}%\033[0m')
133
+ print(f'\t\tyield rate : \033[91m{_yid_rate}%\033[0m')
134
+
135
+ # set each to true datetime(18:30:01 -> 18:30:00) and rindex data
136
+ def _raw_process(self, _df):
137
+ # get time from df and set time to whole time to create time index
138
+ _st, _ed = _df.index.sort_values()[[0, -1]]
139
+ _tm_index = date_range(_st.strftime('%Y%m%d %H00'), _ed.floor('h').strftime('%Y%m%d %H00'),
140
+ freq=self.meta['freq'])
141
+ _tm_index.name = 'time'
142
+
143
+ return _df.apply(to_numeric, errors='coerce').resample(self.meta['freq']).mean().reindex(_tm_index)
143
144
 
144
145
  # process time index
145
146
  @staticmethod
@@ -153,7 +154,7 @@ class AbstractReader(ABC):
153
154
 
154
155
  # append new data to exist pkl
155
156
  @staticmethod
156
- def _apnd_prcs(_df_done, _df_apnd):
157
+ def _append_process(_df_done, _df_apnd):
157
158
 
158
159
  if _df_apnd is not None:
159
160
  _df = concat([_df_apnd.dropna(how='all').copy(), _df_done.dropna(how='all').copy()])
@@ -165,16 +166,16 @@ class AbstractReader(ABC):
165
166
 
166
167
  return _df_done
167
168
 
168
- # remove outlier
169
- def _outlier_prcs(self, _df):
169
+ def _outlier_process(self, _df):
170
+ outlier_file = self.path / 'outlier.json'
170
171
 
171
- if (self.path / 'outlier.json') not in self.path.glob('*.json'):
172
+ if not outlier_file.exists():
172
173
  return _df
173
174
 
174
- with (self.path / 'outlier.json').open('r', encoding='utf-8', errors='ignore') as f:
175
- self.outlier = jsn.load(f)
175
+ with outlier_file.open('r', encoding='utf-8', errors='ignore') as f:
176
+ outliers = json.load(f)
176
177
 
177
- for _st, _ed in self.outlier.values():
178
+ for _st, _ed in outliers.values():
178
179
  _df.loc[_st:_ed] = np.nan
179
180
 
180
181
  return _df
@@ -192,14 +193,13 @@ class AbstractReader(ABC):
192
193
 
193
194
  @staticmethod
194
195
  def _safe_pickle_dump(file_path: Path, data: Any) -> None:
195
- while True:
196
- try:
197
- with file_path.open('wb') as f:
198
- pkl.dump(data, f, protocol=pkl.HIGHEST_PROTOCOL)
199
- break
200
- except PermissionError as err:
201
- print('\n', err)
202
- input('\t\t\33[41m Please close the file and press "Enter" \33[0m\n')
196
+ try:
197
+ with file_path.open('wb') as f:
198
+ pkl.dump(data, f, protocol=pkl.HIGHEST_PROTOCOL)
199
+ except PermissionError as e:
200
+ raise IOError(f"Unable to write to {file_path}. The file may be in use or you may not have permission: {e}")
201
+ except Exception as e:
202
+ raise IOError(f"Error writing to {file_path}: {e}")
203
203
 
204
204
  # read pickle file
205
205
  def _read_pkl(self):
@@ -207,31 +207,41 @@ class AbstractReader(ABC):
207
207
  return pkl.load(raw_data), pkl.load(qc_data)
208
208
 
209
209
  def _read_raw_files(self) -> tuple[DataFrame | None, DataFrame | None]:
210
- patterns = {self.meta['pattern'].lower(), self.meta['pattern'].upper(), self.meta['pattern']}
211
- files = [f for pattern in patterns for f in self.path.glob(pattern)
210
+ files = [f
211
+ for file_pattern in self.meta['pattern']
212
+ for pattern in {file_pattern.lower(), file_pattern.upper(), file_pattern}
213
+ for f in self.path.glob(pattern)
212
214
  if f.name not in [self.csv_out.name, self.csv_nam.name, self.csv_nam_raw.name, f'{self.nam}.log']]
213
215
 
214
216
  if not files:
215
- raise FileNotFoundError(f"\t\t\033[31mNo files in '{self.path}' could be read."
216
- f"Please check the current path.\033[0m")
217
+ raise FileNotFoundError(f"No files in '{self.path}' could be read. Please check the current path.")
217
218
 
218
219
  df_list = []
219
- for file in files:
220
- print(f"\r\t\treading {file.name}", end='')
221
-
222
- try:
223
- df = self._raw_reader(file)
220
+ with Progress(
221
+ TextColumn("[bold blue]{task.description}", style="bold blue"),
222
+ BarColumn(bar_width=18, complete_style="green", finished_style="bright_green"),
223
+ TaskProgressColumn(),
224
+ TimeRemainingColumn(),
225
+ TextColumn("{task.fields[filename]}", style="yellow"),
226
+ console=console,
227
+ expand=False
228
+ ) as progress:
229
+ task = progress.add_task(f"Reading {self.nam} files", total=len(files), filename="")
230
+ for file in files:
231
+ progress.update(task, advance=1, filename=file.name)
232
+ try:
233
+ df = self._raw_reader(file)
224
234
 
225
- if df is not None and not df.empty:
226
- df_list.append(df)
227
- else:
228
- self.logger.warning(f"File {file.name} produced an empty DataFrame or None.")
235
+ if df is not None and not df.empty:
236
+ df_list.append(df)
237
+ else:
238
+ self.logger.warning(f"File {file.name} produced an empty DataFrame or None.")
229
239
 
230
- except pd.errors.ParserError as e:
231
- self.logger.error(f"Error tokenizing data: {e}")
240
+ except pd.errors.ParserError as e:
241
+ self.logger.error(f"Error tokenizing data: {e}")
232
242
 
233
- except Exception as e:
234
- self.logger.error(f"Error reading {file.name}: {e}")
243
+ except Exception as e:
244
+ self.logger.error(f"Error reading {file.name}: {e}")
235
245
 
236
246
  if not df_list:
237
247
  raise ValueError("All files were either empty or failed to read.")
@@ -241,47 +251,42 @@ class AbstractReader(ABC):
241
251
 
242
252
  return raw_data, qc_data
243
253
 
244
- # main flow
245
254
  def _run(self, _start, _end):
246
- _f_raw_done, _f_qc_done = None, None
247
-
248
255
  # read pickle if pickle file exists and 'reset=False' or process raw data or append new data
249
- if self.pkl_nam_raw.exists() and self.pkl_nam.exists() and (not self.reset or self.append):
250
- print(f"\n\t{dtm.now().strftime('%m/%d %X')} : Reading \033[96mPICKLE\033[0m file of {self.nam}")
256
+ if self.pkl_nam_raw.exists() and self.pkl_nam.exists() and not self.reset:
257
+ print(f"\n{dtm.now().strftime('%m/%d %X')} : Reading {self.nam} \033[96mPICKLE\033[0m "
258
+ f"from {_start} to {_end}\n")
251
259
 
252
260
  _f_raw_done, _f_qc_done = self._read_pkl()
253
261
 
254
- if not self.append:
255
- _f_raw_done, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_raw_done)
256
- _f_qc_done, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_qc_done)
257
-
258
- _f_qc_done = self._outlier_prcs(_f_qc_done)
259
-
260
- if self.rate:
261
- self._rate_calculate(_f_raw_done, _f_qc_done, _start_raw, _end_raw)
262
-
263
- return _f_qc_done if self.qc else _f_raw_done
262
+ if self.append:
263
+ print(f"Appending new data from {_start} to {_end}")
264
+ _f_raw_new, _f_qc_new = self._read_raw_files()
265
+ _f_raw = self._append_process(_f_raw_done, _f_raw_new)
266
+ _f_qc = self._append_process(_f_qc_done, _f_qc_new)
267
+ else:
268
+ _f_raw, _f_qc = _f_raw_done, _f_qc_done
264
269
 
265
- # read raw data
266
- print(f"\n\t{dtm.now().strftime('%m/%d %X')} : Reading \033[96mRAW DATA\033[0m of {self.nam} and process it")
270
+ else:
271
+ print(f"\n{dtm.now().strftime('%m/%d %X')} : Reading {self.nam} \033[96mRAW DATA\033[0m "
272
+ f"from {_start} to {_end}\n")
273
+ _f_raw, _f_qc = self._read_raw_files()
267
274
 
268
- _f_raw, _f_qc = self._read_raw_files()
269
-
270
- # append new data and pickle data
271
- if self.append and self.pkl_nam.exists():
272
- _f_raw = self._apnd_prcs(_f_raw_done, _f_raw)
273
- _f_qc = self._apnd_prcs(_f_qc_done, _f_qc)
275
+ # process time index
276
+ _f_raw, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_raw)
277
+ _f_qc, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_qc)
274
278
 
275
- _f_qc = self._outlier_prcs(_f_qc)
279
+ _f_qc = self._outlier_process(_f_qc)
276
280
 
277
281
  # save
278
282
  self._save_data(_f_raw, _f_qc)
279
283
 
280
- # process time index
281
- # if (_start is not None)|(_end is not None):
282
- _f_raw, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_raw)
283
- _f_qc, _start_raw, _end_raw = self._tmidx_process(_start, _end, _f_qc)
284
+ self.logger.info(f"{'=' * 60}")
285
+ self.logger.info(f"Raw data time : {_start_raw} to {_end_raw}")
286
+ self.logger.info(f"Output time : {_start} to {_end}")
287
+ self.logger.info(f"{'-' * 60}")
284
288
 
285
- self._rate_calculate(_f_raw, _f_qc, _start_raw, _end_raw)
289
+ if self.rate:
290
+ self._rate_calculate(_f_raw, _f_qc, _start_raw, _end_raw)
286
291
 
287
292
  return _f_qc if self.qc else _f_raw
@@ -1,4 +1,4 @@
1
- import numpy as n
1
+ import numpy as np
2
2
  from pandas import to_datetime, read_table
3
3
 
4
4
  from AeroViz.rawDataReader.core import AbstractReader
@@ -29,7 +29,7 @@ class Reader(AbstractReader):
29
29
  # QC data
30
30
  def _QC(self, _df):
31
31
  # mask out the data size lower than 7
32
- _df['total'] = _df.sum(axis=1, min_count=1) * (n.diff(n.log(_df.keys().to_numpy(float)))).mean()
32
+ _df['total'] = _df.sum(axis=1, min_count=1) * (np.diff(np.log(_df.keys().to_numpy(float)))).mean()
33
33
  _df_size = _df['total'].dropna().resample('1h').size().resample(_df.index.freq).ffill()
34
34
  _df = _df.mask(_df_size < 7)
35
35
 
@@ -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: