Change the automatic color of matplotlib to hex colors automatically python3 - python-3.x

I created a pie chart using matplotlib and I'd like to change the default colors to more softer colors, such as the hex RGB or RGBA string colors. I have the below script so far:
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
explode = ((0.05,)*(len(annotation_df.index)))
fig1, ax1 = plt.subplots()
ax1.pie(annotation_df['count'], labels=annotation_df['annotation'], autopct='%1.1f%%', startangle=90, pctdistance=0.85, explode=explode,colors=colors) #colors=colors,
# draw circle
centre_circle = plt.Circle((0, 0), 0.70, fc='white')
fig = plt.gcf()
fig.gca().add_artist(centre_circle)
# Equal aspect ratio ensures that pie is drawn as a circle
ax1.axis('equal')
plt.tight_layout()
plt.show()
The problem is I need the colors to be set automatically, and I don't want specifically write the colors, as written above in the script.
Anyone knows how to do it?

You may define a color cycler to contain the colors you want to use.
import matplotlib.pyplot as plt
plt.rcParams['axes.prop_cycle'] = plt.cycler('color',
['#ff9999', '#66b3ff', '#99ff99', '#ffcc99'])
fig1, ax1 = plt.subplots()
ax1.pie([1,2,3], labels=list("ABC"), autopct='%1.1f%%')
ax1.axis('equal')
plt.tight_layout()
plt.show()
If you have less wedges than colors in the cycler only the those colors needed are used. If you have more wedges than colors in the cycler, they would be repeated. You can put as many colors as you like into the color cycler.

Related

How to align heights and widths subplot axes with gridspec and matplotlib?

I am trying to use matplotlib with gridspec to create a subplot such that the axes are arranged to look similar to the figure below; the figure was taken from this unrelated question.
My attempt at recreating this axes arrangement is below. Specifically, my problem is that the axes are not properly aligned. For example, the axis object for the blue histogram is taller than the axis object for the image with various shades of green; the orange histogram seems to properly align in terms of width, but I attribute this to luck. How can I properly align these axes? Unlike the original figure, I would like to add/pad extra empty space between axes such that there borders do not intersect; the slice notation in the code below does this by adding a blank row/column. (In the interest of not making this post longer than it has to be, I did not make the figures "pretty" by playing with axis ticks and the like.)
Unlike the original picture, the axes are not perfectly aligned. Is there a way to do this without using constrained layout? By this, I mean some derivative of fig, ax = plt.subplots(constrained_layout=True)?
The MWE code to recreate my figure is below; note that there was no difference between ax.imshow(...) and ax.matshow(...).
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
## initialize figure and axes
fig = plt.figure()
gs = fig.add_gridspec(6, 6, hspace=0.2, wspace=0.2)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
## generate data
x = np.random.normal(loc=50, scale=10, size=100)
y = np.random.normal(loc=500, scale=50, size=100)
## get singular histograms
x_counts, x_edges = np.histogram(x, bins=np.arange(0, 101, 5))
y_counts, y_edges = np.histogram(y, bins=np.arange(0, 1001, 25))
x_mids = (x_edges[1:] + x_edges[:-1]) / 2
y_mids = (y_edges[1:] + y_edges[:-1]) / 2
## get meshed histogram
sample = np.array([x, y]).T
xy_counts, xy_edges = np.histogramdd(sample, bins=(x_edges, y_edges))
## subplot histogram of x
ax_bottom.bar(x_mids, x_counts,
width=np.diff(x_edges),
color='darkorange')
ax_bottom.set_xlim([x_edges[0], x_edges[-1]])
ax_bottom.set_ylim([0, np.max(x_counts)])
## subplot histogram of y
ax_left.bar(y_mids, y_counts,
width=np.diff(y_edges),
color='steelblue')
ax_left.set_xlim([y_edges[0], y_edges[-1]])
ax_left.set_ylim([0, np.max(y_counts)])
## subplot histogram of xy-mesh
ax_big.imshow(xy_counts,
cmap='Greens',
norm=Normalize(vmin=np.min(xy_counts), vmax=np.max(xy_counts)),
interpolation='nearest',
origin='upper')
plt.show()
plt.close(fig)
EDIT:
One can initialize the axes by explicitly setting width_ratios and height_ratios per row/column; this is shown below. This doesn't affect the output, but maybe I'm using it incorrectly?
## initialize figure and axes
fig = plt.figure()
gs = gridspec.GridSpec(ncols=6, nrows=6, figure=fig, width_ratios=[1]*6, height_ratios=[1]*6)
ax_bottom = fig.add_subplot(gs[4:, 2:])
ax_left = fig.add_subplot(gs[:4, :2])
ax_big = fig.add_subplot(gs[:4, 2:])
The problem is with imshow, which resizes the axes automatically to maintain a square pixel aspect.
You can prevent this by calling:
ax_big.imshow(..., aspect='auto')

How to center a colorbar at a specific color for Seaborn Heatmap?

Is it possible to specify the color of center of colorbar in seaborn heatmap?
As example the ceneter of colorbar of the following heat map is 70, and I want to be specified with black color instead of white.
Thanks in advance.
the example heatmap
You can use DivergingNorm to specify an off-centered normalization. To create the cmap with black in the center, use LinearSegmentedColormap
from matplotlib.colors import LinearSegmentedColormap, DivergingNorm
cmap = LinearSegmentedColormap.from_list('BkR',['blue','black','red'])
norm = DivergingNorm(vmin=0, vcenter=70, vmax=100)
x,y = np.random.randint(0,100, size=(2,50))
plt.figure()
plt.scatter(x,y,c=y, norm=norm, cmap=cmap)
plt.colorbar()
plt.show()

Set matplotlib legend markersize to a constant

I'm making a diagram using matplotlib, and it has plt.Circles and plt.axvlines to represent different shapes. I need a legend to describe these shapes, but the problem is the legend marker (the image part), changes size depending on the input, which looks awful. How do I set the size to a constant?
fig = plt.figure(figsize=(6.4, 6), dpi=200, frameon=False)
ax = fig.gca()
# 3 Circles, they produce different sized legend markers
ax.add_patch(plt.Circle((0,0), radius=1, alpha=0.9, zorder=0, label="Circle"))
ax.add_patch(plt.Circle((-1,0), radius=0.05, color="y", label="Point on Circle"))
ax.add_patch(plt.Circle((1, 0), radius=0.05, color="k", label="Opposite Point on Circle"))
# A vertical line which produces a huge legend marker
ax.axvline(0, ymin=0.5-0.313, ymax=0.5+0.313, linewidth=12, zorder=1, c="g", label="Vertical Line")
ax.legend(loc=2)
ax.set_xlim(-2,1.2) # The figsize and limits are meant to preserve the circle's shape
ax.set_ylim(-1.5, 1.5)
fig.show()
I've seen solutions including legend.legendHandles[0]._size or various assortments of that, and it doesn't seem to change the size regardless of the value I set
The legend markers for the circles are different in size because the first circle has no edgecolor, while the two other ones have an edgecolor set via color. You may instead set the facecolor of the circle. Alternatively, you can set the linewidth of all 3 circles equal.
The legend marker for the line is so huge because it simply copies the attribute from the line in the plot. If you want to use a different linewidth, you can update it via the respective legend handler.
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerLine2D
def update_prop(handle, orig):
handle.update_from(orig)
handle.set_linewidth(2)
fig, ax = plt.subplots(figsize=(6.4, 6), dpi=200, frameon=False)
# 3 Circles, set the facecolor instead of edge- and face-color
ax.add_patch(plt.Circle((0,0), radius=1, alpha=0.9, zorder=0, label="Circle"))
ax.add_patch(plt.Circle((-1,0), radius=0.05, facecolor="y", label="Point on Circle"))
ax.add_patch(plt.Circle((1, 0), radius=0.05, facecolor="k", label="Opposite Point on Circle"))
# Line, update the linewidth via
ax.axvline(0, ymin=0.5-0.313, ymax=0.5+0.313, linewidth=12, zorder=1, c="g", label="Vertical Line")
ax.legend(loc=2, handler_map={plt.Line2D:HandlerLine2D(update_func=update_prop)})
ax.set_xlim(-2,1.2)
ax.set_ylim(-1.5, 1.5)
plt.show()

How to invert color of seaborn heatmap colorbar

I use an heatmap to visualize a confusion matrix. I like the standard colors, but I would like to have 0s in light orange and highest values in dark purple.
I managed to do so only with another set of colors (light to dark violet), setting:
colormap = sns.cubehelix_palette(as_cmap=True)
ax = sns.heatmap(cm_prob, annot=False, fmt=".3f", xticklabels=print_categories, yticklabels=print_categories, vmin=-0.05, cmap=colormap)
But I want to keep these standard ones. This is my code and the image I get.
ax = sns.heatmap(cm_prob, annot=False, fmt=".3f", xticklabels=print_categories, yticklabels=print_categories, vmin=-0.05)
The default cmap is sns.cm.rocket. To reverse it set cmap to sns.cm.rocket_r
Using your code:
cmap = sns.cm.rocket_r
ax = sns.heatmap(cm_prob,
annot=False,
fmt=".3f",
xticklabels=print_categories,
yticklabels=print_categories,
vmin=-0.05,
cmap = cmap)
To expand on Ben's answer, you can do this with most if not any color map.
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
X = np.random.random((4, 4))
sns.heatmap(X,cmap="Blues")
plt.show()
sns.heatmap(X,cmap="Blues_r")
plt.show()
sns.heatmap(X,cmap="YlGnBu")
plt.show()
sns.heatmap(X,cmap="YlGnBu_r")
plt.show()
only add cmap="rocket_r" to sns.heatmap
cmap="rocket": is the default palette of heatmap
add_r: to reverse the colors of palette
ax = sns.heatmap(cm_prob, annot=False, fmt=".3f", xticklabels=print_categories, yticklabels=print_categories, vmin=-0.05,cmap="rocket_r")
we can now quickly achieve reverse color just by putting _r in the end.
For example: for viridis => viridis_r
sns.heatmap(corr_matrix, annot=True, cmap='viridis_r');
Did you try to invert the colormap?
sns.cubehelix_palette(as_cmap=True, reverse=True)

better piechart color scheme

I am trying to create a pie chart, as follows:
import matplotlib.pyplot as plt
import pandas as pd
# make a square figure and axes
plt.figure(1, figsize=(10,10))
plt.axes([0.01, 0.1, 0.6, 0.6])
# plt.style.use('fivethirtyeight')
# The slices will be ordered and plotted counter-clockwise.
labels = 'foo1', 'foo2', 'foo3', 'foo4'
fracs = pd.Series([10,30, 50,10],index=labels)
fracs.plot(kind='pie', labels=None, autopct='%1.0f%%')
plt.legend(bbox_to_anchor=(0.95, .9), loc=2, borderaxespad=0.,labels=labels)
plt.title('pie chart demo which should be center aligned not left', bbox={'facecolor':'0.8', 'pad':5})
plt.show()
Which is yeilding a piechart as:
But, I am facing two problem:
1) I dont like the color scheme. I would like a color scheme more inline with (I need 12 colors)
2) Titel is centered at the pie chart only. The legend is somehow out. I am trying to get the title centered over the chart and the legend.
Can someone kindly help?
I think that is a ggplot colorscheme that you are trying to emulate.
And your plt.axes command is what is displacing your chart to the left.
Try this:
import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.figure(1, figsize=(10,10))
labels = 'foo1', 'foo2', 'foo3', 'foo4'
sizes = [10,30, 50,10]
plt.pie(sizes, labels=labels)
plt.show()

Resources