{ "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 component: 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.13.2" } }, "nbformat": 4, "nbformat_minor": 0 }