teehr 0.4.1__py3-none-any.whl

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.
Files changed (103) hide show
  1. teehr/__init__.py +40 -0
  2. teehr/const.py +36 -0
  3. teehr/evaluation/__init__.py +1 -0
  4. teehr/evaluation/evaluation.py +324 -0
  5. teehr/evaluation/fetch.py +844 -0
  6. teehr/evaluation/metrics.py +175 -0
  7. teehr/evaluation/tables.py +1541 -0
  8. teehr/evaluation/utils.py +60 -0
  9. teehr/example_data/__init__.py +0 -0
  10. teehr/example_data/two_locations.py +39 -0
  11. teehr/example_data/v0_3_test_study.py +55 -0
  12. teehr/examples/01-evaluation_setup.ipynb +360 -0
  13. teehr/examples/02-table_queries.ipynb +227 -0
  14. teehr/examples/03-metric_queries.ipynb +328 -0
  15. teehr/examples/04-table_filters.ipynb +219 -0
  16. teehr/examples/05-2_site_setup.ipynb +231 -0
  17. teehr/examples/06-2_site_query.ipynb +290 -0
  18. teehr/examples/07-2_site_plotting.ipynb +136 -0
  19. teehr/examples/08-sql.ipynb +93 -0
  20. teehr/examples/__init__.py +1 -0
  21. teehr/examples/clone_from_s3.ipynb +175 -0
  22. teehr/examples/ngen_example.py +76 -0
  23. teehr/examples/setup_evaluation.py +114 -0
  24. teehr/examples/two_site_debug_script.py +49 -0
  25. teehr/fetching/__init__.py +1 -0
  26. teehr/fetching/const.py +281 -0
  27. teehr/fetching/nwm/__init__.py +1 -0
  28. teehr/fetching/nwm/grid_utils.py +240 -0
  29. teehr/fetching/nwm/nwm_grids.py +269 -0
  30. teehr/fetching/nwm/nwm_points.py +247 -0
  31. teehr/fetching/nwm/point_utils.py +243 -0
  32. teehr/fetching/nwm/retrospective_grids.py +457 -0
  33. teehr/fetching/nwm/retrospective_points.py +329 -0
  34. teehr/fetching/readme.md +2 -0
  35. teehr/fetching/usgs/__init__.py +1 -0
  36. teehr/fetching/usgs/usgs.py +369 -0
  37. teehr/fetching/utils.py +736 -0
  38. teehr/loading/__init__.py +1 -0
  39. teehr/loading/add_domains.py +253 -0
  40. teehr/loading/joined_timeseries.py +213 -0
  41. teehr/loading/location_attributes.py +220 -0
  42. teehr/loading/location_crosswalks.py +201 -0
  43. teehr/loading/locations.py +204 -0
  44. teehr/loading/readme.md +1 -0
  45. teehr/loading/s3/clone_from_s3.py +259 -0
  46. teehr/loading/timeseries.py +280 -0
  47. teehr/loading/utils.py +240 -0
  48. teehr/metrics/__init__.py +1 -0
  49. teehr/metrics/bootstrap_funcs.py +106 -0
  50. teehr/metrics/gumboot_bootstrap.py +185 -0
  51. teehr/metrics/metric_funcs.py +301 -0
  52. teehr/metrics/readme.md +2 -0
  53. teehr/models/__init__.py +1 -0
  54. teehr/models/fetching/__init__.py +1 -0
  55. teehr/models/fetching/nwm22_grid.py +268 -0
  56. teehr/models/fetching/nwm22_point.py +331 -0
  57. teehr/models/fetching/nwm30_grid.py +136 -0
  58. teehr/models/fetching/nwm30_point.py +142 -0
  59. teehr/models/fetching/utils.py +98 -0
  60. teehr/models/filters.py +142 -0
  61. teehr/models/metrics/__init__.py +1 -0
  62. teehr/models/metrics/bootstrap_models.py +154 -0
  63. teehr/models/metrics/metric_attributes.py +237 -0
  64. teehr/models/metrics/metric_enums.py +47 -0
  65. teehr/models/metrics/metric_models.py +839 -0
  66. teehr/models/readme.md +1 -0
  67. teehr/models/str_enum.py +11 -0
  68. teehr/models/table_enums.py +56 -0
  69. teehr/models/tables.py +91 -0
  70. teehr/querying/__init__.py +1 -0
  71. teehr/querying/filter_format.py +177 -0
  72. teehr/querying/metric_format.py +64 -0
  73. teehr/querying/readme.md +2 -0
  74. teehr/querying/utils.py +100 -0
  75. teehr/template/__init__.py +1 -0
  76. teehr/template/cache/readme.md +1 -0
  77. teehr/template/dataset/attributes/_readme.md +1 -0
  78. teehr/template/dataset/attributes/attributes.csv +2 -0
  79. teehr/template/dataset/configurations/_readme.md +1 -0
  80. teehr/template/dataset/configurations/configurations.csv +162 -0
  81. teehr/template/dataset/joined_timeseries/_readme.md +1 -0
  82. teehr/template/dataset/location_attributes/_readme.md +1 -0
  83. teehr/template/dataset/location_crosswalks/_readme.md +1 -0
  84. teehr/template/dataset/locations/_readme.md +1 -0
  85. teehr/template/dataset/primary_timeseries/_readme.md +1 -0
  86. teehr/template/dataset/secondary_timeseries/_readme.md +1 -0
  87. teehr/template/dataset/units/_readme.md +1 -0
  88. teehr/template/dataset/units/units.csv +5 -0
  89. teehr/template/dataset/variables/_readme.md +1 -0
  90. teehr/template/dataset/variables/variables.csv +4 -0
  91. teehr/template/gitignore_template +2 -0
  92. teehr/template/readme.md +18 -0
  93. teehr/template/scripts/__init__.py +1 -0
  94. teehr/template/scripts/user_defined_fields.py +98 -0
  95. teehr/utilities/__init__.py +1 -0
  96. teehr/utilities/generate_weights.py +302 -0
  97. teehr/utils/install_spark_jars.py +32 -0
  98. teehr/visualization/__init__.py +1 -0
  99. teehr/visualization/dataframe_accessor.py +511 -0
  100. teehr-0.4.1.dist-info/LICENSE.md +21 -0
  101. teehr-0.4.1.dist-info/METADATA +92 -0
  102. teehr-0.4.1.dist-info/RECORD +103 -0
  103. teehr-0.4.1.dist-info/WHEEL +4 -0
teehr/__init__.py ADDED
@@ -0,0 +1,40 @@
1
+ from teehr.evaluation.evaluation import Evaluation # noqa
2
+ from teehr.models.metrics.metric_models import Metrics # noqa
3
+ from teehr.models.metrics.metric_enums import Operators # noqa
4
+ from teehr.models.tables import ( # noqa
5
+ Configuration,
6
+ Attribute,
7
+ Unit,
8
+ Variable
9
+ )
10
+ from teehr.models.metrics.bootstrap_models import Bootstrappers # noqa
11
+ from teehr.models.filters import ( # noqa
12
+ UnitFilter,
13
+ ConfigurationFilter,
14
+ VariableFilter,
15
+ AttributeFilter,
16
+ LocationFilter,
17
+ LocationAttributeFilter,
18
+ LocationCrosswalkFilter,
19
+ TimeseriesFilter,
20
+ JoinedTimeseriesFilter
21
+ )
22
+
23
+ # For docs
24
+ from teehr.evaluation.tables import ( # noqa
25
+ BaseTable,
26
+ UnitTable,
27
+ VariableTable,
28
+ AttributeTable,
29
+ ConfigurationTable,
30
+ LocationTable,
31
+ LocationAttributeTable,
32
+ LocationCrosswalkTable,
33
+ PrimaryTimeseriesTable,
34
+ SecondaryTimeseriesTable,
35
+ JoinedTimeseriesTable,
36
+ )
37
+ from teehr.evaluation.fetch import Fetch # noqa
38
+ from teehr.visualization.dataframe_accessor import TEEHRDataFrameAccessor # noqa
39
+ from teehr.models.metrics import metric_models, bootstrap_models # noqa
40
+ from teehr.metrics import metric_funcs # noqa
teehr/const.py ADDED
@@ -0,0 +1,36 @@
1
+ """This module contains constants
2
+ used throughout the package."""
3
+
4
+
5
+ # Primary evaluation directories
6
+ DATASET_DIR = "dataset"
7
+ CACHE_DIR = "cache"
8
+ SCRIPTS_DIR = "scripts"
9
+ LOCATIONS_DIR = "locations"
10
+ PRIMARY_TIMESERIES_DIR = "primary_timeseries"
11
+ LOCATION_CROSSWALKS_DIR = "location_crosswalks"
12
+ LOCATION_ATTRIBUTES_DIR = "location_attributes"
13
+ SECONDARY_TIMESERIES_DIR = "secondary_timeseries"
14
+ JOINED_TIMESERIES_DIR = "joined_timeseries"
15
+
16
+ # Domain files and directories
17
+ VARIABLES_DIR = "variables"
18
+ CONFIGURATIONS_DIR = "configurations"
19
+ UNITS_DIR = "units"
20
+ ATTRIBUTES_DIR = "attributes"
21
+ VARIABLES_FILE = "variables.csv"
22
+ CONFIGURATIONS_FILE = "configurations.csv"
23
+ UNITS_FILE = "units.csv"
24
+ ATTRIBUTES_FILE = "attributes.csv"
25
+
26
+ # Fetching cache directories
27
+ FETCHING_CACHE_DIR = "fetching"
28
+ USGS_CACHE_DIR = "usgs"
29
+ NWM_CACHE_DIR = "nwm"
30
+ KERCHUNK_DIR = "kerchunk"
31
+ WEIGHTS_DIR = "weights"
32
+
33
+ # Loading cache directories
34
+ LOADING_CACHE_DIR = "loading"
35
+
36
+ S3_EVALUATIONS_PATH = "s3://ciroh-rti-public-data/teehr-data-warehouse/v0_4_evaluations/evaluations.yaml"
@@ -0,0 +1 @@
1
+ """Evaluation module for TEEHR."""
@@ -0,0 +1,324 @@
1
+ """Evaluation module."""
2
+ from datetime import datetime
3
+ from typing import Union, Literal, List
4
+ from pathlib import Path
5
+ from pyspark.sql import SparkSession
6
+ from pyspark import SparkConf
7
+ import logging
8
+ from teehr.loading.utils import (
9
+ copy_template_to,
10
+ )
11
+ from teehr.loading.s3.clone_from_s3 import (
12
+ list_s3_evaluations,
13
+ clone_from_s3
14
+ )
15
+ import teehr.const as const
16
+ from teehr.evaluation.fetch import Fetch
17
+ from teehr.evaluation.metrics import Metrics
18
+ from teehr.evaluation.tables import (
19
+ UnitTable,
20
+ VariableTable,
21
+ AttributeTable,
22
+ ConfigurationTable,
23
+ LocationTable,
24
+ LocationAttributeTable,
25
+ LocationCrosswalkTable,
26
+ PrimaryTimeseriesTable,
27
+ SecondaryTimeseriesTable,
28
+ JoinedTimeseriesTable,
29
+ )
30
+ import pandas as pd
31
+ from teehr.visualization.dataframe_accessor import TEEHRDataFrameAccessor # noqa
32
+
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class Evaluation:
38
+ """The Evaluation class.
39
+
40
+ This is the main class for the TEEHR evaluation.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ dir_path: Union[str, Path],
46
+ create_dir: bool = False,
47
+ spark: SparkSession = None
48
+ ):
49
+ """
50
+ Initialize the Evaluation class.
51
+
52
+ Parameters
53
+ ----------
54
+ dir_path : Union[str, Path]
55
+ The path to the evaluation directory.
56
+ spark : SparkSession, optional
57
+ The SparkSession object, by default None
58
+ """
59
+ self.dir_path = dir_path
60
+ self.spark = spark
61
+
62
+ self.dataset_dir = Path(
63
+ self.dir_path, const.DATASET_DIR
64
+ )
65
+ self.cache_dir = Path(
66
+ self.dir_path, const.CACHE_DIR
67
+ )
68
+ self.scripts_dir = Path(
69
+ self.dir_path, const.SCRIPTS_DIR
70
+ )
71
+ self.units_dir = Path(
72
+ self.dataset_dir, const.UNITS_DIR
73
+ )
74
+ self.variables_dir = Path(
75
+ self.dataset_dir, const.VARIABLES_DIR
76
+ )
77
+ self.configurations_dir = Path(
78
+ self.dataset_dir, const.CONFIGURATIONS_DIR
79
+ )
80
+ self.attributes_dir = Path(
81
+ self.dataset_dir, const.ATTRIBUTES_DIR
82
+ )
83
+ self.locations_dir = Path(
84
+ self.dataset_dir, const.LOCATIONS_DIR
85
+ )
86
+ self.location_crosswalks_dir = Path(
87
+ self.dataset_dir, const.LOCATION_CROSSWALKS_DIR
88
+ )
89
+ self.location_attributes_dir = Path(
90
+ self.dataset_dir, const.LOCATION_ATTRIBUTES_DIR
91
+ )
92
+ self.primary_timeseries_dir = Path(
93
+ self.dataset_dir, const.PRIMARY_TIMESERIES_DIR
94
+ )
95
+ self.secondary_timeseries_dir = Path(
96
+ self.dataset_dir, const.SECONDARY_TIMESERIES_DIR
97
+ )
98
+ self.joined_timeseries_dir = Path(
99
+ self.dataset_dir, const.JOINED_TIMESERIES_DIR
100
+ )
101
+
102
+ if not Path(self.dir_path).is_dir():
103
+ if create_dir:
104
+ logger.info(f"Creating directory {self.dir_path}.")
105
+ Path(self.dir_path).mkdir(parents=True, exist_ok=True)
106
+ else:
107
+ logger.error(f"Directory {self.dir_path} does not exist.")
108
+ raise NotADirectoryError
109
+
110
+ # Create a local Spark Session if one is not provided.
111
+ if not self.spark:
112
+ logger.info("Creating a new Spark session.")
113
+ conf = (
114
+ SparkConf()
115
+ .setAppName("TEEHR")
116
+ .setMaster("local[*]")
117
+ .set("spark.sql.sources.partitionOverwriteMode", "dynamic")
118
+ .set("spark.hadoop.fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem")
119
+ .set("spark.hadoop.fs.s3a.aws.credentials.provider", "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")
120
+ .set("spark.sql.execution.arrow.pyspark.enabled", "true")
121
+ )
122
+ self.spark = SparkSession.builder.config(conf=conf).getOrCreate()
123
+
124
+ @property
125
+ def fetch(self) -> Fetch:
126
+ """The fetch component class for accessing external data."""
127
+ return Fetch(self)
128
+
129
+ @property
130
+ def metrics(self) -> Metrics:
131
+ """The metrics component class for calculating performance metrics."""
132
+ return Metrics(self)
133
+
134
+ @property
135
+ def units(self) -> UnitTable:
136
+ """Access the units table."""
137
+ return UnitTable(self)
138
+
139
+ @property
140
+ def variables(self) -> VariableTable:
141
+ """Access the variables table."""
142
+ return VariableTable(self)
143
+
144
+ @property
145
+ def attributes(self) -> AttributeTable:
146
+ """Access the attributes table."""
147
+ return AttributeTable(self)
148
+
149
+ @property
150
+ def configurations(self) -> ConfigurationTable:
151
+ """Access the configurations table."""
152
+ return ConfigurationTable(self)
153
+
154
+ @property
155
+ def locations(self) -> LocationTable:
156
+ """Access the locations table."""
157
+ return LocationTable(self)
158
+
159
+ @property
160
+ def location_attributes(self) -> LocationAttributeTable:
161
+ """Access the location attributes table."""
162
+ return LocationAttributeTable(self)
163
+
164
+ @property
165
+ def location_crosswalks(self) -> LocationCrosswalkTable:
166
+ """Access the location crosswalks table."""
167
+ return LocationCrosswalkTable(self)
168
+
169
+ @property
170
+ def primary_timeseries(self) -> PrimaryTimeseriesTable:
171
+ """Access the primary timeseries table."""
172
+ return PrimaryTimeseriesTable(self)
173
+
174
+ @property
175
+ def secondary_timeseries(self) -> SecondaryTimeseriesTable:
176
+ """Access the secondary timeseries table."""
177
+ return SecondaryTimeseriesTable(self)
178
+
179
+ @property
180
+ def joined_timeseries(self) -> JoinedTimeseriesTable:
181
+ """Access the joined timeseries table."""
182
+ return JoinedTimeseriesTable(self)
183
+
184
+ def enable_logging(self):
185
+ """Enable logging."""
186
+ logger = logging.getLogger("teehr")
187
+ # logger.addHandler(logging.StreamHandler())
188
+ handler = logging.FileHandler(Path(self.dir_path, 'teehr.log'))
189
+ handler.setFormatter(
190
+ logging.Formatter(
191
+ "%(asctime)s %(levelname)s %(message)s"
192
+ )
193
+ )
194
+ logger.addHandler(
195
+ handler
196
+ )
197
+ logger.setLevel(logging.DEBUG)
198
+
199
+ def clone_template(self):
200
+ """Create a study from the standard template.
201
+
202
+ This method mainly copies the template directory to the specified
203
+ evaluation directory.
204
+ """
205
+ teehr_root = Path(__file__).parent.parent
206
+ template_dir = Path(teehr_root, "template")
207
+ logger.info(f"Copying template from {template_dir} to {self.dir_path}")
208
+ copy_template_to(template_dir, self.dir_path)
209
+
210
+ @staticmethod
211
+ def list_s3_evaluations(
212
+ format: Literal["pandas", "list"] = "pandas"
213
+ ) -> Union[list, pd.DataFrame]:
214
+ """List the evaluations available on S3.
215
+
216
+ Parameters
217
+ ----------
218
+ format : str, optional
219
+ The format of the output. Either "pandas" or "list".
220
+ The default is "pandas".
221
+ """
222
+ return list_s3_evaluations(format=format)
223
+
224
+ def clone_from_s3(
225
+ self,
226
+ evaluation_name: str,
227
+ primary_location_ids: List[str] = None,
228
+ start_date: Union[str, datetime] = None,
229
+ end_date: Union[str, datetime] = None,
230
+ ):
231
+ """Fetch the study data from S3.
232
+
233
+ Copies the study from s3 to the local directory, with the option
234
+ to subset the dataset by primary location ID, start and end dates.
235
+
236
+ Parameters
237
+ ----------
238
+ evaluation_name : str
239
+ The name of the evaluation to clone from S3.
240
+ Use the list_s3_evaluations method to get the available
241
+ evaluations.
242
+ primary_location_ids : List[str], optional
243
+ The list of primary location ids to subset the data.
244
+ The default is None.
245
+ start_date : Union[str, datetime], optional
246
+ The start date to subset the data.
247
+ The default is None.
248
+ end_date : Union[str, datetime], optional
249
+ The end date to subset the data.
250
+ The default is None.
251
+
252
+ Notes
253
+ -----
254
+
255
+ Includes the following tables:
256
+ - units
257
+ - variables
258
+ - attributes
259
+ - configurations
260
+ - locations
261
+ - location_attributes
262
+ - location_crosswalks
263
+ - primary_timeseries
264
+ - secondary_timeseries
265
+ - joined_timeseries
266
+
267
+ Also includes the user_defined_fields.py script.
268
+
269
+ """
270
+ return clone_from_s3(
271
+ self,
272
+ evaluation_name,
273
+ primary_location_ids,
274
+ start_date,
275
+ end_date
276
+ )
277
+
278
+ def clean_cache(self):
279
+ """Clean temporary files.
280
+
281
+ Includes removing temporary files.
282
+ """
283
+ logger.info(f"Removing temporary files from {self.cache_dir}")
284
+ self.cache_dir.rmdir()
285
+ self.cache_dir.mkdir()
286
+
287
+ def sql(self, query: str):
288
+ """Run a SQL query on the Spark session against the TEEHR tables.
289
+
290
+ Parameters
291
+ ----------
292
+ query : str
293
+ The SQL query to run.
294
+
295
+ Returns
296
+ -------
297
+ pyspark.sql.DataFrame
298
+ The result of the SQL query.
299
+ This is lazily evaluated so you need to call an action (e.g., sdf.show()) to get the result.
300
+
301
+ This method has access to the following tables preloaded as temporary views:
302
+ - units
303
+ - variables
304
+ - attributes
305
+ - configurations
306
+ - locations
307
+ - location_attributes
308
+ - location_crosswalks
309
+ - primary_timeseries
310
+ - secondary_timeseries
311
+ - joined_timeseries
312
+ """
313
+ self.units.to_sdf().createOrReplaceTempView("units")
314
+ self.variables.to_sdf().createOrReplaceTempView("variables")
315
+ self.attributes.to_sdf().createOrReplaceTempView("attributes")
316
+ self.configurations.to_sdf().createOrReplaceTempView("configurations")
317
+ self.locations.to_sdf().createOrReplaceTempView("locations")
318
+ self.location_attributes.to_sdf().createOrReplaceTempView("location_attributes")
319
+ self.location_crosswalks.to_sdf().createOrReplaceTempView("location_crosswalks")
320
+ self.primary_timeseries.to_sdf().createOrReplaceTempView("primary_timeseries")
321
+ self.secondary_timeseries.to_sdf().createOrReplaceTempView("secondary_timeseries")
322
+ self.joined_timeseries.to_sdf().createOrReplaceTempView("joined_timeseries")
323
+
324
+ return self.spark.sql(query)