Plotting seaborn heatmap on top of a background picture - python

I am creating a heatmap through seaborn in Jupyter to display the amount of people that would choose a certain coordinate point. I currently have the heatmap created with the following code
cm = metrics.confusion_matrix(yVals, xVals)
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(cm, annot=True, fmt="0.3f", linewidth=0.5, cbar=False,
cmap="Reds", square=True, ax=ax)
plt.show()
My questions are how could I plot this heatmap on top of a background image and to make the squares in the heatmap more transparent the closer to 0 they are to show the background image more? Also is there a way to start the indexes on the heatmap at 1 instead of 0?
Here's a link to the picture as well if needed to see how it looks.

You also need to scale/flip the images so they plot together, because the map is probably much finer resolution than the heatmap. We let Seaborn do its adjustment work and then match it in imshow which displays the map.
You can modify or create a colormap to have transparency near 0, and I left the code in to show you how, but the resulting figure was suboptimal because I couldn't read the map under high-heat locations. As shown, the whole heatmap is translucent.
Left for the reader: change the tickmarks to refer to map coordinates, not heatmap indices.
# add alpha (transparency) to a colormap
import matplotlib.cm from matplotlib.colors
import LinearSegmentedColormap
wd = matplotlib.cm.winter._segmentdata # only has r,g,b
wd['alpha'] = ((0.0, 0.0, 0.3),
(0.3, 0.3, 1.0),
(1.0, 1.0, 1.0))
# modified colormap with changing alpha
al_winter = LinearSegmentedColormap('AlphaWinter', wd)
# get the map image as an array so we can plot it
import matplotlib.image as mpimg
map_img = mpimg.imread('tunis.png')
# making and plotting heatmap
import numpy.random as random
heatmap_data = random.rand(8,9)
import seaborn as sns; sns.set()
hmax = sns.heatmap(heatmap_data,
#cmap = al_winter, # this worked but I didn't like it
cmap = matplotlib.cm.winter,
alpha = 0.5, # whole heatmap is translucent
annot = True,
zorder = 2,
)
# heatmap uses pcolormesh instead of imshow, so we can't pass through
# extent as a kwarg, so we can't mmatch the heatmap to the map. Instead,
# match the map to the heatmap:
hmax.imshow(map_img,
aspect = hmax.get_aspect(),
extent = hmax.get_xlim() + hmax.get_ylim(),
zorder = 1) #put the map under the heatmap
from matplotlib.pyplot import show
show()

import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.image as mpimg
file = "./iris.csv"
df = pd.read_csv(file)
import seaborn as sns
map_img = mpimg.imread('1538287373.02485_image.png')
# Custom it with the same argument as 1D density plot
hmax = sns.kdeplot(df.sepal_width, df.sepal_length, cmap="Reds", shade=True, bw=.15)
hmax.collections[0].set_alpha(0)
plt.imshow(map_img, zorder=0, extent=[0.5, 8.0, 1.0, 7.0])
plt.show()

Related

Transparent faces for violinplots in seaborn

Extending this question, is there a way to control the transparency of a face of the violinplot without affecting the edge?
When accessing the collection this results only in a reduction in overall alpha (here with increasing transparency taken from the original question):
With set_alpha() you change both the face color and the edge color. Instead, you could change just the face color:
import seaborn as sns
from matplotlib.colors import to_rgba
tips = sns.load_dataset("tips")
ax = sns.violinplot(x='day', y='total_bill', data=tips, color='r', linewidth=4)
for violin, alpha in zip(ax.collections[::2], [0.8, 0.6, 0.4, 0.2]):
violin.set_facecolor(to_rgba(violin.get_facecolor(), alpha=alpha))
The only way to get different transparency for edge and face has been to access the PolyCollection and individually setting edge and face colors like so:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.rand(10, 2)).melt(var_name='group')
ax = sns.violinplot(data=df, x='group', y='value', inner=None, linewidth=1, saturation=0.5)
# change alpha for edges and faces
ax.collections[0].set_edgecolor((0.9647058823529412, 0.06274509803921569, 0.403921568627451, 1))
ax.collections[0].set_facecolor((0.9647058823529412, 0.06274509803921569, 0.403921568627451, 0.1))

Set transparency (alpha) of matplotlib 3d grid

I would like to change the transparency of the grid in matplotlib 3d plot.
But I find that it is not as easy as in 2d, which is simply plt.grid(alpha=0.2).
Here I give a mini code
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(3, 100)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.scatter(data[0], data[1], data[2])
# How to change the grid transparency?
plt.show()
How to set the transparency of the x,y,z-grids?
I have tried:
Using ax.zaxis._axinfo['grid'].update({"alpha": 0.1}). But it appears that it does not have the key alpha.
I checked the source code of ax.grid() here in github. From the comments, it seems that the alpha functionality is not implemented for 3d case at all.
plt.grid does not seem to do anything for 3d plots. But you can set the color as a RGB+Alpha tuple using rcparams:
import numpy as np
import matplotlib.pyplot as plt
# fourth parameter is alpha=0.1
plt.rcParams['grid.color'] = (0.5, 0.5, 0.5, 0.1)
data = np.random.randn(3, 100)
fig = plt.figure()
ax = plt.axes(projection ="3d")
ax.scatter(data[0], data[1], data[2])
plt.show()
Result:

Colorbar for sns.jointplot "kde"-style on the side

I'm trying to plot a colorbar next to my density plot with marginal axes.
It does plot the colorbar, but unfortunately not on the side.
That's what a tried so far:
sns.jointplot(x,y, data=df3, kind="kde", color="skyblue", legend=True, cbar=True,
xlim=[-10,40], ylim=[900,1040])
It looks like this:
I also tried this:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
kdeplot = sns.jointplot(x=tumg, y=pumg, kind="kde")
plt.subplots_adjust(left=0.2, right=0.8, top=0.8, bottom=0.2)
cbar_ax = kdeplot.fig.add_axes([.85, .25, .05, .4])
plt.colorbar(cax=cbar_ax)
plt.show()
But with the second option I'm getting a runtime error:
No mappable was found to use for colorbar creation.
First define a mappable such as an image (with imshow) or a contour set (with contourf).
Does anyone have an idea how to solve the problem?
There only seems to be information for a colorbar when effectively creating the colorbar.
So, an idea is to combine both approaches: add a colorbar via kdeplot, and then move it to the desired location. This will leave the main joint plot with insufficient width, so its width also should be adapted:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
# create some dummy data: gaussian multivariate with 10 centers with each 1000 points
tumg = np.random.normal(np.tile(np.random.uniform(10, 20, 10), 1000), 2)
pumg = np.random.normal(np.tile(np.random.uniform(10, 20, 10), 1000), 2)
kdeplot = sns.jointplot(x=tumg, y=pumg, kind="kde", cbar=True)
plt.subplots_adjust(left=0.1, right=0.8, top=0.9, bottom=0.1)
# get the current positions of the joint ax and the ax for the marginal x
pos_joint_ax = kdeplot.ax_joint.get_position()
pos_marg_x_ax = kdeplot.ax_marg_x.get_position()
# reposition the joint ax so it has the same width as the marginal x ax
kdeplot.ax_joint.set_position([pos_joint_ax.x0, pos_joint_ax.y0, pos_marg_x_ax.width, pos_joint_ax.height])
# reposition the colorbar using new x positions and y positions of the joint ax
kdeplot.fig.axes[-1].set_position([.83, pos_joint_ax.y0, .07, pos_joint_ax.height])
plt.show()

How to make the color of one end of colorbar darker in matplotlib?

Say I have the following plot:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap='Blues', vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
The colorbar has the (almost) white color assigned to the lowest values. How do I make it slightly darker? I want that instead of the colorbar ranging from white to blue, it should range from light blue to dark blue. Like, the color for the value 0 should be something like what it is for the value 0.4 in the plot above.
I found this when searching about it, but the question (and the solutions) is about making all the colors darker, which is not what I am looking for.
Although the suggestion of #user3483203 is very good, you do re-interpolate the colormap. You could avoid this by first getting the colormap as a matrix of colors (based on the original interpolation) and then select a part of this matrix as your new colormap:
import matplotlib as mpl
cmap = mpl.cm.Blues(np.linspace(0,1,20))
cmap = mpl.colors.ListedColormap(cmap[10:,:-1])
Your example then becomes
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
cmap = mpl.cm.Blues(np.linspace(0,1,20))
cmap = mpl.colors.ListedColormap(cmap[10:,:-1])
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap=cmap, vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
which gives
which is in this case probably equivalent to re-interpolated colormap, as Blues itself comes from some interpolation.
For other colormaps the results may be quite different. For example, for jet:
No new interpolation, but just a subset of the original colormap (i.e. current solution):
Using re-interpolation (i.e. #user3483203's solution):
Simply define your own custom colormap:
from matplotlib.colors import LinearSegmentedColormap
colors = [(0.6, 0.76, 0.98), (0, 0.21, 0.46)] # Experiment with this
cm = LinearSegmentedColormap.from_list('test', colors, N=10)
Then just plug it in for the cmap parameter:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1)
data = np.sort(np.random.rand(8,12))
plt.figure()
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap=cm, vmin=0.0, vmax=1.0)
plt.colorbar(c)
plt.show()
And the result:
Using set_clim is a simple way to get your colors adjusted the way you probably want:
c.set_clim(-0.5, 1.0)
This sets the color limit (first value is vmin and second is vmax).
↳ https://matplotlib.org/api/_as_gen/matplotlib.pyplot.clim.html

Matplotlib colorbar: how to manually set the intervals?

I am using the following snippet to create a custom colorbar:
import pylab as pl
import numpy as np
a = np.array([[0,10000,100000,400000,500000]])
pl.figure(figsize=(9, 1.5))
mycmap = colors.ListedColormap(['yellow','orange','red','darkred'])
img = pl.imshow(a, cmap=mycmap)
pl.gca().set_visible(False)
cax = pl.axes([0.1, 0.2, 0.8, 0.6])
cbar=pl.colorbar(orientation='horizontal', cax=cax,spacing='proportional');
cbar.set_ticks([0,10000,100000,400000,500000])
cbar.set_ticklabels(['0','10000','100000','400000','500000'])
This is giving me a colorbar with regular intervals, although I have specified spacing='proportional':
The intended result is, instead:
0-10000: yellow
10001-100000: orange
100001-400000: red
400001-500000: dark red
What am I doing wrong?
As can be seen when not turning the axes invisible, the colorbar is correctly representing the color of the data in the image.
If this is not what you want you should start by establishing how the data is represented in the image. Introducing a Normalization for the data to the colormap range is usually the way this is accomplished. Here a BoundaryNorm makes sense.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
a = np.array([[0,10000,100000,400000,500000]])
plt.figure(figsize=(4, 2.5))
mycmap = matplotlib.colors.ListedColormap(['yellow','orange','red','darkred'])
norm = matplotlib.colors.BoundaryNorm(a[0], len(a[0])-1)
img = plt.imshow(a, cmap=mycmap, norm=norm)
cax = plt.axes([0.1, 0.1, 0.8, 0.1])
cbar=plt.colorbar(orientation='horizontal', cax=cax,spacing='proportional');
plt.show()
This now gives a meaningful representation with ticks at the edges of the colorranges.

Categories