nctoolkit 1.1.10__tar.gz → 1.2.0__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.
- nctoolkit-1.2.0/CONTRIBUTIONS.md +39 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/PKG-INFO +1 -2
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/cheatsheet/nctoolkit_cheatsheet.pdf +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/cheatsheet/nctoolkit_cheatsheet.pptx +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/__init__.py +1 -1
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/assign.py +2 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/cleanup.py +4 -4
- nctoolkit-1.2.0/nctoolkit/create_ensemble.py +117 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/plot.py +0 -2
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/runners.py +11 -1
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/setters.py +6 -5
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/static_plot.py +52 -7
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/subset.py +11 -5
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/toxarray.py +11 -2
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/verticals.py +3 -2
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit.egg-info/SOURCES.txt +1 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/setup.py +1 -2
- nctoolkit-1.1.10/nctoolkit/create_ensemble.py +0 -57
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/CITATION.cff +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/CODE_OF_CONDUCT.md +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/LICENSE +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/MANIFEST.in +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/README.md +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/benchmarks/benchmark_nctoolkit_versus_xarray.ipynb +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/checklists/api_checker.ipynb +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/data/geotiff.tif +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/data/pubplot_test.png +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/data/test1.html +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/data/test2.html +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/add_etc.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/anomaly.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/api.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/append.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/cdo_command.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/cellareas.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/centres.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/checks.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/clear.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/compare.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/compare_data.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/corr.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/crop.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/deprecated.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/distgrid.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/drop.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/ensembles.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/esoteric.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/experimental.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/fill.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/flatten.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/fldstat.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/format.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/generate_grid.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/inttime.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/invert.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/masking.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/matchpoint.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/mergers.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/meridonials.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/mp_adders.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/mp_matchers.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/mp_matchups.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/nco_command.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/phenology.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/reduce.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/reduce_grid.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/regrid.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/remove.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/rename.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/resample.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/rollstat.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/run.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/runthis.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/session.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/set.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/shape.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/shift.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/show.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/split.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/strip_vars.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/sumall.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/temp_file.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/temporal_stat.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/temporals.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/thresholds.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/to_lonlat.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/to_nc.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/tozlev.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/unify.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/utils.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/validator.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/validator_funs.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/zip.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/nctoolkit/zonals.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/requirements.txt +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/setup.cfg +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/testing/test_parallel.py +0 -0
- {nctoolkit-1.1.10 → nctoolkit-1.2.0}/testing/test_scripting.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Contributing to nctoolkit
|
|
2
|
+
|
|
3
|
+
🎉 Thank you for your interest in contributing to nctoolkit! Your efforts help improve this tool for the entire community. Whether you're fixing bugs, adding new features, or enhancing documentation, your contributions are invaluable. Let's make this project even better together! 🎉
|
|
4
|
+
|
|
5
|
+
How to Contribute
|
|
6
|
+
|
|
7
|
+
To contribute, please follow these steps:
|
|
8
|
+
|
|
9
|
+
1. Fork the Repository
|
|
10
|
+
Click the "Fork" button at the top right of this page to create a copy of this repository under your GitHub account.
|
|
11
|
+
|
|
12
|
+
2. Clone and Set Up the Repository
|
|
13
|
+
After forking, open your terminal, clone the repository to your local machine, and navigate into the project directory:
|
|
14
|
+
git clone https://github.com/your-username/nctoolkit.git
|
|
15
|
+
cd nctoolkit
|
|
16
|
+
|
|
17
|
+
3. Create a New Branch
|
|
18
|
+
Create a new branch for your contribution to keep your changes organized and separated from the main codebase:
|
|
19
|
+
git checkout -b feature/your-feature-name
|
|
20
|
+
|
|
21
|
+
4. Make Your Changes
|
|
22
|
+
Add your feature, fix bugs, or improve documentation. Be sure to:
|
|
23
|
+
Keep your changes focused on a single feature or fix for easier review.
|
|
24
|
+
Test your modifications to ensure they work as intended.
|
|
25
|
+
Follow any coding style guidelines noted in the project.
|
|
26
|
+
|
|
27
|
+
5. Commit Your Changes
|
|
28
|
+
When you’re ready, stage and commit your changes with a clear, descriptive commit message:
|
|
29
|
+
git add .
|
|
30
|
+
git commit -m "Add detailed description of your changes here"
|
|
31
|
+
|
|
32
|
+
6. Push to Your Fork
|
|
33
|
+
Push your branch to your GitHub repository:
|
|
34
|
+
git push origin feature/your-feature-name
|
|
35
|
+
|
|
36
|
+
7. Submit a Pull Request (PR)
|
|
37
|
+
Go to the original nctoolkit repository on GitHub.
|
|
38
|
+
Click the "New Pull Request" button and select your branch.
|
|
39
|
+
Include a detailed description of your changes to help reviewers understand your contribution.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nctoolkit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: A Python package for netCDF analysis and post-processing
|
|
5
5
|
Home-page: https://github.com/pmlmodelling/nctoolkit
|
|
6
6
|
Author: Robert Wilson
|
|
@@ -56,7 +56,6 @@ Core abilities of nctoolkit include:
|
|
|
56
56
|
- Calculating the correlations between variables
|
|
57
57
|
- Calculating vertical statistics for the likes of oceanic data
|
|
58
58
|
- Calculating ensemble statistics
|
|
59
|
-
- Calculating phenological metrics
|
|
60
59
|
|
|
61
60
|
Operation of the package requires the installation of Climate Data Operators (CDO). This is the computational backend for most of the methods used. No knowledge of CDO is required to use nctoolkit. A couple of methods provide users with the option of using netCDF Operators (NCO) as a backend. Guidance for how to install the backends are available `here <https://nctoolkit.readthedocs.io/en/stable/installing.html>`__.
|
|
62
61
|
|
|
Binary file
|
|
Binary file
|
|
@@ -28,7 +28,7 @@ atexit.register(clean_all)
|
|
|
28
28
|
|
|
29
29
|
signal.signal(signal.SIGTERM, clean_all)
|
|
30
30
|
|
|
31
|
-
from nctoolkit.create_ensemble import create_ensemble
|
|
31
|
+
from nctoolkit.create_ensemble import create_ensemble, glob
|
|
32
32
|
from nctoolkit.session import session_files
|
|
33
33
|
from nctoolkit.show import nc_variables, nc_years, nc_months, nc_times
|
|
34
34
|
|
|
@@ -145,12 +145,12 @@ def temp_check():
|
|
|
145
145
|
if len(mylist) > 0:
|
|
146
146
|
if len(mylist) == 1:
|
|
147
147
|
print(
|
|
148
|
-
f"{len(mylist)} file was created by nctoolkit in prior or current "
|
|
148
|
+
f"{len(mylist)} temporary file was created by nctoolkit in prior or current "
|
|
149
149
|
f"sessions. Consider running deep_clean!"
|
|
150
150
|
)
|
|
151
151
|
else:
|
|
152
152
|
print(
|
|
153
|
-
f"{len(mylist)} files were created by nctoolkit in prior or current"
|
|
153
|
+
f"{len(mylist)} temporary files were created by nctoolkit in prior or current"
|
|
154
154
|
f" sessions. Consider running deep_clean!"
|
|
155
155
|
)
|
|
156
156
|
|
|
@@ -163,12 +163,12 @@ def temp_check():
|
|
|
163
163
|
if len(mylist) > 0:
|
|
164
164
|
if len(mylist) == 1:
|
|
165
165
|
print(
|
|
166
|
-
f"{len(mylist)} file was created by nctoolkit in prior or current "
|
|
166
|
+
f"{len(mylist)} temporary file was created by nctoolkit in prior or current "
|
|
167
167
|
f"sessions. Consider running deep_clean!"
|
|
168
168
|
)
|
|
169
169
|
else:
|
|
170
170
|
print(
|
|
171
|
-
f"{len(mylist)} files were created by nctoolkit in prior or "
|
|
171
|
+
f"{len(mylist)} temporary files were created by nctoolkit in prior or "
|
|
172
172
|
f"current sessions. Consider running deep_clean!"
|
|
173
173
|
)
|
|
174
174
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from glob import glob as globber
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
# function to find files in directory with a specified variable
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_ensemble(path="", recursive=True):
|
|
8
|
+
"""
|
|
9
|
+
create_ensemble: Generate an ensemble of files from a directory.
|
|
10
|
+
|
|
11
|
+
Parameters
|
|
12
|
+
-------------
|
|
13
|
+
path: str
|
|
14
|
+
The directory to search for netCDF files
|
|
15
|
+
recursive : boolean
|
|
16
|
+
True/False depending on whether you want to search the path recursively.
|
|
17
|
+
Defaults to True.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------------
|
|
21
|
+
|
|
22
|
+
files : list of files
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
------------
|
|
26
|
+
|
|
27
|
+
If you wanted to recursively find all netCDF files available in a directory "data", you would do this:
|
|
28
|
+
|
|
29
|
+
>>> import nctoolkit as nc
|
|
30
|
+
>>> nc.create_ensemble("data")
|
|
31
|
+
|
|
32
|
+
If you wanted to find the files in that directory and ignore subdirectories, you would instead do this:
|
|
33
|
+
|
|
34
|
+
>>> nc.create_ensemble("data", recursive = False)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# make sure the path exists
|
|
40
|
+
|
|
41
|
+
if os.path.exists(path) is False:
|
|
42
|
+
raise ValueError("The path provided does not exist!")
|
|
43
|
+
|
|
44
|
+
# make sure the path ends with "/" if it is not empty
|
|
45
|
+
if path != "":
|
|
46
|
+
if path.endswith("/") is False:
|
|
47
|
+
path = path + "/"
|
|
48
|
+
|
|
49
|
+
if recursive:
|
|
50
|
+
files = [f for f in globber(path + "/**/*.nc*", recursive=True)]
|
|
51
|
+
else:
|
|
52
|
+
files = [f for f in globber(path + "*.nc*")]
|
|
53
|
+
|
|
54
|
+
if len(files) == 0:
|
|
55
|
+
raise ValueError("There is no data in the target directory")
|
|
56
|
+
|
|
57
|
+
return files
|
|
58
|
+
|
|
59
|
+
def glob(path="", recursive=True):
|
|
60
|
+
"""
|
|
61
|
+
create_ensemble: Generate an ensemble of files from a directory.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
-------------
|
|
65
|
+
path: str
|
|
66
|
+
The directory to search for netCDF files
|
|
67
|
+
recursive : boolean
|
|
68
|
+
True/False depending on whether you want to search the path recursively.
|
|
69
|
+
Defaults to True.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------------
|
|
73
|
+
|
|
74
|
+
files : list of files
|
|
75
|
+
|
|
76
|
+
Examples
|
|
77
|
+
------------
|
|
78
|
+
|
|
79
|
+
If you wanted to recursively find all netCDF files available in a directory "data", you would do this:
|
|
80
|
+
|
|
81
|
+
>>> import nctoolkit as nc
|
|
82
|
+
>>> nc.create_ensemble("data")
|
|
83
|
+
|
|
84
|
+
If you wanted to find the files in that directory and ignore subdirectories, you would instead do this:
|
|
85
|
+
|
|
86
|
+
>>> nc.create_ensemble("data", recursive = False)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
# make sure the path exists
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
files = globber(path, recursive = recursive)
|
|
95
|
+
if [x for x in files if x.endswith(".nc")]:
|
|
96
|
+
return files
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError("There are no netCDF files in the target directory")
|
|
99
|
+
except:
|
|
100
|
+
|
|
101
|
+
if os.path.exists(path) is False:
|
|
102
|
+
raise ValueError("The path provided does not exist!")
|
|
103
|
+
|
|
104
|
+
# make sure the path ends with "/" if it is not empty
|
|
105
|
+
if path != "":
|
|
106
|
+
if path.endswith("/") is False:
|
|
107
|
+
path = path + "/"
|
|
108
|
+
|
|
109
|
+
if recursive:
|
|
110
|
+
files = [f for f in globber(path + "/**/*.nc*", recursive=True)]
|
|
111
|
+
else:
|
|
112
|
+
files = [f for f in globber(path + "*.nc*")]
|
|
113
|
+
|
|
114
|
+
if len(files) == 0:
|
|
115
|
+
raise ValueError("There is no data in the target directory")
|
|
116
|
+
|
|
117
|
+
return files
|
|
@@ -44,8 +44,6 @@ def plot(self, vars=None, autoscale=True, out=None, coast=None, **kwargs):
|
|
|
44
44
|
A string or list of the variables to plot
|
|
45
45
|
autoscale: bool
|
|
46
46
|
Set to True if you want the colorbar to be scaled to the min/max of the data. Default is True
|
|
47
|
-
vars: str , list
|
|
48
|
-
A string or list of the variables to plot
|
|
49
47
|
out: str
|
|
50
48
|
Name of output file if you want to save as html. Defaults to None.
|
|
51
49
|
coast: bool
|
|
@@ -22,6 +22,12 @@ from nctoolkit.temp_file import temp_file
|
|
|
22
22
|
|
|
23
23
|
from nctoolkit.show import nc_variables
|
|
24
24
|
|
|
25
|
+
def ignore_warning(x):
|
|
26
|
+
text = re.compile("remap weights from .* not used")
|
|
27
|
+
if len(text.findall(x)) > 0:
|
|
28
|
+
return True
|
|
29
|
+
return False
|
|
30
|
+
|
|
25
31
|
def ann_anomaly(
|
|
26
32
|
ff, baseline, metric, window, align, precision, new_files, new_commands, nc_safe):
|
|
27
33
|
"""
|
|
@@ -840,10 +846,14 @@ def run_cdo(command=None, target=None, out_file=None, overwrite=False, precision
|
|
|
840
846
|
else:
|
|
841
847
|
message = f"The following month was missing in the dataset: {sel_month}"
|
|
842
848
|
warnings.warn(message=message)
|
|
849
|
+
#remap weights from /tmp/nctoolkit_rwi_brtnahgznctoolkittmpqwadyf2y.nc not used, lonlat (984x582) grid with mask (407783) not found!
|
|
850
|
+
# detect regex pattern, based on above
|
|
851
|
+
|
|
843
852
|
for ww in w:
|
|
844
853
|
if platform.system() == "Linux":
|
|
845
854
|
if ww.message not in session_warnings:
|
|
846
|
-
|
|
855
|
+
if ignore_warning(str(ww.message)) is False:
|
|
856
|
+
session_warnings.append(ww.message)
|
|
847
857
|
else:
|
|
848
858
|
warnings.warn(ww.message)
|
|
849
859
|
#print(session_warnings)
|
|
@@ -278,12 +278,13 @@ def set_units(self, unit_dict=None, **kwargs):
|
|
|
278
278
|
|
|
279
279
|
# change the units in turn. This doesn't seem to be something you can chain?
|
|
280
280
|
for i in unit_dict:
|
|
281
|
-
if not
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
281
|
+
if unit_dict[i] is not None:
|
|
282
|
+
if not isinstance(i, str):
|
|
283
|
+
raise TypeError("key,values in unit_dict are not strings")
|
|
284
|
+
if not isinstance(unit_dict[i], str):
|
|
285
|
+
raise TypeError("key,values in unit_dict are not strings")
|
|
285
286
|
|
|
286
|
-
cdo_command = f'-setattribute,{i}@units="{unit_dict[i]}"'
|
|
287
|
+
cdo_command = f'-setattribute,{i}@units="{unit_dict[str(i)]}"'
|
|
287
288
|
self.cdo_command(cdo_command, ensemble=False)
|
|
288
289
|
|
|
289
290
|
|
|
@@ -93,6 +93,7 @@ def pub_plot(
|
|
|
93
93
|
out=None,
|
|
94
94
|
breaks=None,
|
|
95
95
|
dpi = "figure",
|
|
96
|
+
font = None,
|
|
96
97
|
**kwargs,
|
|
97
98
|
):
|
|
98
99
|
"""
|
|
@@ -360,8 +361,8 @@ def pub_plot(
|
|
|
360
361
|
|
|
361
362
|
# set globe to True if lon lat spread is big enough
|
|
362
363
|
|
|
363
|
-
if np.max(lon) - np.min(lon) >
|
|
364
|
-
if np.max(lat) - np.min(lat) >
|
|
364
|
+
if np.max(lon) - np.min(lon) > 340:
|
|
365
|
+
if np.max(lat) - np.min(lat) > 160:
|
|
365
366
|
globe = True
|
|
366
367
|
|
|
367
368
|
# proj=ccrs.LambertConformal(central_longitude=np.mean(lon), central_latitude=np.mean(lat), false_easting=0.0, false_northing=0.0, cutoff=38)
|
|
@@ -388,6 +389,13 @@ def pub_plot(
|
|
|
388
389
|
|
|
389
390
|
if projection == None:
|
|
390
391
|
proj = ccrs.PlateCarree()
|
|
392
|
+
|
|
393
|
+
if isinstance(projection, ccrs.NorthPolarStereo) and size == "auto":
|
|
394
|
+
size = [8,8]
|
|
395
|
+
if isinstance(projection, ccrs.SouthPolarStereo) and size == "auto":
|
|
396
|
+
size = [8,8]
|
|
397
|
+
if isinstance(projection, ccrs.AzimuthalEquidistant) and size == "auto":
|
|
398
|
+
size = [12, 8]
|
|
391
399
|
if size == "auto":
|
|
392
400
|
size = [10, 10]
|
|
393
401
|
if proj == ccrs.PlateCarree() or globe:
|
|
@@ -507,9 +515,13 @@ def pub_plot(
|
|
|
507
515
|
if r_max is not None:
|
|
508
516
|
vmax = np.nanpercentile(np.ma.filled(values, np.nan), r_max)
|
|
509
517
|
|
|
510
|
-
if robust
|
|
518
|
+
if robust:
|
|
511
519
|
vmin = np.nanpercentile(np.ma.filled(values, np.nan), 2)
|
|
512
520
|
vmax = np.nanpercentile(np.ma.filled(values, np.nan), 98)
|
|
521
|
+
limits = [0,0]
|
|
522
|
+
limits[0] = vmin
|
|
523
|
+
limits[1] = vmax
|
|
524
|
+
|
|
513
525
|
if limits is None:
|
|
514
526
|
if r_min is not None:
|
|
515
527
|
vmin = np.nanpercentile(np.ma.filled(values, np.nan), r_min)
|
|
@@ -684,10 +696,34 @@ def pub_plot(
|
|
|
684
696
|
else:
|
|
685
697
|
fraction = 0.046 * size[1] / size[0]
|
|
686
698
|
|
|
699
|
+
min_value = np.min(values)
|
|
700
|
+
max_value = np.max(values)
|
|
701
|
+
|
|
702
|
+
min_arrow = False
|
|
703
|
+
|
|
704
|
+
if vmin is not None:
|
|
705
|
+
if vmin > min_value:
|
|
706
|
+
min_arrow = True
|
|
707
|
+
max_arrow = False
|
|
708
|
+
if vmax is not None:
|
|
709
|
+
if vmax < max_value:
|
|
710
|
+
max_arrow = True
|
|
711
|
+
|
|
712
|
+
if min_arrow and max_arrow:
|
|
713
|
+
extend = "both"
|
|
714
|
+
else:
|
|
715
|
+
if min_arrow:
|
|
716
|
+
extend = "min"
|
|
717
|
+
else:
|
|
718
|
+
if max_arrow:
|
|
719
|
+
extend = "max"
|
|
720
|
+
else:
|
|
721
|
+
extend = "neither"
|
|
722
|
+
|
|
687
723
|
if l_location == "bottom":
|
|
688
|
-
cb = plt.colorbar(im, fraction=fraction, pad=0.04, location=l_location)
|
|
724
|
+
cb = plt.colorbar(im, fraction=fraction, pad=0.04, location=l_location, extend = extend)
|
|
689
725
|
else:
|
|
690
|
-
cb = plt.colorbar(im, fraction=fraction, pad=0.04)
|
|
726
|
+
cb = plt.colorbar(im, fraction=fraction, pad=0.04, extend = extend)
|
|
691
727
|
|
|
692
728
|
# add breaks to colorbar cb
|
|
693
729
|
if breaks is not None:
|
|
@@ -742,11 +778,12 @@ def pub_plot(
|
|
|
742
778
|
"Unable to parse legend from dataset contents. Check long names and units"
|
|
743
779
|
)
|
|
744
780
|
label = ""
|
|
781
|
+
# label may need some slight tidying
|
|
745
782
|
|
|
746
783
|
if l_location == "bottom":
|
|
747
784
|
label = "\n".join(wrap(label, 120))
|
|
748
785
|
else:
|
|
749
|
-
label = "\n".join(wrap(label,
|
|
786
|
+
label = "\n".join(wrap(label, 30))
|
|
750
787
|
|
|
751
788
|
if l_location == "bottom":
|
|
752
789
|
cbax.set_xlabel(label)
|
|
@@ -755,10 +792,18 @@ def pub_plot(
|
|
|
755
792
|
|
|
756
793
|
if legend_position is None:
|
|
757
794
|
cb.remove()
|
|
795
|
+
|
|
796
|
+
if font is not None:
|
|
797
|
+
for text in [ax.title, ax.xaxis.label, ax.yaxis.label, cb.ax.yaxis.label, cb.ax.xaxis.label]:
|
|
798
|
+
text.set_fontsize(font)
|
|
799
|
+
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label, cb.ax.yaxis.label, cb.ax.xaxis.label] +
|
|
800
|
+
ax.get_xticklabels() + ax.get_yticklabels()):
|
|
801
|
+
item.set_fontsize(font)
|
|
758
802
|
|
|
759
803
|
if out is not None:
|
|
760
804
|
print("saving as file")
|
|
761
805
|
plt.savefig(out, dpi = dpi)
|
|
806
|
+
plt.savefig(out, dpi = dpi)
|
|
762
807
|
|
|
763
808
|
|
|
764
809
|
def quiver_plot(ds, u=None, v=None, **kwargs):
|
|
@@ -815,4 +860,4 @@ def quiver_plot(ds, u=None, v=None, **kwargs):
|
|
|
815
860
|
if v not in vars:
|
|
816
861
|
raise ValueError("v not in dataset")
|
|
817
862
|
|
|
818
|
-
pub_plot(ds, quiver=True, u=u, v=v, **kwargs)
|
|
863
|
+
pub_plot(ds, quiver=True, u=u, v=v, **kwargs)
|
|
@@ -45,24 +45,30 @@ def select_levels(self, levels=None):
|
|
|
45
45
|
levels : list
|
|
46
46
|
List of the form [min_level, max_level]. Levels/depth between the two will be selected
|
|
47
47
|
"""
|
|
48
|
+
ds_levels = self.levels
|
|
49
|
+
|
|
48
50
|
if not isinstance(levels, list):
|
|
49
51
|
type(levels)
|
|
50
52
|
try:
|
|
51
53
|
levels = float(levels)
|
|
52
54
|
except:
|
|
53
55
|
raise ValueError("levels provided are not valid!")
|
|
56
|
+
levels = [float(levels), float(levels)]
|
|
54
57
|
|
|
55
58
|
if isinstance(levels, list):
|
|
56
59
|
try:
|
|
57
|
-
levels[0] =
|
|
58
|
-
levels[1] =
|
|
60
|
+
levels[0] = float(levels[0])
|
|
61
|
+
levels[1] = float(levels[1])
|
|
59
62
|
except:
|
|
60
63
|
raise ValueError("levels provided are not valid!")
|
|
61
64
|
if levels[0] > levels[1]:
|
|
62
65
|
raise ValueError("levels have the wrong order")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
|
|
67
|
+
sel_levels = [str(x) for x in ds_levels if levels[0] <= x <= levels[1]]
|
|
68
|
+
if len(sel_levels) == 0:
|
|
69
|
+
raise ValueError("No levels found in the dataset")
|
|
70
|
+
sel_levels = ",".join(sel_levels)
|
|
71
|
+
cdo_command = f"-sellevel,{sel_levels}"
|
|
66
72
|
|
|
67
73
|
self.cdo_command(cdo_command, ensemble=False)
|
|
68
74
|
|
|
@@ -102,7 +102,7 @@ def to_xarray(self, decode_times=True, **kwargs):
|
|
|
102
102
|
return data
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def to_dataframe(self, decode_times=True, **kwargs):
|
|
105
|
+
def to_dataframe(self, decode_times=True, drop_bnds = True, **kwargs):
|
|
106
106
|
"""
|
|
107
107
|
to_dataframe: Convert a dataset to a pandas data frame
|
|
108
108
|
|
|
@@ -111,6 +111,10 @@ def to_dataframe(self, decode_times=True, **kwargs):
|
|
|
111
111
|
decode_times: boolean
|
|
112
112
|
Set to False if you do not want xarray to decode the times prior to
|
|
113
113
|
conversion to data frame. Default is True.
|
|
114
|
+
drop_bnds: boolean
|
|
115
|
+
Set to False if you do not want to drop the bounds from the data frame.
|
|
116
|
+
Variables/coordinates with '_bnds' in their names will be dropped.
|
|
117
|
+
Default is True.
|
|
114
118
|
**kwargs : kwargs
|
|
115
119
|
Optional arguments to be sent to subset.
|
|
116
120
|
|
|
@@ -125,4 +129,9 @@ def to_dataframe(self, decode_times=True, **kwargs):
|
|
|
125
129
|
>>> ds.to_dataframe()
|
|
126
130
|
|
|
127
131
|
"""
|
|
128
|
-
|
|
132
|
+
df = self.to_xarray(decode_times=decode_times, **kwargs).to_dataframe()
|
|
133
|
+
df = df.reset_index()
|
|
134
|
+
if drop_bnds:
|
|
135
|
+
df = df.drop(columns=[col for col in df.columns if 'bnds' in col])
|
|
136
|
+
df = df.drop_duplicates().reset_index(drop = True)
|
|
137
|
+
return df
|
|
@@ -180,6 +180,9 @@ def vertical_interp(
|
|
|
180
180
|
if thickness is not None:
|
|
181
181
|
fixed = False
|
|
182
182
|
|
|
183
|
+
if isinstance(levels, (int, float)):
|
|
184
|
+
levels = [levels]
|
|
185
|
+
|
|
183
186
|
if fixed is False:
|
|
184
187
|
self.to_zlevels(
|
|
185
188
|
levels=levels, thickness=thickness, depths=depths, surface=surface
|
|
@@ -191,8 +194,6 @@ def vertical_interp(
|
|
|
191
194
|
|
|
192
195
|
# first a quick fix for the case when there is only one vertical depth
|
|
193
196
|
|
|
194
|
-
if isinstance(levels, (int, float)):
|
|
195
|
-
levels = [levels]
|
|
196
197
|
|
|
197
198
|
# levels = [float(x) for x in levels]
|
|
198
199
|
|
|
@@ -21,7 +21,6 @@ Core abilities of nctoolkit include:
|
|
|
21
21
|
- Calculating the correlations between variables
|
|
22
22
|
- Calculating vertical statistics for the likes of oceanic data
|
|
23
23
|
- Calculating ensemble statistics
|
|
24
|
-
- Calculating phenological metrics
|
|
25
24
|
|
|
26
25
|
Operation of the package requires the installation of Climate Data Operators (CDO). This is the computational backend for most of the methods used. No knowledge of CDO is required to use nctoolkit. A couple of methods provide users with the option of using netCDF Operators (NCO) as a backend. Guidance for how to install the backends are available `here <https://nctoolkit.readthedocs.io/en/stable/installing.html>`__.
|
|
27
26
|
|
|
@@ -47,7 +46,7 @@ extras_require["complete"] = ["geoviews", "rioxarray", "cfchecker", "geocube", "
|
|
|
47
46
|
REQUIREMENTS = [i.strip() for i in open("requirements.txt").readlines()]
|
|
48
47
|
|
|
49
48
|
setup(name='nctoolkit',
|
|
50
|
-
version='1.
|
|
49
|
+
version='1.2.0',
|
|
51
50
|
description=DESCRIPTION,
|
|
52
51
|
description_content_type='text/plain',
|
|
53
52
|
long_description=LONG_DESCRIPTION,
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import glob
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
# function to find files in directory with a specified variable
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def create_ensemble(path="", recursive=True):
|
|
8
|
-
"""
|
|
9
|
-
create_ensemble: Generate an ensemble of files from a directory.
|
|
10
|
-
|
|
11
|
-
Parameters
|
|
12
|
-
-------------
|
|
13
|
-
path: str
|
|
14
|
-
The directory to search for netCDF files
|
|
15
|
-
recursive : boolean
|
|
16
|
-
True/False depending on whether you want to search the path recursively.
|
|
17
|
-
Defaults to True.
|
|
18
|
-
|
|
19
|
-
Returns
|
|
20
|
-
-------------
|
|
21
|
-
|
|
22
|
-
files : list of files
|
|
23
|
-
|
|
24
|
-
Examples
|
|
25
|
-
------------
|
|
26
|
-
|
|
27
|
-
If you wanted to recursively find all netCDF files available in a directory "data", you would do this:
|
|
28
|
-
|
|
29
|
-
>>> import nctoolkit as nc
|
|
30
|
-
>>> nc.create_ensemble("data")
|
|
31
|
-
|
|
32
|
-
If you wanted to find the files in that directory and ignore subdirectories, you would instead do this:
|
|
33
|
-
|
|
34
|
-
>>> nc.create_ensemble("data", recursive = False)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
# make sure the path exists
|
|
40
|
-
|
|
41
|
-
if os.path.exists(path) is False:
|
|
42
|
-
raise ValueError("The path provided does not exist!")
|
|
43
|
-
|
|
44
|
-
# make sure the path ends with "/" if it is not empty
|
|
45
|
-
if path != "":
|
|
46
|
-
if path.endswith("/") is False:
|
|
47
|
-
path = path + "/"
|
|
48
|
-
|
|
49
|
-
if recursive:
|
|
50
|
-
files = [f for f in glob.glob(path + "/**/*.nc*", recursive=True)]
|
|
51
|
-
else:
|
|
52
|
-
files = [f for f in glob.glob(path + "*.nc*")]
|
|
53
|
-
|
|
54
|
-
if len(files) == 0:
|
|
55
|
-
raise ValueError("There is no data in the target directory")
|
|
56
|
-
|
|
57
|
-
return files
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|