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()
Related
I have some datasets that I'm visualizing in a scatter plot. I have a bunch of mean values, and a global mean. What I'm after, but cant really achieve,is to have a scatter plot that is centered in the plot, while also placing the origin at the global mean.
This is the code that defines the layout of the plot:
plt.figure(1)
plt.suptitle('Example')
plt.xlabel('x (pixels)')
plt.ylabel('y (pixels)')
ax = plt.gca()
ax.spines['left'].set_position('center')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('center')
ax.spines['top'].set_color('none')
ax.scatter(x_data, y_data, color=color, alpha=0.08, label=csv_file_name)
ax.plot(global_mean[0], global_mean[1], color='green',
marker='x', label='Global mean')
This produces the following plot (the ax.scatter() is called multiple times for each dataset, but it's not in the code above):
I've tried playing around with the ax.set_position() parameters but nothing have worked well so far. Is there a way to do what I'm after with matplotlib, or do I need to use some other plot library?
You can use the ax.spines() method to move them around.
import numpy as np
import random
import matplotlib.pyplot as plt
#generate some random data
x = np.linspace(1,2, 100)
y = [random.random() for _ in range(100)]
fig = plt.figure(figsize=(10,5))
# original plot
ax = fig.add_subplot(1,2,1)
ax.scatter(x, y)
# same plot, but with the spines moved
ax2 = fig.add_subplot(1,2,2)
ax2.scatter(x, y)
# move the left spine (y axis) to the right
ax2.spines['left'].set_position(('axes', 0.5))
# move the bottom spine (x axis) up
ax2.spines['bottom'].set_position(('axes', 0.5))
# turn off the right and top spines
ax2.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
plt.show()
How do I show a plot with twin axes such that the aspect of the top and right axes are 'equal'. For example, the following code will produce a square plot
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
But this changes as soon as you use the twinx function.
ax2 = ax.twinx()
ax2.set_ylim([0,2])
ax3 = ax.twiny()
ax3.set_xlim([0,2])
Using set_aspect('equal') on ax2 and ax3 seems to force it the the aspect of ax, but set_aspect(0.5) doesn't seem to change anything either.
Put simply, I would like the plot to be square, the bottom and left axes to run from 0 to 1 and the top and right axes to run from 0 to 2.
Can you set the aspect between two twined axes? I've tried stacking the axes:
ax3 = ax2.twiny()
ax3.set_aspect('equal')
I've also tried using the adjustable keyword in set_aspect:
ax.set_aspect('equal', adjustable:'box-forced')
The closest I can get is:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal', adjustable='box-forced')
ax.plot([0,1],[0,1])
ax2=ax.twinx()
ax3 = ax2.twiny()
ax3.set_aspect(1, adjustable='box-forced')
ax2.set_ylim([0,2])
ax3.set_xlim([0,2])
ax.set_xlim([0,1])
ax.set_ylim([0,1])
Which produces:
I would like to remove the extra space to the right and left of the plot
It seems overly complicated to use two different twin axes to get two independent set of axes. If the aim is to create one square plot with one axis on each side of the plot, you may use two axes, both at the same position but with different scales. Both can then be set to have equal aspect ratios.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
ax.plot([0,1],[0,1])
ax2 = fig.add_axes(ax.get_position())
ax2.set_facecolor("None")
ax2.set_aspect('equal')
ax2.plot([2,0],[0,2], color="red")
ax2.tick_params(bottom=0, top=1, left=0, right=1,
labelbottom=0, labeltop=1, labelleft=0, labelright=1)
plt.show()
I am struggling with matplotlib and padding on the x-axis together with a logarithmic scale (see the first picture).
Without a logarithmic scale, the padding applies nicely (see the second one).
Any suggestations how to get a padding between plot lines and the axis line in the bottom left corner so that one can see the points on the line?
Thanks.
The code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import *
from matplotlib.ticker import ScalarFormatter
style.use('fivethirtyeight')
fig, ax = plt.subplots()
T = np.array([2**x for x in range(0,7+1)])
opt1 = np.array([x for x in range(0,7+1)])
opt2 = np.array([x*2 for x in range(0,7+1)])
opt3 = np.array([x*4 for x in range(0,7+1)])
ax.grid(True)
xlabel("#nodes")
ylabel("time(s)")
legend(loc="best")
title(r"Node start times")
plt.xticks([2**x for x in range(0,7+1)])
plt.plot(T,opt1,"o-", label="opt1")
plt.plot(T,opt2, "s-", label="opt2")
plt.plot(T,opt3, "d-", label="opt2")
plt.legend(loc="upper left")
# This should be called after all axes have been added
plt.tight_layout()
plt.margins(0.05, 0.05)
# 1, 2, 4, ...
ax.set_xscale('log', basex=2)
ax.xaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter("%d"))
plt.show()
#savefig("plot_1.pdf")
This does not address your padding issue, but you could use clip_on=False to prevent the points from being cut off. It seems you also need to make sure they're above the axes using zorder
plt.plot(T,opt1,"o-", label="opt1", clip_on=False, zorder=10)
plt.plot(T,opt2, "s-", label="opt2", clip_on=False, zorder=10)
plt.plot(T,opt3, "d-", label="opt2", clip_on=False, zorder=10)
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.
This question already has answers here:
Remove xticks in a matplotlib plot?
(11 answers)
Closed 8 years ago.
I'm using subplots in matplotlib. Since all of my subplots have the same x-axis, I only want to label the x-axis on my bottom plot. How can I remove xtics from just one axis?
As pointed out here, the following works!
plt.tick_params(\
axis='x', # changes apply to the x-axis
which='both', # both major and minor ticks are affected
bottom='off', # ticks along the bottom edge are off
top='off', # ticks along the top edge are off
labelbottom='off') # labels along the bottom edge are off
Dan, if you've set up your plots in an OOP way using
import matplotlib.pyplot as plt
fig, ax_arr = subplots(3, 1, sharex=True)
then it should be easy to hide the x-axis labels using something like
plt.setp([a.get_xticklabels() for a in f.axes[:-1]], visible=False)
# or
plt.setp([a.get_xticklabels() for a in ax_arr[:-1]], visible=False)
But check out this link and some of the further down examples will prove useful.
Edit:
If you can't use plt.subplots(), I'm still assuming you can do
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.plot(x1, y1)
ax2.plot(x2, y2)
plt.setp(ax1.get_xticklabels(), visible=False)
If you have more than 2 subplots, such as
ax1 = fig.add_subplot(N11)
ax2 = fig.add_subplot(N12)
...
axN = fig.add_subplot(N1N)
plt.setp([a.get_xticklabels() for a in (ax1, ..., axN-1)], visible=False)