Different level of transparency for edgeline and fill in matplotlib or seaborn distribution plot - matplotlib

I would like to set different levels of transparency (= alpha) for the edge line and fill of a distribution plot that I created in matplotlib/seaborn. For example:
ax1 = sns.distplot(BSRDI_DF, label="BsrDI", bins=newBins, kde=False,
hist_kws={"edgecolor": (1,0,0,1), "color":(1,0,0,0.25)})
The above approach does not work, unfortunately. Does anybody have any idea how I could accomplish this?

The problem seems to be that seaborn sets an alpha parameter for the histogram. While alpha defaults to None for a usual histogram, such that something like
plt.hist(x, lw=3, edgecolor=(1,0,0,0.75), color=(1,0,0,0.25))
works as expected, seaborn sets this alpha to some given value. This overwrites the alpha that is set in the RGBA tuples.
The solution is to set alpha explicitely to None:
ax = sns.distplot(x, kde=False, hist_kws={"lw":3, "edgecolor": (1,0,0,0.75),
"color":(1,0,0,0.25),"alpha":None})
A complete example:
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
x = np.random.randn(60)
ax = sns.distplot(x, label="BsrDI", bins=np.linspace(-3,3,10), kde=False,
hist_kws={"lw":3, "edgecolor": (1,0,0,0.75),
"color":(1,0,0,0.25),"alpha":None})
plt.show()

EDIT Nevermind, I thought using color instead of facecolor was causing the problem but it seems the output that I got only looked right because the patches were overlapping, giving seemingly darker edges.
After investigating the issue further, it looks like seaborn is hard-setting the alpha level at 0.4, which supersedes the arguments passed to hist_kws=
sns.distplot(x, kde=False, hist_kws={"edgecolor": (1,0,0,1), "lw":5, "facecolor":(0,1,0,0.1), "rwidth":0.8})
While using the same parameters to plt.hist() gives:
plt.hist(x, edgecolor=(1,0,0,1), lw=5, facecolor=(0,1,0,0.1), rwidth=0.8)
Conclusion: if you want different alpha levels for edges and face colors, you'll have to use matplotlib directly, and not seaborn.

Related

Specify Matplotlib's kwargs to Seaborn's displot when hue is used

Suppose we have this:
import seaborn as sns
import pandas as pd
import numpy as np
samples = 2**13
data = pd.DataFrame({'Values': list(np.random.normal(size=samples)) + list(np.random.uniform(size=samples)),
'Kind': ['Normal'] * samples + ['Uniform'] * samples})
sns.displot(data, hue='Kind', x='Values', fill=True)
I want my Normal's histogram (or KDE) emphasized. I'd like it in red and non transparent in the background. Uniform should have alpha = .5.
How do I specify these style parameters in a "per hue" manner?
It's possible to do it with two separate histplots on the same Axes, as #Redox suggested. We can basically recreate the same plot, but with fine-grade control over colours and alpha. However I had to explicitly pass the number of bins in to get the same plot as yours. I also needed to define the colour for Uniform otherwise a ghost element would be added to the legend! I used C1, meaning the first default colour.
_, ax = plt.subplots()
sns.histplot(data=data[data.Kind=='Normal'], x="Values", ax=ax, label='Normal', color='tab:red',bins=130,alpha=1)
sns.histplot(data=data[data.Kind=='Uniform'], x="Values", ax=ax, label='Uniform', color='C1',bins=17, alpha=.5)
ax.set_xlabel('')
ax.legend()
Note that if you just want to set the colour without alpha you can already do this on a displot via the palette argument - pass in a dictionary of your unique hue values to colour names. However, the alpha that you pass in must be a scalar. I tried to use this clever answer to set colours as RGBA colours which include alpha, which seems to work with other figure level plots in Seaborn. However, displot overrides this and sets the alpha separately!

How do I use colourmaps with variable alpha in a Seaborn kdeplot without seeing the contour lines?

Python version: 3.6.4 (Anaconda on Windows)
Seaborn: 0.8.1
Matplotlib: 2.1.2
I'm trying to create a 2D Kernel Density plot using Seaborn but I want each step in the colourmap to have a different alpha value. I had a look at this question to create a matplotlib colourmap with alpha values: Add alpha to an existing matplotlib colormap.
I have a problem in that the lines between contours are visible. The result I get is here:
I thought that I had found the answer when I found this question: Hide contour linestroke on pyplot.contourf to get only fills. I tried the method outlined in the answer (using set_edgecolor("face") but it did not work in this case. That question also seemed to be related to vector graphics formats and I am just writing out a PNG.
Here is my script:
import numpy as np
import seaborn as sns
import matplotlib.colors as cols
import matplotlib.pyplot as plt
def alpha_cmap(cmap):
my_cmap = cmap(np.arange(cmap.N))
# Set a square root alpha.
x = np.linspace(0, 1, cmap.N)
my_cmap[:,-1] = x ** (0.5)
my_cmap = cols.ListedColormap(my_cmap)
return my_cmap
xs = np.random.uniform(size=100)
ys = np.random.uniform(size=100)
kplot = sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30)
plt.savefig("example_plot.png")
Guided by some comments on this question I have tried some other methods that have been successful when this problem has come up. Based on this question (Matplotlib Contourf Plots Unwanted Outlines when Alpha < 1) I have tried altering the plot call to:
sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
With antialiased=True the lines between contours are replaced by a narrow white line:
I have also tried an approach similar to this question - Pyplot pcolormesh confused when alpha not 1. This approach is based on looping over the PathCollections in kplot.collections and tuning the parameters of the edges so that they become invisible. I have tried adding this code and tweaking the linewidth -
for thing in kplot.collections:
thing.set_edgecolor("face")
thing.set_linewidth(0.01)
fig.canvas.draw()
This results in a mix of white and dark lines - .
I believe that I will not be able to tune the line width to make the lines disappear because of the variable width of the contour bands.
Using both methods (antialiasing + linewidth) makes this version, which looks cool but isn't quite what I want:
I also found this question - Changing Transparency of/Remove Contour Lines in Matplotlib
This one suggests overplotting a second plot with a different number of contour levels on the same axis, like:
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=35,
antialiased=True)
This results in:
This is better, and almost works. The problem here is I need variable (and non-linear) alpha throughout the colourmap. The variable banding and lines seem to be a result of the combinations of alpha when contours are plotted over each other. I also still see some clear/white lines in the result.

Use different math font for different subplots in the same figure?

I am trying to use different math font sets for two axes in the same figure, with no success. I have searched this issue using google and I have read the matplotlib's official guide on how to use the math font. But I can not find ways to achieve this effect. My complete code is as follows:
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, (ax1, ax2) = plt.subplots(ncols=2)
mpl.rcParams['mathtext.fontset'] = 'cm' # use font "cm" for first axes
ax1.text(0.3, 0.5, r"$xyz$", fontsize=50)
ax1.set_title('before')
ax1.axis('off')
ax1.set_aspect('equal')
mpl.rcParams['mathtext.fontset'] = 'stixsans' # use font "stixsans" for second axes
ax2.text(0.3, 0.5, r"$xyz$", fontsize=50)
ax2.set_title('after')
ax2.axis('off')
ax2.set_aspect('equal')
plt.show()
The resulting figure shows that both the axes use the "stixsans" font, see picture here.
It seems that mpl.rcParams['mathtext.fontset'] = 'stixsans' in the later part has overruled the previous setting mpl.rcParams['mathtext.fontset'] = 'cm'. Any idea how to prevent this from happening and use "cm" and "stixsans" font for the two axes respectively?

Seaborn stripplot set edgecolor based on hue/palette

I'm trying to create a figure like this one from the seaborn documentation but with the edgecolor of the stripplot determined by the hue. This is my attempt:
import seaborn as sns
df = sns.load_dataset("tips")
ax = sns.stripplot(x="sex", y="tip", hue="day", data=df, jitter=True,
edgecolor=sns.color_palette("hls", 4),
facecolors="none", split=False, alpha=0.7)
But the color palettes for male and female appear to be different. How do I use the same color palette for both categories?
I'm using seaborn 0.6.dev
The edgecolor parameter is just passed straight through to plt.scatter. Currently you're giving it a list of 4 colors. I'm not exactly sure what I would expect it to do in that case (and I am not exactly sure why you end up with what you're seeing here), but I would not have expected it to "work".
The ideal way to have this work would be to have a "hollow circle" marker glyph that colors the edges based on the color (or facecolor) attribute rather than the edges. While it would be nice to have this as an option in core matplotlib, there are some inconsistencies that might make that unworkable. However, it's possible to hack together a custom glyph that will do the trick:
import numpy as np
import matplotlib as mpl
import seaborn as sns
sns.set_style("whitegrid")
df = sns.load_dataset("tips")
pnts = np.linspace(0, np.pi * 2, 24)
circ = np.c_[np.sin(pts) / 2, -np.cos(pts) / 2]
vert = np.r_[circ, circ[::-1] * .7]
open_circle = mpl.path.Path(vert)
sns.stripplot(x="sex", y="tip", hue="day", data=df,
jitter=True, split=False,
palette="hls", marker=open_circle, linewidth=0)
FWIW I should also mention that it's important to be careful when using this approach because the colors become much harder to distinguish. The hls palette exacerbates the problem as the lime green and cyan middle colors end up quite similar. I can imagine situations where this would work nicely, though, for instance a hue variable with two levels represented by gray and a bright color, where you want to emphasize the latter.

How to pick a new color for each plotted line within a figure in matplotlib?

I'd like to NOT specify a color for each plotted line, and have each line get a distinct color. But if I run:
from matplotlib import pyplot as plt
for i in range(20):
plt.plot([0, 1], [i, i])
plt.show()
then I get this output:
If you look at the image above, you can see that matplotlib attempts to pick colors for each line that are different, but eventually it re-uses colors - the top ten lines use the same colors as the bottom ten. I just want to stop it from repeating already used colors AND/OR feed it a list of colors to use.
I usually use the second one of these:
from matplotlib.pyplot import cm
import numpy as np
#variable n below should be number of curves to plot
#version 1:
color = cm.rainbow(np.linspace(0, 1, n))
for i, c in zip(range(n), color):
plt.plot(x, y, c=c)
#or version 2:
color = iter(cm.rainbow(np.linspace(0, 1, n)))
for i in range(n):
c = next(color)
plt.plot(x, y, c=c)
Example of 2:
matplotlib 1.5+
You can use axes.set_prop_cycle (example).
matplotlib 1.0-1.4
You can use axes.set_color_cycle (example).
matplotlib 0.x
You can use Axes.set_default_color_cycle.
You can use a predefined "qualitative colormap" like this:
from matplotlib.cm import get_cmap
name = "Accent"
cmap = get_cmap(name) # type: matplotlib.colors.ListedColormap
colors = cmap.colors # type: list
axes.set_prop_cycle(color=colors)
Tested on matplotlib 3.0.3. See https://github.com/matplotlib/matplotlib/issues/10840 for discussion on why you can't call axes.set_prop_cycle(color=cmap).
A list of predefined qualititative colormaps is available at https://matplotlib.org/gallery/color/colormap_reference.html :
prop_cycle
color_cycle was deprecated in 1.5 in favor of this generalization: http://matplotlib.org/users/whats_new.html#added-axes-prop-cycle-key-to-rcparams
# cycler is a separate package extracted from matplotlib.
from cycler import cycler
import matplotlib.pyplot as plt
plt.rc('axes', prop_cycle=(cycler('color', ['r', 'g', 'b'])))
plt.plot([1, 2])
plt.plot([2, 3])
plt.plot([3, 4])
plt.plot([4, 5])
plt.plot([5, 6])
plt.show()
Also shown in the (now badly named) example: http://matplotlib.org/1.5.1/examples/color/color_cycle_demo.html mentioned at: https://stackoverflow.com/a/4971431/895245
Tested in matplotlib 1.5.1.
I don't know if you can automatically change the color, but you could exploit your loop to generate different colors:
for i in range(20):
ax1.plot(x, y, color = (0, i / 20.0, 0, 1)
In this case, colors will vary from black to 100% green, but you can tune it if you want.
See the matplotlib plot() docs and look for the color keyword argument.
If you want to feed a list of colors, just make sure that you have a list big enough and then use the index of the loop to select the color
colors = ['r', 'b', ...., 'w']
for i in range(20):
ax1.plot(x, y, color = colors[i])
You can also change the default color cycle in your matplotlibrc file.
If you don't know where that file is, do the following in python:
import matplotlib
matplotlib.matplotlib_fname()
This will show you the path to your currently used matplotlibrc file.
In that file you will find amongst many other settings also the one for axes.color.cycle. Just put in your desired sequence of colors and you will find it in every plot you make.
Note that you can also use all valid html color names in matplotlib.
As Ciro's answer notes, you can use prop_cycle to set a list of colors for matplotlib to cycle through. But how many colors? What if you want to use the same color cycle for lots of plots, with different numbers of lines?
One tactic would be to use a formula like the one from https://gamedev.stackexchange.com/a/46469/22397, to generate an infinite sequence of colors where each color tries to be significantly different from all those that preceded it.
Unfortunately, prop_cycle won't accept infinite sequences - it will hang forever if you pass it one. But we can take, say, the first 1000 colors generated from such a sequence, and set it as the color cycle. That way, for plots with any sane number of lines, you should get distinguishable colors.
Example:
from matplotlib import pyplot as plt
from matplotlib.colors import hsv_to_rgb
from cycler import cycler
# 1000 distinct colors:
colors = [hsv_to_rgb([(i * 0.618033988749895) % 1.0, 1, 1])
for i in range(1000)]
plt.rc('axes', prop_cycle=(cycler('color', colors)))
for i in range(20):
plt.plot([1, 0], [i, i])
plt.show()
Output:
Now, all the colors are different - although I admit that I struggle to distinguish a few of them!