Converting gnuplot color map to matplotlib - matplotlib

I am trying to port some plotting code from gnuplot to matplotlib and am struggling with porting a discontinuous color map that is specified by color names. Any suggestions on how to do this in matplotlib?
# Establish a 3-section color palette with lower 1/4 in the blues,
# and middle 1/2 light green to yellow, and top 1/4 reds
set palette defined (0 'dark-blue', 0.5 'light-blue', \\
0.5 'light-green', 1 'green', 1.5 'yellow', \\
1.5 'red', 2 'dark-red')
# Establish that the palette range, such that the middle green range corresponds
# to 0.95 to 1.05
set cbrange [0.9:1.1]

I've used this script for years, can't really remember how or where I got it (edit: after some searching, this seems to be the source, but it requires some minor changes for Python3), but it has helped me a lot in quickly creating custom color maps. It allows you to simply specify a dictionary with locations (0..1) and colors, and creates a linear color map out of that; e.g. make_colormap({0:'w',1:'k'}) creates a linear color map going from white to black.
import numpy as np
import matplotlib.pylab as pl
def make_colormap(colors):
from matplotlib.colors import LinearSegmentedColormap, ColorConverter
from numpy import sort
z = np.array(sorted(colors.keys()))
n = len(z)
z1 = min(z)
zn = max(z)
x0 = (z - z1) / (zn - z1)
CC = ColorConverter()
R = []
G = []
B = []
for i in range(n):
Ci = colors[z[i]]
if type(Ci) == str:
RGB = CC.to_rgb(Ci)
else:
RGB = Ci
R.append(RGB[0])
G.append(RGB[1])
B.append(RGB[2])
cmap_dict = {}
cmap_dict['red'] = [(x0[i],R[i],R[i]) for i in range(len(R))]
cmap_dict['green'] = [(x0[i],G[i],G[i]) for i in range(len(G))]
cmap_dict['blue'] = [(x0[i],B[i],B[i]) for i in range(len(B))]
mymap = LinearSegmentedColormap('mymap',cmap_dict)
return mymap
test1 = make_colormap({0.:'#40004b',0.5:'#ffffff',1.:'#00441b'})
test2 = make_colormap({0.:'b',0.25:'w',0.251:'g',0.75:'y',0.751:'r',1:'k'})
data = np.random.random((10,10))
pl.figure()
pl.subplot(121)
pl.imshow(data, interpolation='nearest', cmap=test1)
pl.colorbar()
pl.subplot(122)
pl.imshow(data, interpolation='nearest', cmap=test2)
pl.colorbar()

Bart's function is very nice. However, if you want to make the colormap yourself, you can define a colormap like this using a dictionary in the way it is done in the custom_cmap example from the mpl website.
Here's an example that's pretty close to your colormap:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
cdict = {'red': ((0.0, 0.0, 0.0), # From 0 to 0.25, we fade the red and green channels
(0.25, 0.5, 0.5), # up a little, to make the blue a bit more grey
(0.25, 0.0, 0.0), # From 0.25 to 0.75, we fade red from 0.5 to 1
(0.75, 1.0, 1.0), # to fade from green to yellow
(1.0, 0.5, 0.5)), # From 0.75 to 1.0, we bring the red down from 1
# to 0.5, to go from bright to dark red
'green': ((0.0, 0.0, 0.0), # From 0 to 0.25, we fade the red and green channels
(0.25, 0.6, 0.6), # up a little, to make the blue a bit more grey
(0.25, 1.0, 1.0), # Green is 1 from 0.25 to 0.75 (we add red
(0.75, 1.0, 1.0), # to turn it from green to yellow)
(0.75, 0.0, 0.0), # No green needed in the red upper quarter
(1.0, 0.0, 0.0)),
'blue': ((0.0, 0.9, 0.9), # Keep blue at 0.9 from 0 to 0.25, and adjust its
(0.25, 0.9, 0.9), # tone using the green and red channels
(0.25, 0.0, 0.0), # No blue needed above 0.25
(1.0, 0.0, 0.0))
}
cmap = colors.LinearSegmentedColormap('BuGnYlRd',cdict)
data = 0.9 + (np.random.rand(8,8) * 0.2) # Data in range 0.9 to 1.1
p=plt.imshow(data,interpolation='nearest',cmap=cmap,vmin=0.9,vmax=1.1)
plt.colorbar(p)
plt.show()

Related

Matplotlib: plt.text with user-defined circle radii

Dear stackoverflow users,
I want to plot some data labels with its coordinates in a x,y-plot. Around the labels I want to put a circle with a user-defined radius as I want to symbolize the magnitude of the data property by the radius of the circle.
An example dataset could look like the following:
point1 = ["label1", 0.5, 0.25, 1e0] # equals [label, x, y, radius]
point2 = ["label2", 0.5, 0.75, 1e1] # equals [label, x, y, radius]
I want to use a code silimar to the following one:
import matplotlib.pyplot as plt
plt.text(point1[1], point1[2], point1[0], bbox = dict(boxstyle="circle")) # here I want to alter the radius by passing point1[3]
plt.text(point2[1], point2[2], point2[0], bbox = dict(boxstyle="circle")) # here I want to alter the radius by passing point2[3]
plt.show()
Is this possible somehow or is the plt.add_patch variant the only possible way?
Regards
In principle, you can use the boxes' pad parameter to define the circle size. However this is then relative to the label. I.e. a small label would have a smaller circle around it for the same value of pad than a larger label. Also the units of pad are fontsize (i.e. if you have a fontsize of 10pt, a padding of 1 would correspond to 10pt).
import numpy as np
import matplotlib.pyplot as plt
points = [["A", 0.2, 0.25, 0], # zero radius
["long label", 0.4, 0.25, 0], # zero radius
["label1", 0.6, 0.25, 1]] # one radius
for point in points:
plt.text(point[1], point[2], point[0], ha="center", va="center",
bbox = dict(boxstyle=f"circle,pad={point[3]}", fc="lightgrey"))
plt.show()
I don't know in how far this is desired.
I guess usually you would rather create a scatterplot at the same positions as the text
import numpy as np
import matplotlib.pyplot as plt
points = [["A", 0.2, 0.25, 100], # 5 pt radius
["long label", 0.4, 0.25, 100], # 5 pt radius
["label1", 0.6, 0.25, 1600]] # 20 pt radius
data = np.array([l[1:] for l in points])
plt.scatter(data[:,0], data[:,1], s=data[:,2], facecolor="gold")
for point in points:
plt.text(point[1], point[2], point[0], ha="center", va="center")
plt.show()

How to make dependent sliders in matplotlib

Similar to Matplotlib dependent sliders, I want to make two sliders whose sum make 10. To do that, i want that when i move one slider, the other one moves to compensate. At the moment, the code is the following :
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 5
delta_f = 5.0
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t, s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 10.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
def update_sfreq(val):
samp.val = 10-sfreq.val
l.set_ydata(samp.val*np.sin(2*np.pi*sfreq.val*t))
fig.canvas.draw_idle()
sfreq.on_changed(update_sfreq)
resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')
def reset(event):
sfreq.reset()
samp.reset()
button.on_clicked(reset)
rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)
def colorfunc(label):
l.set_color(label)
fig.canvas.draw_idle()
radio.on_clicked(colorfunc)
plt.show()
This is one of matplotlib examples that I modified to suit my needs. At the moment, I only implemented s_freq.on_changed(). I want that when I move the freq slider, the graph changes (This part is working), and at the same time, the amp slider moves too (This part is not).
Any thoughts on how to modify my function update_sfreq to correctly update samp?
Note : I do realize that if both my sliders update each other, I might end up in an infinite loop. I have already thought of this and of a solution. The part that is not working is really the part where moving one slider makes the other slider move.
Well, after some digging in matplotlib source code, I managed to find an answer to my question. You need to change
samp.val = ...
to
samp.set_val(...)
This will update the bar correctly.

Setting colors individually in matplotlib

I want to create a custom plot. I want to precisely specify the color of each object. Specifically, I am creating a Gantt chart for system events. I am classifying those events into groups and color coding them to visualize.
Please consider the following code:
import matplotlib.pyplot as plt
import pandas as pd
df = pd.DataFrame()
df['y'] = [0,4,5,6,10]
df['color'] = [(.5, .5, .5, .5),]*len(df)
print df['color']
#fig = plt.figure(figsize=(12, 6))
#vax = fig.add_subplot(1,1,1)
#vax.hlines(df['y'], 0, 10, colors=df['color'])
#fig.savefig('ok.png')
only_four = df['y']==4
df['color'][only_four] = [(0.7, 0.6, 0.5, 0.4),]*sum(only_four)
print df['color']
Note that I first am setting the color for all to be a semi-transparent gray. Later, for a particular set of values, I want to change the color. I end up with this color table.
0 (0.5, 0.5, 0.5, 0.5)
1 0.6
2 (0.5, 0.5, 0.5, 0.5)
3 (0.5, 0.5, 0.5, 0.5)
4 (0.5, 0.5, 0.5, 0.5)
I want to be able to specify any RGBA value (i.e. including transparency) for any subset of the hlines. Could someone share how to do this? I'm open to any other way to do this as long as I can precisely color each line including a transparency.
ADDITION TO QUESTION:
I am able to update multiple rows by iterating as in:
def set_color(df, row_bool, r, g, b, a=1.0):
idx = np.where(row_bool)[0]
for i in idx:
df['color'][i] = (r,g,b,a)
return
This is sufficient, but I really wanted a vector operation (ie no explicit loop by me).
I'm guessing the problem is that you cannot get your updated tuple to be input into the DataFrame and you only get that 0.6 value in the DataFrame. Have you tried using DataFrame.set_value?
In [1]: df
Out[1]:
y color
0 0 (0.5, 0.5, 0.5, 0.5)
1 4 0.6
2 5 (0.5, 0.5, 0.5, 0.5)
3 6 (0.5, 0.5, 0.5, 0.5)
4 10 (0.5, 0.5, 0.5, 0.5)
In [2]: df.set_value(1, 'color', (0.7, 0.6, 0.5, 0.4))
Out[2]:
y color
0 0 (0.5, 0.5, 0.5, 0.5)
1 4 (0.7, 0.6, 0.5, 0.4)
2 5 (0.5, 0.5, 0.5, 0.5)
3 6 (0.5, 0.5, 0.5, 0.5)
4 10 (0.5, 0.5, 0.5, 0.5)

How to make matplotlib contour lines with edgecolors?

I would like to add edgecolors to the lines in matplolib.pyplot.contour. Tried edgecolors and markeredgecolors, without effect. Does anyone know a solution?
For a case such as this, you'll want to plot the dataset twice, the first time with a thicker linewidth (or larger markers, depending on what type of plot you had in mind) and in the color of the "outer" lines/markers. Then, you plot the dataset again, but with smaller lines/markers and in a different color, the color of the inner line.
Here's an example that you can copy-paste to study. The example borrows from the matplotlib contour demo:
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
# generate some sample data
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
plt.figure()
# plot the outer lines thicker
whites = plt.contour(X, Y, Z, colors='white', linewidths=7)
plt.gca().set_axis_bgcolor('red') # you spoke of a red bgcolor in the axis (yuck!)
# and plot the inner lines thinner
CS = plt.contour(X, Y, Z, colors='red', linewidths=3)
This is a commonly used technique in many decent graphs, to highlight the data (even though this example looks awful).

How to format slider

I have a slider:
time_ax = fig.add_axes([0.1, 0.05, 0.8, 0.03])
var_time = Slider(time_ax, 'Time', 0, 100, valinit=10, valfmt='%0.0f')
var_time.on_changed(update)
and I want to customize the appearance of this slider:
I can add axisbg parameter to add_axes function, which will change default white background to assigned color, but that's all I see possible for now.
So, how to change other slider components:
silder border (default: black)
default value indicator (default: red)
slider progress (default: blue)
The slider border is just the spines of the Axes instance. The progress bar can be directly accessed for basic customization in the constructor, and the initial status indicator is an attribute of the slider. I was able to change all of those things:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig = plt.figure()
time_ax = fig.add_axes([0.1, 0.05, 0.8, 0.03])
# Facecolor and edgecolor control the slider itself
var_time = Slider(time_ax, 'Time', 0, 100, valinit=10, valfmt='%0.0f',
facecolor='c', edgecolor='r')
# The vline attribute controls the initial value line
var_time.vline.set_color('blue')
# The spines of the axis control the borders
time_ax.spines['left'].set_color('magenta')
time_ax.spines['right'].set_color('magenta')
time_ax.spines['bottom'].set_color('magenta')
time_ax.spines['top'].set_color('magenta')
The color of the box you can change when you define the axis of the "ax" box:
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
s = a0*np.sin(2*np.pi*f0*t)
l, = plt.plot(t,s, lw=2, color='red')
plt.axis([0, 1, -10, 10])
axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.03, 0.25, 0.03, 0.65], axisbg=axcolor)
axamp = plt.axes([0.08, 0.25, 0.03, 0.65], axisbg=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
# The vline attribute controls the initial value line
samp.vline.set_color('green')