Make pie chart with percent readable in grayscale
I have source code for creating a pie chart
import matplotlib.pyplot as plt
from matplotlib.pyplot import savefig
import numpy as np
import matplotlib.gridspec as gridspec
plt.clf()
plt.cla()
plt.close()
labels_b = ["Negative", "Positive"]
dev_sentences_b = [428, 444]
test_sentences_b = [912, 909]
train_sentences_b = [3310, 3610]
gs = gridspec.GridSpec(2, 2)
ax1= plt.subplot(gs[0, 0])
ax1.pie(train_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90)
ax1.axis('equal')
ax1.set_title("Train")
ax2= plt.subplot(gs[0, 1])
ax2.pie(dev_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90)
ax2.axis('equal')
ax2.set_title("Dev")
ax3 = plt.subplot(gs[1, 1])
ax3.pie(test_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90)
ax3.axis('equal')
ax3.set_title("Test")
ax3.legend(labels=labels_b, bbox_to_anchor=(-1,1), loc="upper left")
plt.savefig('sstbinary', format='pdf')
Result
Color photograph
and grayscale
The grayscale version is a little difficult to read. Is there a suggestion to make a black and white pie chart readable in black and white print?
source to share
It's unclear if you want to create your chart in black and white already, or produce it in color and then convert it. The strategy in both cases can be the same: you can create a new color cycle using colors from the color map. A link to possible color codes is given here . Of course, you can also use your own color list.
eg. creating 5 colors from the color map gray
between 0.2
(dark gray) to 0.8
(lightgray):
from cycler import cycler
colors = plt.cm.gray(np.linspace(0.2,0.8,5))
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)
Likewise, you can use a colorful map (for example magma
), which will still look good when converted to grayscale afterwards.
from cycler import cycler
colors = plt.cm.magma(np.linspace(0.2,0.8,5))
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)
Changing the range of colors, eg. between 0.4
and 0.95
gives a lighter color,
from cycler import cycler
colors = plt.cm.magma(np.linspace(0.4,0.95,5))
plt.rcParams['axes.prop_cycle'] = cycler(color=colors)
Note that you can also apply colors directly to each pie chart instead of defining a color cycle.
ax.pie(..., colors=colors, ...)
Finally, a frequently used technique is to use shading to distinguish shapes in gray scale images. See this example .
pie = ax.pie(..., autopct='%1.1f%%', pctdistance=1.3,
colors=colors, ...)
for patch, hatch in zip(pie[0],hatches):
patch.set_hatch(hatch)
source to share
Assuming you are storing the color digit and then converting to grayscale, you can do the following:
-
Identify the colors on the list from your favorite color card. [It's also worth noting here that using one of the new 4 color maps (available with matplotlib 1.5: viridis, magma, plasma, inferno) means the colors will still be distinguishable when the image is converted to grayscale].
colors = plt.cm.plasma(np.linspace(0., 1., 5))
-
We can then define a function to convert these colors to their equivalent grayscale value:
rgb2gray = lambda rgb: np.dot(rgb[...,:3], [0.299, 0.587, 0.114])
-
If this value is greater than 0.5, the color is a lighter shade and therefore we can use black text, otherwise change the text to a lighter color. We can store these text colors in a list using the following list comprehension:
textcol = ['k' if rgb2gray(color) > 0.5 else 'w' for color in colors ]
-
When plotting your pie chart, use
colors=colors
kwarg to use the colors you defined earlier.matplotlib
returns three things fromax.pie
: patches that make up a pie chart, text labels, and labelsautopct
. The latter are the ones that we want to change.p, t, at = ax1.pie(train_sentences_b, autopct='%1.1f%%', shadow=True, startangle=90, colors=colors)
-
Allows you to define a function for scrolling text labels and set their colors depending on the previously made list:
def fix_colors(textlabels, textcolors): for text, color in zip(textlabels, textcolors): text.set_color(color)
-
We then call this after each pie chart has been plotted using:
fix_colors(at, textcol)
Putting it all together in a script (I added some extra data to get all 5 categories in a pie chart):
import matplotlib.pyplot as plt
from matplotlib.pyplot import savefig
import numpy as np
import matplotlib.gridspec as gridspec
colors = plt.cm.plasma(np.linspace(0., 1., 5))
rgb2gray = lambda rgb: np.dot(rgb[...,:3], [0.299, 0.587, 0.114])
textcol = ['k' if rgb2gray(color) > 0.5 else 'w' for color in colors ]
def fix_colors(textlabels, textcolors):
for text, color in zip(textlabels, textcolors):
text.set_color(color)
plt.clf()
plt.cla()
plt.close()
labels_b = ["Very Negative", "Negative", "Neutral", "Positive", "Very Positive"]
dev_sentences_b = [428, 444, 430, 500, 320]
test_sentences_b = [912, 909, 890, 900, 900]
train_sentences_b = [3310, 3610, 3200, 3500, 3321]
gs = gridspec.GridSpec(2, 2)
ax1= plt.subplot(gs[0, 0])
p, t, at = ax1.pie(train_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90, colors=colors)
fix_colors(at, textcol)
ax1.axis('equal')
ax1.set_title("Train")
ax2= plt.subplot(gs[0, 1])
p, t, at = ax2.pie(dev_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90, colors=colors)
ax2.axis('equal')
ax2.set_title("Dev")
fix_colors(at, textcol)
ax3 = plt.subplot(gs[1, 1])
p, t, at = ax3.pie(test_sentences_b, autopct='%1.1f%%',
shadow=True, startangle=90, colors=colors)
ax3.axis('equal')
ax3.set_title("Test")
fix_colors(at, textcol)
ax3.legend(labels=labels_b, bbox_to_anchor=(-1,1), loc="upper left")
plt.savefig('sstbinary', format='pdf')
Which gives the following image:
And after converting to grayscale:
source to share