I'm trying to make a bar plot with a color bar, each bar's hight is one variable (y) and each bar should have a color depending on another variable (c).
What I've got to is this (simple example):
data_x = [0,1,2,3]
data_hight = [60,60,80,100]
data_color = [1000,500,1000,900]
data_color = [x / max(data_color) for x in data_color]
fig, ax = plt.subplots(figsize=(15, 4))
my_cmap = plt.cm.get_cmap('GnBu')
colors = my_cmap(data_color)
rects = ax.bar(data_x, data_hight, color=colors)
CS = plt.contourf([data_x, data_color],cmap=my_cmap)
cbar = plt.colorbar(CS, cmap=my_cmap)
cbar.set_label('Color', rotation=270,labelpad=25)
plt.xticks(data_x)
plt.ylabel("Y")
plt.show()
The main problem is that the histogram colors are fine but the color bar is in a diferent scale. besides that I can see a blue line at y=0, it shouldn't be there.
Any help will be a preciated.
Thanks!
You are creating a contourf plot inside your bar plot. That makes no sense.
Instead you would need to create a mappable without any visual representation to supply to the colorbar. This would be a ScalarMappable.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
data_x = [0,1,2,3]
data_hight = [60,60,80,100]
data_color = [1000.,500.,1000.,900.]
data_color = [x / max(data_color) for x in data_color]
fig, ax = plt.subplots(figsize=(15, 4))
my_cmap = plt.cm.get_cmap('GnBu')
colors = my_cmap(data_color)
rects = ax.bar(data_x, data_hight, color=colors)
sm = ScalarMappable(cmap=my_cmap, norm=plt.Normalize(0,max(data_color)))
sm.set_array([])
cbar = plt.colorbar(sm)
cbar.set_label('Color', rotation=270,labelpad=25)
plt.xticks(data_x)
plt.ylabel("Y")
plt.show()
Related
I make two subplots with a common shared colorbar. So naturally I want to plot the colorbar only once.
However, when I do so, then my subplots become unequal in size.
How to place the colorbar outside the subplots on the right?
Minimal working example below
import numpy as np
from matplotlib import colors
import matplotlib.pyplot as plt
res = 100
x = np.linspace(0, 2*np.pi, res)
y = np.sin(x)
z = np.cos(x)
y2 = -np.sin(x)+0.4
z2 = 0.5*np.cos(2*x)
fig_width = 200/25.4
fig_height = 100/25.4
fig = plt.figure(figsize=(fig_width, fig_height))
gs = fig.add_gridspec(1, 2, wspace=0)
(ax, ax2) = gs.subplots(sharey='row')
images = []
images.append(ax.scatter(x, y, c=z))
images.append(ax2.scatter(x, y2, c=z2))
vmin = min(image.get_array().min() for image in images)
vmax = max(image.get_array().max() for image in images)
norm = colors.Normalize(vmin=vmin, vmax=vmax)
for im in images:
im.set_norm(norm)
cbar = fig.colorbar(images[0], ax=ax2)
cbar.set_label("mylabel", loc='top')
fig.tight_layout()
plt.show()
Try 1) pass the two axes as ax, and 2) move tight_layout before colorbar:
# other stuff
fig.tight_layout()
cbar = plt.colorbar(images[0], ax=(ax,ax2))
# other - other stuff
Output:
I looked everywhere and nothing really helped.
Here is my code:
fig = plt.figure(figsize=(12, 6))
marker_colors = pca_data2['Frame']
fig.suptitle('PCA')
plt.subplot(1, 2, 1)
x = pca_data2.PC_1
y = pca_data2.PC_2
plt.scatter(x, y, c = marker_colors, cmap = "inferno")
plt.colorbar()
plt.subplot(1, 2, 2)
x1 = pca_data.PC_1
y1 = pca_data.PC_2
plt.scatter(x1, y1, c = marker_colors, cmap = "inferno")
plt.colorbar()
plt.show()
pca_data and pca_data2 are two completely different dataframes from to completele different things. But I need them side by side with the 1 color bar being on the right side for all.
Thats how the figure looks like
When I try to remove the first plt.colorbar() then the two subplots look uneven.
I would really appreciate the help.
... since none of the answers seems to mention the fact that you can tell the colorbar the axes on which it should be drawn... here's a simple example how I would do it:
The benefits of this are:
it's much clearer to read
you have complete control over the size of the colorbar
you can extend this easily to any grid of subplots and any position of the colorbar
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
# generate some data
data, data1 = np.random.rand(10,10), np.random.rand(10,10)
x, y = np.meshgrid(np.linspace(0,1,10), np.linspace(0,1,10))
# initialize a plot-grid with 3 axes (2 plots and 1 colorbar)
gs = GridSpec(1, 3, width_ratios=[.48,.48,.04])
# set vmin and vmax explicitly to ensure that both colorbars have the same range!
vmin = np.min([np.min(data), np.min(data1)])
vmax = np.max([np.max(data), np.max(data1)])
plot_kwargs = dict(cmap = "inferno", vmin=vmin, vmax=vmax)
fig = plt.figure(figsize=(12, 6))
ax_0 = fig.add_subplot(gs[0], aspect='equal')
ax_1 = fig.add_subplot(gs[1], aspect='equal')
ax_cb = fig.add_subplot(gs[2])
s1 = ax_0.scatter(x, y, c = data, **plot_kwargs)
s2 = ax_1.scatter(x, y, c = data1, **plot_kwargs)
plt.colorbar(s1, cax=ax_cb)
You can use aspect to set a fixed aspect ratio on the subplots. Then append the colorbars to the right side of each axis and discard the first colorbar, to get an even layout:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1 import make_axes_locatable
fig = plt.figure(figsize=(12, 6))
marker_colors = range(0,10)
x = x1 = np.random.randint(0,10,10)
y = y1 = np.random.randint(0,10,10)
ax1 = fig.add_subplot(1, 2, 1, aspect="equal") # or e.g. aspect=0.9
g1 = ax1.scatter(x, y, c = marker_colors, cmap = "inferno", )
ax2 = fig.add_subplot(1, 2, 2, aspect="equal") # or e.g. aspect=0.9
g2 = ax2.scatter(x1, y1, c = marker_colors, cmap = "inferno")
# put colorbars right next to axes
divider1 = make_axes_locatable(ax1)
cax1 = divider1.append_axes("right", size="5%", pad=0.05)
divider2 = make_axes_locatable(ax2)
cax2 = divider2.append_axes("right", size="5%", pad=0.05)
# reserve space for 1st colorbar, then remove
cbar1 = fig.colorbar(g1, cax=cax1)
fig.delaxes(fig.axes[2])
# 2nd colorbar
cbar2 = fig.colorbar(g2, cax=cax2)
plt.tight_layout()
plt.show()
If you want a different aspect ratio, you can modify aspect, e.g. to aspect=0.9. The result will have locked aspect ratios for the subplots, even if you resize the figure box:
use following code:
Hope it will match your problem statment.
fig = plt.figure(figsize=(12, 6))
marker_colors = range(0,10)
x=x1=np.random.randint(0,10,10)
y=y1=np.random.randint(0,10,10)
plt.subplot(1, 2, 1)
g1=plt.scatter(x, y, c = marker_colors, cmap = "inferno")
plt.subplot(1, 2, 2)
g2=plt.scatter(x1, y1, c = marker_colors, cmap = "inferno")
g11=plt.colorbar(g1)
g12=plt.colorbar(g2)
g11.ax.set_title('g1')
g12.ax.set_title('g2')
If I make a 3d plot in Matplotlib:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')
x_labels = [10,20,30]
x = [1,2,3,4]
y = [3,1,5,1]
legend = False
for label in x_labels:
x_3d = label*np.ones_like(x)
ax.plot(x_3d, x, y, color='black', label='GMM')
if legend == False:
ax.legend()
legend = True
ax.set_zlabel('test')
It will produce:
The left side have excessive white space. I want to know if it is possible to get rid of it?
It's probably too late, but I came across similar problems and here is what I did to remove the white space: use fig.subplot_adjust() to put left/right outside the normal region. In your case I found fig.subplot_adjust(left=-0.11) gives a reasonable result.
Full code below:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')
x_labels = [10,20,30]
x = [1,2,3,4]
y = [3,1,5,1]
legend = False
for label in x_labels:
x_3d = label*np.ones_like(x)
ax.plot(x_3d, x, y, color='black', label='GMM')
if legend == False:
ax.legend()
legend = True
ax.set_zlabel('test')
fig.tight_layout()
fig.subplots_adjust(left=-0.11) # plot outside the normal area
Matplotlib changes the color of the bar when there are more values plotted :
With 5 columns I get the expected red bars :
ax = vc[vc.index[:5]].plot(color='red', kind='bar', title=col+('(count)'))
ax.set_axis_bgcolor('white')
But going for more values, the colors begin to fade and there is some grey bars appearing :(
ax = vc.plot(color='red', kind='bar', title=col+('(count)'))
ax.set_axis_bgcolor('white')
How can I keep My red bars all along ?
They probably become gray because of the edge colors of the bars:
import matplotlib.pylab as pl
import numpy as np
import pandas as p
pl.figure()
x = np.arange(5)
y = np.random.random(x.size)
vc1 = p.DataFrame(data=y, index=x)
x = np.arange(100)
y = np.random.random(x.size)
vc2 = p.DataFrame(data=y, index=x)
ax = pl.subplot(131)
vc1.plot(ax=ax, kind='bar', color='red')
ax = pl.subplot(132)
vc2.plot(ax=ax, kind='bar', color='red')
ax = pl.subplot(133)
vc2.plot(ax=ax, kind='bar', color='red', edgecolor='none')
Fairly simple: I'm wondering if there is an easy way to rescale the colormap to the visible area which is set in this case by ax.set_xlim. The resulting plot I am looking for would look identical in terms of color to the one where ax.set_xlim([-2,2]) is commented out in the code below.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10,10,100)
y = np.linspace(-10,10,100)
X,Y = np.meshgrid(x,y)
Z = X
fig = plt.figure()
ax = fig.add_subplot(111)
cmap = plt.get_cmap('RdYlBu')
cax = ax.contourf(X,Y,Z,256,cmap=cmap, vmin = np.min(Z), vmax=np.max(Z))
cbar = fig.colorbar(cax, cmap=cmap)
ax.set_xlim([-2,2])
plt.show()
This might not be the most elegant solution, but it will "rescale" your colorbar to the range of the display. Note, if you don't set a ylim as well, you' need to remove get_ylim.
fig = plt.figure()
ax = fig.add_subplot(111)
cmap = plt.get_cmap('RdYlBu')
# Note - if you don't set ylim as well, ax.get_ylim() returns (0,1) & you'd need to leave that part out
ax.set_xlim(-2,2)
xl = ax.get_xlim()
ax.set_ylim(-1,1)
yl = ax.get_ylim()
XL = np.where(((x>xl[0]))&(x<xl[1]))[0]
YL = np.where(((y>yl[0])&(y<yl[1])))[0].repeat(XL.shape[0]).reshape(-1,XL.shape[0])
XL = XL.repeat(YL.shape[0]).reshape(YL.shape[0],-1)
values = np.linspace(Z[YL,XL].min(),Z[YL,XL].max(),256)
cax = ax.contourf(X,Y,Z,values,cmap=cmap, vmin = values.min(), vmax=values.max())
cbar = fig.colorbar(cax, cmap=cmap)
plt.show()