Plotting multiple series on a line/bar graph with pandas - python

I'm trying to make a plot of a line and bar on the same graph. I'm close, but I can't solve a few items. Here's what I have so far...
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.DataFrame({'Value1': np.arange(80, 180, 1),
'Value2': np.arange(1.5, .5, -0.01)},
index=np.arange(10, 110, 1))
fig, ax = plt.subplots(figsize=(10, 10))
data['Value1'].plot(ax=ax)
ax2 = ax.twinx()
data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3))
So the problems I have with this graph are...
The x-ticks look awful. If I only do a line graph, the x-ticks look fine. As soon as I add the twinx axis however, the major/minor ticks logic get's dropped. How can I keep that?
My x-axes is numeric. Note that the line intercepts the x-axis at the value "10" (its hard to see, but that's what's going on). I presume this is because the line's x-axis is supposed to begin at "10" and the bar's x-axis begins at 10 as well, but there's confusion of the value and label so the line's x-axis get's pushed over the label "20".
What's the best way to do this?

Bar plot and line plot has different X coordinate range is different, consider using two x coordinate.

you can try to save xticks and xtickslabels after data['Value1'].plot(ax=ax) and set them back after data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3)):
data['Value1'].plot(ax=ax)
xticks = ax.get_xticks()
xlabels = [x.get_text() for x in ax.get_xticklabels()]
ax2 = ax.twinx()
data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3))
ax.set_xticks(xticks)
ax.set_xticklabels(xlabels)
plt.show()

Related

How to set equal number of ticks for two subplots?

I have two subplots of horizontal bars done in matplotlib. For the first subplot, the number of y-axis ticks is appropriate, but I'm unable to figure out why specifying number of ticks for the second subplot is coming out to be wrong. This is the code:
import matplotlib.pyplot as plt
import numpy as np
# Plot separate subplots for genders
fig, (axes1, axes2) = plt.subplots(nrows=1, ncols=2,
sharex=False,
sharey=False,
figsize=(15,10))
labels = list(out.index)
x = ["20%", "40%", "60%", "80%", "100%"]
y = np.arange(len(out))
width = 0.5
axes1.barh(y, female_distr, width, color="olive",
align="center", alpha=0.8)
axes1.ticks_params(nbins=6)
axes1.set_yticks(y)
axes1.set_yticklabels(labels)
axes1.set_xticklabels(x)
axes1.yaxis.grid(False)
axes1.set_xlabel("Occurence (%)")
axes1.set_ylabel("Language")
axes1.set_title("Language Distribution (Women)")
axes2.barh(y, male_distr, width, color="chocolate",
align="center", alpha=0.8)
axes2.locator_params(nbins=6)
axes2.set_yticks(y)
axes2.set_yticklabels(labels)
axes2.set_xticklabels(x)
axes2.yaxis.grid(False)
axes2.set_xlabel("Occurence (%)")
axes2.set_ylabel("Language")
axes2.set_title("Language Distribution (Men)")
The rest of the objects like out are simple data frames that I don't think need to be described here. The above code returns the following plot:
I would like the second subplot to have equal number of ticks but experimenting with nbins always results in either more or fewer ticks than the first subplot.
First, if you want your two plots to have the same x-axis, why not use sharex=True?
x_ticks = [0,20,40,60,80,100]
fig, (ax1,ax2) = plt.subplots(1,2, sharex=True)
ax1.set_xticks(x_ticks)
ax1.set_xticklabels(['{:.0f}%'.format(x) for x in x_ticks])
ax1.set_xlim(0,100)
ax1.grid(True, axis='x')
ax2.grid(True, axis='x')

Seaborn: gridlines from secondary axis above data (with different ticks)

I am using seaborn and twinx to plot two lines in one figure. However, as replicated below, the blue line is below the horizontal line because it is overlayed by the second plot:
import seaborn as sns
import matplotlib.pyplot as plt
l1 = sns.lineplot(x=[0,1,2],y=[1,2,3],color="#0188A8")
ax1 = plt.gca()
ax2 = ax1.twinx()
l2 = sns.lineplot(x=[0,1,2], y=[100,200,300],color="#D42227")
plt.xlabel('Number of Selves',fontsize=13)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0,1,2])
ax2.set_yticks([100,200,300])
After doing some googling, I found this which was close, but didn't help. Trying out their solution, the axis ticks will get distorted, as both lines are plotted on the second plot:
ax1 = plt.gca()
ax2 = ax1.twinx()
l1 = sns.lineplot(x=[0,1,2],y=[1,2,3],color="#0188A8")
l2 = sns.lineplot(x=[0,1,2], y=[100,200,300],color="#D42227")
plt.xlabel('Number of Selves',fontsize=13)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0,1,2])
ax2.set_yticks([100,200,300])
My question is, how can the blue line be on top of the horizontal grid lines while maintaining the ticks to be at the same position as they are in the first picture?
You cannot easily obtain the desired effect because all the artists of ax2 are drawn above the artists of ax1, regardless of their respective z-order.
The only "good" solution that I can suggest, is, as you had found out, draw both lines on ax2, but you have to use the data transform of ax1 for the first line so that it matches the numbers on the left axis.
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
l1 = sns.lineplot(x=[0,1,2],y=[1,2,3],color="#0188A8", ax=ax2, transform=ax1.transData)
l2 = sns.lineplot(x=[0,1,2], y=[100,200,300],color="#D42227", ax=ax2)
ax1.set_xlabel('Number of Selves',fontsize=13)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0,1,2])
ax2.set_yticks([100,200,300])
ax1.set_ylim(-0.5,3.5)
Note that, because there are actually no data on ax1, you have to manually specify the y-axis limits, it won't autoscale for you.

Stripplot and lineplot weird result

When I use lineplot or stripplot it works well. But using both the median is shifted; I don't understand why! Thank you for your help.
sns.lineplot(x='quality', y='alcohol', data=df, estimator=np.median, err_style=None)
sns.stripplot(x='quality', y='alcohol', data=df, jitter=True, color='red', alpha=0.2, edgecolor='none')
stripplot
lineplot+stripplot
lineplot
What is happening here is that your first plot is creating an x axis with 0 to n range, and relabeling those x tick with a list of integers from 3 to n, then when the second chart or the stripplot plots on top of this x axis it is using the original number therefore xtick 3 for this new chart starts on labelled xtick 6. Hence the offset.
One way to do correct this is to create a xaxis with a predefined range and then plot both charts on this predefined scale, see example below:
import seaborn as sns
import matplotlib.pyplot as plt
x = [3,4,5,6,7,8]
y = [10, 12, 15, 18, 19, 26]
#First axes creates the error in graphing
fig, ax = plt.subplots(1,2)
sns.lineplot(x=x,y=y, ax=ax[0])
sns.stripplot(x=x, y=y, ax=ax[0])
#Second axes shows correction
xplot = range(len(x))
sns.lineplot(x=xplot,y=y, ax=ax[1])
sns.stripplot(x, y=y, ax=ax[1])
Output:

Use matplotlib: plot error bars on two y axes

I'd like to plot a series with x and y error bars, then plot a second series with x and y error bars on a second y axis all on the same subplot. Can this be done with matplotlib?
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.errorbar(voltage, dP, xerr=voltageU, yerr=dPU)
ax2 = plt.errorbar(voltage, current, xerr=voltageU, yerr=currentU)
plt.show()
Basically, I'd like to put ax2 on a second axis and have the scale on the right side.
Thanks!
twinx() is your friend for adding a secondary y-axis, e.g.:
import matplotlib.pyplot as pl
import numpy as np
pl.figure()
ax1 = pl.gca()
ax1.errorbar(np.arange(10), np.arange(10), xerr=np.random.random(10), yerr=np.random.random(10), color='g')
ax2 = ax1.twinx()
ax2.errorbar(np.arange(10), np.arange(10)+5, xerr=np.random.random(10), yerr=np.random.random(10), color='r')
There is not a lot of documentation except for:
matplotlib.pyplot.twinx(ax=None)
Make a second axes that shares the x-axis. The new axes will overlay ax (or the current axes if ax is None). The ticks for ax2 will be placed on the right, and the ax2 instance is returned.
I was struggling to share the x-axis, but thank you #Bart you saved me!
The simple solution is use twiny instead of twinx
ax1.errorbar(layers, scores_means[str(epoch)][h,:],np.array(scores_stds[str(epoch)][h,:]))
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_xlabel('depth', color='b')
ax1.tick_params('x', colors='b')
ax2 = ax1.twiny()
ax2.errorbar(hidden_dim, scores_means[str(epoch)][:,l], np.array(scores_stds[str(epoch)][:,l]))
ax2.set_xlabel('width', color='r')
ax2.tick_params('x', colors='r')
fig.tight_layout()
plt.show()

In matplotlib, how do you display an axis on both sides of the figure?

I want to draw a plot with matplotlib with axis on both sides of the plot, similar to this plot (the color is irrelevant to this question):
How can I do this with matplotlib?
Note: contrary to what is shown in the example graph, I want the two axis to be exactly the same, and want to show only one graph. Adding the two axis is only to make reading the graph easier.
You can use tick_params() (this I did in Jupyter notebook):
import matplotlib.pyplot as plt
bar(range(10), range(10))
tick_params(labeltop=True, labelright=True)
Generates this image:
UPD: added a simple example for subplots. You should use tick_params() with axis object.
This code sets to display only top labels for the top subplot and bottom labels for the bottom subplot (with corresponding ticks):
import matplotlib.pyplot as plt
f, axarr = plt.subplots(2)
axarr[0].bar(range(10), range(10))
axarr[0].tick_params(labelbottom=False, labeltop=True, labelleft=False, labelright=False,
bottom=False, top=True, left=False, right=False)
axarr[1].bar(range(10), range(10, 0, -1))
axarr[1].tick_params(labelbottom=True, labeltop=False, labelleft=False, labelright=False,
bottom=True, top=False, left=False, right=False)
Looks like this:
There are a couple of relevant examples in the online documentation:
Two Scales (seems to do exactly what you're asking for)
Dual Fahrenheit and Celsius
I've done this previously using the following:
# Create figure and initial axis
fig, ax0 = plt.subplots()
# Create a duplicate of the original xaxis, giving you an additional axis object
ax1 = ax.twinx()
# Set the limits of the new axis from the original axis limits
ax1.set_ylim(ax0.get_ylim())
This will exactly duplicate the original y-axis.
Eg:
ax = plt.gca()
plt.bar(range(3), range(1, 4))
plt.axhline(1.75, color="gray", ls=":")
twin_ax = ax.twinx()
twin_ax.set_yticks([1.75])
twin_ax.set_ylim(ax.get_ylim())

Categories