Plotting transparent histogram with non transparent edge
Asked Answered
B

4

47

I am plotting a histogram, and I have three datasets which I want to plot together, each one with different colours and linetype (dashed, dotted, etc). I am also giving some transparency, in order to see the overlapping bars.

The point is that I would like the edge of each bar not to become transparent as the inner part does. Here is an example:

import matplotlib.pyplot as plt
import numpy as np

x = np.random.random(20)
y =np.random.random(20)
z= np.random.random(20)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.hist(x, bins=np.arange(0, 1, 0.1), ls='dashed', alpha = 0.5, lw=3, color= 'b')
ax.hist(y, bins=np.arange(0, 1, 0.1), ls='dotted', alpha = 0.5, lw=3, color= 'r')
ax.hist(z, bins=np.arange(0, 1, 0.1), alpha = 0.5, lw=3, color= 'k')
ax.set_xlim(-0.5, 1.5)
ax.set_ylim(0, 7)
plt.show()

enter image description here

Byron answered 8/2, 2015 at 19:14 Comment(0)
B
53

plt.hist accepts additional keyword arguments that are passed to the constructor for matplotlib.patches.Patch. In particular you can pass an fc= argument which lets you set the patch facecolor using an (R, G, B, A) tuple when you create the histograms. Changing the alpha value of the facecolor does not affect the transparency of the edges:

ax.hist(x, bins=np.arange(0, 1, 0.1), ls='dashed', lw=3, fc=(0, 0, 1, 0.5))
ax.hist(y, bins=np.arange(0, 1, 0.1), ls='dotted', lw=3, fc=(1, 0, 0, 0.5))
ax.hist(z, bins=np.arange(0, 1, 0.1), lw=3, fc=(0, 0, 0, 0.5))

enter image description here

Belvabelvedere answered 8/2, 2015 at 19:56 Comment(3)
Is there a way to still use the automatic color cycle?Cyril
It's a bit of a hack, but it's possible to get the prop cycler from the axis (see this answer and the comments below). You'd probably want ax._get_patches_for_fill.prop_cycler instead of ax._get_lines.prop_cycler). The colors returned by the prop cycler will be strings (e.g. 'b') rather than RGB tuples, so you'd need to use matplotlib.colors.to_rgb(color_string) to convert the string to an RGB tuple and then append your own alpha value.Belvabelvedere
Thanks that does work fine! I have posted a small answer to show a complete solution.Cyril
S
28

I think perhaps the dumbest solution is the easiest and best? Just replot the histograms, without inner color and without alpha. That should replot just the histogram lines.

Additionally you might want to eliminate the chance of having the lines/dashes/dots not overlap by removing them completely from the first histogram plot

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

x = np.random.random(20)
y = np.random.random(20)
z = np.random.random(20)

fig = plt.figure()
ax = fig.add_subplot(111)

ax.hist(x, bins=np.arange(0, 1, 0.1), edgecolor='None', alpha = 0.5, color= 'b')
ax.hist(y, bins=np.arange(0, 1, 0.1), edgecolor='None', alpha = 0.5, color= 'r')
ax.hist(z, bins=np.arange(0, 1, 0.1), edgecolor="None", alpha = 0.5, color= 'k')


ax.hist(x, bins=np.arange(0, 1, 0.1), ls='dashed', lw=3, facecolor="None")
ax.hist(y, bins=np.arange(0, 1, 0.1), ls='dotted', lw=3, facecolor="None")
ax.hist(z, bins=np.arange(0, 1, 0.1), lw=3, facecolor="None")

plt.show()

without the second orders the graph should look like your figure without any borders. With the bottom 3 histogram commands borders are added to produce the image bellow. Works on Python3.4 Win7

[cut because apparently I don't have enough rep to post pictures]

Semite answered 8/2, 2015 at 19:39 Comment(0)
C
8

I needed a solution which did not require me to set the color explicitly, i.e., I wanted to still use the default color cycle. The following solution builds on @ljetibo's idea to draw the histogram twice using @ali_m's idea to extract the state of the color cycle:

# increment and get the "props" cycle (and extract the color)
color = next(ax._get_lines.prop_cycler)["color"]
# 1. draw: inner area with alpha
ax.hist(data, color=color, alpha=0.3)
# 2. draw: only edges with full color
ax.hist(data, color=color, edgecolor=color, fc="None", lw=1)

Note that specifying color=color in addition to edgecolor seems to be necessary. Otherwise the hist call itself will increment the color cycle again.

Cyril answered 5/4, 2018 at 15:30 Comment(0)
M
1

As other have said you can modify facecolor and edgecolor to your need while also having to set color to avoid Matplotlib incrementing the color cycle.

My solution is a mix of everything that has been said here:

color = next(ax._get_lines.prop_cycler)["color"] # Retrieve color from the cycle

# print(color)  # ex: #8dd3c7
# Colors returned by the color cycle are HEX colors,
# represented as #RRGGBBAA.

ax.hist(data, range=range, bins= _nbins, 
        color= color, fc= color + 'A0', ec=color) # Here A0 represents an alpha value of 160
Medicine answered 25/8, 2023 at 4:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.