matplotlib: How to remove ticks&tick values from secondary axis? - python

I have this code for a graph, and I do not want the values & ticks on the top and right axes.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
#Set axis labels
ax.set_xlabel('NEGATIVE')
ax.set_ylabel('HAPPY')
ax2 = ax.secondary_xaxis('top')
ax2.set_xlabel('POSITIVE')
ax2 = ax.secondary_yaxis('right')
ax2.set_ylabel('SAD')
#Remove ticks/values
ax.set_yticklabels([])
ax.set_xticklabels([])
ax.set_yticks([])
ax.set_xticks([])
ax2.set_yticklabels([])
ax2.set_xticklabels([])
ax2.set_yticks([])
ax2.set_xticks([])
#Show graph
plt.show()
it's showing it like this: image of graph

Use tick_params to manipulate the axis ticks and labels:
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
#Set axis labels
ax1.set_xlabel('NEGATIVE')
ax1.set_ylabel('HAPPY')
ax2 = ax1.secondary_xaxis('top')
ax2.set_xlabel('POSITIVE')
ax3 = ax1.secondary_yaxis('right')
ax3.set_ylabel('SAD')
#Remove ticks/values
for ax in (ax1, ax2, ax3):
ax.tick_params(left=False, labelleft=False, top=False, labeltop=False,
right=False, labelright=False, bottom=False, labelbottom=False)
#Show graph
plt.show()
A comment asked for how to only turn top and left ticks and labels off. This would be
for ax in (ax1, ax2, ax3):
ax.tick_params(top=False, labeltop=False, right=False, labelright=False)

Interesting why SecondaryAxis doesn't accept tick params, however let's use twinx and twiny:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
#Set axis labels
ax.set_xlabel('NEGATIVE')
ax.set_ylabel('HAPPY')
ax2x = ax.twiny()
ax2.set_yticks([])
ax2x.set_xlabel('POSITIVE')
ax2y = ax.twinx()
ax2y.set_ylabel('SAD')
ax2x.set_xticks([])
ax2y.set_yticks([])
#Show graph
plt.show()
Output:

Related

Adjusting legend layout for multiple legends associated to one Python plot?

I am creating a Python plot from a dataframe with 3 y-axes. For each y-axis, there are multiple y-values I want to plot. All data sets for the y-axes are plotted against a shared Date x-axis.
The code looks as follows:
df = pd.read_excel (r'test.xlsx', sheet_name='test', engine='openpyxl')
fig, ax = plt.subplots()
ax3 = ax.twinx()
rspine = ax3.spines['right']
rspine.set_position(('axes', 1.15))
ax3.set_frame_on(True)
ax3.patch.set_visible(False)
fig.subplots_adjust(right=0.7)
ax.plot(df['Date'], df['Gas1'], label="Gas1", color='g')
ax.plot(df['Date'], df['Gas2'], label="Gas2", color='b')
ax.plot(df['Date'], df['Gas3'], label="Gas3", marker="o", markersize=2, color='r')
ax.set_xlabel("Date")
ax.set_ylabel("Gas Rate")
ax2 = ax.twinx()
ax2.plot(df['Date'], df['Water1'], label="Water1", color='k')
ax2.plot(df['Date'], df['Water2'], label="Water2", color='y')
ax2.set_ylabel("Water")
ax3.plot(df['Date'], df['Pressure1'], label="Pressure1")
ax3.plot(df['Date'], df['Pressure2'], label="Pressure2")
ax3.set_ylabel("Pressure")
ax.legend()
ax2.legend()
ax3.legend()
plt.show()
The problem I am having is that I want the legends to be outside of the plot, preferably on the right-hand side after the 2nd y-axis. Is this possible? Right now the legends are just overlayed on the plot and not fully visible. I have tried using bbox_to_anchor and loc functions but had no luck. Thank you!
ax.get_legend_handles_labels() collects all the legend handles and their labels. Combining those for each of the axes, a new legend can be created.
bbox_to_anchor= sets an anchor point for the legend, using axes coordinates. loc= needs to be set, to tell which point of the legend's box will get fixed by the anchor.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.DataFrame({'Date': pd.date_range('20210401', periods=30, freq='D'),
'Gas1': np.random.randn(30).cumsum(),
'Gas2': np.random.randn(30).cumsum(),
'Gas3': np.random.randn(30).cumsum(),
'Water1': np.random.randn(30).cumsum(),
'Water2': np.random.randn(30).cumsum(),
'Pressure1': np.random.randn(30).cumsum(),
'Pressure2': np.random.randn(30).cumsum()})
fig, ax = plt.subplots()
ax3 = ax.twinx()
rspine = ax3.spines['right']
rspine.set_position(('axes', 1.15))
ax3.set_frame_on(True)
ax3.patch.set_visible(False)
fig.subplots_adjust(right=0.7)
ax.plot(df['Date'], df['Gas1'], label="Gas1", color='g')
ax.plot(df['Date'], df['Gas2'], label="Gas2", color='b')
ax.plot(df['Date'], df['Gas3'], label="Gas3", marker="o", markersize=2, color='r')
ax.set_ylabel("Gas Rate")
plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
ax2 = ax.twinx()
ax2.plot(df['Date'], df['Water1'], label="Water1", color='k')
ax2.plot(df['Date'], df['Water2'], label="Water2", color='y')
ax2.set_ylabel("Water")
ax3.plot(df['Date'], df['Pressure1'], label="Pressure1")
ax3.plot(df['Date'], df['Pressure2'], label="Pressure2")
ax3.set_ylabel("Pressure")
handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
handles3, labels3 = ax3.get_legend_handles_labels()
ax.legend(handles=handles1 + handles2 + handles3,
labels=labels1 + labels2 + labels3,
bbox_to_anchor=(1.28, 1.02), loc='upper left')
plt.tight_layout()
plt.show()

Color all tick labels in scientific notation

I want to color the tick labels of my left vertical axis. However, the following code:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1,5,10],[1,5,10])
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlim([1e0,1e1])
ax.set_ylim([1e0,1e1])
ax.yaxis.label.set_color('b')
ax.spines['left'].set_edgecolor('b')
ax.tick_params(axis='y', colors='b')
plt.savefig('test.png')
plt.show()
fails to color all lables:
Use
ax.tick_params(axis='y', colors='b', which='both')
where both corresponds to the major as well as the minor ticks.
Output

Plotting grids across the subplots Python matplotlib

I have tried the following:
d = [1,2,3,4,5,6,7,8,9]
f = [0,1,0,0,1,0,1,1,0]
fig = plt.figure()
fig.set_size_inches(30,10)
ax1 = fig.add_subplot(211)
line1 = ax1.plot(d,marker='.',color='b',label="1 row")
ax2 = fig.add_subplot(212)
line1 = ax2.plot(f,marker='.',color='b',label="1 row")
ax1.grid()
ax2.grid()
plt.show()
I got the following output :
But I was expecting the following output:
How I can get the grids across the two plots?
There is no built-in option to create inter-subplot grids. In this case I'd say an easy option is to create a third axes in the background with the same grid in x direction, such that the gridline can be seen in between the two subplots.
import matplotlib.pyplot as plt
d = [1,2,3,4,5,6,7,8,9]
f = [0,1,0,0,1,0,1,1,0]
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True)
ax3 = fig.add_subplot(111, zorder=-1)
for _, spine in ax3.spines.items():
spine.set_visible(False)
ax3.tick_params(labelleft=False, labelbottom=False, left=False, right=False )
ax3.get_shared_x_axes().join(ax3,ax1)
ax3.grid(axis="x")
line1 = ax1.plot(d, marker='.', color='b', label="1 row")
line1 = ax2.plot(f, marker='.', color='b', label="1 row")
ax1.grid()
ax2.grid()
plt.show()
Here is my solution:
import matplotlib.pyplot as plt
x1 = [1,2,3,4,5,6,7,8,9]
x2= [0,1,0,0,1,0,1,1,0]
x3= range(-10,0)
# frameon=False removes frames
# fig, (ax1,ax2, ax3) = plt.subplots(nrows=3, sharex=True, subplot_kw=dict(frameon=False))
fig, (ax1,ax2, ax3) = plt.subplots(nrows=3, sharex=True)
# remove vertical gap between subplots
plt.subplots_adjust(hspace=.0)
ax1.grid()
ax2.grid()
ax3.grid()
ax1.plot(x1)
ax2.plot(x2)
ax3.plot(x3)
Without frames subplot_kw=dict(frameon=False):
An option is to create a single plot then just offset the data. So one set plots above the other.

How to Return a MatPlotLib Figure with its corresponding Legend

I'm trying to define a function which returns a pre-styled figure with certain grid, style, width and other properties. However, when I return the fig and its axes, the legend is missing. Here's a simplified example:
def getfig():
plt.style.use('default')
fig, axs = plt.subplots(1, 1, figsize=(1,1), sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=[axs]
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
ax.legend(loc=9, bbox_to_anchor=(0.5, -0.3), ncol=2)
return fig,axs
fig,axs=getfig()
axs[0].plot(range(10), label="label")
What am I missing?
Thanks!
UPDATE:
This is what I'm using so far but I think there really should be a way to force all future legends associated to a figure to have a certain style.
def fig_new(rows=1,columns=1,figsize=(1,1)):
plt.style.use('default')
fig, axs = plt.subplots(rows,columns, figsize=figsize, sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=[axs]
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
return fig,axs
def fig_leg(fig):
for ax in fig.get_axes():
ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.3), ncol=5)
fig,axs=fig_new()
axs[0].plot(range(10), label="label")
fig_leg(fig)
You need to call the legend after an artist with a label is plotted to the axes.
An option is to let the function return the arguments to use for the legend afterwards.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
def getfig():
plt.style.use('default')
fig, axs = plt.subplots(1, 1, figsize=(1,1), sharey=False)
if issubclass(type(axs),mpl.axes.SubplotBase):
axs=np.array([axs])
legendkw = []
for ax in axs:
ax.grid(color='grey', axis='both', linestyle='-.', linewidth=0.4)
legendkw.append(dict(loc=9, bbox_to_anchor=(0.5, -0.3), ncol=2))
return fig,axs,legendkw
fig,axs,kw=getfig()
axs[0].plot(range(10), label="label")
for i,ax in enumerate(axs.flat):
ax.legend(**kw[i])
plt.show()

Python: Suplots with secondary-axis

I wrote the following code below to do the following graph:
fig, ax = plt.subplots(figsize=(8, 6))
ax.patch.set_facecolor('white')
ax.plot(df.index, df.X1.values, 'b',
label='NMA', linewidth=1.5)
ax.set_ylabel('Index')
ax2 = ax.twinx()
ax2.plot(df.index, df.Y.values, 'r--',
label='Rate', linewidth=1.5)
ax2.set_ylabel('Rate')
lines = ax.get_lines() + ax2.get_lines()
lgd = ax.legend(lines, [line.get_label() for line in lines],
loc='lower center', ncol=2, bbox_to_anchor=(0.5, -0.15),
frameon=False)
ax.set_title('Economic Rate and Index',
weight='bold')
for i in range(5):
plt.axvspan(Dates['Peak'][i], Dates['Trough'][i],
facecolor='grey', alpha=0.5)
plt.grid(False)
plt.savefig('C:\\test.pdf',
bbox_extra_artists=(lgd,), bbox_inches='tight')
I am having a hard time to reproduce this figure in a subplot (2X2). The only thing I would change in each of the subplots is the blue line (X1 in df... for X2, X3...). How can I have a 2X2 subplot of the above graph? Of Course I would only keep one legend at the bottom of the subplots. Thanks for the help.
The data is here and the "Dates" to reproduce the gray bars here.
This is how you could create a 2x2 raster with twinx each:
import matplotlib.pyplot as plt
fig, ((ax1a, ax2a), (ax3a, ax4a)) = plt.subplots(2, 2)
ax1b = ax1a.twinx()
ax2b = ax2a.twinx()
ax3b = ax3a.twinx()
ax4b = ax4a.twinx()
ax1a.set_ylabel('ax1a')
ax2a.set_ylabel('ax2a')
ax3a.set_ylabel('ax3a')
ax4a.set_ylabel('ax4a')
ax1b.set_ylabel('ax1b')
ax2b.set_ylabel('ax2b')
ax3b.set_ylabel('ax3b')
ax4b.set_ylabel('ax4b')
plt.tight_layout()
plt.show()
Result:

Categories