Skip to content

Commit a05249b

Browse files
authored
Merge pull request #18 from espdev/refactoring-internals
Refactoring internals
2 parents 6a301dc + 47c2fc4 commit a05249b

File tree

10 files changed

+191
-198
lines changed

10 files changed

+191
-198
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## v0.11.0
4+
5+
* Internal re-design `SplinePPForm` and `NdGridSplinePPForm` classes [#17](https://github.com/espdev/csaps/issues/17):
6+
- Remove `shape` and `axis` properties and reshaping data in these classes
7+
- `NdGridSplinePPForm` coefficients array for 1D grid now is 1-d instead of 2-d
8+
* Refactoring the code and decrease memory consumption
9+
* Add `overload` type-hints for `csaps` function signatures
10+
311
## v0.10.1
412

513
* Fix call of `numpy.pad` function for numpy <1.17 [#15](https://github.com/espdev/csaps/issues/15)

README.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
**csaps** is a Python package for univariate, multivariate and n-dimensional grid data approximation using cubic smoothing splines.
1515
The package can be useful in practical engineering tasks for data approximation and smoothing.
1616

17-
## Installation
17+
## Installing
1818

1919
Use pip for installing:
2020

@@ -89,20 +89,22 @@ plt.show()
8989

9090
## Documentation
9191

92-
More examples of usage and the full documentation can be found at ReadTheDocs.
93-
94-
https://csaps.readthedocs.io
92+
More examples of usage and the full documentation can be found at https://csaps.readthedocs.io.
9593

9694
## Testing
9795

9896
pytest, tox and Travis CI are used for testing. Please see [tests](tests).
9997

100-
## Algorithms and implementations
98+
## Algorithm and Implementation
10199

102-
**csaps** package is a Python modified port of MATLAB [CSAPS](https://www.mathworks.com/help/curvefit/csaps.html) function that is an implementation of
100+
**csaps** Python package is inspired by MATLAB [CSAPS](https://www.mathworks.com/help/curvefit/csaps.html) function that is an implementation of
103101
Fortran routine SMOOTH from [PGS](http://pages.cs.wisc.edu/~deboor/pgs/) (originally written by Carl de Boor).
104102

105-
[csaps-cpp](https://github.com/espdev/csaps-cpp) C++11 Eigen based implementation of the algorithm.
103+
Also the algothithm implementation in other languages:
104+
105+
* [csaps-rs](https://github.com/espdev/csaps-rs) Rust ndarray/sprs based implementation
106+
* [csaps-cpp](https://github.com/espdev/csaps-cpp) C++11 Eigen based implementation (incomplete)
107+
106108

107109
## References
108110

csaps/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
)
2424
from csaps._types import (
2525
UnivariateDataType,
26-
UnivariateVectorizedDataType,
2726
MultivariateDataType,
2827
NdGridDataType,
2928
)
@@ -46,7 +45,6 @@
4645

4746
# Type-hints
4847
'UnivariateDataType',
49-
'UnivariateVectorizedDataType',
5048
'MultivariateDataType',
5149
'NdGridDataType',
5250
]

csaps/_shortcut.py

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,93 @@
66
"""
77

88
from collections import abc as c_abc
9-
from typing import Optional, Union, Sequence, NamedTuple
10-
11-
import numpy as np
9+
from typing import Optional, Union, Sequence, NamedTuple, overload
1210

1311
from ._base import ISmoothingSpline
1412
from ._sspumv import CubicSmoothingSpline
1513
from ._sspndg import ndgrid_prepare_data_sites, NdGridCubicSmoothingSpline
16-
from ._types import (
17-
UnivariateDataType,
18-
UnivariateVectorizedDataType,
19-
NdGridDataType,
20-
)
21-
22-
_XDataType = Union[UnivariateDataType, NdGridDataType]
23-
_YDataType = Union[UnivariateVectorizedDataType, np.ndarray]
24-
_XiDataType = Optional[Union[UnivariateDataType, NdGridDataType]]
25-
_WeightsDataType = Optional[Union[UnivariateDataType, NdGridDataType]]
26-
_SmoothDataType = Optional[Union[float, Sequence[Optional[float]]]]
14+
from ._types import UnivariateDataType, MultivariateDataType, NdGridDataType
2715

2816

2917
class AutoSmoothingResult(NamedTuple):
3018
"""The result for auto smoothing for `csaps` function"""
3119

32-
values: _YDataType
20+
values: MultivariateDataType
3321
"""Smoothed data values"""
3422

35-
smooth: _SmoothDataType
23+
smooth: Union[float, Sequence[Optional[float]]]
3624
"""The calculated smoothing parameter"""
3725

3826

39-
_ReturnType = Union[
40-
_YDataType,
41-
AutoSmoothingResult,
42-
ISmoothingSpline,
43-
]
27+
# **************************************
28+
# csaps signatures
29+
#
30+
@overload
31+
def csaps(xdata: UnivariateDataType,
32+
ydata: MultivariateDataType,
33+
*,
34+
weights: Optional[UnivariateDataType] = None,
35+
smooth: Optional[float] = None,
36+
axis: Optional[int] = None) -> ISmoothingSpline: ...
37+
38+
39+
@overload
40+
def csaps(xdata: UnivariateDataType,
41+
ydata: MultivariateDataType,
42+
xidata: UnivariateDataType,
43+
*,
44+
weights: Optional[UnivariateDataType] = None,
45+
axis: Optional[int] = None) -> AutoSmoothingResult: ...
46+
47+
48+
@overload
49+
def csaps(xdata: UnivariateDataType,
50+
ydata: MultivariateDataType,
51+
xidata: UnivariateDataType,
52+
*,
53+
smooth: float,
54+
weights: Optional[UnivariateDataType] = None,
55+
axis: Optional[int] = None) -> MultivariateDataType: ...
56+
57+
58+
@overload
59+
def csaps(xdata: NdGridDataType,
60+
ydata: MultivariateDataType,
61+
*,
62+
weights: Optional[NdGridDataType] = None,
63+
smooth: Optional[Sequence[float]] = None,
64+
axis: Optional[int] = None) -> ISmoothingSpline: ...
65+
66+
67+
@overload
68+
def csaps(xdata: NdGridDataType,
69+
ydata: MultivariateDataType,
70+
xidata: NdGridDataType,
71+
*,
72+
weights: Optional[NdGridDataType] = None,
73+
axis: Optional[int] = None) -> AutoSmoothingResult: ...
74+
75+
76+
@overload
77+
def csaps(xdata: NdGridDataType,
78+
ydata: MultivariateDataType,
79+
xidata: NdGridDataType,
80+
*,
81+
smooth: Sequence[float],
82+
weights: Optional[NdGridDataType] = None,
83+
axis: Optional[int] = None) -> MultivariateDataType: ...
84+
#
85+
# csaps signatures
86+
# **************************************
4487

4588

46-
def csaps(xdata: _XDataType,
47-
ydata: _YDataType,
48-
xidata: _XiDataType = None,
89+
def csaps(xdata: Union[UnivariateDataType, NdGridDataType],
90+
ydata: MultivariateDataType,
91+
xidata: Optional[Union[UnivariateDataType, NdGridDataType]] = None,
4992
*,
50-
weights: _WeightsDataType = None,
51-
smooth: _SmoothDataType = None,
52-
axis: Optional[int] = None) -> _ReturnType:
93+
weights: Optional[Union[UnivariateDataType, NdGridDataType]] = None,
94+
smooth: Optional[Union[float, Sequence[float]]] = None,
95+
axis: Optional[int] = None) -> Union[MultivariateDataType, ISmoothingSpline, AutoSmoothingResult]:
5396
"""Smooths the univariate/multivariate/gridded data or computes the corresponding splines
5497
5598
This function might be used as the main API for smoothing any data.

csaps/_sspndg.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -37,82 +37,64 @@ class NdGridSplinePPForm(SplinePPFormBase[ty.Sequence[np.ndarray], ty.Tuple[int,
3737
3838
Parameters
3939
----------
40-
breaks : np.ndarray
41-
Breaks values 1-D array
40+
breaks : Sequence[np.ndarray]
41+
The sequence of the breaks 1-D arrays for each dimension
42+
4243
coeffs : np.ndarray
43-
Spline coefficients N-D array
44+
Tensor-product spline coefficients N-D array
45+
4446
"""
4547

4648
def __init__(self, breaks: ty.Sequence[np.ndarray], coeffs: np.ndarray) -> None:
4749
self._breaks = breaks
4850
self._coeffs = coeffs
4951
self._pieces = tuple(x.size - 1 for x in breaks)
52+
self._order = tuple(s // p for s, p in zip(coeffs.shape, self._pieces))
5053
self._ndim = len(breaks)
5154

52-
if self._ndim > 1:
53-
self._order = tuple(s // p for s, p in zip(coeffs.shape, self._pieces))
54-
else:
55-
# the corner case for univariate spline that is represented as 1d-grid
56-
self._order = (coeffs.shape[1] // self._pieces[0], )
57-
5855
@property
5956
def breaks(self) -> ty.Sequence[np.ndarray]:
57+
"""Returns the sequence of the data breaks for each dimension"""
6058
return self._breaks
6159

6260
@property
6361
def coeffs(self) -> np.ndarray:
62+
"""Returns n-d array of tensor-product n-d grid spline coefficients"""
6463
return self._coeffs
6564

6665
@property
6766
def order(self) -> ty.Tuple[int, ...]:
67+
"""Returns the tuple of the spline orders for each dimension"""
6868
return self._order
6969

7070
@property
7171
def pieces(self) -> ty.Tuple[int, ...]:
72+
"""Returns the tuple of the spline pieces for each dimension"""
7273
return self._pieces
7374

7475
@property
7576
def ndim(self) -> int:
77+
"""Returns the dimensionality of n-d grid data"""
7678
return self._ndim
7779

78-
@property
79-
def shape(self) -> ty.Tuple[int, ...]:
80-
"""Returns the original data shape
81-
"""
82-
return tuple(x.size for x in self.breaks)
83-
8480
def evaluate(self, xi: ty.Sequence[np.ndarray]) -> np.ndarray:
81+
"""Evaluates the spline for given data point(s) on the n-d grid"""
8582
shape = tuple(x.size for x in xi)
8683

8784
coeffs = self.coeffs
88-
coeffs_shape = list(coeffs.shape)
85+
coeffs_shape = coeffs.shape
8986

90-
d = self.ndim - 1
91-
permuted_axes = (d, *range(d))
87+
ndim_m1 = self.ndim - 1
88+
permuted_axes = (ndim_m1, *range(ndim_m1))
9289

9390
for i in reversed(range(self.ndim)):
94-
xii = xi[i]
95-
ndim = int(np.prod(coeffs_shape[:d]))
96-
97-
if self.ndim > 2:
98-
coeffs = coeffs.reshape((ndim, self.pieces[i] * self.order[i]))
99-
100-
spp = SplinePPForm(
101-
breaks=self.breaks[i],
102-
coeffs=coeffs,
103-
pieces=self.pieces[i],
104-
order=self.order[i],
105-
shape=(ndim, xii.size)
106-
)
91+
umv_ndim = int(np.prod(coeffs_shape[:ndim_m1]))
92+
coeffs = coeffs.reshape((umv_ndim, self.pieces[i] * self.order[i]))
10793

108-
coeffs = spp.evaluate(xii)
94+
coeffs = SplinePPForm(breaks=self.breaks[i], coeffs=coeffs).evaluate(xi[i])
10995

110-
if self.ndim > 2:
111-
coeffs = coeffs.reshape((*coeffs_shape[:d], shape[i]))
112-
113-
if self.ndim > 1:
114-
coeffs = coeffs.transpose(permuted_axes)
115-
coeffs_shape = list(coeffs.shape)
96+
coeffs = coeffs.reshape((*coeffs_shape[:ndim_m1], shape[i])).transpose(permuted_axes)
97+
coeffs_shape = coeffs.shape
11698

11799
return coeffs.reshape(shape)
118100

@@ -261,4 +243,6 @@ def _make_spline(self, smooth: ty.List[ty.Optional[float]]) -> ty.Tuple[NdGridSp
261243
coeffs = coeffs.transpose(permute_axes)
262244
coeffs_shape = list(coeffs.shape)
263245

246+
coeffs = coeffs.squeeze()
247+
264248
return NdGridSplinePPForm(breaks=self._xdata, coeffs=coeffs), tuple(reversed(smooths))

0 commit comments

Comments
 (0)