pydartdiags 0.0.3b0__tar.gz → 0.0.41__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 (24) hide show
  1. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/PKG-INFO +22 -15
  2. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/README.md +11 -8
  3. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/pyproject.toml +6 -6
  4. pydartdiags-0.0.41/setup.cfg +4 -0
  5. pydartdiags-0.0.41/setup.py +26 -0
  6. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/src/pydartdiags/obs_sequence/obs_sequence.py +73 -74
  7. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/src/pydartdiags/plots/plots.py +27 -24
  8. pydartdiags-0.0.41/src/pydartdiags.egg-info/PKG-INFO +399 -0
  9. pydartdiags-0.0.41/src/pydartdiags.egg-info/SOURCES.txt +16 -0
  10. pydartdiags-0.0.41/src/pydartdiags.egg-info/dependency_links.txt +1 -0
  11. pydartdiags-0.0.41/src/pydartdiags.egg-info/requires.txt +4 -0
  12. pydartdiags-0.0.41/src/pydartdiags.egg-info/top_level.txt +1 -0
  13. pydartdiags-0.0.41/tests/test_obs_sequence.py +29 -0
  14. pydartdiags-0.0.41/tests/test_plots.py +52 -0
  15. pydartdiags-0.0.3b0/.gitignore +0 -4
  16. pydartdiags-0.0.3b0/docs/images/bias.png +0 -0
  17. pydartdiags-0.0.3b0/docs/images/rankhist.png +0 -0
  18. pydartdiags-0.0.3b0/docs/images/rmse.png +0 -0
  19. pydartdiags-0.0.3b0/src/pydartdiags/obs_sequence/composite_types.yaml +0 -35
  20. pydartdiags-0.0.3b0/src/pydartdiags/plots/tests/test_rank_histogram.py +0 -18
  21. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/LICENSE +0 -0
  22. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/src/pydartdiags/__init__.py +0 -0
  23. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/src/pydartdiags/obs_sequence/__init__.py +0 -0
  24. {pydartdiags-0.0.3b0 → pydartdiags-0.0.41}/src/pydartdiags/plots/__init__.py +0 -0
@@ -1,29 +1,36 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.1
2
2
  Name: pydartdiags
3
- Version: 0.0.3b0
3
+ Version: 0.0.41
4
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>
5
8
  Project-URL: Homepage, https://github.com/NCAR/pyDARTdiags.git
6
9
  Project-URL: Issues, https://github.com/NCAR/pyDARTdiags/issues
7
- Author-email: Helen Kershaw <hkershaw@ucar.edu>
8
- License-File: LICENSE
10
+ Project-URL: Documentation, https://ncar.github.io/pyDARTdiags
11
+ Classifier: Programming Language :: Python :: 3
9
12
  Classifier: License :: OSI Approved :: Apache Software License
10
13
  Classifier: Operating System :: OS Independent
11
- Classifier: Programming Language :: Python :: 3
12
14
  Requires-Python: >=3.8
13
- Requires-Dist: numpy>=1.26
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
14
17
  Requires-Dist: pandas>=2.2.0
18
+ Requires-Dist: numpy>=1.26
15
19
  Requires-Dist: plotly>=5.22.0
16
- Description-Content-Type: text/markdown
20
+ Requires-Dist: pyyaml>=6.0.2
17
21
 
18
22
  # pyDARTdiags
19
23
 
20
- pyDARTdiags is a python library for obsevation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
24
+ pyDARTdiags is a Python library for obsevation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
21
25
 
22
26
  pyDARTdiags is under initial development, so please use caution.
23
27
  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).
24
28
 
25
29
 
26
- pyDARTdiags can be installed through pip. We recommend installing pydartdiags in a virtual enviroment:
30
+ pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
31
+ Documenation : https://ncar.github.io/pyDARTdiags/
32
+
33
+ We recommend installing pydartdiags in a virtual enviroment:
27
34
 
28
35
 
29
36
  ```
@@ -35,14 +42,14 @@ pip install pydartdiags
35
42
  ## Example importing the obs\_sequence and plots modules
36
43
 
37
44
  ```python
38
- from pydartdiags.obs_sequence import obs_sequence as obs_seq
45
+ from pydartdiags.obs_sequence import obs_sequence as obsq
39
46
  from pydartdiags.plots import plots
40
47
  ```
41
48
 
42
49
  ## Examining the dataframe
43
50
 
44
51
  ```python
45
- obs_seq = obs_seq.obs_sequence('obs_seq.final.ascii')
52
+ obs_seq = obsq.obs_sequence('obs_seq.final.ascii')
46
53
  obs_seq.df.head()
47
54
  ```
48
55
 
@@ -203,7 +210,7 @@ obs_seq.df.head()
203
210
  Find the numeber of assimilated (used) observations vs. possible observations by type
204
211
 
205
212
  ```python
206
- obs_seq.possible_vs_used(obs_seq.df)
213
+ obsq.possible_vs_used(obs_seq.df)
207
214
  ```
208
215
 
209
216
  <table border="1" class="dataframe">
@@ -360,7 +367,7 @@ obs_seq.possible_vs_used(obs_seq.df)
360
367
  * plot the rank histogram
361
368
 
362
369
  ```python
363
- df_qc0 = obs_seq.select_by_dart_qc(obs_seq.df, 0)
370
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0)
364
371
  plots.plot_rank_histogram(df_qc0)
365
372
  ```
366
373
  ![Rank Histogram](docs/images/rankhist.png)
@@ -376,7 +383,7 @@ plots.plot_rank_histogram(df_qc0)
376
383
  hPalevels = [0.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0, 700, 850, 925, 1000]# float("inf")] # Pa?
377
384
  plevels = [i * 100 for i in hPalevels]
378
385
 
379
- df_qc0 = obs_seq.select_by_dart_qc(obs_seq.df, 0) # only qc 0
386
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0) # only qc 0
380
387
  df_profile, figrmse, figbias = plots.plot_profile(df_qc0, plevels)
381
388
  ```
382
389
 
@@ -389,4 +396,4 @@ Contributions are welcome! If you have a feature request, bug report, or a sugge
389
396
 
390
397
  ## License
391
398
 
392
- DartLabPlot 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).
399
+ 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).
@@ -1,12 +1,15 @@
1
1
  # pyDARTdiags
2
2
 
3
- pyDARTdiags is a python library for obsevation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
3
+ pyDARTdiags is a Python library for obsevation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
4
4
 
5
5
  pyDARTdiags is under initial development, so please use caution.
6
6
  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).
7
7
 
8
8
 
9
- pyDARTdiags can be installed through pip. We recommend installing pydartdiags in a virtual enviroment:
9
+ pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
10
+ Documenation : https://ncar.github.io/pyDARTdiags/
11
+
12
+ We recommend installing pydartdiags in a virtual enviroment:
10
13
 
11
14
 
12
15
  ```
@@ -18,14 +21,14 @@ pip install pydartdiags
18
21
  ## Example importing the obs\_sequence and plots modules
19
22
 
20
23
  ```python
21
- from pydartdiags.obs_sequence import obs_sequence as obs_seq
24
+ from pydartdiags.obs_sequence import obs_sequence as obsq
22
25
  from pydartdiags.plots import plots
23
26
  ```
24
27
 
25
28
  ## Examining the dataframe
26
29
 
27
30
  ```python
28
- obs_seq = obs_seq.obs_sequence('obs_seq.final.ascii')
31
+ obs_seq = obsq.obs_sequence('obs_seq.final.ascii')
29
32
  obs_seq.df.head()
30
33
  ```
31
34
 
@@ -186,7 +189,7 @@ obs_seq.df.head()
186
189
  Find the numeber of assimilated (used) observations vs. possible observations by type
187
190
 
188
191
  ```python
189
- obs_seq.possible_vs_used(obs_seq.df)
192
+ obsq.possible_vs_used(obs_seq.df)
190
193
  ```
191
194
 
192
195
  <table border="1" class="dataframe">
@@ -343,7 +346,7 @@ obs_seq.possible_vs_used(obs_seq.df)
343
346
  * plot the rank histogram
344
347
 
345
348
  ```python
346
- df_qc0 = obs_seq.select_by_dart_qc(obs_seq.df, 0)
349
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0)
347
350
  plots.plot_rank_histogram(df_qc0)
348
351
  ```
349
352
  ![Rank Histogram](docs/images/rankhist.png)
@@ -359,7 +362,7 @@ plots.plot_rank_histogram(df_qc0)
359
362
  hPalevels = [0.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0, 700, 850, 925, 1000]# float("inf")] # Pa?
360
363
  plevels = [i * 100 for i in hPalevels]
361
364
 
362
- df_qc0 = obs_seq.select_by_dart_qc(obs_seq.df, 0) # only qc 0
365
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0) # only qc 0
363
366
  df_profile, figrmse, figbias = plots.plot_profile(df_qc0, plevels)
364
367
  ```
365
368
 
@@ -372,4 +375,4 @@ Contributions are welcome! If you have a feature request, bug report, or a sugge
372
375
 
373
376
  ## License
374
377
 
375
- DartLabPlot 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).
378
+ 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).
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pydartdiags"
7
- version = "0.0.3b"
7
+ version = "0.0.41"
8
8
  authors = [
9
9
  { name="Helen Kershaw", email="hkershaw@ucar.edu" },
10
10
  ]
@@ -19,12 +19,12 @@ classifiers = [
19
19
  dependencies = [
20
20
  "pandas>=2.2.0",
21
21
  "numpy>=1.26",
22
- "plotly>=5.22.0"
22
+ "plotly>=5.22.0",
23
+ "pyyaml>=6.0.2"
23
24
  ]
24
25
 
25
26
  [project.urls]
26
27
  Homepage = "https://github.com/NCAR/pyDARTdiags.git"
27
28
  Issues = "https://github.com/NCAR/pyDARTdiags/issues"
29
+ Documentation = "https://ncar.github.io/pyDARTdiags"
28
30
 
29
- [tool.hatch.build.targets.wheel]
30
- packages = ["src/pydartdiags"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,26 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="pydartdiags",
5
+ version="0.0.41",
6
+ packages=find_packages(where="src"),
7
+ package_dir={"": "src"},
8
+ author="Helen Kershaw",
9
+ author_email="hkershaw@ucar.edu",
10
+ description="Observation Sequence Diagnostics for DART",
11
+ long_description=open("README.md").read(),
12
+ long_description_content_type="text/markdown",
13
+ url="https://github.com/NCAR/pyDARTdiags.git",
14
+ classifiers=[
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: Apache Software License",
17
+ "Operating System :: OS Independent",
18
+ ],
19
+ python_requires=">=3.8",
20
+ install_requires=[
21
+ "pandas>=2.2.0",
22
+ "numpy>=1.26",
23
+ "plotly>=5.22.0",
24
+ "pyyaml>=6.0.2"
25
+ ],
26
+ )
@@ -5,37 +5,38 @@ import os
5
5
  import yaml
6
6
 
7
7
  class obs_sequence:
8
- """Create an obs_sequence object from an ascii observation
9
- sequence file.
10
-
11
- Attributes:
12
-
13
- df : pandas Dataframe containing all the observations
14
- all_obs : list of all observations, each observation is a list
15
- header : header from the ascii file
16
- vert : dictionary of dart vertical units
17
- types : dictionary of types in the observation sequence file
18
- copie_names : names of copies in the observation sequence file.
19
- Spelled copie to avoid conflict with python built-in copy function.
20
- Spaces are replaced with underscores in copie_names.
21
- file : the input observation sequence ascii file
22
-
23
- usage:
24
- Read the observation sequence from file:
25
- obs_seq = obs_sequence('/home/data/obs_seq.final.ascii.small')
26
- Access the resulting pandas dataFrame:
27
- obs_seq.df
28
-
29
- For 3D sphere models: latitude and longitude are in degrees in the DataFrame
30
- sq_err = (mean-obs)**2
31
- bias = (mean-obs)
32
-
33
- rmse = sqrt( sum((mean-obs)**2)/n )
34
- bias = sum((mean-obs)/n)
35
- spread = sum(sd)
36
- totalspread = sqrt(sum(sd+obs_err_var))
37
-
38
-
8
+ """Create an obs_sequence object from an ascii observation sequence file.
9
+
10
+ Attributes:
11
+ df (pandas.DataFrame): DataFrame containing all the observations.
12
+ all_obs (list): List of all observations, each observation is a list.
13
+ header (str): Header from the ascii file.
14
+ vert (dict): Dictionary of dart vertical units.
15
+ types (dict): Dictionary of types in the observation sequence file.
16
+ copie_names (list): Names of copies in the observation sequence file.
17
+ Spelled 'copie' to avoid conflict with the Python built-in copy function.
18
+ Spaces are replaced with underscores in copie_names.
19
+
20
+ Parameters:
21
+ file : the input observation sequence ascii file
22
+
23
+ Example:
24
+ Read the observation sequence from file:
25
+ ``obs_seq = obs_sequence('/home/data/obs_seq.final.ascii.small')``
26
+ Access the resulting pandas DataFrame:
27
+ ``obs_seq.df``
28
+
29
+ For 3D sphere models: latitude and longitude are in degrees in the DataFrame
30
+
31
+ Calculations:
32
+
33
+ - sq_err = (mean-obs)**2
34
+ - bias = (mean-obs)
35
+ - rmse = sqrt( sum((mean-obs)**2)/n )
36
+ - bias = sum((mean-obs)/n)
37
+ - spread = sum(sd)
38
+ - totalspread = sqrt(sum(sd+obs_err_var))
39
+
39
40
  """
40
41
  ## static variables
41
42
  # vertrical coordinate:
@@ -59,7 +60,8 @@ class obs_sequence:
59
60
  'AIRS observation',
60
61
  'GTSPP observation',
61
62
  'SST observation',
62
- 'observations']
63
+ 'observations',
64
+ 'WOD observation']
63
65
 
64
66
  def __init__(self, file):
65
67
  self.loc_mod = 'None'
@@ -101,6 +103,7 @@ class obs_sequence:
101
103
 
102
104
  def obs_to_list(self, obs):
103
105
  """put single observation into a list
106
+
104
107
  discards obs_def
105
108
  """
106
109
  data = []
@@ -170,7 +173,7 @@ class obs_sequence:
170
173
  def write_obs_seq(self, file, df=None):
171
174
  """
172
175
  Write the observation sequence to a file.
173
-
176
+
174
177
  This function writes the observation sequence to disk.
175
178
  If no DataFrame is provided, it writes the obs_sequence object to a file using the
176
179
  header and all observations stored in the object.
@@ -178,19 +181,17 @@ class obs_sequence:
178
181
  then writes the DataFrame obs to an obs_sequence file. Note the DataFrame is assumed
179
182
  to have been created from obs_sequence object.
180
183
 
181
-
184
+
182
185
  Parameters:
183
- file (str): The path to the file where the observation sequence will be written.
184
- df (pandas.DataFrame, optional): A DataFrame containing the observation data.
185
- If not provided, the function uses self.header
186
- and self.all_obs.
187
-
186
+ file (str): The path to the file where the observation sequence will be written.
187
+ df (pandas.DataFrame, optional): A DataFrame containing the observation data. If not provided, the function uses self.header and self.all_obs.
188
+
188
189
  Returns:
189
- None
190
-
191
- Usage:
192
- obs_seq.write_obs_seq('/path/to/output/file')
193
- obs_seq.write_obs_seq('/path/to/output/file', df=obs_seq.df)
190
+ None
191
+
192
+ Examples:
193
+ ``obs_seq.write_obs_seq('/path/to/output/file')``
194
+ ``obs_seq.write_obs_seq('/path/to/output/file', df=obs_seq.df)``
194
195
  """
195
196
  with open(file, 'w') as f:
196
197
 
@@ -281,14 +282,13 @@ class obs_sequence:
281
282
  """
282
283
  Extracts the names of the copies from the header of an obs_seq file.
283
284
 
284
-
285
285
  Parameters:
286
- header (list): A list of strings representing the lines in the header of the obs_seq file.
286
+ header (list): A list of strings representing the lines in the header of the obs_seq file.
287
287
 
288
288
  Returns:
289
- tuple: A tuple containing two elements:
290
- - copie_names (list): A list of strings representing the copy names with _ for spaces.
291
- - len(copie_names) (int): The number of copy names.
289
+ tuple: A tuple containing two elements:
290
+ - copie_names (list): A list of strings representing the copy names with underscores for spaces.
291
+ - len(copie_names) (int): The number of copy names.
292
292
  """
293
293
  for i, line in enumerate(header):
294
294
  if "num_obs:" in line and "max_num_obs:" in line:
@@ -348,15 +348,13 @@ class obs_sequence:
348
348
  components and adds them to the DataFrame.
349
349
 
350
350
  Parameters:
351
- composite_types (str, optional): The YAML configuration for composite types.
352
- If 'use_default', the default configuration is used.
353
- Otherwise, a custom YAML configuration can be provided.
351
+ composite_types (str, optional): The YAML configuration for composite types. If 'use_default', the default configuration is used. Otherwise, a custom YAML configuration can be provided.
354
352
 
355
353
  Returns:
356
- pd.DataFrame: The updated DataFrame with the new composite rows added.
354
+ pd.DataFrame: The updated DataFrame with the new composite rows added.
357
355
 
358
356
  Raises:
359
- Exception: If there are repeat values in the components.
357
+ Exception: If there are repeat values in the components.
360
358
  """
361
359
 
362
360
  if composite_types == 'use_default':
@@ -386,10 +384,10 @@ def load_yaml_to_dict(file_path):
386
384
  Load a YAML file and convert it to a dictionary.
387
385
 
388
386
  Parameters:
389
- - file_path (str): The path to the YAML file.
387
+ file_path (str): The path to the YAML file.
390
388
 
391
389
  Returns:
392
- - dict: The YAML file content as a dictionary.
390
+ dict: The YAML file content as a dictionary.
393
391
  """
394
392
  try:
395
393
  with open(file_path, 'r') as file:
@@ -402,8 +400,9 @@ def load_yaml_to_dict(file_path):
402
400
  def convert_dart_time(seconds, days):
403
401
  """covert from seconds, days after 1601 to datetime object
404
402
 
405
- base year for Gregorian calendar is 1601
406
- dart time is seconds, days since 1601
403
+ Note:
404
+ - base year for Gregorian calendar is 1601
405
+ - dart time is seconds, days since 1601
407
406
  """
408
407
  time = dt.datetime(1601,1,1) + dt.timedelta(days=days, seconds=seconds)
409
408
  return time
@@ -413,14 +412,14 @@ def select_by_dart_qc(df, dart_qc):
413
412
  Selects rows from a DataFrame based on the DART quality control flag.
414
413
 
415
414
  Parameters:
416
- df (DataFrame): A pandas DataFrame.
417
- dart_qc (int): The DART quality control flag to select.
415
+ df (DataFrame): A pandas DataFrame.
416
+ dart_qc (int): The DART quality control flag to select.
418
417
 
419
418
  Returns:
420
- DataFrame: A DataFrame containing only the rows with the specified DART quality control flag.
419
+ DataFrame: A DataFrame containing only the rows with the specified DART quality control flag.
421
420
 
422
421
  Raises:
423
- ValueError: If the DART quality control flag is not present in the DataFrame.
422
+ ValueError: If the DART quality control flag is not present in the DataFrame.
424
423
  """
425
424
  if dart_qc not in df['DART_quality_control'].unique():
426
425
  raise ValueError(f"DART quality control flag '{dart_qc}' not found in DataFrame.")
@@ -432,10 +431,10 @@ def select_failed_qcs(df):
432
431
  Selects rows from a DataFrame where the DART quality control flag is greater than 0.
433
432
 
434
433
  Parameters:
435
- df (DataFrame): A pandas DataFrame.
434
+ df (DataFrame): A pandas DataFrame.
436
435
 
437
436
  Returns:
438
- DataFrame: A DataFrame containing only the rows with a DART quality control flag greater than 0.
437
+ DataFrame: A DataFrame containing only the rows with a DART quality control flag greater than 0.
439
438
  """
440
439
  return df[df['DART_quality_control'] > 0]
441
440
 
@@ -450,14 +449,14 @@ def possible_vs_used(df):
450
449
  used observations.
451
450
 
452
451
  Parameters:
453
- - df (pd.DataFrame): A DataFrame with at least two columns: 'type' for the observation type and 'observation'
454
- for the observation data. It may also contain other columns required by the `select_failed_qcs` function
455
- to determine failed quality control checks.
452
+ df (pd.DataFrame): A DataFrame with at least two columns: 'type' for the observation type and 'observation'
453
+ for the observation data. It may also contain other columns required by the `select_failed_qcs` function
454
+ to determine failed quality control checks.
456
455
 
457
456
  Returns:
458
- - pd.DataFrame: A DataFrame with three columns: 'type', 'possible', and 'used'. 'type' is the observation type,
459
- 'possible' is the count of all observations of that type, and 'used' is the count of observations of that type
460
- that passed quality control checks.
457
+ pd.DataFrame: A DataFrame with three columns: 'type', 'possible', and 'used'. 'type' is the observation type,
458
+ 'possible' is the count of all observations of that type, and 'used' is the count of observations of that type
459
+ that passed quality control checks.
461
460
 
462
461
  """
463
462
  possible = df.groupby('type')['observation'].count()
@@ -476,12 +475,12 @@ def construct_composit(df_comp, composite, components):
476
475
  specified columns using the square root of the sum of squares method.
477
476
 
478
477
  Parameters:
479
- df_comp (pd.DataFrame): The DataFrame containing the component rows to be combined.
480
- composite (str): The type name for the new composite rows.
481
- components (list of str): A list containing the type names of the two components to be combined.
478
+ df_comp (pd.DataFrame): The DataFrame containing the component rows to be combined.
479
+ composite (str): The type name for the new composite rows.
480
+ components (list of str): A list containing the type names of the two components to be combined.
482
481
 
483
482
  Returns:
484
- merged_df (pd.DataFrame): The updated DataFrame with the new composite rows added.
483
+ merged_df (pd.DataFrame): The updated DataFrame with the new composite rows added.
485
484
  """
486
485
  selected_rows = df_comp[df_comp['type'] == components[0].upper()]
487
486
  selected_rows_v = df_comp[df_comp['type'] == components[1].upper()]
@@ -6,6 +6,7 @@ import pandas as pd
6
6
  def plot_rank_histogram(df):
7
7
  """
8
8
  Plots a rank histogram colored by observation type.
9
+
9
10
  All histogram bars are initalized to be hidden and can be toggled visible in the plot's legend
10
11
  """
11
12
  _, _, df_hist = calculate_rank(df)
@@ -27,12 +28,12 @@ def calculate_rank(df):
27
28
  size plus one.
28
29
 
29
30
  Parameters:
30
- - df (pd.DataFrame): A DataFrame with columns for mean, standard deviation, observed values,
31
- ensemble size, and observation type. The DataFrame should have one row per observation.
31
+ df (pd.DataFrame): A DataFrame with columns for mean, standard deviation, observed values,
32
+ ensemble size, and observation type. The DataFrame should have one row per observation.
32
33
 
33
34
  Returns:
34
- - tuple: A tuple containing the rank array, ensemble size, and a result DataFrame. The result
35
- DataFrame contains columns for 'rank' and 'obstype'.
35
+ tuple: A tuple containing the rank array, ensemble size, and a result DataFrame. The result
36
+ DataFrame contains columns for 'rank' and 'obstype'.
36
37
  """
37
38
  ensemble_values = df.filter(regex='prior_ensemble_member').to_numpy().copy()
38
39
  std_dev = np.sqrt(df['obs_err_var']).to_numpy()
@@ -72,24 +73,24 @@ def plot_profile(df, levels):
72
73
  the vertical profile in the atmosphere correctly.
73
74
 
74
75
  Parameters:
75
- - df (pd.DataFrame): The input DataFrame containing at least the 'vertical' column for pressure levels,
76
- and other columns required by the `rmse_bias` function for calculating RMSE and Bias.
77
- - levels (array-like): The bin edges for categorizing the 'vertical' column values into pressure levels.
76
+ df (pd.DataFrame): The input DataFrame containing at least the 'vertical' column for pressure levels,
77
+ and other columns required by the `rmse_bias` function for calculating RMSE and Bias.
78
+ levels (array-like): The bin edges for categorizing the 'vertical' column values into pressure levels.
78
79
 
79
80
  Returns:
80
- - tuple: A tuple containing the DataFrame with RMSE and Bias calculations, the RMSE plot figure, and the
81
- Bias plot figure. The DataFrame includes a 'plevels' column representing the categorized pressure levels
82
- and 'hPa' column representing the midpoint of each pressure level bin.
81
+ tuple: A tuple containing the DataFrame with RMSE and Bias calculations, the RMSE plot figure, and the
82
+ Bias plot figure. The DataFrame includes a 'plevels' column representing the categorized pressure levels
83
+ and 'hPa' column representing the midpoint of each pressure level bin.
83
84
 
84
85
  Raises:
85
- - ValueError: If there are missing values in the 'vertical' column of the input DataFrame.
86
+ ValueError: If there are missing values in the 'vertical' column of the input DataFrame.
86
87
 
87
88
  Note:
88
- - The function modifies the input DataFrame by adding 'plevels' and 'hPa' columns.
89
- - The 'hPa' values are calculated as half the midpoint of each pressure level bin, which may need
90
- adjustment based on the specific requirements for pressure level representation.
91
- - The plots are generated using Plotly Express and are displayed inline. The y-axis of the plots is
92
- reversed to align with standard atmospheric pressure level representation.
89
+ - The function modifies the input DataFrame by adding 'plevels' and 'hPa' columns.
90
+ - The 'hPa' values are calculated as half the midpoint of each pressure level bin, which may need
91
+ adjustment based on the specific requirements for pressure level representation.
92
+ - The plots are generated using Plotly Express and are displayed inline. The y-axis of the plots is
93
+ reversed to align with standard atmospheric pressure level representation.
93
94
  """
94
95
 
95
96
  pd.options.mode.copy_on_write = True
@@ -116,15 +117,17 @@ def mean_then_sqrt(x):
116
117
  Calculates the mean of an array-like object and then takes the square root of the result.
117
118
 
118
119
  Parameters:
119
- arr (array-like): An array-like object (such as a list or a pandas Series).
120
- The elements should be numeric.
120
+ arr (array-like): An array-like object (such as a list or a pandas Series).
121
+ The elements should be numeric.
121
122
 
122
123
  Returns:
123
- float: The square root of the mean of the input array.
124
+ float: The square root of the mean of the input array.
124
125
 
125
126
  Raises:
126
- TypeError: If the input is not an array-like object containing numeric values.
127
+ TypeError: If the input is not an array-like object containing numeric values.
128
+ ValueError: If the input array is empty.
127
129
  """
130
+
128
131
  return np.sqrt(np.mean(x))
129
132
 
130
133
  def rmse_bias(df):
@@ -139,14 +142,14 @@ def rmse_bias_by_obs_type(df, obs_type):
139
142
  Calculate the RMSE and bias for a given observation type.
140
143
 
141
144
  Parameters:
142
- df (DataFrame): A pandas DataFrame.
143
- obs_type (str): The observation type for which to calculate the RMSE and bias.
145
+ df (DataFrame): A pandas DataFrame.
146
+ obs_type (str): The observation type for which to calculate the RMSE and bias.
144
147
 
145
148
  Returns:
146
- DataFrame: A DataFrame containing the RMSE and bias for the given observation type.
149
+ DataFrame: A DataFrame containing the RMSE and bias for the given observation type.
147
150
 
148
151
  Raises:
149
- ValueError: If the observation type is not present in the DataFrame.
152
+ ValueError: If the observation type is not present in the DataFrame.
150
153
  """
151
154
  if obs_type not in df['type'].unique():
152
155
  raise ValueError(f"Observation type '{obs_type}' not found in DataFrame.")
@@ -0,0 +1,399 @@
1
+ Metadata-Version: 2.1
2
+ Name: pydartdiags
3
+ Version: 0.0.41
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
+
22
+ # pyDARTdiags
23
+
24
+ pyDARTdiags is a Python library for obsevation space diagnostics for the Data Assimilation Research Testbed ([DART](https://github.com/NCAR/DART)).
25
+
26
+ pyDARTdiags is under initial development, so please use caution.
27
+ 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).
28
+
29
+
30
+ pyDARTdiags can be installed through pip: https://pypi.org/project/pydartdiags/
31
+ Documenation : https://ncar.github.io/pyDARTdiags/
32
+
33
+ We recommend installing pydartdiags in a virtual enviroment:
34
+
35
+
36
+ ```
37
+ python3 -m venv dartdiags
38
+ source dartdiags/bin/activate
39
+ pip install pydartdiags
40
+ ```
41
+
42
+ ## Example importing the obs\_sequence and plots modules
43
+
44
+ ```python
45
+ from pydartdiags.obs_sequence import obs_sequence as obsq
46
+ from pydartdiags.plots import plots
47
+ ```
48
+
49
+ ## Examining the dataframe
50
+
51
+ ```python
52
+ obs_seq = obsq.obs_sequence('obs_seq.final.ascii')
53
+ obs_seq.df.head()
54
+ ```
55
+
56
+ <table border="1" class="dataframe">
57
+ <thead>
58
+ <tr style="text-align: right;">
59
+ <th></th>
60
+ <th>obs_num</th>
61
+ <th>observation</th>
62
+ <th>prior_ensemble_mean</th>
63
+ <th>prior_ensemble_spread</th>
64
+ <th>prior_ensemble_member_1</th>
65
+ <th>prior_ensemble_member_2</th>
66
+ <th>prior_ensemble_member_3</th>
67
+ <th>prior_ensemble_member_4</th>
68
+ <th>prior_ensemble_member_5</th>
69
+ <th>prior_ensemble_member_6</th>
70
+ <th>...</th>
71
+ <th>latitude</th>
72
+ <th>vertical</th>
73
+ <th>vert_unit</th>
74
+ <th>type</th>
75
+ <th>seconds</th>
76
+ <th>days</th>
77
+ <th>time</th>
78
+ <th>obs_err_var</th>
79
+ <th>bias</th>
80
+ <th>sq_err</th>
81
+ </tr>
82
+ </thead>
83
+ <tbody>
84
+ <tr>
85
+ <th>0</th>
86
+ <td>1</td>
87
+ <td>230.16</td>
88
+ <td>231.310652</td>
89
+ <td>0.405191</td>
90
+ <td>231.304725</td>
91
+ <td>231.562874</td>
92
+ <td>231.333915</td>
93
+ <td>231.297690</td>
94
+ <td>232.081416</td>
95
+ <td>231.051063</td>
96
+ <td>...</td>
97
+ <td>0.012188</td>
98
+ <td>23950.0</td>
99
+ <td>pressure (Pa)</td>
100
+ <td>ACARS_TEMPERATURE</td>
101
+ <td>75603</td>
102
+ <td>153005</td>
103
+ <td>2019-12-01 21:00:03</td>
104
+ <td>1.00</td>
105
+ <td>1.150652</td>
106
+ <td>1.324001</td>
107
+ </tr>
108
+ <tr>
109
+ <th>1</th>
110
+ <td>2</td>
111
+ <td>18.40</td>
112
+ <td>15.720527</td>
113
+ <td>0.630827</td>
114
+ <td>14.217207</td>
115
+ <td>15.558196</td>
116
+ <td>15.805599</td>
117
+ <td>16.594644</td>
118
+ <td>14.877743</td>
119
+ <td>16.334438</td>
120
+ <td>...</td>
121
+ <td>0.012188</td>
122
+ <td>23950.0</td>
123
+ <td>pressure (Pa)</td>
124
+ <td>ACARS_U_WIND_COMPONENT</td>
125
+ <td>75603</td>
126
+ <td>153005</td>
127
+ <td>2019-12-01 21:00:03</td>
128
+ <td>6.25</td>
129
+ <td>-2.679473</td>
130
+ <td>7.179578</td>
131
+ </tr>
132
+ <tr>
133
+ <th>2</th>
134
+ <td>3</td>
135
+ <td>1.60</td>
136
+ <td>-4.932073</td>
137
+ <td>0.825899</td>
138
+ <td>-5.270562</td>
139
+ <td>-5.955998</td>
140
+ <td>-4.209766</td>
141
+ <td>-5.105016</td>
142
+ <td>-4.669405</td>
143
+ <td>-4.365305</td>
144
+ <td>...</td>
145
+ <td>0.012188</td>
146
+ <td>23950.0</td>
147
+ <td>pressure (Pa)</td>
148
+ <td>ACARS_V_WIND_COMPONENT</td>
149
+ <td>75603</td>
150
+ <td>153005</td>
151
+ <td>2019-12-01 21:00:03</td>
152
+ <td>6.25</td>
153
+ <td>-6.532073</td>
154
+ <td>42.667980</td>
155
+ </tr>
156
+ <tr>
157
+ <th>3</th>
158
+ <td>4</td>
159
+ <td>264.16</td>
160
+ <td>264.060532</td>
161
+ <td>0.035584</td>
162
+ <td>264.107192</td>
163
+ <td>264.097270</td>
164
+ <td>264.073212</td>
165
+ <td>264.047718</td>
166
+ <td>264.074140</td>
167
+ <td>264.019895</td>
168
+ <td>...</td>
169
+ <td>0.010389</td>
170
+ <td>56260.0</td>
171
+ <td>pressure (Pa)</td>
172
+ <td>ACARS_TEMPERATURE</td>
173
+ <td>75603</td>
174
+ <td>153005</td>
175
+ <td>2019-12-01 21:00:03</td>
176
+ <td>1.00</td>
177
+ <td>-0.099468</td>
178
+ <td>0.009894</td>
179
+ </tr>
180
+ <tr>
181
+ <th>4</th>
182
+ <td>5</td>
183
+ <td>11.60</td>
184
+ <td>10.134115</td>
185
+ <td>0.063183</td>
186
+ <td>10.067956</td>
187
+ <td>10.078798</td>
188
+ <td>10.120263</td>
189
+ <td>10.084885</td>
190
+ <td>10.135112</td>
191
+ <td>10.140610</td>
192
+ <td>...</td>
193
+ <td>0.010389</td>
194
+ <td>56260.0</td>
195
+ <td>pressure (Pa)</td>
196
+ <td>ACARS_U_WIND_COMPONENT</td>
197
+ <td>75603</td>
198
+ <td>153005</td>
199
+ <td>2019-12-01 21:00:03</td>
200
+ <td>6.25</td>
201
+ <td>-1.465885</td>
202
+ <td>2.148818</td>
203
+ </tr>
204
+ </tbody>
205
+ </table>
206
+ <p>5 rows × 97 columns</p>
207
+ </div>
208
+
209
+
210
+ Find the numeber of assimilated (used) observations vs. possible observations by type
211
+
212
+ ```python
213
+ obsq.possible_vs_used(obs_seq.df)
214
+ ```
215
+
216
+ <table border="1" class="dataframe">
217
+ <thead>
218
+ <tr style="text-align: right;">
219
+ <th></th>
220
+ <th>type</th>
221
+ <th>possible</th>
222
+ <th>used</th>
223
+ </tr>
224
+ </thead>
225
+ <tbody>
226
+ <tr>
227
+ <th>0</th>
228
+ <td>ACARS_TEMPERATURE</td>
229
+ <td>175429</td>
230
+ <td>128040</td>
231
+ </tr>
232
+ <tr>
233
+ <th>1</th>
234
+ <td>ACARS_U_WIND_COMPONENT</td>
235
+ <td>176120</td>
236
+ <td>126946</td>
237
+ </tr>
238
+ <tr>
239
+ <th>2</th>
240
+ <td>ACARS_V_WIND_COMPONENT</td>
241
+ <td>176120</td>
242
+ <td>127834</td>
243
+ </tr>
244
+ <tr>
245
+ <th>3</th>
246
+ <td>AIRCRAFT_TEMPERATURE</td>
247
+ <td>21335</td>
248
+ <td>13663</td>
249
+ </tr>
250
+ <tr>
251
+ <th>4</th>
252
+ <td>AIRCRAFT_U_WIND_COMPONENT</td>
253
+ <td>21044</td>
254
+ <td>13694</td>
255
+ </tr>
256
+ <tr>
257
+ <th>5</th>
258
+ <td>AIRCRAFT_V_WIND_COMPONENT</td>
259
+ <td>21044</td>
260
+ <td>13642</td>
261
+ </tr>
262
+ <tr>
263
+ <th>6</th>
264
+ <td>AIRS_SPECIFIC_HUMIDITY</td>
265
+ <td>6781</td>
266
+ <td>0</td>
267
+ </tr>
268
+ <tr>
269
+ <th>7</th>
270
+ <td>AIRS_TEMPERATURE</td>
271
+ <td>19583</td>
272
+ <td>7901</td>
273
+ </tr>
274
+ <tr>
275
+ <th>8</th>
276
+ <td>GPSRO_REFRACTIVITY</td>
277
+ <td>81404</td>
278
+ <td>54626</td>
279
+ </tr>
280
+ <tr>
281
+ <th>9</th>
282
+ <td>LAND_SFC_ALTIMETER</td>
283
+ <td>21922</td>
284
+ <td>0</td>
285
+ </tr>
286
+ <tr>
287
+ <th>10</th>
288
+ <td>MARINE_SFC_ALTIMETER</td>
289
+ <td>9987</td>
290
+ <td>0</td>
291
+ </tr>
292
+ <tr>
293
+ <th>11</th>
294
+ <td>MARINE_SFC_SPECIFIC_HUMIDITY</td>
295
+ <td>4196</td>
296
+ <td>0</td>
297
+ </tr>
298
+ <tr>
299
+ <th>12</th>
300
+ <td>MARINE_SFC_TEMPERATURE</td>
301
+ <td>8646</td>
302
+ <td>0</td>
303
+ </tr>
304
+ <tr>
305
+ <th>13</th>
306
+ <td>MARINE_SFC_U_WIND_COMPONENT</td>
307
+ <td>8207</td>
308
+ <td>0</td>
309
+ </tr>
310
+ <tr>
311
+ <th>14</th>
312
+ <td>MARINE_SFC_V_WIND_COMPONENT</td>
313
+ <td>8207</td>
314
+ <td>0</td>
315
+ </tr>
316
+ <tr>
317
+ <th>15</th>
318
+ <td>RADIOSONDE_SPECIFIC_HUMIDITY</td>
319
+ <td>14272</td>
320
+ <td>0</td>
321
+ </tr>
322
+ <tr>
323
+ <th>16</th>
324
+ <td>RADIOSONDE_SURFACE_ALTIMETER</td>
325
+ <td>601</td>
326
+ <td>0</td>
327
+ </tr>
328
+ <tr>
329
+ <th>17</th>
330
+ <td>RADIOSONDE_TEMPERATURE</td>
331
+ <td>29275</td>
332
+ <td>22228</td>
333
+ </tr>
334
+ <tr>
335
+ <th>18</th>
336
+ <td>RADIOSONDE_U_WIND_COMPONENT</td>
337
+ <td>36214</td>
338
+ <td>27832</td>
339
+ </tr>
340
+ <tr>
341
+ <th>19</th>
342
+ <td>RADIOSONDE_V_WIND_COMPONENT</td>
343
+ <td>36214</td>
344
+ <td>27975</td>
345
+ </tr>
346
+ <tr>
347
+ <th>20</th>
348
+ <td>SAT_U_WIND_COMPONENT</td>
349
+ <td>107212</td>
350
+ <td>82507</td>
351
+ </tr>
352
+ <tr>
353
+ <th>21</th>
354
+ <td>SAT_V_WIND_COMPONENT</td>
355
+ <td>107212</td>
356
+ <td>82647</td>
357
+ </tr>
358
+ </tbody>
359
+ </table>
360
+
361
+
362
+ ## Example plotting
363
+
364
+ ### rank histogram
365
+
366
+ * Select only observations that were assimliated (QC === 0).
367
+ * plot the rank histogram
368
+
369
+ ```python
370
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0)
371
+ plots.plot_rank_histogram(df_qc0)
372
+ ```
373
+ ![Rank Histogram](docs/images/rankhist.png)
374
+
375
+
376
+ ### plot profile of RMSE and Bias
377
+
378
+ * Chose levels
379
+ * Select only observations that were assimliated (QC === 0).
380
+ * plot the profiles
381
+
382
+ ```python
383
+ hPalevels = [0.0, 100.0, 150.0, 200.0, 250.0, 300.0, 400.0, 500.0, 700, 850, 925, 1000]# float("inf")] # Pa?
384
+ plevels = [i * 100 for i in hPalevels]
385
+
386
+ df_qc0 = obsq.select_by_dart_qc(obs_seq.df, 0) # only qc 0
387
+ df_profile, figrmse, figbias = plots.plot_profile(df_qc0, plevels)
388
+ ```
389
+
390
+ ![RMSE Plot](docs/images/rmse.png)
391
+
392
+ ![Bias Plot](docs/images/bias.png)
393
+
394
+ ## Contributing
395
+ Contributions are welcome! If you have a feature request, bug report, or a suggestion, please open an issue on our GitHub repository.
396
+
397
+ ## License
398
+
399
+ 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,16 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ src/pydartdiags/__init__.py
6
+ src/pydartdiags.egg-info/PKG-INFO
7
+ src/pydartdiags.egg-info/SOURCES.txt
8
+ src/pydartdiags.egg-info/dependency_links.txt
9
+ src/pydartdiags.egg-info/requires.txt
10
+ src/pydartdiags.egg-info/top_level.txt
11
+ src/pydartdiags/obs_sequence/__init__.py
12
+ src/pydartdiags/obs_sequence/obs_sequence.py
13
+ src/pydartdiags/plots/__init__.py
14
+ src/pydartdiags/plots/plots.py
15
+ tests/test_obs_sequence.py
16
+ tests/test_plots.py
@@ -0,0 +1,4 @@
1
+ pandas>=2.2.0
2
+ numpy>=1.26
3
+ plotly>=5.22.0
4
+ pyyaml>=6.0.2
@@ -0,0 +1 @@
1
+ pydartdiags
@@ -0,0 +1,29 @@
1
+ import datetime as dt
2
+
3
+ from pydartdiags.obs_sequence import obs_sequence as obs_seq
4
+
5
+
6
+ def test_convert_dart_time():
7
+ # Test case 1: Convert 0 seconds and 0 days
8
+ result = obs_seq.convert_dart_time(0, 0)
9
+ expected = dt.datetime(1601, 1, 1)
10
+ assert result == expected
11
+
12
+ # Test case 2: Convert 86400 seconds (1 day) and 0 days
13
+ result = obs_seq.convert_dart_time(86400, 0)
14
+ expected = dt.datetime(1601, 1, 2)
15
+ assert result == expected
16
+
17
+ # Test case 3: Convert 0 seconds and 1 day
18
+ result = obs_seq.convert_dart_time(0, 1)
19
+ expected = dt.datetime(1601, 1, 2)
20
+ assert result == expected
21
+
22
+ # Test case 4: Convert 3600 seconds (1 hour) and 1 day
23
+ result = obs_seq.convert_dart_time(2164, 151240)
24
+ expected = dt.datetime(2015, 1, 31, 0, 36, 4)
25
+ assert result == expected
26
+
27
+
28
+ if __name__ == '__main__':
29
+ pytest.main()
@@ -0,0 +1,52 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ import pytest
4
+ from pydartdiags.plots import plots as plts
5
+
6
+ def test_calculate_rank():
7
+ # Example DataFrame setup
8
+ data = {
9
+ 'observation': [2.5, 3.0, 4.5], # Actual observation values
10
+ 'obs_err_var': [0.1, 0.2, 0.3], # Variance of the observation error
11
+ 'prior_ensemble_member1': [2.3, 3.1, 4.6],
12
+ 'prior_ensemble_member2': [2.4, 2.9, 4.4],
13
+ 'prior_ensemble_member3': [2.5, 3.2, 4.5],
14
+ 'type': ['A', 'B', 'A'] # Observation type
15
+ }
16
+ df = pd.DataFrame(data)
17
+
18
+ # Call the function
19
+ rank, ens_size, df_hist = plts.calculate_rank(df)
20
+
21
+ # Assertions to check if the function works as expected
22
+ assert ens_size == 3 # 3 ensemble members
23
+ assert 'rank' in df_hist.columns
24
+ assert 'obstype' in df_hist.columns
25
+
26
+ def test_mean_then_sqrt():
27
+ # Test with a list
28
+ data = [1, -4, 9.1, 16]
29
+ result = plts.mean_then_sqrt(data)
30
+ expected = np.sqrt(np.mean(data))
31
+ assert result == expected, f"Expected {expected}, but got {result}"
32
+
33
+ # Test with a numpy array
34
+ data = np.array([1, -4, 9.1, 16])
35
+ result = plts.mean_then_sqrt(data)
36
+ expected = np.sqrt(np.mean(data))
37
+ assert result == expected, f"Expected {expected}, but got {result}"
38
+
39
+ # Test with a pandas Series of positive numbers
40
+ data = pd.Series([1, -4, 9.1, 16])
41
+ result = plts.mean_then_sqrt(data)
42
+ expected = np.sqrt(np.mean(data))
43
+ assert result == expected, f"Expected {expected}, but got {result}"
44
+
45
+ # Test with non-numeric values
46
+ data = ['a', 'b', 'c']
47
+ with pytest.raises(TypeError):
48
+ plts.mean_then_sqrt(data)
49
+
50
+ # If you want to run the test directly from this script
51
+ if __name__ == "__main__":
52
+ test_calculate_rank()
@@ -1,4 +0,0 @@
1
- # Python files
2
- dist
3
- .ipynb_checkpoints
4
- __pycache__/
Binary file
Binary file
@@ -1,35 +0,0 @@
1
- acars_horizontal_wind:
2
- description: ACARS-derived Horizontal wind speed
3
- components:
4
- - acars_u_wind_component
5
- - acars_v_wind_component
6
-
7
- sat_horizontal_wind:
8
- description: Satellite-derived horizontal wind speed
9
- components:
10
- - sat_u_wind_component
11
- - sat_v_wind_component
12
-
13
- radiosonde_horizontal_wind:
14
- description: Radiosonde-derived horizontal wind speed
15
- components:
16
- - radiosonde_u_wind_component
17
- - radiosonde_v_wind_component
18
-
19
- aircraft_horizontal_wind:
20
- description: Aircraft-derived horizontal wind speed
21
- components:
22
- - aircraft_u_wind_component
23
- - aircraft_v_wind_component
24
-
25
- 10_m_horizontal_wind:
26
- description: 10 meter horizontal wind speed
27
- components:
28
- - 10m_u_wind_component
29
- - 10m_v_wind_component
30
-
31
- marine_sfc_horizontal_wind:
32
- description: Marine surface horizontal wind speed
33
- components:
34
- - marine_sfc_u_wind_component
35
- - marine_sfc_v_wind_component
@@ -1,18 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
-
4
- # Example DataFrame setup
5
- data = {
6
- 'observation': [2.5, 3.0, 4.5], # Actual observation values
7
- 'obs_err_var': [0.1, 0.2, 0.3], # Variance of the observation error
8
- 'prior_ensemble_member1': [2.3, 3.1, 4.6],
9
- 'prior_ensemble_member2': [2.4, 2.9, 4.4],
10
- 'prior_ensemble_member3': [2.5, 3.2, 4.5]
11
- }
12
- df = pd.DataFrame(data)
13
-
14
- # Call the function
15
- rank, ens_size, _ = calculate_rank(df)
16
-
17
- print("Rank:", rank)
18
- print("Ensemble Size:", ens_size)
File without changes