fluxdataqaqc 0.3.0__tar.gz → 0.3.2__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.
Files changed (31) hide show
  1. {fluxdataqaqc-0.3.0/fluxdataqaqc.egg-info → fluxdataqaqc-0.3.2}/PKG-INFO +10 -7
  2. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/README.rst +3 -4
  3. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/environment.yml +2 -2
  4. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/AMF_US-Tw3_BASE_HH_5-5.csv +1 -1
  5. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/US-Tw3_config.ini +0 -1
  6. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/data_for_multiple_soil_vars.csv +1 -1
  7. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/__init__.py +1 -1
  8. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/data.py +92 -41
  9. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/qaqc.py +342 -223
  10. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/util.py +81 -1
  11. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2/fluxdataqaqc.egg-info}/PKG-INFO +10 -7
  12. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/requires.txt +2 -2
  13. fluxdataqaqc-0.3.2/requirements.txt +11 -0
  14. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/setup.py +7 -4
  15. fluxdataqaqc-0.3.2/tests/test_fluxdataqaqc.py +885 -0
  16. fluxdataqaqc-0.3.0/requirements.txt +0 -9
  17. fluxdataqaqc-0.3.0/tests/test_fluxdataqaqc.py +0 -520
  18. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/LICENSE.txt +0 -0
  19. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/MANIFEST.in +0 -0
  20. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/FLX_US-AR1_FLUXNET2015_SUBSET_DD_2009-2012_1-3.xlsx +0 -0
  21. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/USGS_config.ini +0 -0
  22. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/fluxnet_config.ini +0 -0
  23. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/raw_subhour_DVDV_10.xlsx +0 -0
  24. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/config_for_QC_flag_filtering.ini +0 -0
  25. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/config_for_multiple_soil_vars.ini +0 -0
  26. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/data_for_QC_flag_filtering.csv +0 -0
  27. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/plot.py +0 -0
  28. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/SOURCES.txt +0 -0
  29. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/dependency_links.txt +0 -0
  30. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/top_level.txt +0 -0
  31. {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluxdataqaqc
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: Tools for QA/QC of eddy covariance station data
5
5
  Home-page: https://github.com/Open-ET/flux-data-qaqc
6
6
  Author: John Volk
@@ -11,6 +11,10 @@ Platform: Linux
11
11
  Platform: Mac OS X
12
12
  Classifier: License :: OSI Approved :: BSD License
13
13
  Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
14
18
  Classifier: Environment :: Console
15
19
  Classifier: Development Status :: 4 - Beta
16
20
  Classifier: Topic :: Scientific/Engineering
@@ -21,8 +25,8 @@ License-File: LICENSE.txt
21
25
  Requires-Dist: bokeh>=3
22
26
  Requires-Dist: netCDF4
23
27
  Requires-Dist: numpy>=1.15
24
- Requires-Dist: pandas>=1.0
25
- Requires-Dist: refet==0.3.10
28
+ Requires-Dist: pandas>=2.2
29
+ Requires-Dist: refet>=0.3.10
26
30
  Requires-Dist: scikit-learn
27
31
  Requires-Dist: xarray
28
32
  Requires-Dist: openpyxl
@@ -80,11 +84,10 @@ Using PIP:
80
84
 
81
85
  PIP should install the necessary dependencies however it is recommended to use
82
86
  conda and first install the provided virtual environment. This is useful to
83
- avoid changing your local Python environment. Note, ``flux-data-qaqc`` has been
84
- tested for Python 3.7+, although it may work with versions greater than or
85
- equal to 3.4.
87
+ avoid changing your local Python environment. Note, ``flux-data-qaqc``
88
+ requires Python 3.9 or newer and is tested with Python 3.9, 3.11, and 3.13.
86
89
 
87
- First make sure you have the ``fluxdataqaqc`` environment file, you can download it `here <https://raw.githubusercontent.com/Open-ET/flux-data-qaqc/master/environment.yml?token=AB3BJKUKL2ELEM7WPLYLXFC45WQOG>`_. Next to install run,
90
+ First make sure you have the ``fluxdataqaqc`` environment file, you can download it `here <https://raw.githubusercontent.com/Open-ET/flux-data-qaqc/master/environment.yml>`_. Next to install run,
88
91
 
89
92
  .. code-block:: bash
90
93
 
@@ -37,11 +37,10 @@ Using PIP:
37
37
 
38
38
  PIP should install the necessary dependencies however it is recommended to use
39
39
  conda and first install the provided virtual environment. This is useful to
40
- avoid changing your local Python environment. Note, ``flux-data-qaqc`` has been
41
- tested for Python 3.7+, although it may work with versions greater than or
42
- equal to 3.4.
40
+ avoid changing your local Python environment. Note, ``flux-data-qaqc``
41
+ requires Python 3.9 or newer and is tested with Python 3.9, 3.11, and 3.13.
43
42
 
44
- First make sure you have the ``fluxdataqaqc`` environment file, you can download it `here <https://raw.githubusercontent.com/Open-ET/flux-data-qaqc/master/environment.yml?token=AB3BJKUKL2ELEM7WPLYLXFC45WQOG>`_. Next to install run,
43
+ First make sure you have the ``fluxdataqaqc`` environment file, you can download it `here <https://raw.githubusercontent.com/Open-ET/flux-data-qaqc/master/environment.yml>`_. Next to install run,
45
44
 
46
45
  .. code-block:: bash
47
46
 
@@ -3,9 +3,9 @@ channels:
3
3
  - conda-forge
4
4
  dependencies:
5
5
  - bokeh>=3.0.0
6
- - pandas>=1.0
6
+ - pandas>=2.2
7
7
  - python>=3.9
8
- - refet=0.3.10
8
+ - refet>=0.3.10
9
9
  - scikit-learn
10
10
  - netCDF4
11
11
  - numpy>=1.15
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
- name: fluxdataqaqc-0.3.0/examples/Basic_usage/AMF_US-Tw3_BASE_HH_5-5.csv
2
+ name: fluxdataqaqc-0.3.2/examples/Basic_usage/AMF_US-Tw3_BASE_HH_5-5.csv
3
3
  size: 38180927 bytes
4
4
  sha256: 0febe82fef616bd6f4b7664a971d2e6316a4f2f42090a80e2c43279102bfd383
@@ -52,7 +52,6 @@ site_snow_cover_days = 0.0
52
52
  state = CA
53
53
  location_date_start = 20130524.0
54
54
  acknowledgement = Biometeorology Lab, University of California, Berkeley, PI: Dennis Baldocchi
55
- gridmet_file_path = /Users/John.Volk@dri.edu/flux-data-qaqc/examples/Basic_usage/gridMET_data/US-Tw3_38.1083N_-121.6417W.csv
56
55
 
57
56
  [DATA]
58
57
  datestring_col = TIMESTAMP_START
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
- name: fluxdataqaqc-0.3.0/examples/Config_options/data_for_multiple_soil_vars.csv
2
+ name: fluxdataqaqc-0.3.2/examples/Config_options/data_for_multiple_soil_vars.csv
3
3
  size: 44901633 bytes
4
4
  sha256: 4c372d81a79b2b0f474ef6184fc2d84e3b8d40520c798196b1633e640ece4861
@@ -4,7 +4,7 @@ Tools for performing energy balance correction and QA/QC of eddy covariance flux
4
4
 
5
5
  __name__ = 'fluxdataqaqc'
6
6
  __author__ = 'John Volk'
7
- __version__ = '0.3.0'
7
+ __version__ = '0.3.2'
8
8
 
9
9
 
10
10
  from fluxdataqaqc.data import Data
@@ -9,7 +9,6 @@ import pandas as pd
9
9
  import refet
10
10
  from datetime import datetime
11
11
  from pathlib import Path
12
- from openpyxl import load_workbook
13
12
  from .plot import Plot
14
13
  from .util import Convert, get_subdaily_timestep_info
15
14
 
@@ -111,6 +110,7 @@ class Data(Plot, Convert):
111
110
  self.config = self._load_config(self.config_file)
112
111
  self.variables = self._get_config_vars()
113
112
  self.units = self._get_config_units()
113
+ self._check_required_units()
114
114
  self.na_val = self.config.get('METADATA', 'missing_data_value', fallback=None)
115
115
  # try to parse na_val as numeric
116
116
  try:
@@ -265,6 +265,9 @@ class Data(Plot, Convert):
265
265
  Returns:
266
266
  :obj:`None` or :obj:`pandas.Series`
267
267
 
268
+ Raises:
269
+ ValueError: if ``reference`` is not "short" or "tall".
270
+
268
271
  Hint:
269
272
  The input variables needed to run this method are: vapor pressure,
270
273
  wind speed, incoming shortwave radiation, and average air
@@ -272,6 +275,12 @@ class Data(Plot, Convert):
272
275
  exist, the actual vapor pressure will automatically be calculated.
273
276
  """
274
277
 
278
+ if reference not in ('short', 'tall'):
279
+ raise ValueError(
280
+ f'ERROR: reference must be "short" or "tall", but '
281
+ f'{reference} was given.'
282
+ )
283
+
275
284
  self.df.head(); # creates vp/vpd
276
285
  df = self.df.rename(columns=self.inv_map)
277
286
 
@@ -299,6 +308,8 @@ class Data(Plot, Convert):
299
308
  'the config files metadata, proceeding with height of 2 m'
300
309
  )
301
310
  anemometer_height = 2
311
+ # values read from the config file are strings
312
+ anemometer_height = float(anemometer_height)
302
313
 
303
314
  for v, u in self.units.items():
304
315
  # only converting variables needed for ref ET
@@ -353,17 +364,18 @@ class Data(Plot, Convert):
353
364
  elev = np.full(length, self.elevation)
354
365
  time = tmean.index.hour
355
366
 
367
+ # use keywords, refet >= 0.4 changed the order of arguments
356
368
  REF = refet.Hourly(
357
- tmean,
358
- ea,
359
- rs,
360
- uz,
361
- zw,
362
- elev,
363
- lat,
364
- lon,
365
- doy,
366
- time,
369
+ tmean=tmean,
370
+ ea=ea,
371
+ rs=rs,
372
+ uz=uz,
373
+ zw=zw,
374
+ elev=elev,
375
+ lat=lat,
376
+ lon=lon,
377
+ doy=doy,
378
+ time=time,
367
379
  method='asce',
368
380
  input_units=input_units,
369
381
  )
@@ -752,20 +764,9 @@ class Data(Plot, Convert):
752
764
  Read only top line of climate time series file return header names.
753
765
  """
754
766
  if climate_file.suffix in ('.xlsx', '.xls'):
755
- try: # using xlrd
756
- workbook = pd.ExcelFile(climate_file)
757
- rows = workbook.book.sheet_by_index(0).nrows
758
- header = pd.read_excel(workbook, skipfooter = (rows - 1))
759
- header = header.columns
760
- except: # fallback openpyxl- slower
761
- wb = load_workbook(climate_file, enumerate)
762
- sheet = wb.worksheets[0]
763
- header = sheet._shared_strings
764
- self.xl_parser='openpyxl'
765
- #rows = sheet.max_row
766
- #header = pd.read_excel(workbook, skipfooter = (rows - 1))
767
- #header = header.columns
768
-
767
+ # read only the header row of the first sheet
768
+ header = pd.read_excel(climate_file, nrows=0).columns
769
+
769
770
  else: # assume CSV
770
771
  skiprows=None
771
772
  if 'skiprows' in dict(self.config.items('METADATA')):
@@ -806,7 +807,7 @@ class Data(Plot, Convert):
806
807
  # later in df
807
808
  elif (k.startswith('g_') or k.startswith('theta_')) and not \
808
809
  weight_name in all_keys and not k.endswith('_units') and \
809
- not k.endswith('_weight'):
810
+ not k.endswith('_weight') and not k.endswith('_qc'):
810
811
  tmp = {'name': var_name, 'weight' : 1}
811
812
  soil_var_weight_pairs[k] = tmp
812
813
 
@@ -837,14 +838,14 @@ class Data(Plot, Convert):
837
838
  # get multiple G flux/soil moisture variables
838
839
  all_keys = dict(self.config.items('DATA')).keys()
839
840
  # should be named as 'g_ or 'theta_ what comes after not strict yet
841
+ # QC names for these (e.g. g_1_qc) are handled in _get_qc_flags
842
+ not_var_suffixes = ('_units', '_weight', '_qc')
840
843
  added_g_keys = []
841
844
  added_theta_keys = []
842
845
  for k in all_keys:
843
- if k.startswith('g_') and not k.endswith('_units') \
844
- and not k.endswith('_weight'):
846
+ if k.startswith('g_') and not k.endswith(not_var_suffixes):
845
847
  added_g_keys.append(k)
846
- if k.startswith('theta_') and not k.endswith('_units') \
847
- and not k.endswith('_weight'):
848
+ if k.startswith('theta_') and not k.endswith(not_var_suffixes):
848
849
  added_theta_keys.append(k)
849
850
 
850
851
  if added_g_keys:
@@ -877,12 +878,12 @@ class Data(Plot, Convert):
877
878
 
878
879
  Note:
879
880
  Parsing of correct units and conversion if needed is performed
880
- in the :obj:`.QaQc` class. Also, if units are not given
881
- in the config file a warning message is printed and the units are
882
- not included and thus will either need to be manually added later
883
- e.g. in Python by adding to :attr:`Data.units` or by adding them
884
- to the config and recreating a :obj:`Data` object otherwise the
885
- units will remain unknown and not be able to be later converted.
881
+ in the :obj:`.QaQc` class. If units are not given in the config
882
+ file for a variable that is used in calculations (see
883
+ :attr:`Convert.required_units`) an error is raised when the
884
+ :obj:`Data` object is created, units are never assumed. For
885
+ other variables, e.g. relative humidity or soil moisture, a
886
+ warning is printed and the units remain unknown.
886
887
  """
887
888
  no_unit_vars = ('datestring_col', 'year_col', 'month_col', 'day_col')
888
889
  config_dict = dict(self.config.items('DATA'))
@@ -930,6 +931,49 @@ class Data(Plot, Convert):
930
931
 
931
932
  return units
932
933
 
934
+ def _check_required_units(self):
935
+ """
936
+ Raise an error if units are missing from the config for variables
937
+ that are used in calculations or unit conversions.
938
+
939
+ These are variables in :attr:`Convert.required_units` and multiple
940
+ soil heat flux variables (e.g. g_1) that are averaged into G. Units
941
+ are never assumed for these. Variables whose units are only used
942
+ for plot labels (e.g. relative humidity, wind direction, soil
943
+ moisture) only get a warning in :meth:`Data._get_config_units`.
944
+
945
+ Raises:
946
+ ValueError: if units are missing for one or more variables.
947
+ """
948
+ missing = []
949
+ for var, col in self.variables.items():
950
+ # variables given as 'na' in the config are not in the data
951
+ if col == 'na':
952
+ continue
953
+ needs_units = (
954
+ var in Convert.required_units or var.startswith('g_')
955
+ )
956
+ if not needs_units:
957
+ continue
958
+ if var in self.variable_names_dict:
959
+ config_key = self.variable_names_dict[var].replace(
960
+ '_col', '_units'
961
+ )
962
+ else:
963
+ config_key = '{}_units'.format(var)
964
+ # check the config itself, units of a g_ variable that uses the
965
+ # same column as G are not stored in Data.units
966
+ if not self.config.get('DATA', config_key, fallback=None):
967
+ missing.append('{} ({})'.format(var, config_key))
968
+
969
+ if missing:
970
+ raise ValueError(
971
+ 'ERROR: units are missing from the DATA section of the config '
972
+ 'file for these variables:\n{}\nAdd them to the config, '
973
+ 'allowable units are listed in Convert.allowable_units.'
974
+ .format('\n'.join(missing))
975
+ )
976
+
933
977
  def _get_qc_flags(self):
934
978
  """
935
979
  Process any existing QC flags for variables in config, also add
@@ -940,7 +984,7 @@ class Data(Plot, Convert):
940
984
  qc_var_pairs = {}
941
985
  tmp = {}
942
986
 
943
- no_qc_vars = ('datestring_col')
987
+ no_qc_vars = ('datestring_col',)
944
988
  # dictionary that maps config QC values to keys for main variables
945
989
  # other variables like multiple g or theta (with unknown names) are
946
990
  # search for in loop below
@@ -954,16 +998,23 @@ class Data(Plot, Convert):
954
998
  # internal name for the variable (e.g. LE or Rn)
955
999
  var_name = qc_config[k]
956
1000
  user_var_name = self.variables.get(var_name)
957
- # keys are internal names for multiple G and theta
1001
+ # keys are internal names for multiple G and theta, e.g. g_1_qc
958
1002
  elif k.startswith(('g_','theta_')) and k.endswith('_qc'):
959
- var_name = k
1003
+ var_name = k[:-len('_qc')]
1004
+ user_var_name = self.variables.get(var_name)
960
1005
  # key is not for a QC flag header name...
961
1006
  else:
962
1007
  continue
1008
+ if user_var_name is None:
1009
+ print('WARNING: {} quality control name specified in the config'
1010
+ ' file but variable {} was not found, it will not be '
1011
+ 'used.'.format(v, var_name)
1012
+ )
1013
+ continue
963
1014
  if not v in self.header:
964
1015
  print('WARNING: {} quality control name specified in the config'
965
1016
  ' file for variable: {} does not exist in the input file, '
966
- 'it will not be used.'.format(v, self.variables[var_name])
1017
+ 'it will not be used.'.format(v, user_var_name)
967
1018
  )
968
1019
  continue
969
1020
  internal_name = '{}_qc_flag'.format(var_name)
@@ -1396,7 +1447,7 @@ class Data(Plot, Convert):
1396
1447
  key = 'G'
1397
1448
  # calculate mean (sum weighted values)
1398
1449
  df[val] = tmp_df.sum(axis=1)
1399
- df.loc[df[tmp_df.columns].isnull().all(1), val] = np.nan
1450
+ df.loc[df[tmp_df.columns].isnull().all(axis=1), val] = np.nan
1400
1451
 
1401
1452
  self.variables[key] = val
1402
1453
  elif len(vs) == 1: