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.
- {fluxdataqaqc-0.3.0/fluxdataqaqc.egg-info → fluxdataqaqc-0.3.2}/PKG-INFO +10 -7
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/README.rst +3 -4
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/environment.yml +2 -2
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/AMF_US-Tw3_BASE_HH_5-5.csv +1 -1
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/US-Tw3_config.ini +0 -1
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/data_for_multiple_soil_vars.csv +1 -1
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/__init__.py +1 -1
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/data.py +92 -41
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/qaqc.py +342 -223
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/util.py +81 -1
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2/fluxdataqaqc.egg-info}/PKG-INFO +10 -7
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/requires.txt +2 -2
- fluxdataqaqc-0.3.2/requirements.txt +11 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/setup.py +7 -4
- fluxdataqaqc-0.3.2/tests/test_fluxdataqaqc.py +885 -0
- fluxdataqaqc-0.3.0/requirements.txt +0 -9
- fluxdataqaqc-0.3.0/tests/test_fluxdataqaqc.py +0 -520
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/LICENSE.txt +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/MANIFEST.in +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/FLX_US-AR1_FLUXNET2015_SUBSET_DD_2009-2012_1-3.xlsx +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/USGS_config.ini +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/fluxnet_config.ini +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Basic_usage/raw_subhour_DVDV_10.xlsx +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/config_for_QC_flag_filtering.ini +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/config_for_multiple_soil_vars.ini +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/data_for_QC_flag_filtering.csv +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc/plot.py +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/SOURCES.txt +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/dependency_links.txt +0 -0
- {fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/fluxdataqaqc.egg-info/top_level.txt +0 -0
- {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.
|
|
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>=
|
|
25
|
-
Requires-Dist: refet
|
|
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``
|
|
84
|
-
|
|
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
|
|
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``
|
|
41
|
-
|
|
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
|
|
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
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
|
-
name: fluxdataqaqc-0.3.
|
|
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
|
{fluxdataqaqc-0.3.0 → fluxdataqaqc-0.3.2}/examples/Config_options/data_for_multiple_soil_vars.csv
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
|
-
name: fluxdataqaqc-0.3.
|
|
2
|
+
name: fluxdataqaqc-0.3.2/examples/Config_options/data_for_multiple_soil_vars.csv
|
|
3
3
|
size: 44901633 bytes
|
|
4
4
|
sha256: 4c372d81a79b2b0f474ef6184fc2d84e3b8d40520c798196b1633e640ece4861
|
|
@@ -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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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,
|
|
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:
|