I have a chart with data going below and above 0, and I want to have my x-axis with tick marks at y==0, while tick labels are still below the chart. Note that using axhline is not sufficient as I need tick marks. Also, there are workarounds on SO that use spines to put the top spine at 0, with tick marks, but in my case I would need to keep the spines add the top and bottom.
Is there a way to do this?
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(-2, 3))
plt.show()
Maybe this will help:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(-2, 3))
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.yaxis.tick_left()
ax.xaxis.tick_bottom()
ax.yaxis.set_label_coords(-0.1,0.5)
plt.show()
Related
This question already has answers here:
How to move tick labels off left spine
(2 answers)
Closed 3 years ago.
I am attempting to plot a distribution which is centred around zero, and as such I want to show the y-axis spine at 0, but I want to keep the tick labels themselves to the left of the graph (i.e. outside the plot area). I thought this might be achievable through tick_params, but the labelleft option seems to keep the labels in the centre. A short example is as follows:
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(1)
vals = np.random.normal(loc=0, scale=10, size=300)
bins = range(int(min(vals)), int(max(vals))+1)
fig, ax = plt.subplots(figsize=(15,5))
ax.hist(vals, bins=bins)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.grid(axis='y', which='major', alpha=0.5)
plt.show()
This gives you:
I would like the labels to be at the left end of the gridlines, rather than the centre of the plot.
Probably not the best solution, but you can set left spines invisible and draw a straight line at 0:
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.plot((0,0), (0,ax.get_ylim()[-1]),color='k',linewidth=1)
ax.grid(axis='y', which='major', alpha=0.5)
plt.show()
Output:
On possibility is to instruct the tick labels to use the "Axes coordinates" for their x position, and the "Data coordinates" for their y position. This implies changing their tranform property.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.transforms as transforms
np.random.seed(1)
vals = np.random.normal(loc=0, scale=10, size=300)
bins = range(int(min(vals)), int(max(vals))+1)
fig, ax = plt.subplots()
ax.hist(vals, bins=bins)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.grid(axis='y', which='major', alpha=0.5)
trans = transforms.blended_transform_factory(ax.transAxes,ax.transData)
plt.setp(ax.get_yticklabels(), 'transform', trans)
plt.show()
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
I have an issue where adding tick labels interferes with my given padding preference between subplots. What I want, is a tight_layout with no padding at all in between, but with some custom ticks along the x-axis. This snippet and resulting figures shows the issue:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig_names = ['fig1']
gs = gridspec.GridSpec(1, len(fig_names))
gs.update(hspace=0.0)
figs = dict()
for fig_name in fig_names:
figs[fig_name] = plt.figure(figsize=(3*len(fig_names),6))
for i in range(0,len(fig_names)):
ax = figs[fig_name].add_subplot(gs[i])
ax.plot([0,1],[0,1], 'r-')
if i != 0:
ax.set_yticks(list())
ax.set_yticklabels(list())
ax.set_xticks(list())
ax.set_xticklabels(list())
for name,fig in figs.items():
fig.text(0.5, 0.03, 'Common xlabel', ha='center', va='center')
gs.tight_layout(fig, h_pad=0.0, w_pad=0.0)
ax = fig.add_subplot(gs[len(fig_names)-1])
ax.legend(('Some plot'), loc=2)
plt.show()
By changing the corresponding lines into:
ax.set_xticks([0.5,1.0])
ax.set_xticklabels(['0.5','1.0'])
...unwanted padding is added to the graphs.
How can I customize the tick text so that the graph plots has no padding, regardless of what tick text I enter? The text may "overlap" with the next subplot.
Perhaps you could simply create the axes with plt.subplots:
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(ncols=2, sharey=True)
for ax in axs:
ax.plot([0,1],[0,1], 'r-')
ax.set_xticks([0.5,1.0])
ax.set_xticklabels(['0.5','1.0'])
axs[-1].legend(('Some plot'), loc=2)
for ax in axs[1:]:
ax.yaxis.set_visible(False)
fig.subplots_adjust(wspace=0)
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.
In the histogram produced with the following code, there's no x axis at the zero level
import matplotlib.pyplot as plt
plt.bar(left=[0,4,5],height=[-100,10,110],color=['red','green','green'],width=0.1)
plt.show()
How to put it there?
I tend to use spines to get the x-axis centered:
import matplotlib.pyplot as plt
fig = plt.figure(facecolor='white')
ax = fig.add_subplot(1,1,1)
ax.bar(left=[0,4,5],height=[-100,10,110],color=['red','green','green'],width=0.1)
ax.grid(b=True)
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
plt.show()
Which will produce the next plot:
By default matplotlib does not consider the y=0 line important. You can turn on the grid by a call such as plt.grid().
An alternative used often in the matplotlib.pylab docs is to set a horizontal line at 0. This is done by
plt.axhline(0, color='black', lw=2)