I need to plot a collection of patches using a custom colormap. I have gotten this working fine, however I can't use alpha properly with my custom colormap. As you can see in the images and attached code, the alpha is applied to the patches, but the colorbar still shows with alpha=1 making the map "wrong". To check, I tested the with jet, and the colorbar reflects the proper alpha. I am assuming something is missing in the way I defined my colorbar, but the documentation has not been helpful in figuring out what...
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
x = np.random.rand(50,1)*500; y = np.random.rand(50,1)*500;
radius = np.random.rand(50,1)*50
patches = []
for i in range(len(radius)):
circle = matplotlib.patches.Circle((x[i], y[i]), radius[i])
patches.append(circle)
fig, ax = plt.subplots()
colors = 100*np.random.rand(len(patches))
p = matplotlib.collections.PatchCollection(patches)
p.set(array = colors, cmap = 'Spectral', alpha=0.5)
ax.add_collection(p)
plt.colorbar(p, alpha=0.5)
plt.xlim(0,500);plt.ylim(0,500);
plt.gca().set_aspect(1)
plt.show()
############
r = np.hstack((np.zeros(425),np.linspace(0,255,430), np.linspace(254,0,425)))
g = np.hstack((np.linspace(0,255,430), np.linspace(254,0,425), np.zeros(425)))
b = np.hstack((np.linspace(255,0,430), np.zeros(425),np.linspace(0,254,425)))
c = np.array([r,g,b]).T
mycm = matplotlib.colors.ListedColormap(c/255.0)
###########
x = np.random.rand(50,1)*500; y = np.random.rand(50,1)*500;
radius = np.random.rand(50,1)*50
patches = []
for i in range(len(radius)):
circle = matplotlib.patches.Circle((x[i], y[i]), radius[i])
patches.append(circle)
fig, ax = plt.subplots()
colors = 100*np.random.rand(len(patches))
p = matplotlib.collections.PatchCollection(patches)
p.set(array = colors, cmap = mycm, alpha=0.5)
ax.add_collection(p)
plt.colorbar(p, alpha=0.5)
plt.xlim(0,500);plt.ylim(0,500);
plt.gca().set_aspect(1)
plt.show()
As can be seen in the first case picture, matplotlib adds small lines between the colors in the colorbar. Those lines come from the pcolormesh that is used to produce the colorbar. I have no idea why they are there, nor would I know how to get rid of them, but those lines are the reason for the problem of the alpha value not being shown correctly.
As they appear a bit darker then the real color shown, it is clear that if one adds more and more lines to the colorbar, the complete colorbar will be composed of those lines and thus appear darker or less transparent.
This is exactly what is done in the second case, where a ListedColormap with 430+425+425 = 1280 values is used.
The solution would therefore be to reduce the number of colors in the ListedColormap to a value below 255. In the example code below I used 90.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
############
n= 30
r = np.hstack((np.zeros(n),np.linspace(0,255,n), np.linspace(254,0,n)))
g = np.hstack((np.linspace(0,255,n), np.linspace(254,0,n), np.zeros(n)))
b = np.hstack((np.linspace(255,0,n), np.zeros(n),np.linspace(0,254,n)))
c = np.array([r,g,b]).T
mycm = matplotlib.colors.ListedColormap(c/255.0)
###########
x = np.random.rand(50,1)*500; y = np.random.rand(50,1)*500;
radius = np.random.rand(50,1)*50
patches = []
for i in range(len(radius)):
circle = matplotlib.patches.Circle((x[i], y[i]), radius[i])
patches.append(circle)
fig, ax = plt.subplots()
colors = 100*np.random.rand(len(patches))
p = matplotlib.collections.PatchCollection(patches)
p.set(array = colors, cmap = mycm, alpha=0.5)
ax.add_collection(p)
plt.colorbar(p, alpha=0.5)
plt.xlim(0,500);plt.ylim(0,500);
plt.gca().set_aspect(1)
plt.savefig(__file__+".png")
plt.show()
Related
I have two datasets (corresponding with the time-positional data of hydrogen atoms and time-positional data of alumina atoms) in the same system.
I want to plot the density of each element by overlaying two hist2d plots using matplotlib.
I am currently doing this by setting an alpha value on the second hist2d:
fig, ax = plt.subplots(figsize=(4, 4))
v = ax.hist2d(x=alx, y=aly,
bins=50, cmap='Reds')
h = ax.hist2d(x=hx, y=hy,
bins=50, cmap='Blues',
alpha=0.7)
ax.set_title('Adsorption over time, {} K'.format(temp))
ax.set_xlabel('picoseconds')
ax.set_ylabel('z-axis')
fig.colorbar(h[3], ax=ax)
fig.savefig(savename, dpi=300)
I do get the plot that I want, however the colors seem washed out due to the alpha value.
Is there a more correct way to do generate such plots?
One way to achieve this would be a to add fading alphas towards lower levels to the existing color maps:
import numpy as np
import matplotlib.pylab as pl
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
# modify existing Reds colormap with a linearly fading alpha
red = pl.cm.Reds # original colormap
fading_red = red(np.arange(red.N)) # extract colors
fading_red[:, -1] = np.linspace(0, 1, red.N) # modify alpha
fading_red = ListedColormap(fading_red) # convert to colormap
# data generation
random_1 = np.random.randn(10000)+1
random_2 = np.random.randn(10000)+1
random_3 = np.random.randn(10000)
random_4 = np.random.randn(10000)
# plot
fig, ax = plt.subplots(1,1)
plt.hist2d(x=random_3, y=random_4, bins=100, cmap="Blues")
plt.hist2d(x=random_1, y=random_2, bins=50, cmap=fading_red)
plt.show()
Please forgive the crude explanation but I'm unsure how to describe the issue and as they say, a picture says a thousand words, so what I am trying to achieve is to draw a graph in matplotlib that looks like the below:
whereby the scale of the color range is the same across all bars as the x limits of the x-axis.
The closest I have got to so far is this (please ignore the fact it's not horizontal - I was planning on editing that once I had figured out the coloring):
fig, ax = plt.subplots()
mpl.pyplot.viridis()
bars = ax.bar(df['Profile'], df['noise_result'])
grad = np.atleast_2d(np.linspace(0,1,256)).T
ax = bars[0].axes
lim = ax.get_xlim()+ax.get_ylim()
for bar in bars:
bar.set_zorder(1)
bar.set_facecolor('none')
x,y = bar.get_xy()
w, h = bar.get_width(), bar.get_height()
ax.imshow(grad, extent=[x,x+w,y,y+h], aspect='auto', zorder=1,interpolation='nearest')
ax.axis(lim)
which only results in a graph like below:
Many thanks
I'm going along with your approach. The idea is to:
choose an appropriate colormap
create a normalizer for the bar values.
create a mappable which is going to map the normalized values to the colormap in order to create a colorbar.
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from matplotlib.colors import Normalize
import pandas as pd
import numpy as np
df = pd.DataFrame({'key':['A', 'B', 'C', 'D', 'E'], 'val':[100, 20, 70, 40, 100]})
# create a normalizer
norm = Normalize(vmin=df['val'].min(), vmax=df['val'].max())
# choose a colormap
cmap = cm.plasma
# map values to a colorbar
mappable = cm.ScalarMappable(norm=norm, cmap=cmap)
mappable.set_array(df['val'])
fig, ax = plt.subplots()
bars = ax.bar(df['key'], df['val'])
ax = bars[0].axes
lim = ax.get_xlim()+ax.get_ylim()
for bar, val in zip(bars, df['val']):
grad = np.atleast_2d(np.linspace(0,val,256)).T
bar.set_zorder(1)
bar.set_facecolor('none')
x, y = bar.get_xy()
w, h = bar.get_width(), bar.get_height()
ax.imshow(np.flip(grad), extent=[x,x+w,y,y+h], aspect='auto', zorder=1,interpolation='nearest', cmap=cmap, norm=norm)
ax.axis(lim)
cb = fig.colorbar(mappable)
cb.set_label("Values")
Using what you have, you could change line 12 to:
ax.imshow(grad, extent=[x,x+w,y,y+h], aspect='auto', zorder=1, cmap = plt.get_cmap('gist_heat_r'))
or some other color map from:
https://matplotlib.org/stable/tutorials/colors/colormaps.html
You could also change line 3 to start as:
bars = ax.barh
for horizontal bars.
I would like to achieve two objectives with matplotlib:
Dynamically update a scatter plot
Slowly make the points that were plotted at previous iterations more transparent.
Currently, I am able to achieve the opposite goal using colormaps. That is, I can plot points over time but the recent points look more transparent.
Is it possible to get a 'fading' effect on matplotlib using cmap or other tools? Thanks! My code is below:
def plotter_fader(iterations = 100, stay_open = True):
# Set up plot
fig, ax = plt.subplots()
x_data = []
y_data = []
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
t_vals = np.linspace(0,1, iterations)
cmap = (0.09803921568627451, 0.09803921568627451, 0.09803921568627451, .05)
for t in t_vals:
# Get intermediate points
intermediate = (1-t)*A + t*B
new_xvals, new_yvals = ... #Get these through some process
x_vals.extend(new_xvals)
y_vals.extend(new_yvals)
# Put new values in your plot
plt.plot(x_vals, y_vals, '.', color = cmap)
# Recompute plot limits
ax.relim()
# Set title and wait a little bit for 'smoothness'
ax.set_xlabel('X Axis', size = 12)
ax.set_ylabel('Y Axis', size = 12)
ax.set_title('Time: %0.3f' %t)
ax.autoscale_view()
fig.canvas.draw()
time.sleep(0.005)
# Stay open after plotting ends
while stay_open:
pass
Just as usual with a scatter plot you may define an array of values and a colormap that maps those values to colors. You can change this array in each iteration to make older points have a different value.
In the following we take a value of 0 as transparent and a value of 1 as dark blue and create a colormap with those colors.
In each iteration old values are multiplied by a number smaller than one, new values are set to have a value of 1.
Running the animation will hence produce fading points.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation
from matplotlib.colors import LinearSegmentedColormap
fig, ax = plt.subplots()
ax.set_xlabel('X Axis', size = 12)
ax.set_ylabel('Y Axis', size = 12)
ax.axis([0,1,0,1])
x_vals = []
y_vals = []
intensity = []
iterations = 100
t_vals = np.linspace(0,1, iterations)
colors = [[0,0,1,0],[0,0,1,0.5],[0,0.2,0.4,1]]
cmap = LinearSegmentedColormap.from_list("", colors)
scatter = ax.scatter(x_vals,y_vals, c=[], cmap=cmap, vmin=0,vmax=1)
def get_new_vals():
n = np.random.randint(1,5)
x = np.random.rand(n)
y = np.random.rand(n)
return list(x), list(y)
def update(t):
global x_vals, y_vals, intensity
# Get intermediate points
new_xvals, new_yvals = get_new_vals()
x_vals.extend(new_xvals)
y_vals.extend(new_yvals)
# Put new values in your plot
scatter.set_offsets(np.c_[x_vals,y_vals])
#calculate new color values
intensity = np.concatenate((np.array(intensity)*0.96, np.ones(len(new_xvals))))
scatter.set_array(intensity)
# Set title
ax.set_title('Time: %0.3f' %t)
ani = matplotlib.animation.FuncAnimation(fig, update, frames=t_vals,interval=50)
plt.show()
Is this what you are looking for ?
from matplotlib import pyplot as plt
# Creates new axis.
plt.axis([0, 10, 0, 1])
# Allows interactive plotting
plt.ion()
# Transparency
alpha = 1
# Plotting first point outside of loop because more convenient for example
point = plt.scatter(0.5, 0.5, alpha=alpha)
for i in range(10):
# As the loop goes on, increase transparency, remove the current point,
# and plots another one, more transparent.
alpha -= 0.1
point.remove()
point = plt.scatter(5, .5, alpha=alpha, color='r')
plt.pause(0.05)
while True:
plt.pause(0.05)
In the example below I want to add contour labels to a filled contour. I do the same for regular contours, and the result seems to be correct. For the filled contours, however, the labels are off. Is this a bug or did I misunderstand something?
import matplotlib.pyplot as plt
import numpy
X,Z = numpy.meshgrid(range(5),range(5))
V = numpy.zeros([len(X),len(X[0])])
for kx in range(len(X[0])):
for kz in range(len(X)):
V[kz][kx] = X[kx][kz]
fig = plt.figure(figsize=(12,8))
ax1 = fig.add_subplot(121)
CS1 = ax1.contour(X,Z,V,range(5))
ax1.clabel(CS1,fontsize=16,colors='k')
cb1 = fig.colorbar(CS1)
ax2 = fig.add_subplot(122)
CS2 = ax2.contourf(X,Z,V,range(5))
ax2.clabel(CS2,fontsize=16,colors='k')
cb2 = fig.colorbar(CS2)
fig.savefig('contour')
The recent versions of Matplotlib have improved in this aspect. I ran your code on matplotlib 2.0.2, and got the following plots:
import matplotlib.pyplot as plt
import numpy
X,Z = numpy.meshgrid(range(5),range(5))
V = numpy.zeros([len(X),len(X[0])])
for kx in range(len(X[0])):
for kz in range(len(X)):
V[kz][kx] = X[kx][kz]
fig = plt.figure(figsize=(12,8))
ax1 = fig.add_subplot(121)
CS1 = ax1.contour(X,Z,V,range(5))
ax1.clabel(CS1,fontsize=16,colors='k')
cb1 = fig.colorbar(CS1)
ax2 = fig.add_subplot(122)
CS2 = ax2.contourf(X,Z,V,range(5))
ax2.clabel(CS2,fontsize=16,colors='k')
cb2 = fig.colorbar(CS2)
fig.savefig('contour')
This certainly looks better but it doesn't solve the problem completely. We want the labels on the filled contour plot to look like the labels in the contour line plot. Now as tom pointed out, we can't do that easily since clabel is designed to work with contour and not contourf. There is a not-so-neat workaround for this. What we can do is to first create the contour plot, whose labels can be easily manipulated with clabel function and then we fill this plot using contourf.
import matplotlib.pyplot as plt
import numpy
X,Z = numpy.meshgrid(range(5),range(5))
V = numpy.zeros([len(X),len(X[0])])
for kx in range(len(X[0])):
for kz in range(len(X)):
V[kz][kx] = X[kx][kz]
fig = plt.figure(figsize=(12,8))
ax1 = fig.add_subplot(121)
CS1 = ax1.contour(X,Z,V,range(5))
ax1.clabel(CS1,fontsize=16,colors='k')
cb1 = fig.colorbar(CS1)
ax2 = fig.add_subplot(122)
CS2 = ax2.contour(X,Z,V,range(5)) # Creating the contour plot
ax2.clabel(CS2,fontsize=16,colors='k')
CS3 = ax2.contourf(X,Z,V,range(5)) # Creating another filled contour plot on top
cb2 = fig.colorbar(CS3) # Display colorbar for filled contour plot
fig.savefig('contour')
I would still like these labels to be centred in the different regions of the plot, but I couldn't find a way to do that.
I am using matplotlib to make some plots and I have run into a few difficulties that I need help with.
problem 1) In order to keep a consistent colorscheme I need to only use half of the color axis. There are only positive values, so I want the zero values to be green, the mid values to be yellow and the highest values to be red. The color scheme that most closely matches this is gist_rainbow_r, but I only want the top half of it.
problem 2) I can't seem to figure out how to get the colorbar on the right hand side of the plot to show up or how to get it to let me label the axes.
If it helps, I am using the latest version of Anaconda wth the latext version of matplotlib
cmap = plt.get_cmap('gist_rainbow_r')
edosfig2 = plt.figure(2)
edossub2 = edosfig.add_subplot(1,1,1)
edossub2 = plt.contourf(eVec,kints,smallEDOS,cmap=cmap)
edosfig2.show()
If you have a specific set of colors that you want to use for you colormap, you can build it based on those. For example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list('name', ['green', 'yellow', 'red'])
# Generate some data similar to yours
y, x = np.mgrid[-200:1900, -300:2000]
z = np.cos(np.hypot(x, y) / 100) + 1
fig, ax = plt.subplots()
cax = ax.contourf(x, y, z, cmap=cmap)
cbar = fig.colorbar(cax)
cbar.set_label('Z-Values')
plt.show()
However, if you did just want the top half of some particularly complex colormap, you can copy a portion of it by evaluating the colormap over the range you're interested in. For example, if you wanted the "top" half, you'd evaluate it from 0.5 to 1:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
# Evaluate an existing colormap from 0.5 (midpoint) to 1 (upper end)
cmap = plt.get_cmap('gist_earth')
colors = cmap(np.linspace(0.5, 1, cmap.N // 2))
# Create a new colormap from those colors
cmap2 = LinearSegmentedColormap.from_list('Upper Half', colors)
y, x = np.mgrid[-200:1900, -300:2000]
z = np.cos(np.hypot(x, y) / 100) + 1
fig, axes = plt.subplots(ncols=2)
for ax, cmap in zip(axes.flat, [cmap, cmap2]):
cax = ax.imshow(z, cmap=cmap, origin='lower',
extent=[x.min(), x.max(), y.min(), y.max()])
cbar = fig.colorbar(cax, ax=ax, orientation='horizontal')
cbar.set_label(cmap.name)
plt.show()