polytope-python 1.0.30__tar.gz → 1.0.32__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.
- polytope_python-1.0.32/PKG-INFO +21 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/fdb.py +4 -1
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/exceptions.py +5 -0
- polytope_python-1.0.32/polytope_feature/version.py +1 -0
- polytope_python-1.0.32/polytope_python.egg-info/PKG-INFO +21 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/SOURCES.txt +62 -1
- polytope_python-1.0.32/tests/test_axis_mappers.py +122 -0
- polytope_python-1.0.32/tests/test_bad_request_error.py +65 -0
- polytope_python-1.0.32/tests/test_combinatorics.py +71 -0
- polytope_python-1.0.32/tests/test_cyclic_axis_over_negative_vals.py +156 -0
- polytope_python-1.0.32/tests/test_cyclic_axis_slicer_not_0.py +156 -0
- polytope_python-1.0.32/tests/test_cyclic_axis_slicing.py +270 -0
- polytope_python-1.0.32/tests/test_cyclic_nearest.py +121 -0
- polytope_python-1.0.32/tests/test_cyclic_simple.py +76 -0
- polytope_python-1.0.32/tests/test_cyclic_snapping.py +36 -0
- polytope_python-1.0.32/tests/test_datacube_axes_init.py +126 -0
- polytope_python-1.0.32/tests/test_datacube_mock.py +24 -0
- polytope_python-1.0.32/tests/test_datacube_xarray.py +137 -0
- polytope_python-1.0.32/tests/test_date_time_unmerged.py +69 -0
- polytope_python-1.0.32/tests/test_ecmwf_oper_data_fdb.py +186 -0
- polytope_python-1.0.32/tests/test_engine_slicer.py +86 -0
- polytope_python-1.0.32/tests/test_fdb_datacube.py +127 -0
- polytope_python-1.0.32/tests/test_fdb_unmap_tree.py +85 -0
- polytope_python-1.0.32/tests/test_float_type.py +61 -0
- polytope_python-1.0.32/tests/test_healpix_mapper.py +81 -0
- polytope_python-1.0.32/tests/test_healpix_nested_grid.py +182 -0
- polytope_python-1.0.32/tests/test_hull_slicer.py +86 -0
- polytope_python-1.0.32/tests/test_hullslicer_engine.py +45 -0
- polytope_python-1.0.32/tests/test_incomplete_tree_fdb.py +111 -0
- polytope_python-1.0.32/tests/test_local_grid_cyclic.py +110 -0
- polytope_python-1.0.32/tests/test_local_regular_grid.py +337 -0
- polytope_python-1.0.32/tests/test_local_swiss_grid.py +102 -0
- polytope_python-1.0.32/tests/test_mappers.py +107 -0
- polytope_python-1.0.32/tests/test_merge_cyclic_octahedral.py +57 -0
- polytope_python-1.0.32/tests/test_merge_octahedral_one_axis.py +51 -0
- polytope_python-1.0.32/tests/test_merge_transformation.py +40 -0
- polytope_python-1.0.32/tests/test_multiple_param_fdb.py +71 -0
- polytope_python-1.0.32/tests/test_octahedral_grid.py +89 -0
- polytope_python-1.0.32/tests/test_override_md5_hash_options.py +80 -0
- polytope_python-1.0.32/tests/test_point_nearest.py +211 -0
- polytope_python-1.0.32/tests/test_point_shape.py +49 -0
- polytope_python-1.0.32/tests/test_profiling_requesttree.py +44 -0
- polytope_python-1.0.32/tests/test_reduced_ll_grid.py +122 -0
- polytope_python-1.0.32/tests/test_regular_grid.py +135 -0
- polytope_python-1.0.32/tests/test_regular_reduced_grid.py +108 -0
- polytope_python-1.0.32/tests/test_request_tree.py +331 -0
- polytope_python-1.0.32/tests/test_request_trees_after_slicing.py +99 -0
- polytope_python-1.0.32/tests/test_reverse_transformation.py +30 -0
- polytope_python-1.0.32/tests/test_shapes.py +120 -0
- polytope_python-1.0.32/tests/test_slice_date_range_fdb.py +230 -0
- polytope_python-1.0.32/tests/test_slice_date_range_fdb_v2.py +82 -0
- polytope_python-1.0.32/tests/test_slice_fdb_box.py +97 -0
- polytope_python-1.0.32/tests/test_slicer_engine.py +45 -0
- polytope_python-1.0.32/tests/test_slicer_era5.py +41 -0
- polytope_python-1.0.32/tests/test_slicer_xarray.py +44 -0
- polytope_python-1.0.32/tests/test_slicing_unsliceable_axis.py +42 -0
- polytope_python-1.0.32/tests/test_slicing_xarray_3D.py +259 -0
- polytope_python-1.0.32/tests/test_slicing_xarray_4D.py +306 -0
- polytope_python-1.0.32/tests/test_snapping.py +88 -0
- polytope_python-1.0.32/tests/test_snapping_real_data.py +77 -0
- polytope_python-1.0.32/tests/test_tree_protobuf.py +25 -0
- polytope_python-1.0.32/tests/test_tree_protobuf_encoding.py +70 -0
- polytope_python-1.0.32/tests/test_tree_protobuf_encoding_fdb.py +82 -0
- polytope_python-1.0.32/tests/test_type_change_transformation.py +31 -0
- polytope_python-1.0.32/tests/test_union_gj.py +166 -0
- polytope_python-1.0.32/tests/test_union_point_box.py +75 -0
- polytope_python-1.0.32/tests/test_wave_spectra_data.py +99 -0
- polytope-python-1.0.30/PKG-INFO +0 -13
- polytope-python-1.0.30/polytope_feature/version.py +0 -1
- polytope-python-1.0.30/polytope_python.egg-info/PKG-INFO +0 -13
- {polytope-python-1.0.30 → polytope_python-1.0.32}/LICENSE +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/MANIFEST.in +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/datacube.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/mock.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/xarray.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/datacube_axis.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/index_tree_pb2.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/tensor_index_tree.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/tree_encoding.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/engine.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/hullslicer.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/options.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/polytope.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/shapes.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/__init__.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/combinatorics.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/geometry.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/list_tools.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/profiling.py +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/dependency_links.txt +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/not-zip-safe +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/requires.txt +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/top_level.txt +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/pyproject.toml +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/requirements.txt +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/setup.cfg +0 -0
- {polytope-python-1.0.30 → polytope_python-1.0.32}/setup.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: polytope-python
|
|
3
|
+
Version: 1.0.32
|
|
4
|
+
Summary: Polytope datacube feature extraction library
|
|
5
|
+
Home-page: https://github.com/ecmwf/polytope
|
|
6
|
+
Author: ECMWF
|
|
7
|
+
Author-email: James.Hawkes@ecmwf.int, Mathilde.Leuridan@ecmwf.int
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: numpy==1.26.4
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: scipy
|
|
12
|
+
Requires-Dist: sortedcontainers
|
|
13
|
+
Requires-Dist: tripy
|
|
14
|
+
Requires-Dist: xarray
|
|
15
|
+
Requires-Dist: conflator
|
|
16
|
+
Requires-Dist: protobuf
|
|
17
|
+
|
|
18
|
+
Polytope is a library for extracting complex data from datacubes. It provides an API for
|
|
19
|
+
non-orthogonal access to data, where the stencil used to extract data from the datacube can be
|
|
20
|
+
any arbitrary n-dimensional polygon (called a polytope). This can be used to efficiently
|
|
21
|
+
extract complex features from a datacube, such as polygon regions or spatio-temporal paths.
|
|
@@ -3,7 +3,7 @@ import operator
|
|
|
3
3
|
from copy import deepcopy
|
|
4
4
|
from itertools import product
|
|
5
5
|
|
|
6
|
-
from ...utility.exceptions import BadGridError, BadRequestError
|
|
6
|
+
from ...utility.exceptions import BadGridError, BadRequestError, GribJumpNoIndexError
|
|
7
7
|
from ...utility.geometry import nearest_pt
|
|
8
8
|
from .datacube import Datacube, TensorIndexTree
|
|
9
9
|
|
|
@@ -145,6 +145,9 @@ class FDBDatacube(Datacube):
|
|
|
145
145
|
if "BadValue: Grid hash mismatch" in str(e):
|
|
146
146
|
logging.info("Error is: %s", e)
|
|
147
147
|
raise BadGridError()
|
|
148
|
+
if "Missing JumpInfo" in str(e):
|
|
149
|
+
logging.info("Error is: %s", e)
|
|
150
|
+
raise GribJumpNoIndexError()
|
|
148
151
|
else:
|
|
149
152
|
raise e
|
|
150
153
|
|
|
@@ -38,3 +38,8 @@ class UnsliceableShapeError(PolytopeError, KeyError):
|
|
|
38
38
|
class BadGridError(PolytopeError, ValueError):
|
|
39
39
|
def __init__(self):
|
|
40
40
|
self.message = "Data on this grid is not supported by Polytope."
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class GribJumpNoIndexError(PolytopeError, ValueError):
|
|
44
|
+
def __init__(self):
|
|
45
|
+
self.message = "GribJump index for this data is not yet available."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.32"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: polytope-python
|
|
3
|
+
Version: 1.0.32
|
|
4
|
+
Summary: Polytope datacube feature extraction library
|
|
5
|
+
Home-page: https://github.com/ecmwf/polytope
|
|
6
|
+
Author: ECMWF
|
|
7
|
+
Author-email: James.Hawkes@ecmwf.int, Mathilde.Leuridan@ecmwf.int
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: numpy==1.26.4
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: scipy
|
|
12
|
+
Requires-Dist: sortedcontainers
|
|
13
|
+
Requires-Dist: tripy
|
|
14
|
+
Requires-Dist: xarray
|
|
15
|
+
Requires-Dist: conflator
|
|
16
|
+
Requires-Dist: protobuf
|
|
17
|
+
|
|
18
|
+
Polytope is a library for extracting complex data from datacubes. It provides an API for
|
|
19
|
+
non-orthogonal access to data, where the stencil used to extract data from the datacube can be
|
|
20
|
+
any arbitrary n-dimensional polygon (called a polytope). This can be used to efficiently
|
|
21
|
+
extract complex features from a datacube, such as polygon regions or spatio-temporal paths.
|
|
@@ -52,4 +52,65 @@ polytope_python.egg-info/SOURCES.txt
|
|
|
52
52
|
polytope_python.egg-info/dependency_links.txt
|
|
53
53
|
polytope_python.egg-info/not-zip-safe
|
|
54
54
|
polytope_python.egg-info/requires.txt
|
|
55
|
-
polytope_python.egg-info/top_level.txt
|
|
55
|
+
polytope_python.egg-info/top_level.txt
|
|
56
|
+
tests/test_axis_mappers.py
|
|
57
|
+
tests/test_bad_request_error.py
|
|
58
|
+
tests/test_combinatorics.py
|
|
59
|
+
tests/test_cyclic_axis_over_negative_vals.py
|
|
60
|
+
tests/test_cyclic_axis_slicer_not_0.py
|
|
61
|
+
tests/test_cyclic_axis_slicing.py
|
|
62
|
+
tests/test_cyclic_nearest.py
|
|
63
|
+
tests/test_cyclic_simple.py
|
|
64
|
+
tests/test_cyclic_snapping.py
|
|
65
|
+
tests/test_datacube_axes_init.py
|
|
66
|
+
tests/test_datacube_mock.py
|
|
67
|
+
tests/test_datacube_xarray.py
|
|
68
|
+
tests/test_date_time_unmerged.py
|
|
69
|
+
tests/test_ecmwf_oper_data_fdb.py
|
|
70
|
+
tests/test_engine_slicer.py
|
|
71
|
+
tests/test_fdb_datacube.py
|
|
72
|
+
tests/test_fdb_unmap_tree.py
|
|
73
|
+
tests/test_float_type.py
|
|
74
|
+
tests/test_healpix_mapper.py
|
|
75
|
+
tests/test_healpix_nested_grid.py
|
|
76
|
+
tests/test_hull_slicer.py
|
|
77
|
+
tests/test_hullslicer_engine.py
|
|
78
|
+
tests/test_incomplete_tree_fdb.py
|
|
79
|
+
tests/test_local_grid_cyclic.py
|
|
80
|
+
tests/test_local_regular_grid.py
|
|
81
|
+
tests/test_local_swiss_grid.py
|
|
82
|
+
tests/test_mappers.py
|
|
83
|
+
tests/test_merge_cyclic_octahedral.py
|
|
84
|
+
tests/test_merge_octahedral_one_axis.py
|
|
85
|
+
tests/test_merge_transformation.py
|
|
86
|
+
tests/test_multiple_param_fdb.py
|
|
87
|
+
tests/test_octahedral_grid.py
|
|
88
|
+
tests/test_override_md5_hash_options.py
|
|
89
|
+
tests/test_point_nearest.py
|
|
90
|
+
tests/test_point_shape.py
|
|
91
|
+
tests/test_profiling_requesttree.py
|
|
92
|
+
tests/test_reduced_ll_grid.py
|
|
93
|
+
tests/test_regular_grid.py
|
|
94
|
+
tests/test_regular_reduced_grid.py
|
|
95
|
+
tests/test_request_tree.py
|
|
96
|
+
tests/test_request_trees_after_slicing.py
|
|
97
|
+
tests/test_reverse_transformation.py
|
|
98
|
+
tests/test_shapes.py
|
|
99
|
+
tests/test_slice_date_range_fdb.py
|
|
100
|
+
tests/test_slice_date_range_fdb_v2.py
|
|
101
|
+
tests/test_slice_fdb_box.py
|
|
102
|
+
tests/test_slicer_engine.py
|
|
103
|
+
tests/test_slicer_era5.py
|
|
104
|
+
tests/test_slicer_xarray.py
|
|
105
|
+
tests/test_slicing_unsliceable_axis.py
|
|
106
|
+
tests/test_slicing_xarray_3D.py
|
|
107
|
+
tests/test_slicing_xarray_4D.py
|
|
108
|
+
tests/test_snapping.py
|
|
109
|
+
tests/test_snapping_real_data.py
|
|
110
|
+
tests/test_tree_protobuf.py
|
|
111
|
+
tests/test_tree_protobuf_encoding.py
|
|
112
|
+
tests/test_tree_protobuf_encoding_fdb.py
|
|
113
|
+
tests/test_type_change_transformation.py
|
|
114
|
+
tests/test_union_gj.py
|
|
115
|
+
tests/test_union_point_box.py
|
|
116
|
+
tests/test_wave_spectra_data.py
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from polytope_feature.datacube.datacube_axis import (
|
|
4
|
+
DatacubeAxisCyclic,
|
|
5
|
+
FloatDatacubeAxis,
|
|
6
|
+
IntDatacubeAxis,
|
|
7
|
+
PandasTimedeltaDatacubeAxis,
|
|
8
|
+
PandasTimestampDatacubeAxis,
|
|
9
|
+
)
|
|
10
|
+
from polytope_feature.options import PolytopeOptions
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestAxisMappers:
|
|
14
|
+
def setup_method(self, method):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def test_int_axis(self):
|
|
18
|
+
axis = IntDatacubeAxis()
|
|
19
|
+
assert axis.parse(2) == 2.0
|
|
20
|
+
assert axis.to_float(2) == 2.0
|
|
21
|
+
assert axis.from_float(2) == 2.0
|
|
22
|
+
assert axis.serialize(2) == 2
|
|
23
|
+
assert axis.remap([2, 3]) == [[2, 3]]
|
|
24
|
+
|
|
25
|
+
def test_float_axis(self):
|
|
26
|
+
axis = FloatDatacubeAxis()
|
|
27
|
+
assert axis.parse(2) == 2.0
|
|
28
|
+
assert axis.to_float(2) == 2.0
|
|
29
|
+
assert axis.from_float(2) == 2.0
|
|
30
|
+
assert axis.serialize(2.0) == 2.0
|
|
31
|
+
assert axis.remap([2, 3]) == [[2, 3]]
|
|
32
|
+
|
|
33
|
+
def test_float_axis_cyclic(self):
|
|
34
|
+
axis = FloatDatacubeAxis()
|
|
35
|
+
|
|
36
|
+
axis.is_cyclic = True
|
|
37
|
+
assert axis.parse(2) == 2.0
|
|
38
|
+
assert axis.to_float(2) == 2.0
|
|
39
|
+
assert axis.from_float(2) == 2.0
|
|
40
|
+
assert axis.serialize(2.0) == 2.0
|
|
41
|
+
|
|
42
|
+
options = {
|
|
43
|
+
"axis_config": [
|
|
44
|
+
{"axis_name": "long", "transformations": [{"name": "cyclic", "range": [0, 1.0]}]},
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
transformation = PolytopeOptions.get_polytope_options(options)[0][0]
|
|
48
|
+
transformation_option = transformation.transformations[0]
|
|
49
|
+
transformation = DatacubeAxisCyclic("", transformation_option)
|
|
50
|
+
# Test the to_intervals function
|
|
51
|
+
transformation.range = [1, 3]
|
|
52
|
+
assert transformation.to_intervals([4, 7], [[]], axis) == [[4, 5], [5, 7], [7, 7]]
|
|
53
|
+
# Test the cyclic_remap function
|
|
54
|
+
|
|
55
|
+
transformation.range = [0, 1]
|
|
56
|
+
assert transformation.remap([0, 2], [[]], axis) == [[-1e-12, 1.000000000001], [-1e-12, 1.000000000001]]
|
|
57
|
+
|
|
58
|
+
transformation.range = [1, 2]
|
|
59
|
+
assert transformation.remap([1, 3], [[]], axis) == [
|
|
60
|
+
[0.999999999999, 2.000000000001],
|
|
61
|
+
[0.999999999999, 2.000000000001],
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
transformation.range = [1, 3]
|
|
65
|
+
assert transformation.remap([1, 4], [[]], axis) == [
|
|
66
|
+
[0.999999999999, 3.000000000001],
|
|
67
|
+
[0.999999999999, 2.000000000001],
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
transformation.range = [2, 4]
|
|
71
|
+
assert transformation.remap([0, 5], [[]], axis) == [
|
|
72
|
+
[1.999999999999, 4.000000000001],
|
|
73
|
+
[1.999999999999, 4.000000000001],
|
|
74
|
+
[1.999999999999, 3.000000000001],
|
|
75
|
+
]
|
|
76
|
+
transformation.range = [2.3, 4.6]
|
|
77
|
+
remapped = transformation.remap([0.3, 5.7], [[]], axis)
|
|
78
|
+
assert remapped == [
|
|
79
|
+
[2.5999999999989996, 4.600000000001],
|
|
80
|
+
[2.2999999999989997, 4.600000000001],
|
|
81
|
+
[2.2999999999989997, 3.4000000000010004],
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
# Test the to_cyclic_value function
|
|
85
|
+
transformation.range = [1, 3]
|
|
86
|
+
remapped = transformation.remap([0, 7], [[]], axis)
|
|
87
|
+
assert remapped == [
|
|
88
|
+
[1.999999999999, 3.000000000001],
|
|
89
|
+
[0.999999999999, 3.000000000001],
|
|
90
|
+
[0.999999999999, 3.000000000001],
|
|
91
|
+
[0.999999999999, 3.000000000001],
|
|
92
|
+
]
|
|
93
|
+
remapped = transformation.remap([-1, 2], [[]], axis)
|
|
94
|
+
assert remapped == [[0.999999999999, 3.000000000001], [0.999999999999, 2.000000000001]]
|
|
95
|
+
|
|
96
|
+
# Test the cyclic_offset function
|
|
97
|
+
assert transformation.offset([3.05, 3.1], axis, 0) == 2
|
|
98
|
+
assert transformation.offset([1.05, 1.1], axis, 0) == 0
|
|
99
|
+
assert transformation.offset([-5.0, -4.95], axis, 0) == -6
|
|
100
|
+
assert transformation.offset([5.05, 5.1], axis, 0) == 4
|
|
101
|
+
|
|
102
|
+
def test_timedelta_axis(self):
|
|
103
|
+
axis = PandasTimedeltaDatacubeAxis()
|
|
104
|
+
time1 = pd.Timedelta("1 days")
|
|
105
|
+
time2 = pd.Timedelta("1 days 2 hours")
|
|
106
|
+
assert axis.parse(time1) == pd.Timedelta("1 days 00:00:00")
|
|
107
|
+
assert axis.to_float(time1) == 86400.0
|
|
108
|
+
assert axis.from_float(3600) == pd.Timedelta("0 days 01:00:00")
|
|
109
|
+
assert axis.serialize(time1) == "1 days 00:00:00"
|
|
110
|
+
assert axis.remap([time1, time2]) == [[pd.Timedelta("1 days 00:00:00"), pd.Timedelta("1 days 02:00:00")]]
|
|
111
|
+
|
|
112
|
+
def test_timestamp_axis(self):
|
|
113
|
+
axis = PandasTimestampDatacubeAxis()
|
|
114
|
+
time1 = pd.Timestamp("2017-01-01 11:00:00")
|
|
115
|
+
time2 = pd.Timestamp("2017-01-01 12:00:00")
|
|
116
|
+
assert axis.parse(time1) == pd.Timestamp("2017-01-01 11:00:00")
|
|
117
|
+
assert axis.to_float(time1) == 1483268400.0
|
|
118
|
+
assert axis.from_float(1483268400.0) == pd.Timestamp("2017-01-01 11:00:00")
|
|
119
|
+
assert axis.serialize(time1) == "2017-01-01 11:00:00"
|
|
120
|
+
assert axis.remap([time1, time2]) == [
|
|
121
|
+
[pd.Timestamp("2017-01-01 11:00:00"), pd.Timestamp("2017-01-01 12:00:00")]
|
|
122
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from polytope_feature.engine.hullslicer import HullSlicer
|
|
4
|
+
from polytope_feature.polytope import Polytope
|
|
5
|
+
from polytope_feature.utility.exceptions import BadRequestError
|
|
6
|
+
|
|
7
|
+
# import geopandas as gpd
|
|
8
|
+
# import matplotlib.pyplot as plt
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestSlicingFDBDatacube:
|
|
12
|
+
def setup_method(self, method):
|
|
13
|
+
# Create a dataarray with 3 labelled axes using different index types
|
|
14
|
+
self.options = {
|
|
15
|
+
"axis_config": [
|
|
16
|
+
{"axis_name": "step", "transformations": [{"name": "type_change", "type": "int"}]},
|
|
17
|
+
{"axis_name": "number", "transformations": [{"name": "type_change", "type": "int"}]},
|
|
18
|
+
{
|
|
19
|
+
"axis_name": "date",
|
|
20
|
+
"transformations": [{"name": "merge", "other_axis": "time", "linkers": ["T", "00"]}],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"axis_name": "values",
|
|
24
|
+
"transformations": [
|
|
25
|
+
{"name": "mapper", "type": "octahedral", "resolution": 1280, "axes": ["latitude", "longitude"]}
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{"axis_name": "latitude", "transformations": [{"name": "reverse", "is_reverse": True}]},
|
|
29
|
+
{"axis_name": "longitude", "transformations": [{"name": "cyclic", "range": [0, 360]}]},
|
|
30
|
+
],
|
|
31
|
+
"compressed_axes_config": [
|
|
32
|
+
"longitude",
|
|
33
|
+
"latitude",
|
|
34
|
+
"levtype",
|
|
35
|
+
"step",
|
|
36
|
+
"date",
|
|
37
|
+
"domain",
|
|
38
|
+
"expver",
|
|
39
|
+
"param",
|
|
40
|
+
"class",
|
|
41
|
+
"stream",
|
|
42
|
+
"type",
|
|
43
|
+
],
|
|
44
|
+
"pre_path": {
|
|
45
|
+
"class": "od",
|
|
46
|
+
"expver": "0001",
|
|
47
|
+
"levtype": "sfc",
|
|
48
|
+
"stream": "oper",
|
|
49
|
+
"date": "20230621T120000",
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Testing different shapes
|
|
54
|
+
@pytest.mark.fdb
|
|
55
|
+
def test_fdb_datacube(self):
|
|
56
|
+
import pygribjump as gj
|
|
57
|
+
|
|
58
|
+
with pytest.raises(BadRequestError):
|
|
59
|
+
self.fdbdatacube = gj.GribJump()
|
|
60
|
+
self.slicer = HullSlicer()
|
|
61
|
+
self.API = Polytope(
|
|
62
|
+
datacube=self.fdbdatacube,
|
|
63
|
+
engine=self.slicer,
|
|
64
|
+
options=self.options,
|
|
65
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from polytope_feature import ConvexPolytope
|
|
5
|
+
from polytope_feature.utility.combinatorics import group, tensor_product, validate_axes
|
|
6
|
+
from polytope_feature.utility.exceptions import (
|
|
7
|
+
AxisNotFoundError,
|
|
8
|
+
AxisOverdefinedError,
|
|
9
|
+
AxisUnderdefinedError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestCombinatorics:
|
|
14
|
+
def setup_method(self, method):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def test_group_and_product(self):
|
|
18
|
+
p1 = ConvexPolytope(["x", "y"], [[1, 2], [3, 4], [5, 6]])
|
|
19
|
+
p2 = ConvexPolytope(["a", "b"], [[1, 2], [3, 4], [5, 6]])
|
|
20
|
+
p3 = ConvexPolytope(["b", "a"], [[1, 2], [3, 4], [5, 6]])
|
|
21
|
+
p4 = ConvexPolytope(np.array(["a", "b"]), [[1, 2], [3, 4], [5, 6]])
|
|
22
|
+
p5 = ConvexPolytope(("a", "b"), [[1, 2], [3, 4], [5, 6]])
|
|
23
|
+
|
|
24
|
+
groups, all_axes = group([p1, p2, p3, p4, p5])
|
|
25
|
+
|
|
26
|
+
assert len(groups) == 2
|
|
27
|
+
assert len(groups[("x", "y")]) == 1
|
|
28
|
+
assert len(groups[("a", "b")]) == 4
|
|
29
|
+
assert len(all_axes) == 4
|
|
30
|
+
|
|
31
|
+
combinations = tensor_product(groups)
|
|
32
|
+
|
|
33
|
+
assert len(combinations) == 4
|
|
34
|
+
for c in combinations:
|
|
35
|
+
assert len(c) == 2
|
|
36
|
+
|
|
37
|
+
def test_group_with_different_number_of_axes(self):
|
|
38
|
+
p1 = ConvexPolytope(["x", "y"], [[1, 2], [3, 4], [5, 6]])
|
|
39
|
+
p2 = ConvexPolytope(["a", "b", "c"], [[1, 2], [3, 4], [5, 6]])
|
|
40
|
+
|
|
41
|
+
groups, all_axes = group([p1, p2])
|
|
42
|
+
|
|
43
|
+
assert len(groups) == 2
|
|
44
|
+
assert len(groups[("x", "y")]) == 1
|
|
45
|
+
assert len(groups[("a", "b", "c")]) == 1
|
|
46
|
+
assert len(all_axes) == 5
|
|
47
|
+
|
|
48
|
+
def test_validate_axes(self):
|
|
49
|
+
with pytest.raises(AxisNotFoundError):
|
|
50
|
+
validate_axes(["x", "y", "z"], ["x", "y", "z", "a"])
|
|
51
|
+
with pytest.raises(AxisUnderdefinedError):
|
|
52
|
+
validate_axes(["x", "y", "z"], ["x", "y"])
|
|
53
|
+
with pytest.raises(AxisUnderdefinedError):
|
|
54
|
+
validate_axes(["x", "y", "z"], ["x", "y", "b"])
|
|
55
|
+
with pytest.raises(AxisUnderdefinedError):
|
|
56
|
+
validate_axes(["x", "y", "z"], [])
|
|
57
|
+
with pytest.raises(AxisUnderdefinedError):
|
|
58
|
+
validate_axes(["x", "y", "z"], ["a"])
|
|
59
|
+
with pytest.raises(AxisOverdefinedError):
|
|
60
|
+
validate_axes(["x", "y", "z"], ["x", "x", "y", "z"])
|
|
61
|
+
with pytest.raises(AxisOverdefinedError):
|
|
62
|
+
validate_axes(["x", "y", "z"], ["x", "x", "x"])
|
|
63
|
+
|
|
64
|
+
assert validate_axes([], [])
|
|
65
|
+
assert validate_axes(["x"], ["x"])
|
|
66
|
+
assert validate_axes(["x", "y", "z"], ["x", "y", "z"])
|
|
67
|
+
assert validate_axes(["x", "y", "z"], ["z", "x", "y"])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
TestCombinatorics().test_group_and_product()
|
|
71
|
+
TestCombinatorics().test_validate_axes()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import xarray as xr
|
|
4
|
+
|
|
5
|
+
from polytope_feature.engine.hullslicer import HullSlicer
|
|
6
|
+
from polytope_feature.polytope import Polytope, Request
|
|
7
|
+
from polytope_feature.shapes import Box, Select
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestSlicingCyclicAxisNegVals:
|
|
11
|
+
def setup_method(self, method):
|
|
12
|
+
# Create a dataarray with 3 labelled axes using different index types
|
|
13
|
+
array = xr.DataArray(
|
|
14
|
+
np.random.randn(3, 6, 129, 11),
|
|
15
|
+
dims=("date", "step", "level", "long"),
|
|
16
|
+
coords={
|
|
17
|
+
"date": pd.date_range("2000-01-01", "2000-01-03", 3),
|
|
18
|
+
"step": [0, 3, 6, 9, 12, 15],
|
|
19
|
+
"level": range(1, 130),
|
|
20
|
+
"long": [-0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1][::-1],
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
options = {
|
|
24
|
+
"axis_config": [
|
|
25
|
+
{"axis_name": "long", "transformations": [{"name": "cyclic", "range": [-1.1, -0.1]}]},
|
|
26
|
+
{"axis_name": "level", "transformations": [{"name": "cyclic", "range": [1, 129]}]},
|
|
27
|
+
],
|
|
28
|
+
"compressed_axes_config": ["long", "level", "step", "date"],
|
|
29
|
+
}
|
|
30
|
+
self.slicer = HullSlicer()
|
|
31
|
+
self.API = Polytope(datacube=array, engine=self.slicer, options=options)
|
|
32
|
+
|
|
33
|
+
# Testing different shapes
|
|
34
|
+
|
|
35
|
+
def test_cyclic_float_axis_across_seam(self):
|
|
36
|
+
request = Request(
|
|
37
|
+
Box(["step", "long"], [0, 0.8], [3, 1.7]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
38
|
+
)
|
|
39
|
+
result = self.API.retrieve(request)
|
|
40
|
+
result.pprint()
|
|
41
|
+
assert len(result.leaves) == 1
|
|
42
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
43
|
+
(-1.1,),
|
|
44
|
+
(-1.0,),
|
|
45
|
+
(-0.9,),
|
|
46
|
+
(-0.8,),
|
|
47
|
+
(-0.7,),
|
|
48
|
+
(-0.6,),
|
|
49
|
+
(-0.5,),
|
|
50
|
+
(-0.4,),
|
|
51
|
+
(-0.3,),
|
|
52
|
+
(-0.2,),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
def test_cyclic_float_axis_inside_cyclic_range(self):
|
|
56
|
+
request = Request(
|
|
57
|
+
Box(["step", "long"], [0, 0.0], [3, 0.7]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
58
|
+
)
|
|
59
|
+
result = self.API.retrieve(request)
|
|
60
|
+
# result.pprint()
|
|
61
|
+
assert len(result.leaves) == 1
|
|
62
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
63
|
+
(-1.0,),
|
|
64
|
+
(-0.9,),
|
|
65
|
+
(-0.8,),
|
|
66
|
+
(-0.7,),
|
|
67
|
+
(-0.6,),
|
|
68
|
+
(-0.5,),
|
|
69
|
+
(-0.4,),
|
|
70
|
+
(-0.3,),
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
def test_cyclic_float_axis_above_axis_range(self):
|
|
74
|
+
request = Request(
|
|
75
|
+
Box(["step", "long"], [0, 1.3], [3, 1.7]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
76
|
+
)
|
|
77
|
+
result = self.API.retrieve(request)
|
|
78
|
+
# result.pprint()
|
|
79
|
+
assert len(result.leaves) == 1
|
|
80
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
81
|
+
(-0.7,),
|
|
82
|
+
(-0.6,),
|
|
83
|
+
(-0.5,),
|
|
84
|
+
(-0.4,),
|
|
85
|
+
(-0.3,),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
def test_cyclic_float_axis_two_range_loops(self):
|
|
89
|
+
request = Request(
|
|
90
|
+
Box(["step", "long"], [0, 0.3], [3, 2.7]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
91
|
+
)
|
|
92
|
+
result = self.API.retrieve(request)
|
|
93
|
+
result.pprint()
|
|
94
|
+
assert len(result.leaves) == 1
|
|
95
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
96
|
+
(-1.1,),
|
|
97
|
+
(-1.1,),
|
|
98
|
+
(-1.0,),
|
|
99
|
+
(-1.0,),
|
|
100
|
+
(-0.9,),
|
|
101
|
+
(-0.9,),
|
|
102
|
+
(-0.8,),
|
|
103
|
+
(-0.8,),
|
|
104
|
+
(-0.7,),
|
|
105
|
+
(-0.7,),
|
|
106
|
+
(-0.7,),
|
|
107
|
+
(-0.6,),
|
|
108
|
+
(-0.6,),
|
|
109
|
+
(-0.6,),
|
|
110
|
+
(-0.5,),
|
|
111
|
+
(-0.5,),
|
|
112
|
+
(-0.5,),
|
|
113
|
+
(-0.4,),
|
|
114
|
+
(-0.4,),
|
|
115
|
+
(-0.4,),
|
|
116
|
+
(-0.3,),
|
|
117
|
+
(-0.3,),
|
|
118
|
+
(-0.3,),
|
|
119
|
+
(-0.2,),
|
|
120
|
+
(-0.2,),
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
def test_cyclic_float_axis_below_axis_range(self):
|
|
124
|
+
request = Request(
|
|
125
|
+
Box(["step", "long"], [0, -0.7], [3, -0.3]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
126
|
+
)
|
|
127
|
+
result = self.API.retrieve(request)
|
|
128
|
+
assert len(result.leaves) == 1
|
|
129
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
130
|
+
(-0.7,),
|
|
131
|
+
(-0.6,),
|
|
132
|
+
(-0.5,),
|
|
133
|
+
(-0.4,),
|
|
134
|
+
(-0.3,),
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
def test_cyclic_float_axis_below_axis_range_crossing_seam(self):
|
|
138
|
+
request = Request(
|
|
139
|
+
Box(["step", "long"], [0, -0.7], [3, 0.3]), Select("date", ["2000-01-01"]), Select("level", [128])
|
|
140
|
+
)
|
|
141
|
+
result = self.API.retrieve(request)
|
|
142
|
+
# result.pprint()
|
|
143
|
+
assert len(result.leaves) == 1
|
|
144
|
+
assert [(val,) for val in result.leaves[0].values] == [
|
|
145
|
+
(-1.0,),
|
|
146
|
+
(-0.9,),
|
|
147
|
+
(-0.8,),
|
|
148
|
+
(-0.7,),
|
|
149
|
+
(-0.7,),
|
|
150
|
+
(-0.6,),
|
|
151
|
+
(-0.5,),
|
|
152
|
+
(-0.4,),
|
|
153
|
+
(-0.3,),
|
|
154
|
+
(-0.2,),
|
|
155
|
+
(-0.1,),
|
|
156
|
+
]
|