7

enter image description here

The figure above is a great artwork showing the wind speed, wind direction and temperature simultaneously. detailedly:

  • The X axes represent the date
  • The Y axes shows the wind direction(Southern, western, etc)
  • The variant widths of the line were stand for the wind speed through timeseries
  • The variant colors of the line were stand for the atmospheric temperature

This simple figure visualized 3 different attribute without redundancy.

So, I really want to reproduce similar plot in matplotlib.

My attempt now

## Reference 1 http://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width
## Reference 2 http://stackoverflow.com/questions/17240694/python-how-to-plot-one-line-in-different-colors

def plot_colourline(x,y,c):
    c = plt.cm.jet((c-np.min(c))/(np.max(c)-np.min(c)))
    lwidths=1+x[:-1]
    ax = plt.gca()
    for i in np.arange(len(x)-1):
        ax.plot([x[i],x[i+1]], [y[i],y[i+1]], c=c[i],linewidth = lwidths[i])# = lwidths[i])
    return

x=np.linspace(0,4*math.pi,100)
y=np.cos(x)
lwidths=1+x[:-1]

fig = plt.figure(1, figsize=(5,5))
ax  = fig.add_subplot(111)
plot_colourline(x,y,prop)

ax.set_xlim(0,4*math.pi)
ax.set_ylim(-1.1,1.1)

enter image description here

Does someone has a more interested way to achieve this? Any advice would be appreciate!

Han Zhengzu
  • 3,694
  • 7
  • 44
  • 94
  • 1
    `LineCollections`, as used in the first url in your code, will probably perform better and therefore allow you to use more segments for a smoother result. See: http://stackoverflow.com/questions/21352580/matplotlib-plotting-numerous-disconnected-line-segments-with-different-colors – tom10 Jun 08 '16 at 17:06
  • may use LineCollections. See https://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width – Eric Wang Aug 18 '17 at 01:52
  • 1
    You could use LineCollections. See https://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width – Eric Wang Aug 18 '17 at 01:54

2 Answers2

7

Using as inspiration another question.

One option would be to use fill_between. But perhaps not in the way it was intended. Instead of using it to create your line, use it to mask everything that is not the line. Under it you can have a pcolormesh or contourf (for example) to map color any way you want.

Look, for instance, at this example:

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d

def windline(x,y,deviation,color):
    y1 = y-deviation/2
    y2 = y+deviation/2
    tol = (y2.max()-y1.min())*0.05
    X, Y = np.meshgrid(np.linspace(x.min(), x.max(), 100), np.linspace(y1.min()-tol, y2.max()+tol, 100))
    Z = X.copy()
    for i in range(Z.shape[0]):
        Z[i,:] = c

    #plt.pcolormesh(X, Y, Z)
    plt.contourf(X, Y, Z, cmap='seismic')

    plt.fill_between(x, y2, y2=np.ones(x.shape)*(y2.max()+tol), color='w')
    plt.fill_between(x, np.ones(x.shape) * (y1.min() - tol), y2=y1, color='w')
    plt.xlim(x.min(), x.max())
    plt.ylim(y1.min()-tol, y2.max()+tol)
    plt.show()

x = np.arange(100)
yo = np.random.randint(20, 60, 21)
y = interp1d(np.arange(0, 101, 5), yo, kind='cubic')(x)
dv = np.random.randint(2, 10, 21)
d = interp1d(np.arange(0, 101, 5), dv, kind='cubic')(x)
co = np.random.randint(20, 60, 21)
c = interp1d(np.arange(0, 101, 5), co, kind='cubic')(x)
windline(x, y, d, c)

, which results in this:

matplotlib line with different thickness and color

The function windline accepts as arguments numpy arrays with x, y , a deviation (like a thickness value per x value), and color array for color mapping. I think it can be greatly improved by messing around with other details but the principle, although not perfect, should be solid.

Community
  • 1
  • 1
armatita
  • 12,825
  • 8
  • 48
  • 49
2
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(0,4*np.pi,10000) # x data
y = np.cos(x) # y data
r = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: 1-x/(2*np.pi), 0]) # red
g = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: x/(2*np.pi), lambda x: -x/(2*np.pi)+2]) # green
b = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [0, lambda x: x/(2*np.pi)-1]) # blue

a = np.ones(10000) # alpha
w = x # width

fig, ax = plt.subplots(2)

ax[0].plot(x, r, color='r')
ax[0].plot(x, g, color='g')
ax[0].plot(x, b, color='b')

# mysterious parts
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# mysterious parts

rgba = list(zip(r,g,b,a))

lc = LineCollection(segments, linewidths=w, colors=rgba)

ax[1].add_collection(lc)
ax[1].set_xlim(0,4*np.pi)
ax[1].set_ylim(-1.1,1.1)
fig.show()

enter image description here

I notice this is what I suffered.