Skip to content

Commit cabada5

Browse files
authored
Merge pull request #9 from k-kramer/feature/detect_column_ancillary
Feature/detect column ancillary
2 parents b6eb439 + 30d7441 commit cabada5

File tree

8 files changed

+56
-26
lines changed

8 files changed

+56
-26
lines changed

.github/workflows/build.yml

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ name: Automated Tests
99
on:
1010
push:
1111
pull_request:
12-
workflow_dispatch:
12+
schedule:
13+
- cron: "0 11 * * 1" # Every Monday at 11 UTC
1314

1415
jobs:
1516
build:
1617
name: Build py${{ matrix.python-version }} @ ${{ matrix.os }} 🐍
1718
runs-on: ${{ matrix.os }}
1819
strategy:
1920
matrix:
20-
python-version: ['3.8', '3.9', '3.10']
21+
python-version: ['3.9', '3.13']
2122
os: ["ubuntu-latest"]
2223
steps:
23-
- uses: actions/checkout@v2
24+
- uses: actions/checkout@v4
2425
with:
2526
submodules: true
2627
fetch-depth: 0
@@ -67,7 +68,7 @@ jobs:
6768
python setup.py sdist --dist-dir .artifacts/dist
6869
ls .artifacts/dist
6970
- name: Upload Artifacts
70-
uses: actions/upload-artifact@v2
71+
uses: actions/upload-artifact@v4
7172
with:
7273
name: Artifacts
7374
path: .artifacts/*
@@ -93,7 +94,7 @@ jobs:
9394
echo "GITHUB_REF = $GITHUB_REF"
9495
echo "GITHUB_REPOSITORY = $GITHUB_REPOSITORY"
9596
- name: Download Artifacts
96-
uses: actions/download-artifact@v2
97+
uses: actions/download-artifact@v4
9798
- name: Display downloaded files
9899
run: ls -aR
99100
- name: Upload to PyPI

docs/example.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ As Input a pandas.DataFrame of the following format is required:
4141
4242
# initialize interface and run all flagging procedures
4343
flag = flagit.Interface(df)
44-
result_df = flag.run(sat_point = 42.7)
44+
result_df = flag.run(sat_point = 42.7) # Saturation Point in % vol
4545
46-
# alternatively: choose only specific procedures by providing a list or string as name:
46+
# alternatively: choose only specific procedures by providing a list as name:
4747
flag = flagit.Interface(df)
4848
result_df = flag.run(name = ['D06', 'D07', 'D09'])
49-
result_df = flag.run(name = 'C01')
49+
result_df = flag.run(name = ['C01'])
5050
5151
.. code:: python
5252

environment.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ channels:
44
dependencies:
55
- numpy
66
- scipy
7-
- pandas
7+
- pandas<3
88
- pip
99
- pip:
1010
- pytest-cov
1111
- pytest
12+
- setuptools

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[tool.pytest.ini_options]
2+
pythonpath = ["src"]

setup.cfg

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ package_dir =
2626
# DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD!
2727
setup_requires = pyscaffold>=3.2a0,<3.3a0
2828
# Add here dependencies of your project (semicolon/line-separated), e.g.
29-
install_requires = numpy; scipy; pandas;
29+
install_requires =
30+
setuptools
31+
numpy
32+
scipy
33+
pandas
3034
# The usage of test_requires is discouraged, see `Dependency Management` docs
3135
# tests_require = pytest; pytest-cov
3236
# Require a specific Python version, e.g. Python 2.7 or >= 3.4

src/flagit/flagit.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
class FormatError(Exception):
3232
pass
3333

34+
class VariableNotKnown(Exception):
35+
pass
3436

3537
t = Variables()
3638

@@ -57,7 +59,7 @@ class provides interface to apply ISMN quality control procedures to in situ soi
5759
data : pandas.DataFrame
5860
Input for Interface Object containing in situ soil moisture measurements
5961
sat_point : float
60-
Saturation Point for soil at the respective location.
62+
Saturation Point in % vol for soil at the respective location.
6163
At ISMN the saturation point is calculated from Harmonized World Soil Database (HWSD) sand, clay and organic
6264
content for each station using Equations [2,3,5] from Saxton & Rawls (2006).
6365
(Saxton, K. E., & Rawls, W. J. (2006). Soil water characteristic estimates by texture and organic matter for
@@ -90,7 +92,7 @@ def __init__(self, data, sat_point=None, depth_from=None):
9092
raise FormatError('Please provide pandas.DataFrame as data.')
9193

9294
if 'soil_moisture' not in self.data.columns:
93-
self.variable = self.data.keys()[0]
95+
self.variable = self.get_variable_from_data()
9496
self.data['qflag'] = data[self.variable].apply(lambda x: set())
9597

9698
else:
@@ -106,10 +108,10 @@ def run(
106108
107109
Parameters
108110
----------
109-
name : string or list, optional
110-
provide name of flag or list of flags to only apply these flags
111+
name : list
112+
provide list of flags to only apply these flags
111113
sat_point : float
112-
Saturation Point for soil at the respective location.
114+
Saturation Point in % vol for soil at the respective location.
113115
At ISMN the saturation point is calculated from Harmonized World Soil Database (HWSD) sand, clay
114116
and organic content for each station using Equations [2,3,5] from Saxton & Rawls (2006).
115117
(Saxton, K. E., & Rawls, W. J. (2006). Soil water characteristic estimates by texture and organic matter
@@ -127,6 +129,10 @@ def run(
127129
DataFrame including ISMN quality flags in column "qflag".
128130
"""
129131
keys = self.data.keys()
132+
133+
if name:
134+
assert isinstance(name, (list)), "If 'name' is provided then it must be a list"
135+
130136
if not self.sat_point:
131137
self.sat_point = sat_point
132138
if not self.depth_from:
@@ -200,6 +206,19 @@ def apply_savgol(self) -> None:
200206
self.data['deriv1'] = savgol(self.data.soil_moisture, 3, 2, 1, mode='nearest')
201207
self.data['deriv2'] = savgol(self.data.soil_moisture, 3, 2, 2, mode='nearest')
202208

209+
210+
def get_variable_from_data(self) -> str:
211+
"""
212+
Gets first occuring and known Variable from the pandas dataframe
213+
Returns v:string
214+
-------
215+
216+
"""
217+
for v in self.data.keys():
218+
if v in t.variable_list:
219+
return v
220+
raise VariableNotKnown
221+
203222
def flag_C01(self, tag):
204223
"""
205224
Soil moisture below threshold:

src/flagit/settings.py

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ class Variables():
66
ancillary_ts_lower = 0
77
ancillary_p_min = 0.2
88
plateau_count = 0
9+
10+
variable_list = ['soil_moisture','soil_temperature','air_temperature','precipitation',
11+
'surface_temperature', 'soil_suction', 'snow_water_equivalent','snow_depth']
912

1013
def low_boundary(self, var):
1114
"""

tests/test_flagit.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -49,47 +49,47 @@ def test_check_C01(self) -> None:
4949
"""
5050
Test flag C01
5151
"""
52-
self.iface.run(name='C01')
52+
self.iface.run(name=['C01'])
5353
assert self.data.qflag[30] == {'C01'}
5454
assert self.data.qflag[31] == set()
5555

5656
def test_check_C02(self) -> None:
5757
"""
5858
Test flag C02
5959
"""
60-
self.iface.run(name='C02')
60+
self.iface.run(name=['C02'])
6161
assert self.data.qflag[70] == {'C02'}
6262
assert self.data.qflag[69] == set()
6363

6464
def test_check_C03(self) -> None:
6565
"""
6666
Test flag C03
6767
"""
68-
self.iface.run(name='C03')
68+
self.iface.run(name=['C03'])
6969
assert self.data.qflag[80] == {'C03'}
7070
assert self.data.qflag[79] == set()
7171

7272
def test_check_D01(self) -> None:
7373
"""
7474
Test flag D01
7575
"""
76-
self.iface.run(name='D01')
76+
self.iface.run(name=['D01'])
7777
assert self.data.qflag[35] == {'D01'}
7878
assert self.data.qflag[136] == set()
7979

8080
def test_check_D02(self) -> None:
8181
"""
8282
Test flag D02
8383
"""
84-
self.iface.run(name='D02')
84+
self.iface.run(name=['D02'])
8585
assert self.data.qflag[2] == {'D02'}
8686
assert self.data.qflag[62] == set()
8787

8888
def test_check_D03(self) -> None:
8989
"""
9090
Test flag D03
9191
"""
92-
self.iface.run(name='D03')
92+
self.iface.run(name=['D03'])
9393
assert self.data.qflag[70] == {'D03'}
9494
assert self.data.qflag[636] == set()
9595
assert self.data.qflag[0] == {'D03'}
@@ -98,23 +98,23 @@ def test_check_D04(self) -> None:
9898
"""
9999
Test flag D04
100100
"""
101-
self.iface.run(name='D04')
101+
self.iface.run(name=['D04'])
102102
assert self.data.qflag[70] == {'D04'}
103103
assert self.data.qflag[71] == set()
104104

105105
def test_check_D05(self) -> None:
106106
"""
107107
Test flag D05
108108
"""
109-
self.iface.run(name='D05')
109+
self.iface.run(name=['D05'])
110110
assert self.data.qflag[70] == {'D05'}
111111
assert self.data.qflag[636] == set()
112112

113113
def test_check_D06(self) -> None:
114114
"""
115115
Test flag D06
116116
"""
117-
self.iface.run(name='D06')
117+
self.iface.run(name=['D06'])
118118
np.testing.assert_almost_equal(self.data.deriv1[58], -5.551115123125783e-17)
119119
np.testing.assert_almost_equal(self.data.deriv2[29], -6.200000000000003)
120120
assert self.data.qflag[30] == {'D06'}
@@ -135,15 +135,15 @@ def test_check_D10(self) -> None:
135135
"""
136136
Test flag D10
137137
"""
138-
self.iface.run(name='D10')
138+
self.iface.run(name=['D10'])
139139
assert self.data.qflag[99] == {'D10'}
140140
assert self.data.qflag[75] == set()
141141

142142
def test_check_good(self) -> None:
143143
"""
144144
Test flag "good"
145145
"""
146-
self.iface.run(name='G')
146+
self.iface.run(name=['G'])
147147
assert self.data.qflag[3] == {'G'}
148148
assert len(np.unique(self.data.qflag)) == 1
149149

0 commit comments

Comments
 (0)