Here is my question:
I plot 7 variable's coefficient using sns.clustermap()
x/y tickslabel seems really small(In my case, s1,s2,... s9)
My attempt
label='big ==> no effect
plt.tick_params(axis='both', which='minor', labelsize=12) ===> cbar label has changed, but the x/y axes looks the same.
Add
My code:
ds = pd.read_csv("xxxx.csv")
corr = ds.corr().mul(100).astype(int)
cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True)
sns.clustermap(data=corr_s, annot=True, fmt='d',cmap = "Blues",annot_kws={"size": 16},)
Consider calling sns.set(font_scale=1.4) before plotting your data. This will scale all fonts in your legend and on the axes.
My plot went from this,
To this,
Of course, adjust the scaling to whatever you feel is a good setting.
Code:
sns.set(font_scale=1.4)
cmap = sns.diverging_palette(h_neg=210, h_pos=350, s=90, l=30, as_cmap=True)
sns.clustermap(data=corr, annot=True, fmt='d', cmap="Blues", annot_kws={"size": 16})
Or just use the set_xticklabels:
g = sns.clustermap(data=corr_s, annot=True, fmt='d',cmap = "Blues")
g.ax_heatmap.set_xticklabels(g.ax_heatmap.get_xmajorticklabels(), fontsize = 16)
To get different colors for the ticklabels:
import matplotlib.cm as cm
colors = cm.rainbow(np.linspace(0, 1, corr_s.shape[0]))
for i, ticklabel in enumerate(g.ax_heatmap.xaxis.get_majorticklabels()):
ticklabel.set_color(colors[i])
Related
How can I set the labels on the extra axes?
The ticks and labels should be the same on all 4 axes. I'm doing something wrong... Thanks!
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
plt.figure(figsize=(5,5))
f, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax3 = ax1.twiny()
plt.show()
# create reusable ticks and labels
ticks = [0,1/2,3.14159/4,3.14159/2,1]
labels = [r"$0$", r"$\displaystyle\frac{1}{2}$", r"$\displaystyle\frac{\pi}{4}$", r"$\displaystyle\frac{\pi}{2}$", r"$1$"]
# Version 1: twinx() + xaxis.set_ticks()
plt.figure(figsize=(5,5))
f, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax3 = ax1.twiny()
ax1.xaxis.set_ticks(ticks, labels=labels)
ax1.yaxis.set_ticks(ticks, labels=labels)
ax2.xaxis.set_ticks(ticks, labels=labels)
ax3.yaxis.set_ticks(ticks, labels=labels)
plt.show()
# Version 2: twinx() + set_xticklabels)()
plt.figure(figsize=(5,5))
f, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax3 = ax1.twiny()
ax1.set_xticks(ticks)
ax1.set_xticklabels(labels)
ax1.set_yticks(ticks)
ax1.set_yticklabels(labels)
ax2.set_xticks(ticks)
ax2.set_xticklabels(labels)
ax3.set_yticks(ticks)
ax3.set_yticklabels(labels)
plt.show()
Confused: How come ax1 has both xaxis and yaxis, while ax2, ax3 do not appear to?
A unintuitive solution based on matplotlib.axes.Axes.twinx:
Create a new Axes with an invisible x-axis and an independent y-axis
positioned opposite to the original one (i.e. at right).
This means unintuitively (at least for me) you have to switch x/y at the .twin call.
unintuitively not concerning the general matplotlib twinx functionality, but concerning such a manual ticks and label assignment
To highlight that a bit more I used ax2_x and ax3_y in the code.
Disclaimer: Not sure if that will break your plot intention when data is added.
Probably at least you have to take special care with the data assignment to those twin axes - keeping that "axis switch" in mind.
Also keep that axis switch" in mind when assigning different ticks and labels to the x/y axis.
But for now I think that's the plot you were looking for:
Code:
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
# create reusable ticks and labels
ticks = [0,1/2,3.14159/4,3.14159/2,1]
labels = [r"$0$", r"$\displaystyle\frac{1}{2}$", r"$\displaystyle\frac{\pi}{4}$", r"$\displaystyle\frac{\pi}{2}$", r"$1$"]
plt.figure(figsize=(5,5))
f, ax1 = plt.subplots()
ax1.xaxis.set_ticks(ticks, labels=labels)
ax1.yaxis.set_ticks(ticks, labels=labels)
ax2_x = ax1.twiny() # switch
ax3_y = ax1.twinx() # switch
ax2_x.xaxis.set_ticks(ticks, labels=labels)
ax3_y.yaxis.set_ticks(ticks, labels=labels)
plt.show()
Or switch the x/yaxis.set_ticks - with the same effect:
On second thought, I assume that's the preferred way to do it, especially when data comes into play.
ax2_x = ax1.twinx()
ax3_y = ax1.twiny()
ax2_x.yaxis.set_ticks(ticks, labels=labels) # switch
ax3_y.xaxis.set_ticks(ticks, labels=labels) # switch
In case you don't intend to use the twin axis functionality (that means having different data with different scaling assigned to those axis) but 'only' want the ticks and labels on all 4 axis for better plot readability:
Solution based on answer of ImportanceOfBeingErnest with the same plot result:
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
# create reusable ticks and labels
ticks = [0,1/2,3.14159/4,3.14159/2,1]
labels = [r"$0$", r"$\displaystyle\frac{1}{2}$", r"$\displaystyle\frac{\pi}{4}$", r"$\displaystyle\frac{\pi}{2}$", r"$1$"]
plt.figure(figsize=(5,5))
f, ax1 = plt.subplots()
ax1.xaxis.set_ticks(ticks, labels=labels)
ax1.yaxis.set_ticks(ticks, labels=labels)
ax1.tick_params(axis="x", bottom=True, top=True, labelbottom=True, labeltop=True)
ax1.tick_params(axis="y", left=True, right=True, labelleft=True, labelright=True)
plt.show()
ax2 = ax1.twinx() shares the x-axis with ax1.
ax3 = ax1.twiny() shares the y-axis with ax1.
As a result, the two lines where you set ax2.xaxis and ax3.yaxis's ticks and ticklabels are redundant with the changes you already applied on ax1.
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = False # My computer doesn't have LaTeX, don't mind me.
# Create reusable ticks and labels.
ticks = [0, 1/2, 3.14159/4, 3.14159/2, 1]
labels = [r"$0$", r"$\frac{1}{2}$", r"$\frac{\pi}{4}$", r"$\frac{\pi}{2}$", r"$1$"]
# Set the ticks and ticklabels for each axis.
fig = plt.figure(figsize=(5,5))
ax1 = fig.add_subplot()
ax2 = ax1.twinx()
ax3 = ax1.twiny()
for axis in (ax1.xaxis,
ax1.yaxis,
ax2.yaxis,
ax3.xaxis):
axis.set_ticks(ticks)
axis.set_ticklabels(labels)
fig.show()
Notice that if I comment out the work on ax2 and ax3, we get exactly what you have in your question:
for axis in (ax1.xaxis, ax1.yaxis,
# ax2.yaxis,
# ax3.xaxis,
):
axis.set_ticks(ticks)
axis.set_ticklabels(labels)
Now let's ruin ax1 via modifications on ax2, just to show that the bound between twins works well:
ax2.xaxis.set_ticks(range(10))
ax2.xaxis.set_ticklabels(tuple("abcdefghij"))
How do I use a single legend for multiple geopandas plots?
Right now I have a Figure like this:
This post explains how to set legend values to the same for each plot. Though, i would like to have single legend for all plots. Optimally it should be possible to have multiple legends for different df's that I want to plot. E.g. the lines you see in the pictures also have a description.
Here is my current code:
years = [2005, 2009, 2013]
# initialize figure
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(10, 10), dpi=300, constrained_layout=True)
for i, year in enumerate(years):
# subset lines
lines_plot = lines[lines['year'] == year]
# subset controls plot
controls_plot = controls[controls['year'] == year]
# draw subfig
controls_plot.plot(column='pop_dens', ax=ax[i], legend=True, legend_kwds={'orientation': "horizontal"})
lines_plot.plot(ax=ax[i], color='red', lw=2, zorder=2)
Regarding the first of your questions 'How do I use a single legend for multiple geopandas plots?' you could make sure your plots all use the same colors (using the vmin and vmax args of the .plot() function) and then add a single colorbar to the figure like shown below. for the red lines you can just add another legend (the first thing is technically a colorbar not a legend).
import geopandas as gpd
from matplotlib import pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as mcolors
from matplotlib.lines import Line2D
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
f, ax = plt.subplots(nrows=1, ncols=3, figsize=(9, 4))
# define min and max values and colormap for the plots
value_min = 0
value_max = 1e7
cmap = 'viridis'
world.plot(ax=ax[0], column='pop_est', vmin=value_min, vmax=value_max, cmap=cmap)
world.plot(ax=ax[1], column='pop_est', vmin=value_min, vmax=value_max, cmap=cmap)
world.plot(ax=ax[2], column='pop_est', vmin=value_min, vmax=value_max, cmap=cmap)
# define a mappable based on which the colorbar will be drawn
mappable = cm.ScalarMappable(
norm=mcolors.Normalize(value_min, value_max),
cmap=cmap
)
# define position and extent of colorbar
cb_ax = f.add_axes([0.1, 0.1, 0.8, 0.05])
# draw colorbar
cbar = f.colorbar(mappable, cax=cb_ax, orientation='horizontal')
# add handles for the legend
custom_lines = [
Line2D([0], [0], color='r'),
Line2D([0], [0], color='b'),
]
# define labels for the legend
custom_labels = ['red line', 'blue line']
# plot legend, loc defines the location
plt.legend(
handles=custom_lines,
labels=custom_labels,
loc=(.4, 1.5),
title='2nd legend',
ncol=2
)
plt.tight_layout()
plt.show()
I would like to make a paired histogram like the one shown here using the seaborn distplot.
This kind of plot can also be referred to as the back-to-back histogram shown here, or a bihistogram inverted/mirrored along the x-axis as discussed here.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
green = np.random.normal(20,10,1000)
blue = np.random.poisson(60,1000)
fig, ax = plt.subplots(figsize=(8,6))
sns.distplot(blue, hist=True, kde=True, hist_kws={'edgecolor':'black'}, kde_kws={'linewidth':2}, bins=10, color='blue')
sns.distplot(green, hist=True, kde=True, hist_kws={'edgecolor':'black'}, kde_kws={'linewidth':2}, bins=10, color='green')
ax.set_xticks(np.arange(-20,121,20))
ax.set_yticks(np.arange(0.0,0.07,0.01))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.show()
Here is the output:
When I use the method discussed here (plt.barh), I get the bar plot shown just below, which is not what I am looking for.
Or maybe I haven't understood the workaround well enough...
A simple/short implementation of python-seaborn-distplot similar to these kinds of plots would be perfect. I edited the figure of my first plot above to show the kind of plot I hope to achieve (though y-axis not upside down):
Any leads would be greatly appreciated.
You could use two subplots and invert the y-axis of the lower one and plot with the same bins.
df = pd.DataFrame({'a': np.random.normal(0,5,1000), 'b': np.random.normal(20,5,1000)})
fig =plt.figure(figsize=(5,5))
ax = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
bins = np.arange(-20,40)
ax.hist(df['a'], bins=bins)
ax2.hist(df['b'],color='orange', bins=bins)
ax2.invert_yaxis()
edit:
improvements suggested by #mwaskom
fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True, figsize=(5,5))
bins = np.arange(-20,40)
for ax, column, color, invert in zip(axes.ravel(), df.columns, ['teal', 'orange'], [False,True]):
ax.hist(df[column], bins=bins, color=color)
if invert:
ax.invert_yaxis()
plt.subplots_adjust(hspace=0)
Here is a possible approach using seaborn's displots.
Seaborn doesn't return the created graphical elements, but the ax can be interrogated. To make sure the ax only contains the elements you want upside down, those elements can be drawn first. Then, all the patches (the rectangular bars) and the lines (the curve for the kde) can be given their height in negative. Optionally the x-axis can be set at y == 0 using ax.spines['bottom'].set_position('zero').
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
green = np.random.normal(20, 10, 1000)
blue = np.random.poisson(60, 1000)
fig, ax = plt.subplots(figsize=(8, 6))
sns.distplot(green, hist=True, kde=True, hist_kws={'edgecolor': 'black'}, kde_kws={'linewidth': 2}, bins=10,
color='green')
for p in ax.patches: # turn the histogram upside down
p.set_height(-p.get_height())
for l in ax.lines: # turn the kde curve upside down
l.set_ydata(-l.get_ydata())
sns.distplot(blue, hist=True, kde=True, hist_kws={'edgecolor': 'black'}, kde_kws={'linewidth': 2}, bins=10,
color='blue')
ax.set_xticks(np.arange(-20, 121, 20))
ax.set_yticks(np.arange(0.0, 0.07, 0.01))
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
pos_ticks = np.array([t for t in ax.get_yticks() if t > 0])
ticks = np.concatenate([-pos_ticks[::-1], [0], pos_ticks])
ax.set_yticks(ticks)
ax.set_yticklabels([f'{abs(t):.2f}' for t in ticks])
ax.spines['bottom'].set_position('zero')
plt.show()
Here is an example that reproduces my problem:
import matplotlib.pyplot as plt
import numpy as np
data1,data2,data3,data4 = np.random.random(100),np.random.random(100),np.random.random(100),np.random.random(100)
fig,ax = plt.subplots()
ax.plot(data1)
ax.plot(data2)
ax.plot(data3)
ax2 = ax.twinx()
ax2.plot(data4)
plt.grid('on')
ax.legend(['1','2','3'], loc='center')
ax2.legend(['4'], loc=1)
How can I get the legend in the center to plot on top of the lines?
To get exactly what you have asked for, try the following. Note I have modified your code to define the labels when you generate the plot and also the colors so you don't get a repeated blue line.
import matplotlib.pyplot as plt
import numpy as np
data1,data2,data3,data4 = (np.random.random(100),
np.random.random(100),
np.random.random(100),
np.random.random(100))
fig,ax = plt.subplots()
ax.plot(data1, label="1", color="k")
ax.plot(data2, label="2", color="r")
ax.plot(data3, label="3", color="g")
ax2 = ax.twinx()
ax2.plot(data4, label="4", color="b")
# First get the handles and labels from the axes
handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
# Add the first legend to the second axis so it displaysys 'on top'
first_legend = plt.legend(handles1, labels1, loc='center')
ax2.add_artist(first_legend)
# Add the second legend as usual
ax2.legend(handles2, labels2)
plt.show()
Now I will add that it would be clearer if you just use a single legend adding all the lines to that. This is described in this SO post and in the code above can easily be achieved with
ax2.legend(handles1+handles2, labels1+labels2)
But obviously you may have your own reasons for wanting two legends.
I am trying to use a colorbar to label discrete, coded values plotted using imshow. I can achieve the colorbar that I want using the boundaries and values keywords, which makes the maximum value of the colorbar effectively 1 greater than the maximum value of the data being plotted.
Now I want ticks to be in the middle of each color range in the colorbar, but cannot specify a tick position for the largest color block in the colorbar, seemingly because it is outside of the data value limits.
Here's a quick block of code to demonstrate the problem:
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
ax.imshow(data[None], aspect='auto')
cax = fig.add_subplot(122)
cbar = fig.colorbar(ax.images[0], cax=cax, boundaries=[0,1,2,3,4], values=[0,1,2,3])
cbar.set_ticks([.5, 1.5, 2.5, 3.5])
cbar.set_ticklabels(['one', 'two', 'three', 'four'])
Note the missing tick where 'four' should be. What's the right way to do this?
To summarize, this works for me:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import cm
from matplotlib import colors
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cmap = cm.get_cmap('jet', 4)
bounds = np.arange(5)
vals = bounds[:-1]
norm = colors.BoundaryNorm(bounds, cmap.N)
ax.imshow(data[None], aspect='auto', interpolation='nearest', cmap=cmap, norm=norm)
cax = fig.add_subplot(122)
cbar = fig.colorbar(ax.images[0], cax=cax, boundaries=bounds, values=vals)
cbar.set_ticks(vals + .5)
cbar.set_ticklabels(['one', 'two', 'three', 'four'])
The solution was to specify the colormap explicitly for the image using get_cmap and bounded by BoundaryNorm. Then specifying the tick positions just works. The resulting plot is:
You are not using the same colormap in imshow and cbar. As your data and cbar is defined in the same way (same limits etc.) so you do not realize the inconsistency in the above example. You should define the colormap first.
Let's say you want to divide your data into 4-discrete colors, then you can use
import numpy as np
import pylab as plt
from matplotlib import colors, cm
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cax = fig.add_subplot(122)
cmap = cm.get_cmap('jet', 4) # 4 discrete color
im=ax.imshow(data[None], aspect='auto',cmap=cmap)
cbar = fig.colorbar(ax.images[0], cax=cax, cmap=cmap)
plt.show()
You can now put the ticks according to your need.
In case you want to define the bounds as well as the colors in these bounds then you can use ListedColormap as follows:
data = np.tile(np.arange(4), 2)
fig = plt.figure()
ax = fig.add_subplot(121)
cax = fig.add_subplot(122)
cmap = colors.ListedColormap(['b','g','y','r'])
bounds=[0,1,2,3,4]
norm = colors.BoundaryNorm(bounds, cmap.N)
im=ax.imshow(data[None], aspect='auto',cmap=cmap, norm=norm)
cbar = fig.colorbar(im, cax=cax, cmap=cmap, norm=norm, boundaries=bounds, ticks=[0.5,1.5,2.5,3.5],)
plt.show()