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.
Files changed (121) hide show
  1. polytope_python-1.0.32/PKG-INFO +21 -0
  2. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/fdb.py +4 -1
  3. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/exceptions.py +5 -0
  4. polytope_python-1.0.32/polytope_feature/version.py +1 -0
  5. polytope_python-1.0.32/polytope_python.egg-info/PKG-INFO +21 -0
  6. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/SOURCES.txt +62 -1
  7. polytope_python-1.0.32/tests/test_axis_mappers.py +122 -0
  8. polytope_python-1.0.32/tests/test_bad_request_error.py +65 -0
  9. polytope_python-1.0.32/tests/test_combinatorics.py +71 -0
  10. polytope_python-1.0.32/tests/test_cyclic_axis_over_negative_vals.py +156 -0
  11. polytope_python-1.0.32/tests/test_cyclic_axis_slicer_not_0.py +156 -0
  12. polytope_python-1.0.32/tests/test_cyclic_axis_slicing.py +270 -0
  13. polytope_python-1.0.32/tests/test_cyclic_nearest.py +121 -0
  14. polytope_python-1.0.32/tests/test_cyclic_simple.py +76 -0
  15. polytope_python-1.0.32/tests/test_cyclic_snapping.py +36 -0
  16. polytope_python-1.0.32/tests/test_datacube_axes_init.py +126 -0
  17. polytope_python-1.0.32/tests/test_datacube_mock.py +24 -0
  18. polytope_python-1.0.32/tests/test_datacube_xarray.py +137 -0
  19. polytope_python-1.0.32/tests/test_date_time_unmerged.py +69 -0
  20. polytope_python-1.0.32/tests/test_ecmwf_oper_data_fdb.py +186 -0
  21. polytope_python-1.0.32/tests/test_engine_slicer.py +86 -0
  22. polytope_python-1.0.32/tests/test_fdb_datacube.py +127 -0
  23. polytope_python-1.0.32/tests/test_fdb_unmap_tree.py +85 -0
  24. polytope_python-1.0.32/tests/test_float_type.py +61 -0
  25. polytope_python-1.0.32/tests/test_healpix_mapper.py +81 -0
  26. polytope_python-1.0.32/tests/test_healpix_nested_grid.py +182 -0
  27. polytope_python-1.0.32/tests/test_hull_slicer.py +86 -0
  28. polytope_python-1.0.32/tests/test_hullslicer_engine.py +45 -0
  29. polytope_python-1.0.32/tests/test_incomplete_tree_fdb.py +111 -0
  30. polytope_python-1.0.32/tests/test_local_grid_cyclic.py +110 -0
  31. polytope_python-1.0.32/tests/test_local_regular_grid.py +337 -0
  32. polytope_python-1.0.32/tests/test_local_swiss_grid.py +102 -0
  33. polytope_python-1.0.32/tests/test_mappers.py +107 -0
  34. polytope_python-1.0.32/tests/test_merge_cyclic_octahedral.py +57 -0
  35. polytope_python-1.0.32/tests/test_merge_octahedral_one_axis.py +51 -0
  36. polytope_python-1.0.32/tests/test_merge_transformation.py +40 -0
  37. polytope_python-1.0.32/tests/test_multiple_param_fdb.py +71 -0
  38. polytope_python-1.0.32/tests/test_octahedral_grid.py +89 -0
  39. polytope_python-1.0.32/tests/test_override_md5_hash_options.py +80 -0
  40. polytope_python-1.0.32/tests/test_point_nearest.py +211 -0
  41. polytope_python-1.0.32/tests/test_point_shape.py +49 -0
  42. polytope_python-1.0.32/tests/test_profiling_requesttree.py +44 -0
  43. polytope_python-1.0.32/tests/test_reduced_ll_grid.py +122 -0
  44. polytope_python-1.0.32/tests/test_regular_grid.py +135 -0
  45. polytope_python-1.0.32/tests/test_regular_reduced_grid.py +108 -0
  46. polytope_python-1.0.32/tests/test_request_tree.py +331 -0
  47. polytope_python-1.0.32/tests/test_request_trees_after_slicing.py +99 -0
  48. polytope_python-1.0.32/tests/test_reverse_transformation.py +30 -0
  49. polytope_python-1.0.32/tests/test_shapes.py +120 -0
  50. polytope_python-1.0.32/tests/test_slice_date_range_fdb.py +230 -0
  51. polytope_python-1.0.32/tests/test_slice_date_range_fdb_v2.py +82 -0
  52. polytope_python-1.0.32/tests/test_slice_fdb_box.py +97 -0
  53. polytope_python-1.0.32/tests/test_slicer_engine.py +45 -0
  54. polytope_python-1.0.32/tests/test_slicer_era5.py +41 -0
  55. polytope_python-1.0.32/tests/test_slicer_xarray.py +44 -0
  56. polytope_python-1.0.32/tests/test_slicing_unsliceable_axis.py +42 -0
  57. polytope_python-1.0.32/tests/test_slicing_xarray_3D.py +259 -0
  58. polytope_python-1.0.32/tests/test_slicing_xarray_4D.py +306 -0
  59. polytope_python-1.0.32/tests/test_snapping.py +88 -0
  60. polytope_python-1.0.32/tests/test_snapping_real_data.py +77 -0
  61. polytope_python-1.0.32/tests/test_tree_protobuf.py +25 -0
  62. polytope_python-1.0.32/tests/test_tree_protobuf_encoding.py +70 -0
  63. polytope_python-1.0.32/tests/test_tree_protobuf_encoding_fdb.py +82 -0
  64. polytope_python-1.0.32/tests/test_type_change_transformation.py +31 -0
  65. polytope_python-1.0.32/tests/test_union_gj.py +166 -0
  66. polytope_python-1.0.32/tests/test_union_point_box.py +75 -0
  67. polytope_python-1.0.32/tests/test_wave_spectra_data.py +99 -0
  68. polytope-python-1.0.30/PKG-INFO +0 -13
  69. polytope-python-1.0.30/polytope_feature/version.py +0 -1
  70. polytope-python-1.0.30/polytope_python.egg-info/PKG-INFO +0 -13
  71. {polytope-python-1.0.30 → polytope_python-1.0.32}/LICENSE +0 -0
  72. {polytope-python-1.0.30 → polytope_python-1.0.32}/MANIFEST.in +0 -0
  73. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/__init__.py +0 -0
  74. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/__init__.py +0 -0
  75. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/__init__.py +0 -0
  76. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/datacube.py +0 -0
  77. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/mock.py +0 -0
  78. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/backends/xarray.py +0 -0
  79. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/datacube_axis.py +0 -0
  80. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/index_tree_pb2.py +0 -0
  81. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/tensor_index_tree.py +0 -0
  82. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/__init__.py +0 -0
  83. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_cyclic/__init__.py +0 -0
  84. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_cyclic/datacube_cyclic.py +0 -0
  85. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/__init__.py +0 -0
  86. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/datacube_mappers.py +0 -0
  87. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/__init__.py +0 -0
  88. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix.py +0 -0
  89. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/healpix_nested.py +0 -0
  90. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/local_regular.py +0 -0
  91. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/octahedral.py +0 -0
  92. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_gaussian.py +0 -0
  93. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/reduced_ll.py +0 -0
  94. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_mappers/mapper_types/regular.py +0 -0
  95. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_merger/__init__.py +0 -0
  96. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_merger/datacube_merger.py +0 -0
  97. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_reverse/__init__.py +0 -0
  98. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_reverse/datacube_reverse.py +0 -0
  99. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_transformations.py +0 -0
  100. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_type_change/__init__.py +0 -0
  101. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/transformations/datacube_type_change/datacube_type_change.py +0 -0
  102. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/datacube/tree_encoding.py +0 -0
  103. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/__init__.py +0 -0
  104. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/engine.py +0 -0
  105. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/engine/hullslicer.py +0 -0
  106. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/options.py +0 -0
  107. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/polytope.py +0 -0
  108. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/shapes.py +0 -0
  109. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/__init__.py +0 -0
  110. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/combinatorics.py +0 -0
  111. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/geometry.py +0 -0
  112. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/list_tools.py +0 -0
  113. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_feature/utility/profiling.py +0 -0
  114. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/dependency_links.txt +0 -0
  115. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/not-zip-safe +0 -0
  116. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/requires.txt +0 -0
  117. {polytope-python-1.0.30 → polytope_python-1.0.32}/polytope_python.egg-info/top_level.txt +0 -0
  118. {polytope-python-1.0.30 → polytope_python-1.0.32}/pyproject.toml +0 -0
  119. {polytope-python-1.0.30 → polytope_python-1.0.32}/requirements.txt +0 -0
  120. {polytope-python-1.0.30 → polytope_python-1.0.32}/setup.cfg +0 -0
  121. {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
+ ]