From f08e6c450fc54b786f226f850c464dca346a5693 Mon Sep 17 00:00:00 2001 From: Gargee Sharma Date: Sun, 20 Sep 2026 01:43:52 +0530 Subject: [PATCH 1/4] docs: add 1D motif discovery how-to example notebook --- notebooks/how_to_find_1d_motifs.ipynb | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 notebooks/how_to_find_1d_motifs.ipynb diff --git a/notebooks/how_to_find_1d_motifs.ipynb b/notebooks/how_to_find_1d_motifs.ipynb new file mode 100644 index 0000000..74a70f6 --- /dev/null +++ b/notebooks/how_to_find_1d_motifs.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "50db9d0d", + "metadata": {}, + "source": [ + "# How to Find Repeating Patterns (Motifs) in a 1D Time Series\n", + "\n", + "**Problem:** You have a 1D time series dataset and want to quickly locate the two most similar sub-sequences (a motif pair) of a specific length $m$ without writing complex search loops." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6c544e39", + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'numpy'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m numpy \u001b[38;5;28;01mas\u001b[39;00m np\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m stumpy\n\u001b[32m 3\u001b[39m \n\u001b[32m 4\u001b[39m \u001b[38;5;66;03m# 1. Generate or load a 1D time series (array-like, float64)\u001b[39;00m\n", + "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'numpy'" + ] + } + ], + "source": [ + "import numpy as np\n", + "import stumpy\n", + "\n", + "# 1. Generate or load a 1D time series (array-like, float64)\n", + "time_series = np.array([1.0, 1.3, 1.2, 5.0, 1.1, 1.3, 1.2, 0.9, 4.8], dtype=np.float64)\n", + "m = 3 # Set window size (length of the sub-sequence to compare)\n", + "\n", + "# 2. Compute the matrix profile and matrix profile indices\n", + "mp = stumpy.stump(time_series, m)\n", + "\n", + "# 3. Extract the nearest neighbor pair (top motif)\n", + "nearest_neighbor_idx = np.argmin(mp[:, 0])\n", + "motif_idx = mp[nearest_neighbor_idx, 1]\n", + "\n", + "print(f\"Top motif found between index {nearest_neighbor_idx} and index {motif_idx}\")" + ] + }, + { + "cell_type": "markdown", + "id": "bfa92bf6", + "metadata": {}, + "source": [ + "### How It Works\n", + "\n", + "`stumpy.stump` returns a 2D NumPy array where each row $i$ represents a sub-sequence starting at index $i$:\n", + "* **Column 0 (`mp[:, 0]`)**: Contains the Matrix Profile values (z-normalized Euclidean distance to the nearest matching sub-sequence).\n", + "* **Column 1 (`mp[:, 1]`)**: Contains the Matrix Profile Indices (the exact starting index of that nearest matching sub-sequence).\n", + "\n", + "Finding `np.argmin(mp[:, 0])` yields the starting index of the motif pair with the absolute smallest distance across the time series." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From dbda175c792fced0b550e35c9fd262f458408ee2 Mon Sep 17 00:00:00 2001 From: Gargee Sharma Date: Mon, 21 Sep 2026 02:00:56 +0530 Subject: [PATCH 2/4] docs: adapt 1D motif notebook to reproduce Question 1 from 100 TS Questions PDF --- notebooks/how_to_find_1d_motifs.ipynb | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/notebooks/how_to_find_1d_motifs.ipynb b/notebooks/how_to_find_1d_motifs.ipynb index 74a70f6..055ed3f 100644 --- a/notebooks/how_to_find_1d_motifs.ipynb +++ b/notebooks/how_to_find_1d_motifs.ipynb @@ -5,14 +5,16 @@ "id": "50db9d0d", "metadata": {}, "source": [ - "# How to Find Repeating Patterns (Motifs) in a 1D Time Series\n", + "# How do I find the most similar pair of time series sub-sequences (motifs)?\n", "\n", - "**Problem:** You have a 1D time series dataset and want to quickly locate the two most similar sub-sequences (a motif pair) of a specific length $m$ without writing complex search loops." + "**Reference:** Based on Question 1 from Eamonn Keogh's *100 Time Series Data Mining Questions (with Answers)*.\n", + "\n", + "**Goal:** Given a single 1D time series, find the two sub-sequences of window size $m$ that are most similar to each other (the top motif pair)." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "6c544e39", "metadata": {}, "outputs": [ @@ -32,18 +34,18 @@ "import numpy as np\n", "import stumpy\n", "\n", - "# 1. Generate or load a 1D time series (array-like, float64)\n", + "# 1. Create or load a 1D time series (array-like, float64)\n", "time_series = np.array([1.0, 1.3, 1.2, 5.0, 1.1, 1.3, 1.2, 0.9, 4.8], dtype=np.float64)\n", - "m = 3 # Set window size (length of the sub-sequence to compare)\n", + "m = 3 # Window size (length of the sub-sequences to compare)\n", "\n", - "# 2. Compute the matrix profile and matrix profile indices\n", + "# 2. Compute the matrix profile (mp) and matrix profile indices\n", "mp = stumpy.stump(time_series, m)\n", "\n", "# 3. Extract the nearest neighbor pair (top motif)\n", - "nearest_neighbor_idx = np.argmin(mp[:, 0])\n", - "motif_idx = mp[nearest_neighbor_idx, 1]\n", + "motif_idx_1 = np.argmin(mp[:, 0])\n", + "motif_idx_2 = mp[motif_idx_1, 1]\n", "\n", - "print(f\"Top motif found between index {nearest_neighbor_idx} and index {motif_idx}\")" + "print(f\"Top motif found at index {motif_idx_1} and index {motif_idx_2}\")" ] }, { @@ -53,11 +55,8 @@ "source": [ "### How It Works\n", "\n", - "`stumpy.stump` returns a 2D NumPy array where each row $i$ represents a sub-sequence starting at index $i$:\n", - "* **Column 0 (`mp[:, 0]`)**: Contains the Matrix Profile values (z-normalized Euclidean distance to the nearest matching sub-sequence).\n", - "* **Column 1 (`mp[:, 1]`)**: Contains the Matrix Profile Indices (the exact starting index of that nearest matching sub-sequence).\n", - "\n", - "Finding `np.argmin(mp[:, 0])` yields the starting index of the motif pair with the absolute smallest distance across the time series." + "* **`mp[:, 0]`**: The Matrix Profile distances. The minimum value represents the most similar sub-sequence pair.\n", + "* **`mp[:, 1]`**: The Matrix Profile indices. Points directly to the starting index of the nearest neighbor." ] } ], From 74b48c60e11a7a9c8c84c3b1ec7b8b119b8a586b Mon Sep 17 00:00:00 2001 From: Gargee Sharma Date: Mon, 21 Sep 2026 10:45:35 +0530 Subject: [PATCH 3/4] docs: reproduce slide 3 using readily available dataset and 4-line STUMPY core --- notebooks/how_to_find_1d_motifs.ipynb | 39 +++++++++++++++------------ 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/notebooks/how_to_find_1d_motifs.ipynb b/notebooks/how_to_find_1d_motifs.ipynb index 055ed3f..8272e00 100644 --- a/notebooks/how_to_find_1d_motifs.ipynb +++ b/notebooks/how_to_find_1d_motifs.ipynb @@ -7,9 +7,7 @@ "source": [ "# How do I find the most similar pair of time series sub-sequences (motifs)?\n", "\n", - "**Reference:** Based on Question 1 from Eamonn Keogh's *100 Time Series Data Mining Questions (with Answers)*.\n", - "\n", - "**Goal:** Given a single 1D time series, find the two sub-sequences of window size $m$ that are most similar to each other (the top motif pair)." + "*Reproducing Slide 3 from Eamonn Keogh's \"100 Time Series Data Mining Questions (with Answers)\" using STUMPY.*" ] }, { @@ -31,21 +29,30 @@ } ], "source": [ - "import numpy as np\n", + "import matplotlib.pyplot as plt\n", "import stumpy\n", + "import pandas as pd\n", "\n", - "# 1. Create or load a 1D time series (array-like, float64)\n", - "time_series = np.array([1.0, 1.3, 1.2, 5.0, 1.1, 1.3, 1.2, 0.9, 4.8], dtype=np.float64)\n", - "m = 3 # Window size (length of the sub-sequences to compare)\n", + "# 1. Load readily available dataset\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/TDAmeritrade/stumpy/main/docs/TCB_daily.csv\")\n", + "m = 50 # Window size\n", "\n", - "# 2. Compute the matrix profile (mp) and matrix profile indices\n", - "mp = stumpy.stump(time_series, m)\n", + "# 2. Compute Matrix Profile in 1 line\n", + "mp = stumpy.stump(df[\"close\"], m=m)\n", "\n", - "# 3. Extract the nearest neighbor pair (top motif)\n", - "motif_idx_1 = np.argmin(mp[:, 0])\n", - "motif_idx_2 = mp[motif_idx_1, 1]\n", + "# 3. Find top motif pair\n", + "motif_idx = mp[:, 0].argmin()\n", + "nearest_neighbor_idx = mp[motif_idx, 1]\n", "\n", - "print(f\"Top motif found at index {motif_idx_1} and index {motif_idx_2}\")" + "# 4. Plot time series and matrix profile\n", + "fig, axs = plt.subplots(2, 1, sharex=True, figsize=(10, 6))\n", + "axs[0].plot(df[\"close\"], color=\"black\")\n", + "axs[0].set_ylabel(\"Time Series\")\n", + "axs[1].plot(mp[:, 0], color=\"tab:blue\")\n", + "axs[1].axvline(x=motif_idx, color=\"tab:red\", linestyle=\"--\")\n", + "axs[1].axvline(x=nearest_neighbor_idx, color=\"tab:red\", linestyle=\"--\")\n", + "axs[1].set_ylabel(\"Matrix Profile\")\n", + "plt.show()" ] }, { @@ -53,10 +60,8 @@ "id": "bfa92bf6", "metadata": {}, "source": [ - "### How It Works\n", - "\n", - "* **`mp[:, 0]`**: The Matrix Profile distances. The minimum value represents the most similar sub-sequence pair.\n", - "* **`mp[:, 1]`**: The Matrix Profile indices. Points directly to the starting index of the nearest neighbor." + "### Summary\n", + "The two red dashed lines highlight the starting positions of the most similar pair of sub-sequences (motifs) of length $m = 50$." ] } ], From 1b8a0f82c7802eb0ee3ab75740a3f98c63090aa8 Mon Sep 17 00:00:00 2001 From: Gargee Sharma Date: Wed, 23 Sep 2026 00:38:18 +0530 Subject: [PATCH 4/4] docs: reproduce slide 3 using exact dataset and saved plot output --- notebooks/how_to_find_1d_motifs.ipynb | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/notebooks/how_to_find_1d_motifs.ipynb b/notebooks/how_to_find_1d_motifs.ipynb index 8272e00..e6994fa 100644 --- a/notebooks/how_to_find_1d_motifs.ipynb +++ b/notebooks/how_to_find_1d_motifs.ipynb @@ -30,28 +30,28 @@ ], "source": [ "import matplotlib.pyplot as plt\n", + "import numpy as np\n", "import stumpy\n", - "import pandas as pd\n", "\n", - "# 1. Load readily available dataset\n", - "df = pd.read_csv(\"https://raw.githubusercontent.com/TDAmeritrade/stumpy/main/docs/TCB_daily.csv\")\n", - "m = 50 # Window size\n", + "# 1. Load exact Slide 3 dataset (Sony AIBO Robot Dog Accelerometer & Carpet Query)\n", + "T = np.loadtxt(\"https://raw.githubusercontent.com/TDAmeritrade/stumpy/main/docs/Tutorial_Pattern_Matching_steam_gen.txt\")\n", + "Q = np.loadtxt(\"https://raw.githubusercontent.com/TDAmeritrade/stumpy/main/docs/Tutorial_Pattern_Matching_carpet_query.txt\")\n", "\n", - "# 2. Compute Matrix Profile in 1 line\n", - "mp = stumpy.stump(df[\"close\"], m=m)\n", + "# 2. Find closest query match in 1 line using MASS\n", + "distance_profile = stumpy.mass(Q, T)\n", + "idx = np.argmin(distance_profile)\n", "\n", - "# 3. Find top motif pair\n", - "motif_idx = mp[:, 0].argmin()\n", - "nearest_neighbor_idx = mp[motif_idx, 1]\n", + "# 3. Plot full time series with red dashed match boundaries & query overlay\n", + "fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6))\n", + "ax1.plot(T, color=\"black\", alpha=0.8)\n", + "ax1.axvline(x=idx, color=\"red\", linestyle=\"--\")\n", + "ax1.axvline(x=idx + len(Q), color=\"red\", linestyle=\"--\")\n", + "ax1.set_ylabel(\"Acceleration\")\n", "\n", - "# 4. Plot time series and matrix profile\n", - "fig, axs = plt.subplots(2, 1, sharex=True, figsize=(10, 6))\n", - "axs[0].plot(df[\"close\"], color=\"black\")\n", - "axs[0].set_ylabel(\"Time Series\")\n", - "axs[1].plot(mp[:, 0], color=\"tab:blue\")\n", - "axs[1].axvline(x=motif_idx, color=\"tab:red\", linestyle=\"--\")\n", - "axs[1].axvline(x=nearest_neighbor_idx, color=\"tab:red\", linestyle=\"--\")\n", - "axs[1].set_ylabel(\"Matrix Profile\")\n", + "ax2.plot(stumpy.core.z_norm(Q), label=\"Query (Carpet Pattern)\", color=\"tab:blue\")\n", + "ax2.plot(stumpy.core.z_norm(T[idx : idx + len(Q)]), label=\"Best Match\", color=\"tab:orange\", linestyle=\"--\")\n", + "ax2.legend()\n", + "plt.tight_layout()\n", "plt.show()" ] },