pydartdiags 0.0.42__tar.gz → 0.5.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.

Potentially problematic release.


This version of pydartdiags might be problematic. Click here for more details.

Files changed (30) hide show
  1. pydartdiags-0.5.0/PKG-INFO +49 -0
  2. pydartdiags-0.5.0/README.md +24 -0
  3. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/pyproject.toml +3 -2
  4. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/setup.py +1 -1
  5. pydartdiags-0.5.0/src/pydartdiags/matplots/matplots.py +243 -0
  6. pydartdiags-0.5.0/src/pydartdiags/obs_sequence/obs_sequence.py +1123 -0
  7. pydartdiags-0.5.0/src/pydartdiags/plots/__init__.py +0 -0
  8. pydartdiags-0.5.0/src/pydartdiags/plots/plots.py +191 -0
  9. pydartdiags-0.5.0/src/pydartdiags/stats/__init__.py +0 -0
  10. pydartdiags-0.5.0/src/pydartdiags/stats/stats.py +323 -0
  11. pydartdiags-0.5.0/src/pydartdiags.egg-info/PKG-INFO +49 -0
  12. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/SOURCES.txt +6 -1
  13. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/requires.txt +1 -0
  14. pydartdiags-0.5.0/tests/test_obs_sequence.py +452 -0
  15. pydartdiags-0.5.0/tests/test_plots.py +8 -0
  16. pydartdiags-0.5.0/tests/test_stats.py +82 -0
  17. pydartdiags-0.0.42/PKG-INFO +0 -404
  18. pydartdiags-0.0.42/README.md +0 -383
  19. pydartdiags-0.0.42/src/pydartdiags/obs_sequence/obs_sequence.py +0 -761
  20. pydartdiags-0.0.42/src/pydartdiags/plots/plots.py +0 -161
  21. pydartdiags-0.0.42/src/pydartdiags.egg-info/PKG-INFO +0 -404
  22. pydartdiags-0.0.42/tests/test_obs_sequence.py +0 -87
  23. pydartdiags-0.0.42/tests/test_plots.py +0 -52
  24. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/LICENSE +0 -0
  25. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/setup.cfg +0 -0
  26. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/src/pydartdiags/__init__.py +0 -0
  27. {pydartdiags-0.0.42/src/pydartdiags/obs_sequence → pydartdiags-0.5.0/src/pydartdiags/matplots}/__init__.py +0 -0
  28. {pydartdiags-0.0.42/src/pydartdiags/plots → pydartdiags-0.5.0/src/pydartdiags/obs_sequence}/__init__.py +0 -0
  29. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/dependency_links.txt +0 -0
  30. {pydartdiags-0.0.42 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/top_level.txt +0 -0
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.2
2
+ Name: pydartdiags
3
+ Version: 0.5.0
4
+ Summary: Observation Sequence Diagnostics for DART
5
+ Home-page: https://github.com/NCAR/pyDARTdiags.git
6
+ Author: Helen Kershaw
7
+ Author-email: Helen Kershaw <hkershaw@ucar.edu>
8
+ Project-URL: Homepage, https://github.com/NCAR/pyDARTdiags.git
9
+ Project-URL: Issues, https://github.com/NCAR/pyDARTdiags/issues
10
+ Project-URL: Documentation, https://ncar.github.io/pyDARTdiags
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: pandas>=2.2.0
18
+ Requires-Dist: numpy>=1.26
19
+ Requires-Dist: plotly>=5.22.0
20
+ Requires-Dist: pyyaml>=6.0.2
21
+ Requires-Dist: matplotlib>=3.9.4
22
+ Dynamic: author
23
+ Dynamic: home-page
24
+ Dynamic: requires-python
25
+
26
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
27
+ [![codecov](https://codecov.io/gh/NCAR/pyDARTdiags/graph/badge.svg?token=VK55SQZSVD)](https://codecov.io/gh/NCAR/pyDARTdiags)
28
+ [![PyPI version](https://badge.fury.io/py/pydartdiags.svg)](https://pypi.org/project/pydartdiags/)
29
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
30
+
31
+ # pyDARTdiags
32
+
33
+ pyDARTdiags is a Python library for observation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
34
+
35
+ pyDARTdiags is under initial development, so please use caution.
36
+ The MATLAB [observation space diagnostics](https://docs.dart.ucar.edu/en/latest/guide/matlab-observation-space.html) are available through [DART](https://github.com/NCAR/DART).
37
+
38
+
39
+ pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
40
+ Documentation : https://ncar.github.io/pyDARTdiags/
41
+
42
+ ## Contributing
43
+ Contributions are welcome! If you have a feature request, bug report, or a suggestion, please open an issue on our GitHub repository.
44
+ Please read our [Contributors Guide](https://github.com/NCAR/pyDARTdiags/blob/main/CONTRIBUTING.md) if you would like to contribute to
45
+ pyDARTdiags.
46
+
47
+ ## License
48
+
49
+ pyDARTdiags is released under the Apache License 2.0. For more details, see the LICENSE file in the root directory of this source tree or visit [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
@@ -0,0 +1,24 @@
1
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
2
+ [![codecov](https://codecov.io/gh/NCAR/pyDARTdiags/graph/badge.svg?token=VK55SQZSVD)](https://codecov.io/gh/NCAR/pyDARTdiags)
3
+ [![PyPI version](https://badge.fury.io/py/pydartdiags.svg)](https://pypi.org/project/pydartdiags/)
4
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
5
+
6
+ # pyDARTdiags
7
+
8
+ pyDARTdiags is a Python library for observation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
9
+
10
+ pyDARTdiags is under initial development, so please use caution.
11
+ The MATLAB [observation space diagnostics](https://docs.dart.ucar.edu/en/latest/guide/matlab-observation-space.html) are available through [DART](https://github.com/NCAR/DART).
12
+
13
+
14
+ pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
15
+ Documentation : https://ncar.github.io/pyDARTdiags/
16
+
17
+ ## Contributing
18
+ Contributions are welcome! If you have a feature request, bug report, or a suggestion, please open an issue on our GitHub repository.
19
+ Please read our [Contributors Guide](https://github.com/NCAR/pyDARTdiags/blob/main/CONTRIBUTING.md) if you would like to contribute to
20
+ pyDARTdiags.
21
+
22
+ ## License
23
+
24
+ pyDARTdiags is released under the Apache License 2.0. For more details, see the LICENSE file in the root directory of this source tree or visit [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pydartdiags"
7
- version = "0.0.42"
7
+ version = "0.5.0"
8
8
  authors = [
9
9
  { name="Helen Kershaw", email="hkershaw@ucar.edu" },
10
10
  ]
@@ -20,7 +20,8 @@ dependencies = [
20
20
  "pandas>=2.2.0",
21
21
  "numpy>=1.26",
22
22
  "plotly>=5.22.0",
23
- "pyyaml>=6.0.2"
23
+ "pyyaml>=6.0.2",
24
+ "matplotlib>=3.9.4"
24
25
  ]
25
26
 
26
27
  [project.urls]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="pydartdiags",
5
- version="0.0.41",
5
+ version="0.0.43",
6
6
  packages=find_packages(where="src"),
7
7
  package_dir={"": "src"},
8
8
  author="Helen Kershaw",
@@ -0,0 +1,243 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ from pydartdiags.stats import stats
3
+ import matplotlib.pyplot as plt
4
+
5
+ # HK @todo color scheme class
6
+ dacolors = ["green", "magenta", "orange", "red"]
7
+
8
+
9
+ def plot_profile(obs_seq, levels, type, bias=True, rmse=True, totalspread=True):
10
+ """
11
+ plot_profile on the levels for prior and posterior if present
12
+ - bias
13
+ - rmse
14
+ - totalspread
15
+
16
+ Args:
17
+ obs_seq, levels, type, bias=True, rmse=True, totalspread=True
18
+
19
+ Example:
20
+
21
+ type = 'RADIOSONDE_U_WIND_COMPONENT'
22
+ hPalevels = [0.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0, 700, 850, 925, 1000]
23
+ levels = [i * 100 for i in hPalevels]
24
+
25
+ plot_profile(obs_seq, levels, type, bias=True, rmse=True, totalspread=True)
26
+
27
+ """
28
+
29
+ # calculate stats and add to dataframe
30
+ stats.diag_stats(obs_seq.df)
31
+ qc0 = obs_seq.select_by_dart_qc(0) # filter only qc=0
32
+
33
+ # filter by type
34
+ qc0 = qc0[qc0["type"] == type]
35
+ all_df = obs_seq.df[obs_seq.df["type"] == type]
36
+
37
+ # grand statistics
38
+ grand = stats.grand_statistics(qc0)
39
+
40
+ # add level bins to the dataframe
41
+ stats.bin_by_layer(all_df, levels) # have to count used vs possible
42
+ stats.bin_by_layer(qc0, levels)
43
+
44
+ # aggregate by layer
45
+ df_pvu = stats.possible_vs_used_by_layer(all_df) # possible vs used
46
+ df = stats.layer_statistics(qc0) # bias, rmse, totalspread for plotting
47
+
48
+ fig, ax1 = plt.subplots()
49
+
50
+ # convert to hPa HK @todo only for Pressure (Pa)
51
+ df["midpoint"] = df["midpoint"].astype(float)
52
+ df["midpoint"] = df["midpoint"] / 100.0
53
+
54
+ df_pvu["midpoint"] = df_pvu["midpoint"].astype(float)
55
+ df_pvu["midpoint"] = df_pvu["midpoint"] / 100.0
56
+
57
+ # Add horizontal stripes alternating between gray and white to represent the vertical levels
58
+ left = df["vlevels"].apply(lambda x: x.left / 100.0) # todo convert to HPa
59
+ right = df["vlevels"].apply(lambda x: x.right / 100.0)
60
+ for i in range(len(left)):
61
+ color = "gray" if i % 2 == 0 else "white"
62
+ ax1.axhspan(left.iloc[i], right.iloc[i], color=color, alpha=0.3)
63
+
64
+ # Plot the 'bias' data on the first y-axis
65
+ if bias:
66
+ ax1.plot(
67
+ df["prior_bias"],
68
+ df["midpoint"],
69
+ color=dacolors[0],
70
+ marker=".",
71
+ linestyle="-",
72
+ label="prior bias",
73
+ )
74
+ bias_prior = grand.loc[0, "prior_bias"]
75
+ if "posterior_bias" in df.columns:
76
+ ax1.plot(
77
+ df["posterior_bias"],
78
+ df["midpoint"],
79
+ color=dacolors[0],
80
+ marker=".",
81
+ linestyle="--",
82
+ label="posterior bias",
83
+ )
84
+ bias_posterior = grand.loc[0, "posterior_bias"]
85
+ if rmse:
86
+ ax1.plot(
87
+ df["prior_rmse"],
88
+ df["midpoint"],
89
+ color=dacolors[1],
90
+ marker=".",
91
+ linestyle="-",
92
+ label="prior RMSE",
93
+ )
94
+ rmse_prior = grand.loc[0, "prior_rmse"]
95
+ if "posterior_rmse" in df.columns:
96
+ ax1.plot(
97
+ df["posterior_rmse"],
98
+ df["midpoint"],
99
+ color=dacolors[1],
100
+ marker=".",
101
+ linestyle="--",
102
+ label="posterior RMSE",
103
+ )
104
+ rmse_posterior = grand.loc[0, "posterior_rmse"]
105
+ if totalspread:
106
+ ax1.plot(
107
+ df["prior_totalspread"],
108
+ df["midpoint"],
109
+ color=dacolors[2],
110
+ marker=".",
111
+ linestyle="-",
112
+ label="prior totalspread",
113
+ )
114
+ totalspread_prior = grand.loc[0, "prior_totalspread"]
115
+ if "posterior_totalspread" in df.columns:
116
+ totalspread_posterior = grand.loc[0, "posterior_totalspread"]
117
+ ax1.plot(
118
+ df["posterior_totalspread"],
119
+ df["midpoint"],
120
+ color=dacolors[2],
121
+ marker=".",
122
+ linestyle="--",
123
+ label="posterior totalspread",
124
+ )
125
+
126
+ ax1.set_ylabel("hPa")
127
+ ax1.tick_params(axis="y")
128
+ ax1.set_yticks(df["midpoint"])
129
+ # ax1.set_yticklabels(df['midpoint'])
130
+
131
+ ax3 = ax1.twiny()
132
+ ax3.set_xlabel("# obs (o=possible; +=assimilated)", color=dacolors[-1])
133
+ ax3.tick_params(axis="x", colors=dacolors[-1])
134
+ ax3.plot(
135
+ df_pvu["possible"],
136
+ df_pvu["midpoint"],
137
+ color=dacolors[-1],
138
+ marker="o",
139
+ linestyle="",
140
+ markerfacecolor="none",
141
+ label="possible",
142
+ )
143
+ ax3.plot(
144
+ df_pvu["used"],
145
+ df_pvu["midpoint"],
146
+ color=dacolors[-1],
147
+ marker="+",
148
+ linestyle="",
149
+ label="possible",
150
+ )
151
+ ax3.set_xlim(left=0)
152
+
153
+ ax1.invert_yaxis()
154
+ ax1.set_title(type)
155
+ datalabel = "bias," + " " + "rmse," + " " + "totalspread"
156
+ ax1.set_xlabel(datalabel)
157
+
158
+ lines1, labels1 = ax1.get_legend_handles_labels()
159
+ ax1.legend(lines1, labels1, loc="upper left", bbox_to_anchor=(1.05, 1))
160
+
161
+ ax1.text(
162
+ 0.5, -0.15, obs_seq.file, ha="center", va="center", transform=ax1.transAxes
163
+ )
164
+
165
+ # Add a text box with information below the legend
166
+ textstr = "Grand statistics:\n"
167
+ if bias:
168
+ textstr += f"- prior_bias: {bias_prior:.7f}\n"
169
+ if rmse:
170
+ textstr += f"- rmse_prior: {rmse_prior:.7f}\n"
171
+ if totalspread:
172
+ textstr += f"- totalspread_prior: {totalspread_prior:.7f}\n"
173
+ if "posterior_bias" in df.columns:
174
+ if bias:
175
+ textstr += f"- posterior_bias: {bias_posterior:.7f}\n"
176
+ if rmse:
177
+ textstr += f"- rmse_posterior: {rmse_posterior:.7f}\n"
178
+ if totalspread:
179
+ textstr += f"- totalspread_posterior: {totalspread_posterior:.7f}\n"
180
+
181
+ props = dict(boxstyle="round", facecolor="wheat", alpha=0.5)
182
+ ax1.text(
183
+ 1.05,
184
+ 0.5,
185
+ textstr,
186
+ transform=ax1.transAxes,
187
+ fontsize=10,
188
+ verticalalignment="top",
189
+ bbox=props,
190
+ )
191
+
192
+ plt.show()
193
+
194
+ return fig
195
+
196
+
197
+ def plot_rank_histogram(obs_seq, levels, type, ens_size):
198
+
199
+ qc0 = obs_seq.select_by_dart_qc(0) # filter only qc=0
200
+ qc0 = qc0[qc0["type"] == type] # filter by type
201
+ stats.bin_by_layer(qc0, levels) # bin by level
202
+
203
+ midpoints = qc0["midpoint"].unique()
204
+
205
+ for level in sorted(midpoints):
206
+
207
+ df = qc0[qc0["midpoint"] == level]
208
+
209
+ df = stats.calculate_rank(qc0)
210
+
211
+ if "posterior_rank" in df.columns:
212
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
213
+ else:
214
+ fig, ax1 = plt.subplots()
215
+
216
+ # Plot the prior rank histogram
217
+ bins = list(range(1, ens_size + 2))
218
+ ax1.hist(
219
+ df["prior_rank"], bins=bins, color="blue", alpha=0.5, label="prior rank"
220
+ )
221
+ ax1.set_title("Prior Rank Histogram")
222
+ ax1.set_xlabel("Observation Rank (among ensemble members)")
223
+ ax1.set_ylabel("Count")
224
+
225
+ # Plot the posterior rank histogram if it exists
226
+ if "posterior_rank" in df.columns:
227
+ ax2.hist(
228
+ df["posterior_rank"],
229
+ bins=bins,
230
+ color="green",
231
+ alpha=0.5,
232
+ label="posterior rank",
233
+ )
234
+ ax2.set_title("Posterior Rank Histogram")
235
+ ax2.set_xlabel("Observation Rank (among ensemble members)")
236
+ ax2.set_ylabel("Count")
237
+
238
+ fig.suptitle(f"{type} at Level {level}", fontsize=14)
239
+
240
+ plt.tight_layout(rect=[0, 0.03, 1, 0.95])
241
+ plt.show()
242
+
243
+ return fig