Markers on plot edges cut off in matplotlib [duplicate] - python

This question already has an answer here:
How to autoscale y axis in matplotlib? [duplicate]
(1 answer)
Closed 8 years ago.
I want to make a scatter plot using matplotlib. If I want to use any kind of marker the default plotting behavior of matplotlib cuts off the left half of the marker on the left side of the plot, and the right side of the marker on the right side of the plot. I was looking for the most automatic way of adding some extra space to the left and right side of the plot without adding extra tick labels, so my markers aren't cut off and it also doesn't look like there are x-tick labels that don't correspond to any points.
from matplotlib import pyplot as plt
import numpy as np
xx = np.arange(10)
yy = np.random.random( 10 )
plt.plot(xx, yy, 'o' )
This code results in a graph that looks like this:
I'd like full circles at x = 0 and x = 4.5, but I don't want any more tick labels, and I'd like to be the code to be as short and automatic as possible.

You will have to write some code to do this, but you wont need to know anything about your data in advance. In other words, xx can change and this still will work as you expect (I think).
Basically you'd like the x-tick labels that you have, but you don't like the limits. So write code to
save the ticks,
adjust the limits, and
reinstate the old ticks.
from matplotlib import pyplot as plt
import numpy as np
xx = np.arange(10)
np.random.seed(101)
yy = np.random.random( 10 )
plt.plot(xx, yy, 'o' )
xticks, xticklabels = plt.xticks()
# shift half a step to the left
# x0 - (x1 - x0) / 2 = (3 * x0 - x1) / 2
xmin = (3*xticks[0] - xticks[1])/2.
# shaft half a step to the right
xmax = (3*xticks[-1] - xticks[-2])/2.
plt.xlim(xmin, xmax)
plt.xticks(xticks)
plt.show()
This results in the following figure:
As you can see, you have the same issue for the y values, which you can correct following the same procedure.
Another option is turning off the clipping of the line, using the clip_on keyword: plt.plot(xx, yy, 'o', clip_on=False):
Now the circles are right at the edge, but they are not clipped and extend past the frame of the axes.

Related

Matplotlib: filling the area under the curve between two x-values [duplicate]

This question already has answers here:
fill_between with matplotlib and a where condition of two lists
(1 answer)
Matplotlib fill_between edge effect with `where` argument
(1 answer)
Fill between x and baseline x position in Matplotlib
(1 answer)
How to avoid gaps with matplotlib.fill_between and where
(1 answer)
Closed 1 year ago.
I'm plotting a blackbody curve and would like to fill in the area under the curve in the range of between 3 and 5 micron. However, I'm not sure how to use the fill_between or fill_betweenx plt commands here
import numpy as np
import matplotlib.pyplot as plt
from astropy import units as u
from astropy.modeling import models
from astropy.modeling.models import BlackBody
from astropy.visualization import quantity_support
bb = BlackBody(temperature=308.15*u.K)
wav = np.arange(1.0, 50.0) * u.micron
flux = bb(wav)
with quantity_support():
plt.figure()
plt.plot(wav, flux, lw=4.0)
plt.fill_between(wav,flux, min(flux), color = 'red')
plt.show()
This plots a fill under the whole curve, but only the 3-5micron part is desired to be filled.
example:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2, 100) # Sample data.
# Note that even in the OO-style, we use `.pyplot.figure` to create the figure.
fig, ax = plt.subplots() # Create a figure and an axes.
print(x)
ax.plot(x, x, label='linear') # Plot some data on the axes.
ax.set_xlabel('x label') # Add an x-label to the axes.
ax.set_ylabel('y label') # Add a y-label to the axes.
ax.set_title("Simple Plot") # Add a title to the axes.
ax.legend() # Add a legend.
plt.fill_between(x[:5],x[:5])
plt.show()
You can change the value 5 and play with it, you'll understand quickly. first parameter is Y positions , second is X positions.
fill_betweenx is just the same, but it will fill the other way around.
edit: As said in comments, it is better to use plt.fill_between(x,x, where = (x>0)&(x<0.2)). Both works, second solution is more explicit.

Matplotlib axis label move scientific exponent into same line

I am currently producing a plot which on the x-axis ranges from 0 to 1.3e7. I am plotting this as follows:
plt.errorbar(num_vertices,sampled_ave_path_average,yerr=sampled_ave_path_stdev,fmt='.',markersize='1',capsize=2,capthick=2)
plt.xlabel('Number of vertices')
plt.ylabel('Average shortest path length')
plt.xlim(0,1.3*10**7)
plt.savefig('path_length_vs_N.eps', bbox_inches='tight')
plt.savefig('path_length_vs_N.png', bbox_inches='tight')
plt.close()
This produces a plot where the x-axis tick labels are in scientific notation which is what I would like. I was however wondering whether it is possible to move the 1e7 (circled in red below) onto the same line as the other labels? (I realise this could cause confusion about the exponents of the other values.)
First, you may look at the following questions:
How to move the y axis scale factor to the position next to the y axis label?
Colorbar offsetText (scientific base multiplier) move from top to bottom of colorbar
The first one may be a possible alternative (because you mention that the envisioned solution "might cause confusion about the exponents"). The second might point a way to a possible solution although it is about using a colorbar.
So in order to change the position of the offset text to be in line with the xtick labels, the following would be a way to go.
import matplotlib.pyplot as plt
import numpy as np
import types
x = np.linspace(1e7, 9e7)
y = 1-np.exp(-np.linspace(0,5))
fig, ax = plt.subplots()
ax.plot(x,y)
pad = plt.rcParams["xtick.major.size"] + plt.rcParams["xtick.major.pad"]
def bottom_offset(self, bboxes, bboxes2):
bottom = self.axes.bbox.ymin
self.offsetText.set(va="top", ha="left")
oy = bottom - pad * self.figure.dpi / 72.0
self.offsetText.set_position((1, oy))
ax.xaxis._update_offset_text_position = types.MethodType(bottom_offset, ax.xaxis)
plt.show()

Matplotlib: non-alignment of the dots on a plot

I am using matplotlib to do a Component-Component plus Residual (CCPR) Plots (= partial residual plot)
This script :
fig, ax = plt.subplots(figsize=(5, 5))
fig = sm.graphics.plot_ccpr(lm_full, 'diag[T.sz]', ax=ax)
plt.close
Gives :
How can I modify my script to get something like
I don't want my dots to be aligned. In both cases, the variables of the x axis are dummy variable (ill vs healthy controls).
This may seem stupid, but I don't even know how to express what I want : it's much more easier with the images.
It sounds like you want to add some jitter to the x values, like this:
import numpy as np
# get x and y coordinates from the axes
coords = ax.collections[0].get_offsets()
# add small random number to each x coordinate
coords[:,0] = coords[:,0] + np.random.rand(coords.shape[0]) * 0.01
# move the points to the new coordinates
ax.collections[0].set_offsets(coords)

Is it possible to test if the legend is covering any data in matplotlib/pyplot

Python beginner so apologies if incorrect terminology at any point.
I am using the legend(loc='best', ...) method and it works 99% of the time. However, when stacking more than 9 plots (i.e. i>9 in example below) on a single figure, with individual labels, it defaults to center and covers the data.
Is there a way to run a test in the script that will give a true/false value if the legend is covering any data points?
Very simplified code:
fig = plt.figure()
for i in data:
plt.plot(i[x, y], label=LABEL)
fig.legend(loc='best')
fig.savefig()
Example of legend covering data
One way is to add some extra space at the bottom/top/left or right side of the axis (in your case I would prefer top or bottom), by changing the limits slightly. Doing so makes the legend fit below the data. Add extra space by setting a different y-limit with ax.set_ylim(-3e-4, 1.5e-4) (the upper limit is approximately what it is in your figure and -3 is a estimate of what you need).
What you also need to do is to add split the legend into more columns, with the keyword ncol=N when creating the legend.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.linspace(0, 1, 100)
y = 3.5 * x - 2
for i in range(9):
ax.plot(x, y + i / 10., label='iiiiiiiiiiii={}'.format(i))
ax.set_ylim(-3, 1.5)
ax.legend(loc='lower center', ncol=3) # ncol=3 looked nice for me, maybe you need to change this
plt.show()
EDIT
Another solution is to put the legend in a separate axis like I do in the code below. The data-plot does not need to care about making space for the legend or anything and you should have enough space in the axis below to put all your line-labels. If you need more space, you can easily change the ratio of the upper axis to the lower axis.
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(211)
ax_leg = fig.add_subplot(212)
x = np.linspace(0, 1, 100)
y = 3.5 * x - 2
lines = []
for i in range(9): #for plotting the actual data
li, = ax.plot(x, y + i / 10., label='iiiiiiiiiiii={}'.format(i))
lines.append(li)
for line in lines: # just to make the legend plot
ax_leg.plot([], [], line.get_color(), label=line.get_label())
ax_leg.legend(loc='center', ncol=3, ) # ncol=3 looked nice for me, maybe you need to change this
ax_leg.axis('off')
fig.show()

Multiple y-axis conversion scales

Hi
I'm trying to create plots which incorporate parallel conversion scales for two sets of units on the y-axis; using the two different styles of:
offset ('parasitic') y-axes and
overlaid/shared y-axes
to replicate the style of the left-hand y-axes in the attached example images.
I'd like to find the simplest generic way of producing both of the above example plots, which also allows me to generate the y-axis conversion scales by defining the relationship between the two sets of units as a function (in this example: mmHg = kPa * 7.5).
If it's possible to add the third right-hand y axes (vapour concentration and water content) shown in these examples, which are unrelated to the left hand scales, this would be a bonus.
I've read related stackoverflow.com postings and examples on using multiple x and y axes using the twinx and twiny functions - e.g.
here - as well as the Matplotlib cookbook, but I can't find an example which addresses this particular problem.
I'd be very grateful for any minimal working examples or links.
I'm using Matplotlib in Spyder 2.2.1 / Python 2.7.5
Many thanks in anticipation
Dave
For the first plot, I recommend axisartist. The automatic scaling of the two y-axis on the left-hand-side is achieved through a simple scaling factor that applies to the specified y-limits. This first example is based on the explanations on parasite axes:
import numpy as np
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
import matplotlib.pyplot as plt
# initialize the three axis:
host = host_subplot(111, axes_class=AA.Axes)
plt.subplots_adjust(left=0.25)
par1 = host.twinx()
par2 = host.twinx()
# secify the offset for the left-most axis:
offset = -60
new_fixed_axis = par2.get_grid_helper().new_fixed_axis
par2.axis["right"] = new_fixed_axis(loc="left", axes=par2, offset=(offset, 0))
par2.axis["right"].toggle(all=True)
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot data on y1 and y2, respectively:
host.plot(x,y1,'b')
par1.plot(x,y2,'r')
# specify the axis limits:
host.set_xlim(0.0,70.0)
host.set_ylim(YLIM[0],YLIM[1])
par1.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
par2.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
host.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
par1.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
par2.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
plt.show()
You will end up with this plot:
You can try to modify the above example to give you the second plot, too. One idea is, to reduce offset to zero. However, with the axisartist, certain tick functions are not supported. One of them is specifying if the ticks go inside or outside the axis.
Therefore, for the second plot, the following example (based on matplotlib: overlay plots with different scales?) is appropriate.
import numpy as np
import matplotlib.pyplot as plt
# initialize the three axis:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax3 = ax1.twinx()
# data ratio for the two left y-axis:
y3_to_y1 = 1/7.5
# y-axis limits:
YLIM = [0.0, 150.0,
0.0, 150.0]
# set up dummy data
x = np.linspace(0,70.0,70.0)
y1 = np.asarray([xi**2.0*0.032653 for xi in x])
y2 = np.asarray([xi**2.0*0.02857 for xi in x])
# plot the data
ax1.plot(x,y1,'b')
ax2.plot(x,y2,'r')
# define the axis limits
ax1.set_xlim(0.0,70.0)
ax1.set_ylim(YLIM[0],YLIM[1])
ax2.set_ylim(YLIM[2],YLIM[3])
# when specifying the limits for the left-most y-axis
# you utilize the conversion factor:
ax3.set_ylim(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1)
# move the 3rd y-axis to the left (0.0):
ax3.spines['right'].set_position(('axes', 0.0))
# set y-ticks, use np.arange for defined deltas
# add a small increment to the last ylim value
# to ensure that the last value will be a tick
ax1.set_yticks(np.arange(YLIM[0],YLIM[1]+0.001,10.0))
ax2.set_yticks(np.arange(YLIM[2],YLIM[3]+0.001,10.0))
ax3.set_yticks(np.arange(YLIM[2]*y3_to_y1,YLIM[3]*y3_to_y1+0.001, 2.0))
# for both letf-hand y-axis move the ticks to the outside:
ax1.get_yaxis().set_tick_params(direction='out')
ax3.get_yaxis().set_tick_params(direction='out')
plt.show()
This results in this figure:
Again, the set_tick_params(direction='out') does not work with the axisartist from the first example.
Somewhat counter-intuitive, both the y1 and y3 ticks have to be set to 'out'. For y1, this makes sense, and for y3 you have to remember that it started as a right-hand-side axis. Therefore, those ticks would appear outside (with the default 'in' setting) when the axis is moved to the left.

Categories