-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcolorline.py
More file actions
231 lines (149 loc) · 5.73 KB
/
colorline.py
File metadata and controls
231 lines (149 loc) · 5.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>
# <headingcell level=1>
# Examples of plotting (multi-)colored lines
# <markdowncell>
# Plotting multi-colored lines may be achieved using `LineCollection`.
# These examples use a more simple interface by introducing a function `colorline`.
# <codecell>
%pylab inline
# %config InlineBackend.figure_format = 'retina' # Use for retina display resolution
# <codecell>
# Different styling for graphs (requires separate mpltools package)
# from mpltools import style
# style.use("ggplot")
# <headingcell level=2>
# Function definitions
# <codecell>
# Topics: line, color, LineCollection, cmap, colorline, codex
'''
Defines a function colorline that draws a (multi-)colored 2D line with coordinates x and y.
The color is taken from optional data in z, and creates a LineCollection.
z can be:
- empty, in which case a default coloring will be used based on the position along the input arrays
- a single number, for a uniform color [this can also be accomplished with the usual plt.plot]
- an array of the length of at least the same length as x, to color according to this data
- an array of a smaller length, in which case the colors are repeated along the curve
The function colorline returns the LineCollection created, which can be modified afterwards.
See also: plt.streamplot
'''
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
# Data manipulation:
def make_segments(x, y):
'''
Create list of line segments from x and y coordinates, in the correct format for LineCollection:
an array of the form numlines x (points per line) x 2 (x and y) array
'''
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
return segments
# Interface to LineCollection:
def colorline(x, y, z=None, cmap=plt.get_cmap('copper'), norm=plt.Normalize(0.0, 1.0), linewidth=3, alpha=1.0):
'''
Plot a colored line with coordinates x and y
Optionally specify colors in the array z
Optionally specify a colormap, a norm function and a line width
'''
# Default colors equally spaced on [0,1]:
if z is None:
z = np.linspace(0.0, 1.0, len(x))
# Special case if a single number:
if not hasattr(z, "__iter__"): # to check for numerical input -- this is a hack
z = np.array([z])
z = np.asarray(z)
segments = make_segments(x, y)
lc = LineCollection(segments, array=z, cmap=cmap, norm=norm, linewidth=linewidth, alpha=alpha)
ax = plt.gca()
ax.add_collection(lc)
return lc
def clear_frame(ax=None):
# Taken from a post by Tony S Yu
if ax is None:
ax = plt.gca()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
for spine in ax.spines.itervalues():
spine.set_visible(False)
# <headingcell level=2>
# Example 1: Sine wave colored by time (uses the defaults for colorline)
# <codecell>
# Sine wave colored by time
x = np.linspace(0, 4.*np.pi, 1000)
y = np.sin(x)
fig, axes = plt.subplots()
colorline(x, y)
plt.xlim(x.min(), x.max())
plt.ylim(-1.0, 1.0)
plt.show()
# <headingcell level=2>
# Example 2: Different colors for shifted sine waves
# <codecell>
# Shifting a sine wave: Example modified from mpltools by Tony S Yu
x = np.linspace(0, 4.*np.pi, 1000)
y = np.sin(x)
fig, axes = plt.subplots()
N = 10
for i in xrange(N):
color = i / float(N)
shift = 0.2 * i
colorline(x-shift, y, color, cmap="cool")
plt.xlim(x.min(), x.max())
plt.ylim(-1.0, 1.0)
plt.show()
# <headingcell level=2>
# Example 3: A polar function, plotted with a custom colored dash style
# <codecell>
# Polar function:
max_t = 6*np.pi
theta = np.linspace(0, max_t, 1000)
alpha = 0.3
r = 1 + alpha*np.sin(7./3. * theta)
x = r * np.sin(theta)
y = r * np.cos(theta)
colored_dashes = [0.1]*8 + [0.5]*4 + [0.8]*2 # uses Python list syntax
fig, axes = plt.subplots(figsize=(6,6))
colorline(x, y, colored_dashes, cmap='jet', linewidth=8) #, [0.3]*8+[0.4]*4+[0.5]*2)
plt.xlim(x.min() - 0.1, x.max() + 0.1)
plt.ylim(y.min() - 0.1, y.max() + 0.1)
plt.axis('equal')
clear_frame()
plt.show()
# <headingcell level=2>
# Example 4: A non-periodic Lissajous curve, colored with a continuous palette and different line widths
# <codecell>
# Non-periodic Lissajous curve
max_t = 15.
t = np.linspace(0, max_t, 5000)
x = np.sin(t)
y = np.sin(t * np.pi)
fig, axes = plt.subplots(figsize=(6,6))
lc = colorline(x, y, alpha=0.75, linewidth=t, cmap='coolwarm')
# We can capture the LineCollection created to modify it later, e.g.:
# lc.set_rasterized = False
plt.xlim(x.min() - 0.1, x.max() + 0.1)
plt.ylim(y.min() - 0.1, y.max() + 0.1)
# plt.colorbar() # Does not work at the moment!
plt.show()
# <headingcell level=2>
# Example 5: $\sin(1/x)$, colored according to its derivative and plotted using logarithmic spacing
# <codecell>
# sin(1/x), colored according to the value of its derivative
# Use logarithmic spacing so that looks better:
log_x = np.linspace(np.log(0.005), np.log(0.1), 5000)
x = np.exp(log_x)
y = 0.9 * np.sin(1. / x)
# Use finite differences to calculate the derivative of y:
y_deriv = (y[1:] - y[:-1]) / (x[1:] - x[:-1])
# Create a colormap for red, green and blue:
cmap = ListedColormap(['r', 'g', 'b'])
# Create a 'norm' (normalizing function) which maps data values to the interval [0,1]:
norm = BoundaryNorm([-1000, -200, 200, 1000], cmap.N) # cmap.N is number of items in the colormap
fig, axes = plt.subplots()
colorline(x, y, y_deriv, cmap=cmap, norm=norm, linewidth=100.*x)
# This works even though y_deriv is a different length -- wraps around
plt.xlim(x.min(), x.max())
plt.ylim(-1.0, 1.0)
plt.show()