{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Resampling Data\n\nDownsampling lowers the sample rate or sample size of a signal. In\nthis tutorial, the signal is downsampled when the plot is adjusted\nthrough dragging and zooming.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>This example exercises the interactive capabilities of Matplotlib, and this\n    will not appear in the static documentation. Please run this code on your\n    machine to see the interactivity.\n\n    You can copy and paste individual parts, or download the entire example\n    using the link at the bottom of the page.</p></div>\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\nimport numpy as np\n\n\n# A class that will downsample the data and recompute when zoomed.\nclass DataDisplayDownsampler:\n    def __init__(self, xdata, y1data, y2data):\n        self.origY1Data = y1data\n        self.origY2Data = y2data\n        self.origXData = xdata\n        self.max_points = 50\n        self.delta = xdata[-1] - xdata[0]\n\n    def plot(self, ax):\n        x, y1, y2 = self._downsample(self.origXData.min(), self.origXData.max())\n        (self.line,) = ax.plot(x, y1, 'o-')\n        self.poly_collection = ax.fill_between(x, y1, y2, step=\"pre\", color=\"r\")\n\n    def _downsample(self, xstart, xend):\n        # get the points in the view range\n        mask = (self.origXData > xstart) & (self.origXData < xend)\n        # dilate the mask by one to catch the points just outside\n        # of the view range to not truncate the line\n        mask = np.convolve([1, 1, 1], mask, mode='same').astype(bool)\n        # sort out how many points to drop\n        ratio = max(np.sum(mask) // self.max_points, 1)\n\n        # mask data\n        xdata = self.origXData[mask]\n        y1data = self.origY1Data[mask]\n        y2data = self.origY2Data[mask]\n\n        # downsample data\n        xdata = xdata[::ratio]\n        y1data = y1data[::ratio]\n        y2data = y2data[::ratio]\n\n        print(f\"using {len(y1data)} of {np.sum(mask)} visible points\")\n\n        return xdata, y1data, y2data\n\n    def update(self, ax):\n        # Update the artists\n        lims = ax.viewLim\n        if abs(lims.width - self.delta) > 1e-8:\n            self.delta = lims.width\n            xstart, xend = lims.intervalx\n            x, y1, y2 = self._downsample(xstart, xend)\n            self.line.set_data(x, y1)\n            self.poly_collection.set_data(x, y1, y2, step=\"pre\")\n            ax.figure.canvas.draw_idle()\n\n\n# Create a signal\nxdata = np.linspace(16, 365, (365-16)*4)\ny1data = np.sin(2*np.pi*xdata/153) + np.cos(2*np.pi*xdata/127)\ny2data = y1data + .2\n\nd = DataDisplayDownsampler(xdata, y1data, y2data)\n\nfig, ax = plt.subplots()\n\n# Hook up the line\nd.plot(ax)\nax.set_autoscale_on(False)  # Otherwise, infinite loop\n\n# Connect for changing the view limits\nax.callbacks.connect('xlim_changed', d.update)\nax.set_xlim(16, 365)\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. tags:: interactivity: zoom, interactivity: event-handling\n\n"
      ]
    }
  ],
  "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.14.6"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}