{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Time Series Histogram\n\nThis example demonstrates how to efficiently visualize large numbers of time\nseries in a way that could potentially reveal hidden substructure and patterns\nthat are not immediately obvious, and display them in a visually appealing way.\n\nIn this example, we generate multiple sinusoidal \"signal\" series that are\nburied under a larger number of random walk \"noise/background\" series. For an\nunbiased Gaussian random walk with standard deviation of \u03c3, the RMS deviation\nfrom the origin after n steps is \u03c3*sqrt(n). So in order to keep the sinusoids\nvisible on the same scale as the random walks, we scale the amplitude by the\nrandom walk RMS. In addition, we also introduce a small random offset ``phi``\nto shift the sines left/right, and some additive random noise to shift\nindividual data points up/down to make the signal a bit more \"realistic\" (you\nwouldn't expect a perfect sine wave to appear in your data).\n\nThe first plot shows the typical way of visualizing multiple time series by\noverlaying them on top of each other with ``plt.plot`` and a small value of\n``alpha``. The second and third plots show how to reinterpret the data as a 2d\nhistogram, with optional interpolation between data points, by using\n``np.histogram2d`` and ``plt.pcolormesh``.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "import time\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfig, axes = plt.subplots(nrows=3, figsize=(6, 8), layout='constrained')\n\n# Fix random state for reproducibility\nnp.random.seed(19680801)\n# Make some data; a 1D random walk + small fraction of sine waves\nnum_series = 1000\nnum_points = 100\nSNR = 0.10  # Signal to Noise Ratio\nx = np.linspace(0, 4 * np.pi, num_points)\n# Generate unbiased Gaussian random walks\nY = np.cumsum(np.random.randn(num_series, num_points), axis=-1)\n# Generate sinusoidal signals\nnum_signal = round(SNR * num_series)\nphi = (np.pi / 8) * np.random.randn(num_signal, 1)  # small random offset\nY[-num_signal:] = (\n    np.sqrt(np.arange(num_points))  # random walk RMS scaling factor\n    * (np.sin(x - phi)\n       + 0.05 * np.random.randn(num_signal, num_points))  # small random noise\n)\n\n\n# Plot series using `plot` and a small value of `alpha`. With this view it is\n# very difficult to observe the sinusoidal behavior because of how many\n# overlapping series there are. It also takes a bit of time to run because so\n# many individual artists need to be generated.\ntic = time.time()\naxes[0].plot(x, Y.T, color=\"C0\", alpha=0.1)\ntoc = time.time()\naxes[0].set_title(\"Line plot with alpha\")\nprint(f\"{toc-tic:.3f} sec. elapsed\")\n\n\n# Now we will convert the multiple time series into a histogram. Not only will\n# the hidden signal be more visible, but it is also a much quicker procedure.\ntic = time.time()\n# Linearly interpolate between the points in each time series\nnum_fine = 800\nx_fine = np.linspace(x.min(), x.max(), num_fine)\ny_fine = np.concatenate([np.interp(x_fine, x, y_row) for y_row in Y])\nx_fine = np.broadcast_to(x_fine, (num_series, num_fine)).ravel()\n\n\n# Plot (x, y) points in 2d histogram with log colorscale\n# It is pretty evident that there is some kind of structure under the noise\n# You can tune vmax to make signal more visible\ncmap = plt.colormaps[\"plasma\"]\ncmap = cmap.with_extremes(bad=cmap(0))\nh, xedges, yedges = np.histogram2d(x_fine, y_fine, bins=[400, 100])\npcm = axes[1].pcolormesh(xedges, yedges, h.T, cmap=cmap,\n                         norm=\"log\", vmax=1.5e2, rasterized=True)\nfig.colorbar(pcm, ax=axes[1], label=\"# points\", pad=0)\naxes[1].set_title(\"2d histogram and log color scale\")\n\n# Same data but on linear color scale\npcm = axes[2].pcolormesh(xedges, yedges, h.T, cmap=cmap,\n                         vmax=1.5e2, rasterized=True)\nfig.colorbar(pcm, ax=axes[2], label=\"# points\", pad=0)\naxes[2].set_title(\"2d histogram and linear color scale\")\n\ntoc = time.time()\nprint(f\"{toc-tic:.3f} sec. elapsed\")\nplt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        ".. tags::\n\n   plot-type: histogram2d\n   plot-type: pcolormesh\n   purpose: storytelling\n   styling: color\n   styling: colormap\n\n.. admonition:: References\n\n   The use of the following functions, methods, classes and modules is shown\n   in this example:\n\n   - `matplotlib.axes.Axes.pcolormesh` / `matplotlib.pyplot.pcolormesh`\n   - `matplotlib.figure.Figure.colorbar`\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
}