.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "examples/smooth.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. or to run this example in your browser via Binder .. rst-class:: sphx-glr-example-title .. _sphx_glr_examples_smooth.py: Smooth pose tracks ===================== Smooth pose tracks using the rolling median and Savitzky-Golay filters. .. GENERATED FROM PYTHON SOURCE LINES 8-10 Imports ------- .. GENERATED FROM PYTHON SOURCE LINES 10-21 .. code-block:: Python import matplotlib.pyplot as plt from scipy.signal import welch from movement import sample_data from movement.filtering import ( interpolate_over_time, rolling_filter, savgol_filter, ) .. GENERATED FROM PYTHON SOURCE LINES 22-28 Load a sample dataset --------------------- Let's load a sample dataset and print it to inspect its contents. Note that if you are running this notebook interactively, you can simply type the variable name (here ``ds_wasp``) in a cell to get an interactive display of the dataset's contents. .. GENERATED FROM PYTHON SOURCE LINES 28-32 .. code-block:: Python ds_wasp = sample_data.fetch_dataset("DLC_single-wasp.predictions.h5") print(ds_wasp) .. rst-class:: sphx-glr-script-out .. code-block:: none Size: 61kB Dimensions: (time: 1085, space: 2, keypoints: 2, individuals: 1) Coordinates: * time (time) float64 9kB 0.0 0.025 0.05 0.075 ... 27.05 27.07 27.1 * space (space) ` to compute the rolling mean, maximum, and minimum values (instead of the median), by setting ``statistic`` to ``"mean"``, ``"max"``, or ``"min"``, respectively. .. GENERATED FROM PYTHON SOURCE LINES 132-143 .. code-block:: Python window = int(0.1 * ds_wasp.fps) ds_wasp_smooth = ds_wasp.copy() ds_wasp_smooth.update( { "position": rolling_filter( ds_wasp.position, window, statistic="median", print_report=True ) } ) .. rst-class:: sphx-glr-script-out .. code-block:: none No missing points (marked as NaN) in input. No missing points (marked as NaN) in output. .. GENERATED FROM PYTHON SOURCE LINES 144-147 We see from the printed report that the dataset has no missing values neither before nor after smoothing. Let's visualise the effects of applying the rolling median filter in the time and frequency domains. .. GENERATED FROM PYTHON SOURCE LINES 147-152 .. code-block:: Python plot_raw_and_smooth_timeseries_and_psd( ds_wasp, ds_wasp_smooth, keypoint="stinger" ) .. image-sg:: /examples/images/sphx_glr_smooth_001.png :alt: Time Domain, Frequency Domain :srcset: /examples/images/sphx_glr_smooth_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 153-163 We see that applying the filter has removed the "spikes" present around the 14 second mark in the raw data. However, it has not dealt with the big shift occurring during the final second. In the frequency domain, we can see that the filter has reduced the power in the high-frequency components, without affecting the low frequency components. This shows what the rolling median is good for: removing brief "spikes" (e.g. a keypoint abruptly jumping to a different location for a frame or two) and high-frequency "jitter" (often present due to pose estimation working on a per-frame basis). .. GENERATED FROM PYTHON SOURCE LINES 165-172 Choosing parameters for the rolling filter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can control the behaviour of the rolling filter via three parameters: ``window``, ``min_periods`` and ``statistic`` which was mentioned above. To better understand the effect of these parameters, let's use a dataset that contains missing values. .. GENERATED FROM PYTHON SOURCE LINES 172-176 .. code-block:: Python ds_mouse = sample_data.fetch_dataset("SLEAP_single-mouse_EPM.analysis.h5") print(ds_mouse) .. rst-class:: sphx-glr-script-out .. code-block:: none Size: 1MB Dimensions: (time: 18485, space: 2, keypoints: 6, individuals: 1) Coordinates: * time (time) float64 148kB 0.0 0.03333 0.06667 ... 616.1 616.1 616.1 * space (space) `_. .. GENERATED FROM PYTHON SOURCE LINES 332-340 Combining multiple smoothing filters ------------------------------------ We can also combine multiple smoothing filters by applying them sequentially. For example, we can first apply the rolling median filter with a small ``window`` to remove "spikes" and then apply the Savitzky-Golay filter with a larger ``window`` to further smooth the data. Between the two filters, we can interpolate over small gaps to avoid the excessive proliferation of NaN values. Let's try this on the mouse dataset. .. GENERATED FROM PYTHON SOURCE LINES 340-364 .. code-block:: Python # First, we will apply the rolling median filter. window = int(0.1 * ds_mouse.fps) ds_mouse_smooth.update( { "position": rolling_filter( ds_mouse.position, window, min_periods=2, statistic="median" ) } ) # Next, let's linearly interpolate over gaps smaller # than 1 second (30 frames). ds_mouse_smooth.update( {"position": interpolate_over_time(ds_mouse_smooth.position, max_gap=30)} ) # Finally, let's apply the Savitzky-Golay filter # over a 0.4-second window (12 frames). window = int(0.4 * ds_mouse.fps) ds_mouse_smooth.update( {"position": savgol_filter(ds_mouse_smooth.position, window)} ) .. GENERATED FROM PYTHON SOURCE LINES 365-368 A record of all applied operations is stored in the ``log`` attribute of the ``ds_mouse_smooth.position`` data array. Let's inspect it to summarise what we've done. .. GENERATED FROM PYTHON SOURCE LINES 368-371 .. code-block:: Python print(ds_mouse_smooth.position.log) .. rst-class:: sphx-glr-script-out .. code-block:: none [ { "operation": "rolling_filter", "datetime": "2025-11-10 17:56:07.060738", "window": "3", "statistic": "'median'", "min_periods": "2", "print_report": "False" }, { "operation": "interpolate_over_time", "datetime": "2025-11-10 17:56:07.084784", "method": "'linear'", "max_gap": "30", "print_report": "False" }, { "operation": "savgol_filter", "datetime": "2025-11-10 17:56:07.088783", "window": "12", "polyorder": "2", "print_report": "False" } ] .. GENERATED FROM PYTHON SOURCE LINES 372-374 Now let's visualise the difference between the raw data and the final smoothed result. .. GENERATED FROM PYTHON SOURCE LINES 374-382 .. code-block:: Python plot_raw_and_smooth_timeseries_and_psd( ds_mouse, ds_mouse_smooth, keypoint="snout", time_range=slice(0, 80), ) .. image-sg:: /examples/images/sphx_glr_smooth_006.png :alt: Time Domain, Frequency Domain :srcset: /examples/images/sphx_glr_smooth_006.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 383-385 Feel free to play around with the parameters of the applied filters and to also look at other keypoints and time ranges. .. GENERATED FROM PYTHON SOURCE LINES 387-391 .. seealso:: :ref:`examples/filter_and_interpolate:Filtering multiple data variables` in the :ref:`sphx_glr_examples_filter_and_interpolate.py` example. .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 1.562 seconds) .. _sphx_glr_download_examples_smooth.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: binder-badge .. image:: images/binder_badge_logo.svg :target: https://mybinder.org/v2/gh/neuroinformatics-unit/movement/gh-pages?filepath=notebooks/examples/smooth.ipynb :alt: Launch binder :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: smooth.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: smooth.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: smooth.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_