summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/python/CMakeLists.txt5
-rw-r--r--src/python/doc/point_cloud.rst8
-rw-r--r--src/python/gudhi/point_cloud/__init__.py0
-rw-r--r--src/python/gudhi/point_cloud/timedelay.py95
-rwxr-xr-xsrc/python/test/test_time_delay.py43
5 files changed, 151 insertions, 0 deletions
diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt
index 20e72a5f..22af3ec9 100644
--- a/src/python/CMakeLists.txt
+++ b/src/python/CMakeLists.txt
@@ -56,6 +56,7 @@ if(PYTHONINTERP_FOUND)
# Modules that should not be auto-imported in __init__.py
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'representations', ")
set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'wasserstein', ")
+ set(GUDHI_PYTHON_MODULES_EXTRA "${GUDHI_PYTHON_MODULES_EXTRA}'point_cloud', ")
add_gudhi_debug_info("Python version ${PYTHON_VERSION_STRING}")
add_gudhi_debug_info("Cython version ${CYTHON_VERSION}")
@@ -226,6 +227,7 @@ if(PYTHONINTERP_FOUND)
file(COPY "gudhi/persistence_graphical_tools.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
file(COPY "gudhi/representations" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi/")
file(COPY "gudhi/wasserstein.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
+ file(COPY "gudhi/point_cloud" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/gudhi")
add_custom_command(
OUTPUT gudhi.so
@@ -404,6 +406,9 @@ if(PYTHONINTERP_FOUND)
add_gudhi_py_test(test_representations)
endif()
+ # Time Delay
+ add_gudhi_py_test(test_time_delay)
+
# Documentation generation is available through sphinx - requires all modules
if(SPHINX_PATH)
if(MATPLOTLIB_FOUND)
diff --git a/src/python/doc/point_cloud.rst b/src/python/doc/point_cloud.rst
index d668428a..c0d4b303 100644
--- a/src/python/doc/point_cloud.rst
+++ b/src/python/doc/point_cloud.rst
@@ -20,3 +20,11 @@ Subsampling
:members:
:special-members:
:show-inheritance:
+
+TimeDelayEmbedding
+------------------
+
+.. autoclass:: gudhi.point_cloud.timedelay.TimeDelayEmbedding
+ :members:
+ :special-members: __call__
+
diff --git a/src/python/gudhi/point_cloud/__init__.py b/src/python/gudhi/point_cloud/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/python/gudhi/point_cloud/__init__.py
diff --git a/src/python/gudhi/point_cloud/timedelay.py b/src/python/gudhi/point_cloud/timedelay.py
new file mode 100644
index 00000000..f01df442
--- /dev/null
+++ b/src/python/gudhi/point_cloud/timedelay.py
@@ -0,0 +1,95 @@
+# This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
+# See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
+# Author(s): Martin Royer, Yuichi Ike, Masatoshi Takenouchi
+#
+# Copyright (C) 2020 Inria, Copyright (C) 2020 Fujitsu Laboratories Ltd.
+# Modification(s):
+# - YYYY/MM Author: Description of the modification
+
+import numpy as np
+
+
+class TimeDelayEmbedding:
+ """Point cloud transformation class.
+ Embeds time-series data in the R^d according to [Takens' Embedding Theorem]
+ (https://en.wikipedia.org/wiki/Takens%27s_theorem) and obtains the
+ coordinates of each point.
+
+ Parameters
+ ----------
+ dim : int, optional (default=3)
+ `d` of R^d to be embedded.
+ delay : int, optional (default=1)
+ Time-Delay embedding.
+ skip : int, optional (default=1)
+ How often to skip embedded points.
+
+ Example
+ -------
+
+ Given delay=3 and skip=2, a point cloud which is obtained by embedding
+ a scalar time-series into R^3 is as follows::
+
+ time-series = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ point cloud = [[1, 4, 7],
+ [3, 6, 9]]
+
+ Given delay=1 and skip=1, a point cloud which is obtained by embedding
+ a 2D vector time-series data into R^4 is as follows::
+
+ time-series = [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]
+ point cloud = [[0, 1, 2, 3],
+ [2, 3, 4, 5],
+ [4, 5, 6, 7],
+ [6, 7, 8, 9]]
+ """
+
+ def __init__(self, dim=3, delay=1, skip=1):
+ self._dim = dim
+ self._delay = delay
+ self._skip = skip
+
+ def __call__(self, ts):
+ """Transform method for single time-series data.
+
+ Parameters
+ ----------
+ ts : Iterable[float] or Iterable[Iterable[float]]
+ A single time-series data, with scalar or vector values.
+
+ Returns
+ -------
+ point cloud : n x dim numpy arrays
+ Makes point cloud from a single time-series data.
+ """
+ return self._transform(np.array(ts))
+
+ def fit(self, ts, y=None):
+ return self
+
+ def _transform(self, ts):
+ """Guts of transform method."""
+ if ts.ndim == 1:
+ repeat = self._dim
+ else:
+ assert self._dim % ts.shape[1] == 0
+ repeat = self._dim // ts.shape[1]
+ end = len(ts) - self._delay * (repeat - 1)
+ short = np.arange(0, end, self._skip)
+ vertical = np.arange(0, repeat * self._delay, self._delay)
+ return ts[np.add.outer(short, vertical)].reshape(len(short), -1)
+
+ def transform(self, ts):
+ """Transform method for multiple time-series data.
+
+ Parameters
+ ----------
+ ts : Iterable[Iterable[float]] or Iterable[Iterable[Iterable[float]]]
+ Multiple time-series data, with scalar or vector values.
+
+ Returns
+ -------
+ point clouds : list of n x dim numpy arrays
+ Makes point cloud from each time-series data.
+ """
+ return [self._transform(np.array(s)) for s in ts]
diff --git a/src/python/test/test_time_delay.py b/src/python/test/test_time_delay.py
new file mode 100755
index 00000000..1ead9bca
--- /dev/null
+++ b/src/python/test/test_time_delay.py
@@ -0,0 +1,43 @@
+from gudhi.point_cloud.timedelay import TimeDelayEmbedding
+import numpy as np
+
+
+def test_normal():
+ # Sample array
+ ts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # Normal case.
+ prep = TimeDelayEmbedding()
+ pointclouds = prep(ts)
+ assert (pointclouds[0] == np.array([1, 2, 3])).all()
+ assert (pointclouds[1] == np.array([2, 3, 4])).all()
+ assert (pointclouds[2] == np.array([3, 4, 5])).all()
+ assert (pointclouds[3] == np.array([4, 5, 6])).all()
+ assert (pointclouds[4] == np.array([5, 6, 7])).all()
+ assert (pointclouds[5] == np.array([6, 7, 8])).all()
+ assert (pointclouds[6] == np.array([7, 8, 9])).all()
+ assert (pointclouds[7] == np.array([8, 9, 10])).all()
+ # Delay = 3
+ prep = TimeDelayEmbedding(delay=3)
+ pointclouds = prep(ts)
+ assert (pointclouds[0] == np.array([1, 4, 7])).all()
+ assert (pointclouds[1] == np.array([2, 5, 8])).all()
+ assert (pointclouds[2] == np.array([3, 6, 9])).all()
+ assert (pointclouds[3] == np.array([4, 7, 10])).all()
+ # Skip = 3
+ prep = TimeDelayEmbedding(skip=3)
+ pointclouds = prep(ts)
+ assert (pointclouds[0] == np.array([1, 2, 3])).all()
+ assert (pointclouds[1] == np.array([4, 5, 6])).all()
+ assert (pointclouds[2] == np.array([7, 8, 9])).all()
+ # Delay = 2 / Skip = 2
+ prep = TimeDelayEmbedding(delay=2, skip=2)
+ pointclouds = prep(ts)
+ assert (pointclouds[0] == np.array([1, 3, 5])).all()
+ assert (pointclouds[1] == np.array([3, 5, 7])).all()
+ assert (pointclouds[2] == np.array([5, 7, 9])).all()
+
+ # Vector series
+ ts = np.arange(0, 10).reshape(-1, 2)
+ prep = TimeDelayEmbedding(dim=4)
+ prep.fit([ts])
+ assert (prep.transform([ts])[0] == [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7], [6, 7, 8, 9]]).all()