Skip to content

Commit 35430a6

Browse files
Resolve "Add a command-line interface" (#83)
1 parent 316299e commit 35430a6

21 files changed

+543
-217
lines changed

.github/dependabot.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ updates:
44
directory: "/"
55
schedule:
66
interval: "weekly"
7+
reviewers:
8+
- "btschwertfeger"
79
- package-ecosystem: "pip"
810
directory: "/"
911
schedule:
1012
interval: "weekly"
13+
reviewers:
14+
- "btschwertfeger"
15+
ignore:
16+
- dependency-name: "ruff"

.github/workflows/_codecov.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
run: python -m pip install --upgrade pip
4646

4747
- name: Install package
48-
run: python -m pip install ".[dev]"
48+
run: python -m pip install ".[dev,test]"
4949

5050
- name: Generate coverage report
5151
run: pytest --cov --cov-report=xml

.github/workflows/_test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
python -m pip install --upgrade pip
3838
3939
- name: Install package
40-
run: python -m pip install ".[dev]"
40+
run: python -m pip install ".[dev,test]"
4141

4242
- name: Run unit tests
4343
run: pytest -vv tests

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.2.2
3+
rev: v0.3.5
44
hooks:
55
- id: ruff
66
args:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ build:
2424
.PHONY: dev
2525
dev:
2626
@git lfs install
27-
$(PYTHON) -m pip install -e ".[dev]"
27+
$(PYTHON) -m pip install -e ".[dev,test]"
2828

2929
## install Install the package
3030
##

README.md

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Welcome to python-cmethods, a powerful Python package designed for bias
2424
correction and adjustment of climate data. Built with a focus on ease of use and
2525
efficiency, python-cmethods offers a comprehensive suite of functions tailored
2626
for applying bias correction methods to climate model simulations and
27-
observational datasets.
27+
observational datasets via command-line interface and API.
2828

2929
Please cite this project as described in
3030
https://zenodo.org/doi/10.5281/zenodo.7652755.
@@ -143,7 +143,64 @@ python3 -m pip install python-cmethods
143143

144144
<a name="examples"></a>
145145

146-
## 4. Usage and Examples
146+
## 4. CLI Usage
147+
148+
The python-cmethods package provides a command-line interface for applying
149+
various bias correction methods out of the box.
150+
151+
Keep in mind that due to the various kinds of data and possibilities to
152+
pre-process those, the CLI only provides a basic application of the implemented
153+
techniques. For special parameters, adjustments, and data preparation, please
154+
use programming interface.
155+
156+
Listing the parameters and their requirements is available by passing the
157+
`--help` option:
158+
159+
```bash
160+
cmethods --help
161+
```
162+
163+
Applying the cmethods tool on the provided example data using the linear scaling
164+
approach is shown below:
165+
166+
```bash
167+
cmethods \
168+
--obs examples/input_data/observations.nc \
169+
--simh examples/input_data/control.nc \
170+
--simp examples/input_data/scenario.nc \
171+
--method linear_scaling \
172+
--kind add \
173+
--variable tas \
174+
--group time.month \
175+
--output linear_scaling.nc
176+
177+
2024/04/08 18:11:12 INFO | Loading data sets ...
178+
2024/04/08 18:11:12 INFO | Data sets loaded ...
179+
2024/04/08 18:11:12 INFO | Applying linear_scaling ...
180+
2024/04/08 18:11:15 INFO | Saving result to linear_scaling.nc ...
181+
```
182+
183+
For applying a distribution-based bias correction technique, the following
184+
example may help:
185+
186+
```bash
187+
cmethods \
188+
--obs examples/input_data/observations.nc \
189+
--simh examples/input_data/control.nc \
190+
--simp examples/input_data/scenario.nc \
191+
--method quantile_delta_mapping \
192+
--kind add \
193+
--variable tas \
194+
--quantiles 1000 \
195+
--output quantile_delta_mapping.nc
196+
197+
2024/04/08 18:16:34 INFO | Loading data sets ...
198+
2024/04/08 18:16:35 INFO | Data sets loaded ...
199+
2024/04/08 18:16:35 INFO | Applying quantile_delta_mapping ...
200+
2024/04/08 18:16:35 INFO | Saving result to quantile_delta_mapping.nc ...
201+
```
202+
203+
## 5. Programming Interface Usage and Examples
147204

148205
```python
149206
import xarray as xr

cmethods/__init__.py

Lines changed: 165 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Copyright (C) 2023 Benjamin Thomas Schwertfeger
44
# GitHub: https://github.com/btschwertfeger
55
#
6+
# pylint: disable=consider-using-f-string,logging-not-lazy
67

78
r"""
89
Module providing the a method named "adjust" to apply different bias
@@ -24,12 +25,170 @@
2425
_{m} = long-term monthly interval
2526
"""
2627

27-
from cmethods.core import adjust
28+
from __future__ import annotations
29+
30+
import logging
31+
import sys
2832

29-
__author__ = "Benjamin Thomas Schwertfeger"
30-
__copyright__ = __author__
31-
__email__ = "contact@b-schwertfeger.de"
32-
__link__ = "https://github.com/btschwertfeger"
33-
__github__ = "https://github.com/btschwertfeger/python-cmethods"
33+
import cloup
34+
import xarray as xr
35+
from cloup import (
36+
HelpFormatter,
37+
HelpTheme,
38+
Path,
39+
Style,
40+
command,
41+
option,
42+
option_group,
43+
version_option,
44+
)
45+
from cloup.constraints import Equal, If, require_all
46+
47+
from cmethods.core import adjust
3448

3549
__all__ = ["adjust"]
50+
51+
52+
@command(
53+
context_settings={
54+
"auto_envvar_prefix": "CMETHODS",
55+
"help_option_names": ["-h", "--help"],
56+
},
57+
formatter_settings=HelpFormatter.settings(
58+
theme=HelpTheme(
59+
invoked_command=Style(fg="bright_yellow"),
60+
heading=Style(fg="bright_white", bold=True),
61+
constraint=Style(fg="magenta"),
62+
col1=Style(fg="bright_yellow"),
63+
),
64+
),
65+
)
66+
@version_option(message="%version%")
67+
@option(
68+
"--obs",
69+
"--observations",
70+
required=True,
71+
type=Path(exists=True),
72+
help="Reference data set (control period)",
73+
)
74+
@option(
75+
"--simh",
76+
"--simulated-historical",
77+
required=True,
78+
type=Path(exists=True),
79+
help="Modeled data set (control period)",
80+
)
81+
@option(
82+
"--simp",
83+
"--simulated-scenario",
84+
required=True,
85+
type=Path(exists=True),
86+
help="Modeled data set (scenario period)",
87+
)
88+
@option(
89+
"--method",
90+
required=True,
91+
type=cloup.Choice(
92+
[
93+
"linear_scaling",
94+
"variance_scaling",
95+
"delta_method",
96+
"quantile_mapping",
97+
"quantile_delta_mapping",
98+
],
99+
case_sensitive=False,
100+
),
101+
help="Bias adjustment method to apply",
102+
)
103+
@option(
104+
"--kind",
105+
required=True,
106+
type=cloup.Choice(["+", "add", "*", "mult"]),
107+
help="Kind of adjustment",
108+
)
109+
@option(
110+
"--variable",
111+
required=True,
112+
type=str,
113+
help="Variable of interest",
114+
)
115+
@option(
116+
"-o",
117+
"--output",
118+
required=True,
119+
type=str,
120+
callback=lambda _, __, value: (value if value.endswith(".nc") else f"{value}.nc"),
121+
help="Output file name",
122+
)
123+
@option_group(
124+
"Scaling-Based Adjustment Options",
125+
option(
126+
"--group",
127+
type=str,
128+
help="Temporal grouping",
129+
),
130+
constraint=If(
131+
Equal("method", "linear_scaling")
132+
& Equal("method", "variance_scaling")
133+
& Equal("method", "delta_method"),
134+
then=require_all,
135+
),
136+
)
137+
@option_group(
138+
"Distribution-Based Adjustment Options",
139+
option(
140+
"--quantiles",
141+
type=int,
142+
help="Quantiles to respect",
143+
),
144+
constraint=If(
145+
Equal("method", "quantile_mapping") & Equal("method", "quantile_delta_mapping"),
146+
then=require_all,
147+
),
148+
)
149+
def cli(**kwargs) -> None:
150+
"""
151+
Command-line tool to apply bias correction procedures to climate data.
152+
153+
Copyright (C) 2023 Benjamin Thomas Schwertfeger\n
154+
GitHub: https://github.com/btschwertfeger/python-cmethods
155+
"""
156+
157+
logging.basicConfig(
158+
format="%(asctime)s %(levelname)8s | %(message)s",
159+
datefmt="%Y/%m/%d %H:%M:%S",
160+
level=logging.INFO,
161+
)
162+
163+
logging.info("Loading data sets ...")
164+
try:
165+
for key, message in zip(
166+
("obs", "simh", "simp"),
167+
(
168+
"observation data set",
169+
"modeled data set of the control period",
170+
"modeled data set of the scenario period",
171+
),
172+
):
173+
kwargs[key] = xr.open_dataset(kwargs[key])
174+
if not isinstance(kwargs[key], xr.Dataset):
175+
raise TypeError("The data sets must be type xarray.Dataset")
176+
177+
if kwargs["variable"] not in kwargs[key]:
178+
raise KeyError(
179+
f"Variable '{kwargs['variable']}' is missing in the {message}",
180+
)
181+
kwargs[key] = kwargs[key][kwargs["variable"]]
182+
except (TypeError, KeyError) as exc:
183+
logging.error(exc)
184+
sys.exit(1)
185+
186+
logging.info("Data sets loaded ...")
187+
kwargs["n_quantiles"] = kwargs["quantiles"]
188+
del kwargs["quantiles"]
189+
190+
logging.info("Applying %s ..." % kwargs["method"])
191+
result = adjust(**kwargs)
192+
193+
logging.info("Saving result to %s ..." % kwargs["output"])
194+
result.to_netcdf(kwargs["output"])

doc/cli.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.. -*- coding: utf-8 -*-
2+
.. Copyright (C) 2023 Benjamin Thomas Schwertfeger
3+
.. GitHub: https://github.com/btschwertfeger
4+
..
5+
6+
Command-Line Interface
7+
======================
8+
9+
The command-line interface provides the following help instructions
10+
11+
.. code-block:: bash
12+
13+
cmethods --help
14+
15+
Usage: cmethods [OPTIONS]
16+
17+
Command line tool to apply bias adjustment procedures to climate data.
18+
19+
Scaling-Based Adjustment Options:
20+
[all required if --method="linear_scaling" and --method="variance_scaling" and
21+
--method="delta_method"]
22+
--group TEXT Temporal grouping
23+
24+
Distribution-Based Adjustment Options:
25+
[all required if --method="quantile_mapping" and
26+
--method="quantile_delta_mapping"]
27+
--quantiles INTEGER Quantiles to respect
28+
29+
Other options:
30+
--version Show the version and exit.
31+
--obs, --observations PATH Reference data set (control period) [required]
32+
--simh, --simulated-historical PATH
33+
Modeled data set (control period) [required]
34+
--simp, --simulated-scenario PATH
35+
Modeled data set (scenario period) [required]
36+
--method [linear_scaling|variance_scaling|delta_method|quantile_mapping|quantile_delta_mapping]
37+
Bias adjustment method to apply [required]
38+
--kind [add|mult] Kind of adjustment [required]
39+
--variable TEXT Variable of interest [required]
40+
-o, --output TEXT Output file name [required]
41+
-h, --help Show this message and exit.

doc/cmethods.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.. -*- coding: utf-8 -*-
2+
.. Copyright (C) 2023 Benjamin Thomas Schwertfeger
3+
.. GitHub: https://github.com/btschwertfeger
4+
..
15
26
Classes and Functions
37
=====================

0 commit comments

Comments
 (0)