Transparent faces for violinplots in seaborn - python

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))

Related

Can I overlay a matplotlib contour over a seaborn heatmap?

I have a heatmap done in seaborn and a contour plotted via matplotlib.pyplot.
Is it possible to overlay the two?
Seaborn uses matplotlib under the hood. You can combine seaborn plots as if they were directly created by matplotlib. To draw onto the same subplot, the same ax should be used. To align the centers of the heatmap cells with the contour lines, you need to add 0.5 to the x and the y coordinates.
Here is an example to get you started:
from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
from scipy.ndimage.filters import gaussian_filter
data = gaussian_filter(np.random.randn(20, 40), sigma=2)
fig, ax = plt.subplots(figsize=(15, 5))
sns.heatmap(data=data, cbar_kws={'pad': 0.02}, ax=ax)
ax.contour(np.arange(.5, data.shape[1]), np.arange(.5, data.shape[0]), data, colors='yellow')
plt.show()

Seaborn plot with colorbar, centered around 0

I am trying to change the default behaviour of seaborn by adding a colormap (a continuous color palette) instead of using the hue argument, which creates bins from a continuous variable. I have found the following code to work, however, I would like to add one more option, to center the color bar at 0, that is 0 gets the color white, and the colors diverge from zero to negative/positive.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
y=np.random.normal(30,30,100)
x=np.random.uniform(0,50,100)
s=sns.scatterplot(
y=y,
x=x,
hue=y,
size=y,
palette='RdBu',
sizes=(50,50)
)
norm=plt.Normalize(y.min(),y.max())
sm=plt.cm.ScalarMappable(cmap="RdBu",norm=norm)
sm.set_array([])
s.get_legend().remove()
s.figure.colorbar(sm)
As can be seen from the image 0 gets a slightly reddish color, because the data is not symmetric about zero. How can I center the colormap around 0? I am completely fine with an inflated colormap from say -80 to 80 (because of the asymmetry) if the center is at 0.
Using the c, norm, and cmap key-word arguments which are passed through from seaborn to matplotlib.axes.Axes.scatter (used to colour the points instead of palette) and create a mcolors.TwoSlopeNorm to create the normalisation centred around zero you can generate the plot like so:
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as mcolors
fig, ax = plt.subplots()
y=np.random.normal(30,30,100)
x=np.random.uniform(0,50,100)
vcenter = 0
vmin, vmax = y.min(), y.max()
normalize = mcolors.TwoSlopeNorm(vcenter=vcenter, vmin=vmin, vmax=vmax)
colormap = cm.RdBu
s=sns.scatterplot(
y=y,
x=x,
c=y,
norm=normalize,
cmap=colormap,
ax=ax,
)
scalarmappaple = cm.ScalarMappable(norm=normalize, cmap=colormap)
scalarmappaple.set_array(y)
fig.colorbar(scalarmappaple)

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

Plotting seaborn heatmap on top of a background picture

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()

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