pydartdiags 0.0.43__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.
- {pydartdiags-0.0.43/src/pydartdiags.egg-info → pydartdiags-0.5.0}/PKG-INFO +9 -5
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/README.md +3 -3
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/pyproject.toml +3 -2
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/setup.py +1 -1
- pydartdiags-0.5.0/src/pydartdiags/matplots/matplots.py +243 -0
- pydartdiags-0.5.0/src/pydartdiags/obs_sequence/obs_sequence.py +1123 -0
- pydartdiags-0.5.0/src/pydartdiags/plots/__init__.py +0 -0
- pydartdiags-0.5.0/src/pydartdiags/plots/plots.py +191 -0
- pydartdiags-0.5.0/src/pydartdiags/stats/__init__.py +0 -0
- pydartdiags-0.5.0/src/pydartdiags/stats/stats.py +323 -0
- {pydartdiags-0.0.43 → pydartdiags-0.5.0/src/pydartdiags.egg-info}/PKG-INFO +9 -5
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/SOURCES.txt +6 -1
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/requires.txt +1 -0
- pydartdiags-0.5.0/tests/test_obs_sequence.py +452 -0
- pydartdiags-0.5.0/tests/test_plots.py +8 -0
- pydartdiags-0.5.0/tests/test_stats.py +82 -0
- pydartdiags-0.0.43/src/pydartdiags/obs_sequence/obs_sequence.py +0 -825
- pydartdiags-0.0.43/src/pydartdiags/plots/plots.py +0 -339
- pydartdiags-0.0.43/tests/test_obs_sequence.py +0 -225
- pydartdiags-0.0.43/tests/test_plots.py +0 -52
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/LICENSE +0 -0
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/setup.cfg +0 -0
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/src/pydartdiags/__init__.py +0 -0
- {pydartdiags-0.0.43/src/pydartdiags/obs_sequence → pydartdiags-0.5.0/src/pydartdiags/matplots}/__init__.py +0 -0
- {pydartdiags-0.0.43/src/pydartdiags/plots → pydartdiags-0.5.0/src/pydartdiags/obs_sequence}/__init__.py +0 -0
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/dependency_links.txt +0 -0
- {pydartdiags-0.0.43 → pydartdiags-0.5.0}/src/pydartdiags.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: pydartdiags
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Observation Sequence Diagnostics for DART
|
|
5
5
|
Home-page: https://github.com/NCAR/pyDARTdiags.git
|
|
6
6
|
Author: Helen Kershaw
|
|
@@ -18,22 +18,26 @@ Requires-Dist: pandas>=2.2.0
|
|
|
18
18
|
Requires-Dist: numpy>=1.26
|
|
19
19
|
Requires-Dist: plotly>=5.22.0
|
|
20
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
|
|
21
25
|
|
|
22
26
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
23
27
|
[](https://codecov.io/gh/NCAR/pyDARTdiags)
|
|
24
28
|
[](https://pypi.org/project/pydartdiags/)
|
|
25
|
-
|
|
29
|
+
[](https://github.com/psf/black)
|
|
26
30
|
|
|
27
31
|
# pyDARTdiags
|
|
28
32
|
|
|
29
|
-
pyDARTdiags is a Python library for
|
|
33
|
+
pyDARTdiags is a Python library for observation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
|
|
30
34
|
|
|
31
35
|
pyDARTdiags is under initial development, so please use caution.
|
|
32
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).
|
|
33
37
|
|
|
34
38
|
|
|
35
39
|
pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
|
|
36
|
-
|
|
40
|
+
Documentation : https://ncar.github.io/pyDARTdiags/
|
|
37
41
|
|
|
38
42
|
## Contributing
|
|
39
43
|
Contributions are welcome! If you have a feature request, bug report, or a suggestion, please open an issue on our GitHub repository.
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
2
2
|
[](https://codecov.io/gh/NCAR/pyDARTdiags)
|
|
3
3
|
[](https://pypi.org/project/pydartdiags/)
|
|
4
|
-
|
|
4
|
+
[](https://github.com/psf/black)
|
|
5
5
|
|
|
6
6
|
# pyDARTdiags
|
|
7
7
|
|
|
8
|
-
pyDARTdiags is a Python library for
|
|
8
|
+
pyDARTdiags is a Python library for observation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
|
|
9
9
|
|
|
10
10
|
pyDARTdiags is under initial development, so please use caution.
|
|
11
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
12
|
|
|
13
13
|
|
|
14
14
|
pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
|
|
15
|
-
|
|
15
|
+
Documentation : https://ncar.github.io/pyDARTdiags/
|
|
16
16
|
|
|
17
17
|
## Contributing
|
|
18
18
|
Contributions are welcome! If you have a feature request, bug report, or a suggestion, please open an issue on our GitHub repository.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pydartdiags"
|
|
7
|
-
version = "0.0
|
|
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]
|
|
@@ -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
|