Skip to content

Commit d003376

Browse files
committed
ft: finished Time-Dependent timewindow implementation.
tests: added unit tests for TD timewindows dev: removed mypy and pydocstringformatter from requirements
1 parent 1d911f2 commit d003376

File tree

9 files changed

+188
-71
lines changed

9 files changed

+188
-71
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ include CONTRIBUTING.md
1313
graft floatcsep
1414
graft tests
1515
graft docs
16+
prune build
1617
prune docs/_build
1718
prune docs/referenced/generated
1819

floatcsep/infrastructure/environments.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,4 +494,3 @@ def check_environment_type() -> Union[str, None]:
494494
log.warning("Micromamba not found in PATH.")
495495

496496
return None
497-

floatcsep/infrastructure/logger.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import sys, os
1+
import sys
2+
import os
23
import logging.config
34
import warnings
45

@@ -37,7 +38,7 @@ def add_fhandler(filename):
3738

3839
def is_sphinx_build():
3940
# Check if Sphinx is running
40-
return 'sphinx' in sys.argv[0] or os.getenv('SPHINX_BUILD') is not None
41+
return "sphinx" in sys.argv[0] or os.getenv("SPHINX_BUILD") is not None
4142

4243

4344
def setup_logger():

floatcsep/infrastructure/registries.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,8 @@ def log_results_tree(self):
492492
log.debug(f" Time Window: {timewindow} - All models evaluated.")
493493
else:
494494
log.debug(
495-
f" Time Window: {timewindow} - Missing results for models: {', '.join(missing_models)}"
495+
f" Time Window: {timewindow} - Missing results for models: "
496+
f"{', '.join(missing_models)}"
496497
)
497498

498499
log.debug(f"Total Results: {total_results}")

floatcsep/utils/helpers.py

Lines changed: 90 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@
77
from datetime import datetime, date
88
from typing import Union, Mapping, Sequence
99

10-
# pyCSEP libraries
11-
import csep.core
12-
import csep.utils
13-
1410
# third-party libraries
1511
import numpy
1612
import pandas
1713
import scipy.stats
1814
import seaborn
1915
import yaml
16+
from matplotlib import pyplot
17+
from matplotlib.lines import Line2D
18+
19+
# pyCSEP libraries
20+
import csep.core
21+
import csep.utils
2022
from csep.core.catalogs import CSEPCatalog
2123
from csep.core.exceptions import CSEPCatalogException
2224
from csep.core.forecasts import GriddedForecast
@@ -29,8 +31,6 @@
2931
from csep.models import EvaluationResult
3032
from csep.utils.calc import cleaner_range
3133
from csep.utils.plots import plot_spatial_dataset
32-
from matplotlib import pyplot
33-
from matplotlib.lines import Line2D
3434

3535
# floatCSEP libraries
3636
import floatcsep.utils.accessors
@@ -237,9 +237,7 @@ def read_region_cfg(region_config, **kwargs):
237237
return region_config
238238

239239

240-
def timewindow2str(
241-
datetimes: Union[Sequence[datetime], Sequence[Sequence[datetime]]]
242-
) -> Union[str, list[str]]:
240+
def timewindow2str(datetimes: Sequence) -> Union[str, list[str]]:
243241
"""
244242
Converts a time window (list/tuple of datetimes) to a string that represents it. Can be a
245243
single timewindow or a list of time windows.
@@ -250,10 +248,10 @@ def timewindow2str(
250248
Returns:
251249
A sequence of strings for each time window
252250
"""
253-
if isinstance(datetimes[0], datetime):
251+
if all(isinstance(i, datetime) for i in datetimes):
254252
return "_".join([j.date().isoformat() for j in datetimes])
255253

256-
elif isinstance(datetimes[0], (list, tuple)):
254+
elif all(isinstance(i, (list, tuple)) for i in datetimes):
257255
return ["_".join([j.date().isoformat() for j in i]) for i in datetimes]
258256

259257

@@ -320,14 +318,17 @@ def timewindows_ti(
320318
try:
321319
timelimits = pandas.date_range(
322320
start=start_date, end=end_date, periods=periods, freq=frequency
323-
).to_pydatetime()
321+
)
322+
print(timelimits)
323+
timelimits = timelimits.to_pydatetime()
324324
except ValueError as e_:
325325
raise ValueError(
326326
"The following experiment parameters combinations are possible:\n"
327327
" (start_date, end_date)\n"
328328
" (start_date, end_date, intervals)\n"
329329
" (start_date, end_date, timewindow)\n"
330-
" (start_date, intervals, timewindow)\n"
330+
" (start_date, intervals, timewindow)\n",
331+
e_,
331332
)
332333

333334
if growth == "incremental":
@@ -365,34 +366,74 @@ def timewindows_td(
365366
testing window, as :py:class:`datetime.datetime`.
366367
"""
367368

368-
frequency = None
369+
frequency = parse_timedelta_string(timehorizon)
370+
offset = parse_timedelta_string(timeoffset)
369371

370-
if timehorizon:
371-
n, unit = timehorizon.split("-")
372+
if frequency:
373+
n, unit = frequency.split("-")
372374
frequency = f"{n}{_PD_FORMAT[_UNITS.index(unit)]}"
375+
if offset:
376+
n, unit = offset.split("-")
377+
offset = f"{n}{_PD_FORMAT[_UNITS.index(unit)]}"
373378

374379
periods = timeintervals + 1 if timeintervals else timeintervals
375380

376-
try:
377-
offset = timeoffset.split("-") if timeoffset else None
378-
start_offset = (
379-
start_date + pandas.DateOffset(**{offset[1]: float(offset[0])})
380-
if offset
381-
else start_date
382-
)
383-
end_offset = (
384-
end_date - pandas.DateOffset(**{offset[1]: float(offset[0])})
385-
if offset
386-
else start_date
387-
)
381+
windows = []
388382

389-
lower_limits = pandas.date_range(
390-
start=start_date, end=end_offset, periods=periods, freq=frequency
391-
).to_pydatetime()[:-1]
392-
upper_limits = pandas.date_range(
393-
start=start_offset, end=end_date, periods=periods, freq=frequency
394-
).to_pydatetime()[:-1]
395-
except ValueError as e_:
383+
if start_date and end_date and timehorizon and timeoffset:
384+
385+
current_start = start_date
386+
current_end = start_date
387+
while current_end < end_date:
388+
next_window = pandas.date_range(
389+
start=current_start, periods=2, freq=frequency
390+
).tolist()
391+
392+
current_end = next_window[1]
393+
394+
windows.append((current_start, current_end))
395+
396+
current_start = pandas.date_range(start=current_start, periods=2, freq=offset)[
397+
1
398+
].to_pydatetime()
399+
400+
elif start_date and timeintervals and timehorizon and timeoffset:
401+
402+
for _ in range(timeintervals):
403+
next_window = pandas.date_range(
404+
start=start_date, periods=2, freq=frequency
405+
).tolist()
406+
lower_bound = start_date
407+
upper_bound = next_window[1]
408+
windows.append((lower_bound, upper_bound))
409+
start_date = pandas.date_range(start=start_date, periods=2, freq=offset)[
410+
1
411+
].to_pydatetime()
412+
413+
elif start_date and end_date and timeintervals:
414+
if timeintervals == 1:
415+
log.warning("Only 1 time window is presentL. Consider using exp_class: ti")
416+
417+
timelimits = pandas.date_range(
418+
start=start_date, end=end_date, periods=periods, freq=frequency
419+
).tolist()
420+
windows = [(i, j) for i, j in zip(timelimits[:-1], timelimits[1:])]
421+
422+
# Case 2: (start_date, end_date, timehorizon)
423+
elif start_date and end_date and timehorizon:
424+
timelimits = pandas.date_range(
425+
start=start_date, end=end_date, periods=periods, freq=frequency
426+
).tolist()
427+
windows = [(i, j) for i, j in zip(timelimits[:-1], timelimits[1:])]
428+
429+
# Case 3: (start_date, timeintervals, timehorizon)
430+
elif start_date and timeintervals and timehorizon:
431+
timelimits = pandas.date_range(
432+
start=start_date, end=end_date, periods=periods, freq=frequency
433+
).tolist()
434+
windows = [(i, j) for i, j in zip(timelimits[:-1], timelimits[1:])]
435+
436+
else:
396437
raise ValueError(
397438
"The following experiment parameters combinations are possible:\n"
398439
" (start_date, end_date, timeintervals)\n"
@@ -401,14 +442,7 @@ def timewindows_td(
401442
" (start_date, end_date, timehorizon, timeoffset)\n"
402443
" (start_date, timeinvervals, timehorizon, timeoffset)\n"
403444
)
404-
405-
# if growth == 'incremental':
406-
# timewindows = [(i, j) for i, j in zip(timelimits[:-1],
407-
# timelimits[1:])]
408-
# elif growth == 'cumulative':
409-
# timewindows = [(timelimits[0], i) for i in timelimits[1:]]
410-
411-
# return timewindows
445+
return windows
412446

413447

414448
def parse_nested_dicts(nested_dict: dict) -> dict:
@@ -466,17 +500,18 @@ def sequential_likelihood(
466500
"""
467501
Performs the likelihood test on Gridded Forecast using an Observed Catalog.
468502
469-
Note: The forecast and the observations should be scaled to the same time period before calling this function. This increases
470-
transparency as no assumptions are being made about the length of the forecasts. This is particularly important for
471-
gridded forecasts that supply their forecasts as rates.
503+
Note: The forecast and the observations should be scaled to the same time period before
504+
calling this function. This increases transparency as no assumptions are being made about
505+
the length of the forecasts. This is particularly important for gridded forecasts that
506+
supply their forecasts as rates.
472507
473508
Args:
474509
gridded_forecasts: list csep.core.forecasts.GriddedForecast
475510
observed_catalogs: list csep.core.catalogs.Catalog
476-
timewindows: list str.
477511
seed (int): used fore reproducibility, and testing
478-
random_numbers (numpy.ndarray): random numbers used to override the random number generation.
479-
injection point for testing.
512+
random_numbers (numpy.ndarray): random numbers used to override the random number
513+
generation injection point for testing.
514+
480515
Returns:
481516
evaluation_result: csep.core.evaluations.EvaluationResult
482517
"""
@@ -535,17 +570,19 @@ def sequential_information_gain(
535570
gridded_forecasts: list csep.core.forecasts.GriddedForecast
536571
benchmark_forecasts: list csep.core.forecasts.GriddedForecast
537572
observed_catalogs: list csep.core.catalogs.Catalog
538-
timewindows: list str.
539573
seed (int): used fore reproducibility, and testing
540-
random_numbers (numpy.ndarray): random numbers used to override the random number generation.
541-
injection point for testing.
574+
random_numbers (numpy.ndarray): random numbers used to override the random number
575+
generation injection point for testing.
542576
543577
Returns:
544578
evaluation_result: csep.core.evaluations.EvaluationResult
545579
"""
546580

547581
information_gains = []
548582

583+
gridded_forecast = None
584+
observed_catalog = None
585+
549586
for gridded_forecast, reference_forecast, observed_catalog in zip(
550587
gridded_forecasts, benchmark_forecasts, observed_catalogs
551588
):
@@ -587,6 +624,7 @@ def sequential_information_gain(
587624
result.obs_name = observed_catalog.name
588625
result.status = "normal"
589626
result.min_mw = numpy.min(gridded_forecast.magnitudes)
627+
590628
return result
591629

592630

pyproject.toml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,3 @@ testpaths = [
1313
line-length = 96
1414
skip-string-normalization = false
1515
target-version = ["py39", "py310", "py311"]
16-
17-
[tool.pydocstringformatter]
18-
write = true
19-
exclude = ["examples/", "docs"]
20-
strip-whitespaces = true
21-
split-summary-body = false
22-
style = "pep257"
23-
max-line-length = 96
24-
linewrap-full-docstring = true

requirements_dev.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ gitpython
99
h5py
1010
matplotlib
1111
mercantile
12-
mypy
1312
obspy
1413
packaging
1514
pandas
1615
pycsep
17-
pydocstringformatter
1816
pyproj
1917
pyshp
2018
pytest

setup.cfg

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,10 @@ dev =
5757
h5py
5858
matplotlib
5959
mercantile
60-
mypy
6160
obspy
6261
packaging
6362
pandas
6463
pycsep
65-
pydocstringformatter
6664
pyproj
6765
pyshp
6866
pytest

0 commit comments

Comments
 (0)