I have a heatmap plotted above an image (as shown on image link 1), with gaussian filter and normalize data. The main issue is that there is no value under 92 on the y axis, so the plot doesnt start on (0,0), instead start on (0,92). So, when I put together both pictures (heatmap and background image), there is an abrupt cut on the graph (as shown on 2nd link, where is the heatmap without background).
So, how can I extend the axis on the heatmap so it start on (0,0)?
Below is the code that I'm currently using to plot both images. Thanks!
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from scipy.ndimage.filters import gaussian_filter
import matplotlib.colors as mcolors
from sklearn.preprocessing import normalize
x = df['x_data']
y = df['y_data']
heatmap, xedges, yedges = np.histogram2d(x, y, bins = [800,600])
extent = [0, xedges[-1], yedges[0], yedges[-1]]
heatmap = normalize(heatmap)
heatmap = gaussian_filter(heatmap, 16)
colors = [(1,1-c,0,c) for c in np.linspace(0,1,100)]
cmapred = mcolors.LinearSegmentedColormap.from_list('mycmap', colors, N=5)
map_img = mpimg.imread('dir/to/background/image.png')
fig, ax = plt.subplots(figsize=(16.1, 9.1))
plt.imshow(map_img, extent=[0, 800, 0, 600], cmap = 'Greys_r')
plt.imshow(heatmap.T, extent = extent, origin = 'lower', cmap = cmapred, alpha = 0.7)
plt.ylim([0,600])
plt.xlim([0,800])
plt.show()
Image of heatmap + background: https://imgur.com/2vX6Bw6
Image of only heatmap: https://imgur.com/axMe7K7
You could add rows to your heat map manually. Maybe easier is to try setting the histogram bins explicitly?
bins=[np.arange(0, 800, 1), np.arange(0, 600, 1)]
heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins)
Related
I am trying to reproduce a graph that shows the
hue distribution of an image using an HSV colormap.
I have the information related to the hue channel represented as a dict, aggregated on multiple samples:
hue = {
0 : hue_0,
1 : hue_1,
...
255 : hue_255
}
I have tried to use matplotlib's colorline example from here in the following way:
import matplotlib.pyplot as plt
x = list(hue.keys())
y = list(hue.values())
fig, ax = plt.subplots()
lc = colorline(x, y, cmap='hsv')
plt.colorbar(lc)
plt.xlim(0, 255)
plt.ylim(0, max(y))
plt.show()
but it produced this.
I have figured how to plot the hue dict as a line:
import matplotlib.pyplot as plt
lists = sorted(hue.items())
x, y = zip(*lists)
plt.plot(x, y)
plt.show()
But I cannot figure out how to add an HSV colormap to the plot.
I've got code that produces a square image with smaller plots to the left and below the main image plot by using GridSpec with width_ratios and height_ratios:
import matplotlib.pyplot as plt
import numpy as np
# Some fake data.
imdata = np.random.random((100, 100))
extradata1 = np.max(imdata, axis=1)
extradata2 = np.max(imdata, axis=0)
fig = plt.figure(constrained_layout=True)
spec = fig.add_gridspec(ncols=2, nrows=2, width_ratios=(1, 8), height_ratios=(8, 1))
# Main image plot.
ax1 = fig.add_subplot(spec[:-1, 1:], aspect='equal')
ax1.imshow(imdata, cmap='viridis')
# Vertical (left) plot.
ax2 = fig.add_subplot(spec[:-1, 0], sharey=ax1)
ax2.plot(extradata1, range(imdata.shape[0]))
# Horizontal (bottom) plot.
ax3 = fig.add_subplot(spec[-1, 1:], sharex=ax1)
ax3.plot(range(imdata.shape[1]), extradata2)
plt.show()
I'd like the height of the left plot and the width of the bottom plot to be equal to the height and width of the main image, respectively. Currently as you can see the horizontal plot is wider than the image's horizontal size, and they also scale differently as the figure is scaled. Is it possible to constrain axis dimensions to those of other axes?
Calling imshow() with aspect='auto' should fix your problem:
ax1.imshow(imdata, cmap='viridis',aspect='auto')
For some more explanation on this, please look here:
Imshow: extent and aspect
import matplotlib.pyplot as plt
import numpy as np
# Some fake data.
imdata = np.random.random((100, 100))
extradata1 = np.max(imdata, axis=1)
extradata2 = np.max(imdata, axis=0)
fig = plt.figure(constrained_layout=True)
spec = fig.add_gridspec(ncols=2, nrows=2, width_ratios=(1, 8), height_ratios=(8, 1))
# Main image plot.
ax1 = fig.add_subplot(spec[:-1, 1:])
ax1.imshow(imdata, cmap='viridis',aspect='auto')
# Vertical (left) plot.
ax2 = fig.add_subplot(spec[:-1, 0], sharey=ax1)
ax2.plot(extradata1, range(imdata.shape[0]))
# Horizontal (bottom) plot.
ax3 = fig.add_subplot(spec[-1, 1:], sharex=ax1)
ax3.plot(range(imdata.shape[1]), extradata2)
Result:
Fourier's answer worked nicely, but I also found that I could get the desired behaviour by changing constrained_layout=True to constrained_layout=False in the plt.figure call.
Using aspect aspect="auto" works, it has the disadvantage of giving you non-square pixels.
For this kind of tasks, I found that the axes_grid toolkit is pretty useful
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Some fake data.
imdata = np.random.random((100, 100))
extradata1 = np.max(imdata, axis=1)
extradata2 = np.max(imdata, axis=0)
fig, main_ax = plt.subplots()
divider = make_axes_locatable(main_ax)
bottom_ax = divider.append_axes("bottom", 1.2, pad=0.1, sharex=main_ax)
left_ax = divider.append_axes("left", 1.2, pad=0.1, sharey=main_ax)
bottom_ax.xaxis.set_tick_params(labelbottom=False)
left_ax.yaxis.set_tick_params(labelleft=False)
main_ax.imshow(imdata, cmap='viridis')
left_ax.plot(extradata1, range(imdata.shape[0]))
bottom_ax.plot(range(imdata.shape[1]), extradata2)
plt.show()
I have an Nx2 matrix X and an N-dim vector of labels y. For instance:
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=2)
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolor='k')
plt.show()
In the background of this plot I want to plot two heatmaps, with a colormap that has the point's colour in the zones of high points density, so that the image looks like having a purple and a yellow cloud, each centered at the purple and yellow blobs.
This has been challenging for me. I tried creating a 2D histogram for each blob as shown in this answer, and also created a custom colormap so that the low density areas of the plot are white, and the high density areas are coloured with the blob's colour:
import numpy as np
import seaborn as sns
from matplotlib.colors import ListedColormap
palette_colors = sns.color_palette("deep")
palette = sns.light_palette(palette_colors[0], input="husl", n_colors=100)
my_cmap = ListedColormap(sns.color_palette(palette).as_hex())
whr1 = np.where(y==0)
whr2 = np.where(y==1)
x1 = X[whr1][:, 0]
y1 = X[whr1][:, 1]
x2 = X[whr2][:, 0]
y2 = X[whr2][:, 1]
heatmap1, xedges1, yedges1 = np.histogram2d(x1, y1, bins=50)
extent1 = [xedges1[0], xedges1[-1], yedges1[0], yedges1[-1]]
heatmap2, xedges2, yedges2 = np.histogram2d(x2, y2, bins=50)
extent2 = [xedges2[0], xedges2[-1], yedges2[0], yedges2[-1]]
But now I don't know how to plot those heatmaps using imshow. I also want to make sure that if the blobs overlap, so will the heatmaps so that one heatmap does not cover the other heatmap, but rather there is a combination of the heatmaps colours and intensities in the overlapping region.
I really appreciate your help!
You could use seaborn's kdeplot
x1,y1 = np.random.normal(loc=0.0, scale=1.0, size=(100,)), np.random.normal(loc=2.0, scale=1.0, size=(100,))
x2,y2 = np.random.normal(loc=2., scale=1.0, size=(100,)), np.random.normal(loc=0.0, scale=1.0, size=(100,))
fig, ax = plt.subplots()
sns.kdeplot(x1,y1, shade=True, shade_lowest=False, alpha=0.5, cbar=False, ax=ax, cmap="Blues")
sns.kdeplot(x2,y2, shade=True, shade_lowest=False, alpha=0.5, cbar=False, ax=ax, cmap="Oranges")
ax.scatter(x1,y1, color="C0")
ax.scatter(x2,y2, color="C1")
I am trying to create a figure with several subplots that have a common colorbar. The subplots have to have an equal aspect ratio and the colorbar has to have the same height as the subplots. However, I don't manage to get a narrow colorbar with the same height as the other subplots.
I am using this recipe to generate a colorbar with a range suitable for all subplots; hence this issue is not addressed in the MWE.
When using the axes divider recipe to attach the colorbar, the height of the subplot changes due to the aspect ratio.
Here's the MWE
from matplotlib import pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import itertools as it
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
axes = dict(enumerate(fig.get_axes(),1))
for i,ax in axes.items():
if i==6:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
divider = make_axes_locatable(axes[6])
cax = divider.append_axes("left", size="15%", pad=0.0)
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
I can plot the colorbar over the complete subplot, in which case the height is correct, but it's much to wide to be appealing.
Does anybody have a "robust" solution, i.e. without manually fiddling around with the dimension of the subplots holding the colorbar?
Thanks in advance!
EDIT: Increased width of colorbar to emphasize that it becomes smaller in height.
If the only aim is to get the height of the colorbar correctly aligned with its horizontal neighbor, the last solution from this answer would help.
If however you also want the colorbar to be left-aligned with the plot on top of it, the solution is probably more complicated.
You may use a callback to set the position of the colorbar explicitely as follows:
from matplotlib import pyplot as plt
from matplotlib.transforms import Bbox
import numpy as np
mean = [0, 0]
cov = [[1, 0.5],
[0.5, 4]]
n_samples = 10000
hrange = [[-5,5],[-5,5]]
bins = 20
# RANDOM DATA
Z_random = np.random.multivariate_normal(mean, cov, size=n_samples)
Z, xedges, yedges = np.histogram2d(Z_random[:,0], Z_random[:,1], bins=bins, range=hrange, normed=True)
X, Y = np.meshgrid(xedges, yedges)
# PLOT PCOLORMESHS
fig, axes = plt.subplots(2,3, subplot_kw=dict(aspect="equal"))
for i,ax in enumerate(axes.flat):
if i==5:
break
pcm = ax.pcolormesh(X,Y,Z)
# PLOT COLORBAR
cax = fig.add_axes([0.6,0.01,0.1,0.4])
fig.colorbar(pcm, cax=cax, label=r"Colorbar label")
def align_cbar(cax, hax, vax):
hpos = hax.get_position()
vpos = vax.get_position()
bb = Bbox.from_extents(vpos.x0, hpos.y0, vpos.x0+vpos.width*.05,hpos.y1)
if cax.get_position() != bb:
cax.set_position(bb)
fig.canvas.draw_idle()
align_cbar(cax, axes[1,1], axes[0,2])
fig.canvas.mpl_connect("draw_event", lambda x: align_cbar(cax, axes[1,1], axes[0,2]))
plt.show()
I am trying to plot a heatmap on top of an image.
What I did:
import matplotlib.pyplot as plt
import numpy as np
import numpy.random
import urllib
#downloading an example image
urllib.urlretrieve("http://tekeye.biz/wp-content/uploads/2013/01/small_playing_cards.png", "/tmp/cards.png")
#reading and plotting the image
im = plt.imread('/tmp/cards.png')
implot = plt.imshow(im)
#generating random data for the histogram
x=numpy.random.normal(500, 100, size=1000)
y=numpy.random.normal(100, 50, size=1000)
heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
plt.imshow(heatmap, extent=extent,alpha=.5)
plt.show()
When I plot them together, the image get's rotated, up-side down as in:
Does anyone have a solution for having the old picture back?
you need to set the origin of both the imshow instances. But, you also need to change the yedges around in your extent
implot = plt.imshow(im,origin='upper')
...
extent = [xedges[0], xedges[-1], yedges[-1], yedges[0]]
plt.imshow(heatmap, extent=extent,alpha=.5,origin='upper')