I'm using Seaborn to plot 3 ghaphs. I would like to know how could I align vertically different plots.
This is my plot so far:
And this is my code:
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
import seaborn as sns
import numpy as np
flatui = ["#636EFA", "#EF553B", "#00CC96", "#AB63FA"]
fig, ax = plt.subplots(figsize=(17, 7))
plot=sns.lineplot(ax=ax,x="number of weeks", y="avg streams", hue="year", data=df, palette=flatui)
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: '{:,.2f}'.format(x/1000) + 'K'))
plot.set(title='Streams trend')
plot.xaxis.set_major_locator(ticker.MultipleLocator(2))
fig, ax =plt.subplots(1,2, figsize=(17,7))
plot = sns.barplot(x="Artist", y="Releases", data = result.head(10), ax=ax[0])
plot.set_xticklabels(
plot.get_xticklabels(),
rotation=90,
horizontalalignment='center',
fontweight='light',
fontsize='x-large'
)
plot=sns.barplot(x="Artist", y="Streams", data = result.head(10), ax=ax[1])
plot.set_xticklabels(
plot.get_xticklabels(),
rotation=90,
horizontalalignment='center',
fontweight='light',
fontsize='x-large'
)
Basically I create a figure where I plot the trend graph and then a figure with 2 subplots where I plot my 2 bar plots.
What I would like to do is to align the trend plot and the 2 barplots. As you might notice on the left, the trend plot and the first barplot are not aligned, I would like to make the two figures start from the same point (like at the ending of the trend plot and the second barplot, where the 2 graphs are aligned).
How could I do that?
Here is a solution using GridSpec
fig = plt.figure()
gs0 = matplotlib.gridspec.GridSpec(2,2, figure=fig)
ax1 = fig.add_subplot(gs0[0,:])
ax2 = fig.add_subplot(gs0[1,0])
ax3 = fig.add_subplot(gs0[1,1])
sns.lineplot(ax=ax1, ...)
sns.barplot(ax=ax2, ...)
sns.barplot(ax=ax3, ...)
If you have the newest version of matplotlib, you can also use the new semantic figure composition engine
axd = plt.figure(constrained_layout=True).subplot_mosaic(
"""
AA
BC
"""
)
sns.lineplot(ax=axd['A'], ...)
sns.barplot(ax=axd['B'], ...)
sns.barplot(ax=axd['C'], ...)
Related
Let's use the classic example of weekly precipitation:
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt
from random import randint
data = {
'Week': [i for i in range(1,9)],
'Weekly Precipitation': [randint(1,10) for i in range(1,9)]
}
df = pd.DataFrame(data)
Let's also add a column with the cumulative precipitation:
df['Cumulative'] = df['Weekly Precipitation'].expanding(min_periods=2).sum()
Now, let's say I want a single chart with a barplot for the weekly precipitation, and a lineplot with the cumulative precipitation. So I do this:
fig, ax1 = plt.subplots(figsize=(10,5))
sns.barplot(x='Week', y='Weekly Precipitation', data=df, ax=ax1)
ax2 = ax1.twinx()
sns.lineplot(x='Week', y='Cumulative', data=df, ax=ax2)
Which yields this plot:
And you can see the problem: while both series are commensurate, both x axes use a different scale, which distorts the visualization, as the line should always be higher than the bars.
So, instead of twin axes, I'm trying to put both plot on the same axis:
fig, ax1 = plt.subplots(figsize=(10,5))
ax1.set_facecolor('white')
sns.barplot(x='Week', y='Weekly Precipitation', data=df, ax=ax1)
sns.lineplot(x='Week', y='Cumulative', data=df, ax=ax1)
ax1.set_ylabel('Precipitation')
Now, of course, the scale is right (although I have to do with a single y label), but... the second plot is shifted to the right by one tick!
How does that even make sense?!
Suppose I draw a plot using the code below. How to plot the rug part on the top edge of x-axis?
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.distplot(np.random.normal(0, 0.1, 100), rug=True, hist=False)
plt.show()
The seaborn.rugplot creates a LineCollection with the length of the lines being defined in axes coordinates. Those are always the same, such that the plot does not change if you invert the axes.
You can create your own LineCollection from the data though. The advantage compared to using bars is that the linewidth is in points and therefore no lines will be lost independend of the data range.
import numpy as np; np.random.seed(42)
import matplotlib.pyplot as plt
import seaborn as sns
def upper_rugplot(data, height=.05, ax=None, **kwargs):
from matplotlib.collections import LineCollection
ax = ax or plt.gca()
kwargs.setdefault("linewidth", 1)
segs = np.stack((np.c_[data, data],
np.c_[np.ones_like(data), np.ones_like(data)-height]),
axis=-1)
lc = LineCollection(segs, transform=ax.get_xaxis_transform(), **kwargs)
ax.add_collection(lc)
fig, ax = plt.subplots()
data = np.random.normal(0, 0.1, 100)
sns.distplot(data, rug=False, hist=False, ax=ax)
upper_rugplot(data, ax=ax)
plt.show()
Rugs are just thin lines at the data points. Yo can think of them as thin bars. That being said, you can have a following work around: Plot distplot without rugs and then create a twin x-axis and plot a bar chart with thin bars. Following is a working answer:
import numpy as np; np.random.seed(21)
import matplotlib.pyplot as plt
import seaborn as sns
fig, ax = plt.subplots()
data = np.random.normal(0, 0.1, 100)
sns.distplot(data, rug=False, hist=False, ax=ax)
ax1 = ax.twinx()
ax1.bar(data, height=ax.get_ylim()[1]/10, width=0.001)
ax1.set_ylim(ax.get_ylim())
ax1.invert_yaxis()
ax1.set_yticks([])
plt.show()
I have created the following plot using seaborn kdeplot and customizing the gridlines.
sns.set_style('whitegrid')
cdf_accuracy = sns.kdeplot(eval_df['accuracy'], cumulative=True)
cdf_accuracy.yaxis.set_major_locator(ticker.MultipleLocator(0.25))
cdf_accuracy.xaxis.set_major_locator(ticker.MultipleLocator(10))
However, I would like to show the gridlines on the x-axis just on the points were the y-axis gridlines intersect the plot. There is a way to do this?
Thanks for your answers
As long as your characteristic is monotonic, which should be given with a cumulative dataset, you could simply use interpolation on the y-values:
import numpy as np
y_intrsct = [.25, .5, .75]
x_intrsct = np.interp(y_intrsct, y_data, x_data)
which results in
array([67.69792378, 83.24194722, 92.24041857])
plotted with the following code:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(x_data, y_data)
ax.set_yticks(np.linspace(0, 1, 5))
ax.grid(axis='y')
ax.vlines(x_intrsct, *ax.get_ylim())
How to draw the following graph showing the difference against the average using matplotlib, searborn, Plotly or with any other framework?
I have found that some calls this plot Mean indexed bar chart. Using seaborn, it can be using a code like the following:
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="white", context="talk")
f, ax1 = plt.subplots(figsize=(7, 5), sharex=True)
mean = df.mean()
y2 = mean - df["your column"]
sns.barplot(x=dfCopy.index, y=y2, palette="deep", ax=ax1)
ax1.axhline(0, color="k", clip_on=False)
ax1.set_ylabel("Diverging")
# Finalize the plot
sns.despine(bottom=True)
plt.setp(f.axes, yticks=[])
plt.tight_layout(h_pad=2)
I have two different sets of data with a common index, and I want to represent the first one as a barplot and the second one as a lineplot in the same graph. My current approach is similar to the following.
ax = pt.a.plot(alpha = .75, kind = 'bar')
ax2 = ax.twinx()
ax2.plot(ax.get_xticks(), pt.b.values, alpha = .75, color = 'r')
And the result is similar to this
This image is really nice and almost right. My only problem is that ax.twinx() seems to create a new canvas on top of the previous one, and the white lines are clearly seen on top of the barplot.
Is there any way to plot this without including the white lines?
You can use twinx() method along with seaborn to create a seperate y-axis, one for the lineplot and the other for the barplot. To control the style of the plot (default style of seaborn is darkgrid), you can use set_style method and specify the preferred theme. If you set style=None it resets to white background without the gridlines. You can also try whitegrid. If you want to further customize the gridlines, you can do it on the axis level using the ax2.grid(False).
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
matplotlib.rc_file_defaults()
ax1 = sns.set_style(style=None, rc=None )
fig, ax1 = plt.subplots(figsize=(12,6))
sns.lineplot(data = df['y_var_1'], marker='o', sort = False, ax=ax1)
ax2 = ax1.twinx()
sns.barplot(data = df, x='x_var', y='y_var_2', alpha=0.5, ax=ax2)
You have to remove grid lines of the second axis. Add to the code ax2.grid(False). However y-ticks of the second axis will be not align to y-ticks of the first y-axis, like here:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(pd.Series(np.random.uniform(0,1,size=10)), color='g')
ax2 = ax1.twinx()
ax2.plot(pd.Series(np.random.uniform(0,17,size=10)), color='r')
ax2.grid(False)
plt.show()