1

How can I configure plt.plot such that overlapped lines will have darker colors?
For example, I would like to use plt.plot to display the samples in such a way that the density that can be seen in the upper plot will be clear in the lower plot.
From the lower plot it's hard to understand where most of the samples are located

plot example

Here is the code I used in order to generate the example:

import numpy as np
import matplotlib.pyplot as plt

time = 100
n_samples = 7000
x = np.linspace(0, time, n_samples)
r1 = np.random.normal(0, 1, x.size)
r2 = np.random.uniform(-6, 6, x.size)
data = np.dstack((r1, r2)).flatten()

fig, axs = plt.subplots(2, 1, figsize=(9, 6))
axs[0].scatter(np.arange(len(data)), data, alpha=0.1)
axs[1].plot(np.arange(len(data)), data, alpha=0.2)
plt.show()
Yair
  • 58
  • 6

1 Answers1

2

Update: segmentation and plotting into separated function

Instead of drawing one large curve, you could create each line segment separately and then draw these. That way, the overlapping segments will be blended via the transparency.

import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np

def plot_line_as_segments(xs, ys=None, ax=None, **kwargs):
    ax = ax or plt.gca()
    if ys is None:
        ys = xs
        xs = np.arange(len(ys))
    segments = np.c_[xs[:-1], ys[:-1], xs[1:], ys[1:]].reshape(-1, 2, 2)
    added_collection = ax.add_collection(LineCollection(segments, **kwargs))
    ax.autoscale()
    return added_collection

time = 100
n_samples = 7000
x = np.linspace(0, time, n_samples)
r1 = np.random.normal(0, 1, x.size)
r2 = np.random.uniform(-6, 6, x.size)
data = np.dstack((r1, r2)).flatten()

fig, axs = plt.subplots(2, 1, figsize=(9, 6))
axs[0].scatter(np.arange(len(data)), data, alpha=0.1)
axs[0].margins(x=0)

plot_line_as_segments(data, ax=axs[1], alpha=0.05)
axs[1].margins(x=0)
plt.show()

drawing individual segments

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • This is very close to what I need. But is there any other way to still draw the fully large curve, while all overlapped lines will blend via the transparency? Exactly the same as the dots in the scatter plot – Yair Jan 02 '22 at 13:11
  • @Yair You could use the method proposed by JohanC, and plot the "fully large cuve" with your own method. That way you display both overlap and enveloppe. Pick another color for the enveloppe or a different zorder and alpha. – Guimoute Jan 02 '22 at 13:16
  • @Yair If you plot everything as one curve, the backend doesn't apply transparency composition on overlapping line segments. You need to convert the curve into separate segments. This is similar to what happens for [How to plot a gradient color line in matplotlib?](https://stackoverflow.com/questions/8500700/how-to-plot-a-gradient-color-line-in-matplotlib/25941474#25941474). If you want to, the segmentation could be done inside of function. – JohanC Jan 02 '22 at 13:30
  • Great! and is there any way to set the plot to have different color range? something like in a histogram? i.e `cmap='inferno'` – Yair Jan 07 '22 at 18:41
  • Based on density, where more/less lines are located – Yair Jan 07 '22 at 19:07
  • That's quite hard. Now the color is determined by compositing semitransparent layers. You'd need something like a heatmap, but that is based on individual points, not on line segments. – JohanC Jan 07 '22 at 19:35
  • @JohanC OK thank you very much! – Yair Jan 07 '22 at 20:03