Tick labels displaying outside axis limits - matplotlib

Is there a way to automatically not display tick mark labels if they would protrude past the axis itself? For example, consider the following code
#!/usr/bin/python
import pylab as P, numpy as N, math as M
xvals=N.arange(-10,10,0.1)
yvals=[ M.sin(x) for x in xvals ]
P.plot( xvals, yvals )
P.show()
See how the -10 and 10 labels on the x-axis poke out to the left and right of the plot? And similar for the -1.0 and 1.0 labels on the y-axis. Can I automatically suppress plotting these but retain the ones that do not go outside the plot limits?

I think you could just format the axis ticks yourself and then prune the ones
that are hanging over. The recommended way to deal with setting up the axis is
to use the ticker API. So for example
from matplotlib.ticker import MaxNLocator
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
xvals=np.arange(-10,10,0.1)
yvals=[ np.sin(x) for x in xvals ]
ax.plot( xvals, yvals )
ax.xaxis.set_major_locator(MaxNLocator(prune='both'))
plt.show()
Here we are creating a figure and axes, plotting the data, and then setting the xaxis
major ticks. The formatter MaxNLocator is given the
argument prune='both' which is described in the docs here.
This is not exactly what you were asking for, but maybe it will solve your problem.

Related

Pyplot axis limits within boundaries

Is there an easy way to avoid pyplot zooming far into noisy data?
Something like a lower boundary for the axis limits.
I am not trying to set a fix boundary to my axis, as this will fully disable automatic scaling.
Maybe a "minimum tick distance" would also work.
Right now I am using an additional 'invisible' plot in my graph that will define the maximum zoom.
Some example that illustrates what I want to achieve:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 100, 1)
noise = np.random.randn(len(x))*0.1
y = 10+noise
y_dummy_low = [0]*len(x)
y_dummy_high = [20]*len(x)
plt.figure()
plt.plot(x, y) # noise data i actually want to plot
plt.plot(x, y_dummy_low, y_dummy_high, marker="None", linestyle="None") # this will avoid zooming too much
plt.show()
Zooming too far
Zooming OK

Problem with text and annotation x and y coordinates changing while looping through subplots matplotlib

I would like to iterate through subplots, plot data, and annotate the subplots with either the text function or the annotation function in matplotlib. Both functions ask for x and y coordinates in order to place text or annotations. I can get this to work fine, until I plot data. Then the annotations and the text jump all over the place and I cannot figure out why.
My set up is something like this, which produces well-aligned annotations with no data:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
fig, ax=plt.subplots(nrows=3, ncols=3, sharex=True)
fig.suptitle('Axes ylim unpacking error demonstration')
annotation_colors=["red", "lightblue", "tan", "purple", "lightgreen", "black", "pink", "blue", "magenta"]
for jj, ax in enumerate(ax.flat):
bott, top = plt.ylim()
left, right = plt.xlim()
ax.text(left+0.1*(right-left), bott+0.1*(top-bott), 'Annotation', color=annotation_colors[jj])
plt.show
When I add random data (or my real data), the annotations jump:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
#Same as above but but with 9 random data frames plotted.
df_cols = ['y' + str(x) for x in range(1,10)]
df=pd.DataFrame(np.random.randint(0,10, size=(10,9)), columns=df_cols)
df['x']=range(0,10)
#Make a few columns much larger in terms of magnitude of mean values
df['y2']=df['y2']*-555
df['y5']=df['y5']*123
fig, ax=plt.subplots(nrows=3, ncols=3, sharex=True)
fig.suptitle('Axes ylim unpacking error demonstration')
annotation_colors=["red", "lightblue", "tan", "purple", "lightgreen", "black", "pink", "blue", "magenta"]
for jj, ax in enumerate(ax.flat):
ax.plot(df['x'], df['y'+str(jj+1)], color=annotation_colors[jj])
bott, top = plt.ylim()
left, right = plt.xlim()
ax.text(left+0.1*(right-left), bott+0.1*(top-bott), 'Annotation', color=annotation_colors[jj])
plt.show()
This is just to demonstrate the issue that is likely caused by my lack of understanding of how the ax and fig calls are working. It seems to me that the coordinates x and y of the ax.text call may actually apply to the coordinates of of the fig, or something similar. The end result is far worse with my actual data!!! In that case, some of the annotations end up miles above the actual plots and not even within the coordinates of any of the subplot axes. Others completely overlap! What I am misunderstanding?
Edit for more details:
I have tried Stef's solution of using axes coordinates of axes.text(0.1, 0.1, 'Annotation'...)
I get the following plot, which still shows the same problem of moving the text all over the place. Because I am running this example with random numbers, the annotations are moving randomly with every run - i.e. they are not just displaced in the subplots with different axis ranges (y2 and y5).
You can specify the text location in axes coordinates (as opposed to data coordinates as you did implicitely):
ax.text(.1, .1, 'Annotation', color=annotation_colors[jj], transform=ax.transAxes)
See the Transformations Tutorial for further information.

changing the size of subplots with matplotlib

I am trying to plot multiple rgb images with matplotlib
the code I am using is:
import numpy as np
import matplotlib.pyplot as plt
for i in range(0, images):
test = np.random.rand(1080, 720,3)
plt.subplot(images,2,i+1)
plt.imshow(test, interpolation='none')
the subplots appear tiny though as thumbnails
How can I make them bigger?
I have seen solutions using
fig, ax = plt.subplots()
syntax before but not with plt.subplot ?
plt.subplots initiates a subplot grid, while plt.subplot adds a subplot. So the difference is whether you want to initiate you plot right away or fill it over time. Since it seems, that you know how many images to plot beforehand, I would also recommend going with subplots.
Also notice, that the way you use plt.subplot you generate empy subplots in between the ones you are actually using, which is another reason they are so small.
import numpy as np
import matplotlib.pyplot as plt
images = 4
fig, axes = plt.subplots(images, 1, # Puts subplots in the axes variable
figsize=(4, 10), # Use figsize to set the size of the whole plot
dpi=200, # Further refine size with dpi setting
tight_layout=True) # Makes enough room between plots for labels
for i, ax in enumerate(axes):
y = np.random.randn(512, 512)
ax.imshow(y)
ax.set_title(str(i), fontweight='bold')

Customize the axis label in seaborn jointplot

I seem to have got stuck at a relatively simple problem but couldn't fix it after searching for last hour and after lot of experimenting.
I have two numpy arrays x and y and I am using seaborn's jointplot to plot them:
sns.jointplot(x, y)
Now I want to label the xaxis and yaxis as "X-axis label" and "Y-axis label" respectively. If I use plt.xlabel, the labels goes to the marginal distribution. How can I make them appear on the joint axes?
sns.jointplot returns a JointGrid object, which gives you access to the matplotlib axes and you can then manipulate from there.
import seaborn as sns
import numpy as np
# example data
X = np.random.randn(1000,)
Y = 0.2 * np.random.randn(1000) + 0.5
h = sns.jointplot(X, Y)
# JointGrid has a convenience function
h.set_axis_labels('x', 'y', fontsize=16)
# or set labels via the axes objects
h.ax_joint.set_xlabel('new x label', fontweight='bold')
# also possible to manipulate the histogram plots this way, e.g.
h.ax_marg_y.grid('on') # with ugly consequences...
# labels appear outside of plot area, so auto-adjust
h.figure.tight_layout()
(The problem with your attempt is that functions such as plt.xlabel("text") operate on the current axis, which is not the central one in sns.jointplot; but the object-oriented interface is more specific as to what it will operate on).
Note that the last command uses the figure attribute of the JointGrid. The initial version of this answer used the simpler - but not object-oriented - approach via the matplotlib.pyplot interface.
To use the pyplot interface:
import matplotlib.pyplot as plt
plt.tight_layout()
Alternatively, you can specify the axes labels in a pandas DataFrame in the call to jointplot.
import pandas as pd
import seaborn as sns
x = ...
y = ...
data = pd.DataFrame({
'X-axis label': x,
'Y-axis label': y,
})
sns.jointplot(x='X-axis label', y='Y-axis label', data=data)

Why does logarithmic scatter axis not start at zero?

I'm generating a scatter plot in matplotlib. Everything works fine if I use linear scales.
But since I'm mainly interested in the lower values, I thought I'd use logarithmic scaling. However, even though I have set my x-axis limits explicitly to (0,1), the axis starts at 0.1, so i miss everything below that!
Why does the logarithmic axis not start at zero, and how can I force it to?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,1,100)
y = np.random.randint(1000, size=100)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x, y)
ax.set_xlim(0,1.2)
ax.set_ylim(0,1000)
ax.set_yscale('log')
ax.set_xscale('log')
ax.yaxis.set_major_formatter(plt.FormatStrFormatter('%1.0f'))
ax.xaxis.set_major_formatter(plt.FormatStrFormatter('%.1f'))
ax.xaxis.set_minor_formatter(plt.FormatStrFormatter('%.1f'))
# this red line at x = 0.1
ax.axvline(x=0.1,color='r')
plt.show()
Any help is greatly appreciated!
Usually logarithmic axes never start at zero because there is no "good" value for log(0) on the x-axis, because log(0)==x only for x->-infinity.