transformnd 0.2.1__tar.gz → 0.3.0__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.
- {transformnd-0.2.1 → transformnd-0.3.0}/PKG-INFO +1 -1
- {transformnd-0.2.1 → transformnd-0.3.0}/pyproject.toml +12 -1
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/base.py +52 -29
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/pandas.py +8 -6
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/polars.py +8 -6
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/shapely.py +10 -4
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/base.py +35 -18
- transformnd-0.3.0/src/transformnd/graph.py +412 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/affine.py +75 -72
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/bijection.py +18 -2
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/by_dimension.py +8 -3
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/map_axis.py +7 -2
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/moving_least_squares.py +3 -3
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/reflection.py +12 -12
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/simple.py +7 -6
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/thinplate.py +3 -3
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/util.py +24 -17
- transformnd-0.2.1/src/transformnd/graph.py +0 -251
- {transformnd-0.2.1 → transformnd-0.3.0}/README.md +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/__init__.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/__init__.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/adapters/bounding_box.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/constants.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/extents/__init__.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/extents/base.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/extents/bounding_box.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/py.typed +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/transforms/__init__.py +0 -0
- {transformnd-0.2.1 → transformnd-0.3.0}/src/transformnd/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "transformnd"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "ND coordinate transformations"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Chris Barnes", email = "chris.barnes@gerbi-gmb.de" }]
|
|
@@ -56,6 +56,7 @@ lint = [
|
|
|
56
56
|
"mypy",
|
|
57
57
|
"prek>=0.3.9",
|
|
58
58
|
"types-shapely>=2.1.0.20260408",
|
|
59
|
+
"pydoclint>=0.8.6",
|
|
59
60
|
]
|
|
60
61
|
doc = [
|
|
61
62
|
"pdoc>=16.0.0",
|
|
@@ -80,3 +81,13 @@ check_untyped_defs = true
|
|
|
80
81
|
[tool.pytest]
|
|
81
82
|
testpaths = ["tests", "bench"]
|
|
82
83
|
addopts = ["--benchmark-skip"]
|
|
84
|
+
|
|
85
|
+
[tool.pydoclint]
|
|
86
|
+
arg-type-hints-in-docstring = false
|
|
87
|
+
allow-init-docstring = true
|
|
88
|
+
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
lint.external = [
|
|
91
|
+
"DOC", # pydoclint
|
|
92
|
+
"D", # pydocstyle
|
|
93
|
+
]
|
|
@@ -14,22 +14,27 @@ ObjectT = TypeVar("ObjectT")
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class BaseAdapter[ObjectT, ArrayT](ABC):
|
|
17
|
+
"""Base class for adapters that transform non-array objects."""
|
|
18
|
+
|
|
17
19
|
@abstractmethod
|
|
18
20
|
def apply(self, transform: Transform[ArrayT], obj: ObjectT) -> ObjectT:
|
|
19
21
|
"""Apply the given transformation to a non-array object.
|
|
20
22
|
|
|
21
23
|
Parameters
|
|
22
24
|
----------
|
|
23
|
-
transform
|
|
24
|
-
|
|
25
|
+
transform
|
|
26
|
+
The transformation to apply.
|
|
27
|
+
obj
|
|
28
|
+
The object to transform.
|
|
25
29
|
|
|
26
30
|
Returns
|
|
27
31
|
-------
|
|
28
|
-
|
|
32
|
+
ObjectT
|
|
33
|
+
The transformed object.
|
|
29
34
|
"""
|
|
30
35
|
pass
|
|
31
36
|
|
|
32
|
-
def partial(self, *args, **kwargs) -> Callable[..., ObjectT]:
|
|
37
|
+
def partial(self, *args: Any, **kwargs: Any) -> Callable[..., ObjectT]:
|
|
33
38
|
"""Create a partial function with frozen arguments.
|
|
34
39
|
|
|
35
40
|
Useful for applying the same transform to many objects,
|
|
@@ -37,9 +42,17 @@ class BaseAdapter[ObjectT, ArrayT](ABC):
|
|
|
37
42
|
or for adapters with additional arguments,
|
|
38
43
|
using the same config repeatedly.
|
|
39
44
|
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
*args
|
|
48
|
+
Positional arguments to freeze.
|
|
49
|
+
**kwargs
|
|
50
|
+
Keyword arguments to freeze.
|
|
51
|
+
|
|
40
52
|
Returns
|
|
41
53
|
-------
|
|
42
|
-
Callable
|
|
54
|
+
Callable[..., ObjectT]
|
|
55
|
+
A partial function with the given arguments frozen.
|
|
43
56
|
"""
|
|
44
57
|
return partial(self.apply, *args, **kwargs)
|
|
45
58
|
|
|
@@ -52,15 +65,16 @@ class NullAdapter(BaseAdapter[ArrayT, ArrayT]):
|
|
|
52
65
|
|
|
53
66
|
|
|
54
67
|
class FnAdapter(BaseAdapter[ObjectT, ArrayT]):
|
|
55
|
-
|
|
56
|
-
"""Adapter which simply wraps a function, for typing purposes.
|
|
68
|
+
"""Adapter which simply wraps a function, for typing purposes.
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
fn
|
|
73
|
+
Function which takes the object,
|
|
74
|
+
and applies the transformation to it.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(self, fn: Callable[[Transform[ArrayT], ObjectT], ObjectT]):
|
|
64
78
|
self.fn = fn
|
|
65
79
|
|
|
66
80
|
def apply(self, transform: Transform[ArrayT], obj: ObjectT) -> ObjectT:
|
|
@@ -68,18 +82,19 @@ class FnAdapter(BaseAdapter[ObjectT, ArrayT]):
|
|
|
68
82
|
|
|
69
83
|
|
|
70
84
|
class AttrAdapter(BaseAdapter[ObjectT, ArrayT]):
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
"""Adapter which transforms an object by applying transforms to its attributes.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
**kwargs
|
|
90
|
+
Keys are attribute names, values are adapters with which
|
|
91
|
+
to apply the transform to those attributes.
|
|
92
|
+
`None` is shorthand for `NullAdapter()`;
|
|
93
|
+
i.e. the attribute is an array and can be transformed
|
|
94
|
+
without being adapted.
|
|
95
|
+
"""
|
|
73
96
|
|
|
74
|
-
|
|
75
|
-
----------
|
|
76
|
-
adapters : Dict[str, Optional[BaseAdapter]]
|
|
77
|
-
Keys are attribute names, values are adapters with which
|
|
78
|
-
to apply the transform to those attributes.
|
|
79
|
-
`None` is shorthand for `NullAdapter()`;
|
|
80
|
-
i.e. the attribute is an array and can be transformed
|
|
81
|
-
without being adapted.
|
|
82
|
-
"""
|
|
97
|
+
def __init__(self, **kwargs: BaseAdapter[Any, ArrayT] | None) -> None:
|
|
83
98
|
self.adapters = {
|
|
84
99
|
k: NullAdapter[ArrayT]() if v is None else v for k, v in kwargs.items()
|
|
85
100
|
}
|
|
@@ -91,15 +106,23 @@ class AttrAdapter(BaseAdapter[ObjectT, ArrayT]):
|
|
|
91
106
|
|
|
92
107
|
Parameters
|
|
93
108
|
----------
|
|
94
|
-
transform
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
transform
|
|
110
|
+
The transformation to apply.
|
|
111
|
+
obj
|
|
112
|
+
The object to transform.
|
|
113
|
+
in_place
|
|
97
114
|
Whether to mutate the given object in place,
|
|
98
115
|
by default False (i.e. make a deep copy of it).
|
|
99
116
|
|
|
100
117
|
Returns
|
|
101
118
|
-------
|
|
102
|
-
|
|
119
|
+
ObjectT
|
|
120
|
+
The transformed object.
|
|
121
|
+
|
|
122
|
+
Raises
|
|
123
|
+
------
|
|
124
|
+
TypeError
|
|
125
|
+
If the adapter does not support the in_place argument.
|
|
103
126
|
"""
|
|
104
127
|
if not in_place:
|
|
105
128
|
obj = deepcopy(obj)
|
|
@@ -147,7 +170,7 @@ class ReshapeAdapter(BaseAdapter[ArrayT, ArrayT]):
|
|
|
147
170
|
|
|
148
171
|
Parameters
|
|
149
172
|
----------
|
|
150
|
-
dim_axis
|
|
173
|
+
dim_axis
|
|
151
174
|
Which axis contains the coordinates' dimensions,
|
|
152
175
|
by default -1 (last)
|
|
153
176
|
"""
|
|
@@ -15,7 +15,7 @@ class PandasAdapter(BaseAdapter[pd.DataFrame, np.ndarray]):
|
|
|
15
15
|
|
|
16
16
|
Parameters
|
|
17
17
|
----------
|
|
18
|
-
columns
|
|
18
|
+
columns
|
|
19
19
|
Keys for columns containing coordinates, e.g. `["x", "y", "z"]`
|
|
20
20
|
"""
|
|
21
21
|
self.columns = columns
|
|
@@ -27,16 +27,18 @@ class PandasAdapter(BaseAdapter[pd.DataFrame, np.ndarray]):
|
|
|
27
27
|
|
|
28
28
|
Parameters
|
|
29
29
|
----------
|
|
30
|
-
transform
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
transform
|
|
31
|
+
The transformation to apply.
|
|
32
|
+
df
|
|
33
|
+
The DataFrame to transform.
|
|
34
|
+
in_place
|
|
34
35
|
Whether to mutate the dataframe in place,
|
|
35
36
|
by default False (i.e. make a copy of it).
|
|
36
37
|
|
|
37
38
|
Returns
|
|
38
39
|
-------
|
|
39
|
-
|
|
40
|
+
pd.DataFrame
|
|
41
|
+
The transformed DataFrame.
|
|
40
42
|
"""
|
|
41
43
|
coords = df[self.columns].to_numpy()
|
|
42
44
|
transformed = transform.apply(coords)
|
|
@@ -13,7 +13,7 @@ class PolarsAdapter(BaseAdapter[pl.DataFrame, np.ndarray]):
|
|
|
13
13
|
|
|
14
14
|
Parameters
|
|
15
15
|
----------
|
|
16
|
-
columns
|
|
16
|
+
columns
|
|
17
17
|
Keys for columns containing coordinates, e.g. `["x", "y", "z"]`
|
|
18
18
|
"""
|
|
19
19
|
self.columns = columns
|
|
@@ -25,16 +25,18 @@ class PolarsAdapter(BaseAdapter[pl.DataFrame, np.ndarray]):
|
|
|
25
25
|
|
|
26
26
|
Parameters
|
|
27
27
|
----------
|
|
28
|
-
transform
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
transform
|
|
29
|
+
The transformation to apply.
|
|
30
|
+
df
|
|
31
|
+
The DataFrame to transform.
|
|
32
|
+
in_place
|
|
32
33
|
Whether to mutate the dataframe in place,
|
|
33
34
|
by default False (i.e. make a copy of it).
|
|
34
35
|
|
|
35
36
|
Returns
|
|
36
37
|
-------
|
|
37
|
-
|
|
38
|
+
pl.DataFrame
|
|
39
|
+
The transformed DataFrame.
|
|
38
40
|
"""
|
|
39
41
|
coords = df[self.columns].to_numpy()
|
|
40
42
|
transformed = transform.apply(coords)
|
|
@@ -96,19 +96,25 @@ class GeometryAdapter(BaseAdapter[BaseGeometry, ArrayT]):
|
|
|
96
96
|
) -> BaseGeometry:
|
|
97
97
|
"""Transform the shapely geometry.
|
|
98
98
|
|
|
99
|
-
See the other `apply_*` methods if you already know what type of geometry
|
|
100
|
-
this may be a bit faster.
|
|
99
|
+
See the other `apply_*` methods if you already know what type of geometry
|
|
100
|
+
you're working with; this may be a bit faster.
|
|
101
101
|
|
|
102
102
|
Parameters
|
|
103
103
|
----------
|
|
104
|
-
transform
|
|
105
|
-
|
|
104
|
+
transform
|
|
105
|
+
The transformation to apply.
|
|
106
|
+
obj
|
|
106
107
|
Some shapely geometry in 2 or 3D
|
|
107
108
|
|
|
108
109
|
Returns
|
|
109
110
|
-------
|
|
110
111
|
BaseGeometry
|
|
111
112
|
An object of the same type as the input.
|
|
113
|
+
|
|
114
|
+
Raises
|
|
115
|
+
------
|
|
116
|
+
ValueError
|
|
117
|
+
If the geometry type is not supported.
|
|
112
118
|
"""
|
|
113
119
|
if isinstance(obj, BaseMultipartGeometry):
|
|
114
120
|
return self.apply_multipart(transform, obj)
|
|
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
from collections.abc import Iterator, Sequence
|
|
7
7
|
from copy import copy
|
|
8
8
|
from typing import Self, TYPE_CHECKING
|
|
9
|
+
from types import ModuleType
|
|
9
10
|
|
|
10
11
|
from array_api_compat import array_namespace
|
|
11
12
|
|
|
@@ -35,6 +36,8 @@ class Transform[ArrayT](ABC):
|
|
|
35
36
|
"""
|
|
36
37
|
Parameters
|
|
37
38
|
----------
|
|
39
|
+
ndims
|
|
40
|
+
Source and target dimensionality.
|
|
38
41
|
spaces
|
|
39
42
|
Optional source and target spaces
|
|
40
43
|
"""
|
|
@@ -50,7 +53,7 @@ class Transform[ArrayT](ABC):
|
|
|
50
53
|
|
|
51
54
|
Returns
|
|
52
55
|
-------
|
|
53
|
-
|
|
56
|
+
Affine[ArrayT] | None
|
|
54
57
|
The affine transformation, if conversion is possible.
|
|
55
58
|
None otherwise.
|
|
56
59
|
"""
|
|
@@ -63,9 +66,14 @@ class Transform[ArrayT](ABC):
|
|
|
63
66
|
|
|
64
67
|
Parameters
|
|
65
68
|
----------
|
|
66
|
-
coords
|
|
69
|
+
coords
|
|
67
70
|
NxD array of N D-dimensional coordinates.
|
|
68
71
|
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
ArrayT
|
|
75
|
+
The validated coordinates.
|
|
76
|
+
|
|
69
77
|
Raises
|
|
70
78
|
------
|
|
71
79
|
ValueError
|
|
@@ -87,12 +95,12 @@ class Transform[ArrayT](ABC):
|
|
|
87
95
|
|
|
88
96
|
Parameters
|
|
89
97
|
----------
|
|
90
|
-
coords
|
|
98
|
+
coords
|
|
91
99
|
NxD array of N D-dimensional coordinates.
|
|
92
100
|
|
|
93
101
|
Returns
|
|
94
102
|
-------
|
|
95
|
-
|
|
103
|
+
ArrayT
|
|
96
104
|
Transformed coordinates in the same shape.
|
|
97
105
|
"""
|
|
98
106
|
pass
|
|
@@ -116,7 +124,7 @@ class Transform[ArrayT](ABC):
|
|
|
116
124
|
return NotImplemented
|
|
117
125
|
return t
|
|
118
126
|
|
|
119
|
-
def to_device(self, xp, device=None) -> Self: # noqa: ARG002
|
|
127
|
+
def to_device(self, xp: ModuleType, device: str | None = None) -> Self: # noqa: ARG002
|
|
120
128
|
"""Return a copy of this transform with array parameters placed on the given device.
|
|
121
129
|
|
|
122
130
|
Useful for pre-allocating parameters on GPU before a tight apply() loop,
|
|
@@ -124,15 +132,15 @@ class Transform[ArrayT](ABC):
|
|
|
124
132
|
|
|
125
133
|
Parameters
|
|
126
134
|
----------
|
|
127
|
-
xp
|
|
135
|
+
xp
|
|
128
136
|
The target array namespace (e.g. jax.numpy, torch).
|
|
129
|
-
device
|
|
137
|
+
device
|
|
130
138
|
Target device (e.g. from array_api_compat.device(array)).
|
|
131
139
|
If None, uses xp's default device.
|
|
132
140
|
|
|
133
141
|
Returns
|
|
134
142
|
-------
|
|
135
|
-
|
|
143
|
+
Self
|
|
136
144
|
A new transform instance with parameters on the target device,
|
|
137
145
|
or NotImplemented if the subclass does not support device placement.
|
|
138
146
|
"""
|
|
@@ -145,11 +153,13 @@ class Transform[ArrayT](ABC):
|
|
|
145
153
|
|
|
146
154
|
Parameters
|
|
147
155
|
----------
|
|
148
|
-
other
|
|
156
|
+
other
|
|
157
|
+
The transform to compose with.
|
|
149
158
|
|
|
150
159
|
Returns
|
|
151
160
|
-------
|
|
152
|
-
TransformSequence
|
|
161
|
+
TransformSequence[ArrayT]
|
|
162
|
+
The composed transform sequence.
|
|
153
163
|
"""
|
|
154
164
|
if not isinstance(other, Transform):
|
|
155
165
|
return NotImplemented
|
|
@@ -166,11 +176,13 @@ class Transform[ArrayT](ABC):
|
|
|
166
176
|
|
|
167
177
|
Parameters
|
|
168
178
|
----------
|
|
169
|
-
other
|
|
179
|
+
other
|
|
180
|
+
The transform to compose with.
|
|
170
181
|
|
|
171
182
|
Returns
|
|
172
183
|
-------
|
|
173
|
-
TransformSequence
|
|
184
|
+
TransformSequence[ArrayT]
|
|
185
|
+
The composed transform sequence.
|
|
174
186
|
"""
|
|
175
187
|
if not isinstance(other, Transform):
|
|
176
188
|
return NotImplemented
|
|
@@ -205,9 +217,13 @@ class TransformWrapper(Transform[ArrayT]):
|
|
|
205
217
|
|
|
206
218
|
Parameters
|
|
207
219
|
----------
|
|
208
|
-
fn
|
|
220
|
+
fn
|
|
209
221
|
Callable.
|
|
210
|
-
|
|
222
|
+
in_ndim
|
|
223
|
+
Dimensionality of the input coordinates.
|
|
224
|
+
out_ndim
|
|
225
|
+
Dimensionality of the output coordinates.
|
|
226
|
+
spaces
|
|
211
227
|
Optional source and target spaces
|
|
212
228
|
"""
|
|
213
229
|
super().__init__(NDims(in_ndim, out_ndim), spaces=spaces)
|
|
@@ -331,22 +347,23 @@ class TransformSequence(Transform[ArrayT], Sequence[Transform[ArrayT]]):
|
|
|
331
347
|
coords = t.apply(coords)
|
|
332
348
|
return coords
|
|
333
349
|
|
|
334
|
-
def to_device(self, xp, device=None) -> Self:
|
|
350
|
+
def to_device(self, xp: ModuleType, device: str | None = None) -> Self:
|
|
335
351
|
result = copy(self)
|
|
336
352
|
result.transforms = [t.to_device(xp, device) for t in self.transforms]
|
|
337
353
|
return result
|
|
338
354
|
|
|
339
|
-
def list_spaces(self, skip_none=False) -> list[SpaceRef]:
|
|
355
|
+
def list_spaces(self, skip_none: bool = False) -> list[SpaceRef]:
|
|
340
356
|
"""List spaces in this transform.
|
|
341
357
|
|
|
342
358
|
Parameters
|
|
343
359
|
----------
|
|
344
|
-
skip_none
|
|
360
|
+
skip_none
|
|
345
361
|
Whether to skip undefined spaces, default False.
|
|
346
362
|
|
|
347
363
|
Returns
|
|
348
364
|
-------
|
|
349
|
-
|
|
365
|
+
list[SpaceRef]
|
|
366
|
+
The list of spaces.
|
|
350
367
|
"""
|
|
351
368
|
spaces = [self.spaces.source] + [t.spaces.target for t in self.transforms]
|
|
352
369
|
if skip_none:
|