Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7ccfd38

Browse filesBrowse files
authored
DOC: New color line by value example (#28307)
* New color line example with multiple coloring methods
1 parent feab921 commit 7ccfd38
Copy full SHA for 7ccfd38

File tree

Expand file treeCollapse file tree

1 file changed

+173
-32
lines changed
Filter options
Expand file treeCollapse file tree

1 file changed

+173
-32
lines changed

‎galleries/examples/lines_bars_and_markers/multicolored_line.py

Copy file name to clipboardExpand all lines: galleries/examples/lines_bars_and_markers/multicolored_line.py
+173-32Lines changed: 173 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,188 @@
33
Multicolored lines
44
==================
55
6-
This example shows how to make a multicolored line. In this example, the line
7-
is colored based on its derivative.
6+
The example shows two ways to plot a line with the a varying color defined by
7+
a third value. The first example defines the color at each (x, y) point.
8+
The second example defines the color between pairs of points, so the length
9+
of the color value list is one less than the length of the x and y lists.
10+
11+
Color values at points
12+
----------------------
13+
814
"""
915

16+
import warnings
17+
1018
import matplotlib.pyplot as plt
1119
import numpy as np
1220

1321
from matplotlib.collections import LineCollection
14-
from matplotlib.colors import BoundaryNorm, ListedColormap
1522

23+
24+
def colored_line(x, y, c, ax, **lc_kwargs):
25+
"""
26+
Plot a line with a color specified along the line by a third value.
27+
28+
It does this by creating a collection of line segments. Each line segment is
29+
made up of two straight lines each connecting the current (x, y) point to the
30+
midpoints of the lines connecting the current point with its two neighbors.
31+
This creates a smooth line with no gaps between the line segments.
32+
33+
Parameters
34+
----------
35+
x, y : array-like
36+
The horizontal and vertical coordinates of the data points.
37+
c : array-like
38+
The color values, which should be the same size as x and y.
39+
ax : Axes
40+
Axis object on which to plot the colored line.
41+
**lc_kwargs
42+
Any additional arguments to pass to matplotlib.collections.LineCollection
43+
constructor. This should not include the array keyword argument because
44+
that is set to the color argument. If provided, it will be overridden.
45+
46+
Returns
47+
-------
48+
matplotlib.collections.LineCollection
49+
The generated line collection representing the colored line.
50+
"""
51+
if "array" in lc_kwargs:
52+
warnings.warn('The provided "array" keyword argument will be overridden')
53+
54+
# Default the capstyle to butt so that the line segments smoothly line up
55+
default_kwargs = {"capstyle": "butt"}
56+
default_kwargs.update(lc_kwargs)
57+
58+
# Compute the midpoints of the line segments. Include the first and last points
59+
# twice so we don't need any special syntax later to handle them.
60+
x = np.asarray(x)
61+
y = np.asarray(y)
62+
x_midpts = np.hstack((x[0], 0.5 * (x[1:] + x[:-1]), x[-1]))
63+
y_midpts = np.hstack((y[0], 0.5 * (y[1:] + y[:-1]), y[-1]))
64+
65+
# Determine the start, middle, and end coordinate pair of each line segment.
66+
# Use the reshape to add an extra dimension so each pair of points is in its
67+
# own list. Then concatenate them to create:
68+
# [
69+
# [(x1_start, y1_start), (x1_mid, y1_mid), (x1_end, y1_end)],
70+
# [(x2_start, y2_start), (x2_mid, y2_mid), (x2_end, y2_end)],
71+
# ...
72+
# ]
73+
coord_start = np.column_stack((x_midpts[:-1], y_midpts[:-1]))[:, np.newaxis, :]
74+
coord_mid = np.column_stack((x, y))[:, np.newaxis, :]
75+
coord_end = np.column_stack((x_midpts[1:], y_midpts[1:]))[:, np.newaxis, :]
76+
segments = np.concatenate((coord_start, coord_mid, coord_end), axis=1)
77+
78+
lc = LineCollection(segments, **default_kwargs)
79+
lc.set_array(c) # set the colors of each segment
80+
81+
return ax.add_collection(lc)
82+
83+
84+
# -------------- Create and show plot --------------
85+
# Some arbitrary function that gives x, y, and color values
86+
t = np.linspace(-7.4, -0.5, 200)
87+
x = 0.9 * np.sin(t)
88+
y = 0.9 * np.cos(1.6 * t)
89+
color = np.linspace(0, 2, t.size)
90+
91+
# Create a figure and plot the line on it
92+
fig1, ax1 = plt.subplots()
93+
lines = colored_line(x, y, color, ax1, linewidth=10, cmap="plasma")
94+
fig1.colorbar(lines) # add a color legend
95+
96+
# Set the axis limits and tick positions
97+
ax1.set_xlim(-1, 1)
98+
ax1.set_ylim(-1, 1)
99+
ax1.set_xticks((-1, 0, 1))
100+
ax1.set_yticks((-1, 0, 1))
101+
ax1.set_title("Color at each point")
102+
103+
plt.show()
104+
105+
####################################################################
106+
# This method is designed to give a smooth impression when distances and color
107+
# differences between adjacent points are not too large. The following example
108+
# does not meet this criteria and by that serves to illustrate the segmentation
109+
# and coloring mechanism.
110+
x = [0, 1, 2, 3, 4]
111+
y = [0, 1, 2, 1, 1]
112+
c = [1, 2, 3, 4, 5]
113+
fig, ax = plt.subplots()
114+
ax.scatter(x, y, c=c, cmap='rainbow')
115+
colored_line(x, y, c=c, ax=ax, cmap='rainbow')
116+
117+
plt.show()
118+
119+
####################################################################
120+
# Color values between points
121+
# ---------------------------
122+
#
123+
124+
125+
def colored_line_between_pts(x, y, c, ax, **lc_kwargs):
126+
"""
127+
Plot a line with a color specified between (x, y) points by a third value.
128+
129+
It does this by creating a collection of line segments between each pair of
130+
neighboring points. The color of each segment is determined by the
131+
made up of two straight lines each connecting the current (x, y) point to the
132+
midpoints of the lines connecting the current point with its two neighbors.
133+
This creates a smooth line with no gaps between the line segments.
134+
135+
Parameters
136+
----------
137+
x, y : array-like
138+
The horizontal and vertical coordinates of the data points.
139+
c : array-like
140+
The color values, which should have a size one less than that of x and y.
141+
ax : Axes
142+
Axis object on which to plot the colored line.
143+
**lc_kwargs
144+
Any additional arguments to pass to matplotlib.collections.LineCollection
145+
constructor. This should not include the array keyword argument because
146+
that is set to the color argument. If provided, it will be overridden.
147+
148+
Returns
149+
-------
150+
matplotlib.collections.LineCollection
151+
The generated line collection representing the colored line.
152+
"""
153+
if "array" in lc_kwargs:
154+
warnings.warn('The provided "array" keyword argument will be overridden')
155+
156+
# Check color array size (LineCollection still works, but values are unused)
157+
if len(c) != len(x) - 1:
158+
warnings.warn(
159+
"The c argument should have a length one less than the length of x and y. "
160+
"If it has the same length, use the colored_line function instead."
161+
)
162+
163+
# Create a set of line segments so that we can color them individually
164+
# This creates the points as an N x 1 x 2 array so that we can stack points
165+
# together easily to get the segments. The segments array for line collection
166+
# needs to be (numlines) x (points per line) x 2 (for x and y)
167+
points = np.array([x, y]).T.reshape(-1, 1, 2)
168+
segments = np.concatenate([points[:-1], points[1:]], axis=1)
169+
lc = LineCollection(segments, **lc_kwargs)
170+
171+
# Set the values used for colormapping
172+
lc.set_array(c)
173+
174+
return ax.add_collection(lc)
175+
176+
177+
# -------------- Create and show plot --------------
16178
x = np.linspace(0, 3 * np.pi, 500)
17179
y = np.sin(x)
18180
dydx = np.cos(0.5 * (x[:-1] + x[1:])) # first derivative
19181

20-
# Create a set of line segments so that we can color them individually
21-
# This creates the points as an N x 1 x 2 array so that we can stack points
22-
# together easily to get the segments. The segments array for line collection
23-
# needs to be (numlines) x (points per line) x 2 (for x and y)
24-
points = np.array([x, y]).T.reshape(-1, 1, 2)
25-
segments = np.concatenate([points[:-1], points[1:]], axis=1)
26-
27-
fig, axs = plt.subplots(2, 1, sharex=True, sharey=True)
28-
29-
# Create a continuous norm to map from data points to colors
30-
norm = plt.Normalize(dydx.min(), dydx.max())
31-
lc = LineCollection(segments, cmap='viridis', norm=norm)
32-
# Set the values used for colormapping
33-
lc.set_array(dydx)
34-
lc.set_linewidth(2)
35-
line = axs[0].add_collection(lc)
36-
fig.colorbar(line, ax=axs[0])
37-
38-
# Use a boundary norm instead
39-
cmap = ListedColormap(['r', 'g', 'b'])
40-
norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)
41-
lc = LineCollection(segments, cmap=cmap, norm=norm)
42-
lc.set_array(dydx)
43-
lc.set_linewidth(2)
44-
line = axs[1].add_collection(lc)
45-
fig.colorbar(line, ax=axs[1])
46-
47-
axs[0].set_xlim(x.min(), x.max())
48-
axs[0].set_ylim(-1.1, 1.1)
182+
fig2, ax2 = plt.subplots()
183+
line = colored_line_between_pts(x, y, dydx, ax2, linewidth=2, cmap="viridis")
184+
fig2.colorbar(line, ax=ax2, label="dy/dx")
185+
186+
ax2.set_xlim(x.min(), x.max())
187+
ax2.set_ylim(-1.1, 1.1)
188+
ax2.set_title("Color between points")
189+
49190
plt.show()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.