vertical & horizontal lines in matplotlib - matplotlib

I do not quite understand why I am unable to create horizontal and vertical lines at specified limits. I would like to bound the data by this box. However, the sides do not seem to comply with my instructions. Why is this?
# CREATING A BOUNDING BOX
# BOTTOM HORIZONTAL
plt.axhline(y=.4, xmin=0.25, xmax=0.402, linewidth=2, color = 'k')
# RIGHT VERTICAL
plt.axvline(x=0.402, ymin=0.4, ymax = 0.615, linewidth=2, color='k')
# LEFT VERTICAL
plt.axvline(x=0.1, ymin=0.58, ymax = 0.79, linewidth=2, color='k')
plt.show()

The pyplot functions you are calling, axhline() and axvline() draw lines that span a portion of the axis range, regardless of coordinates. The parameters xmin or ymin use value 0.0 as the minimum of the axis and 1.0 as the maximum of the axis.
Instead, use plt.plot((x1, x2), (y1, y2), 'k-') to draw a line from the point (x1, y1) to the point (x2, y2) in color k. See pyplot.plot.

This may be a common problem for new users of Matplotlib to draw vertical and horizontal lines. In order to understand this problem, you should be aware that different coordinate systems exist in Matplotlib.
The method axhline and axvline are used to draw lines at the axes coordinate. In this coordinate system, coordinate for the bottom left point is (0,0), while the coordinate for the top right point is (1,1), regardless of the data range of your plot. Both the parameter xmin and xmax are in the range [0,1].
On the other hand, method hlines and vlines are used to draw lines at the data coordinate. The range for xmin and xmax are the in the range of data limit of x axis.
Let's take a concrete example,
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 100)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.axhline(y=0.5, xmin=0.0, xmax=1.0, color='r')
ax.hlines(y=0.6, xmin=0.0, xmax=1.0, color='b')
plt.show()
It will produce the following plot:
The value for xmin and xmax are the same for the axhline and hlines method. But the length of produced line is different.

If you want to add a bounding box, use a rectangle:
ax = plt.gca()
r = matplotlib.patches.Rectangle((.5, .5), .25, .1, fill=False)
ax.add_artist(r)
Rectangle doc

Related

matplotlib text in display coordinate

I try to understand how different coordinate systems work in matplotlib. My understanding is that if I add some texts using data coordinates, then I can achieve the same effect if I first transform the data coordinates to the display coordinates and then add the text using these display coordinates. In the following snippet, I expect the 'x' in green are right on top of the 'x' in red. But they are not. What do I miss here? Thank you!
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
ax.grid()
ax.text(4,0,'x', horizontalalignment='center', verticalalignment='center', color='r')
_x2, _y2 = ax.transData.transform((4.0, 0))
ax.text(_x2,_y2,'x', horizontalalignment='center', verticalalignment='center', transform=None, color='g')
Result:
Once again, we have examined the FIGURE, DATA, and AXIS criteria. We have modified them for clarity from the data range in question. The same is obtained with the figure criterion, divided by the graph size in inches and dpi value. The position of the origin of the axis is also obtained with the figure reference, and when drawn, it overlaps with the origin of the graph. The origin of the graph is shown in black.
import matplotlib.pyplot as plt
fig_x, fig_y = plt.rcParams['figure.figsize']
dpi = plt.rcParams['figure.dpi']
print(fig_x, fig_y, dpi)
fig, ax = plt.subplots()
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.grid()
g = ax.text(0,0,'o', horizontalalignment='center', verticalalignment='center', color='r')
print('get_position', g.get_position())
_x2, _y2 = ax.transData.transform((0.0, 0))
print('ax.transData', _x2, _y2)
_x3, _y3 = ax.transAxes.transform((0.0, 0))
print('ax.transAxes', _x3, _y3)
# From Figure position to ax.transData
ax.text(_x2/fig_x/dpi,_y2/fig_y/dpi,'x', horizontalalignment='center', verticalalignment='center', color='g', transform=fig.transFigure)
# From Figure position to ax.transAxes
ax.text(_x3/fig_x/dpi,_y3/fig_y/dpi,'x', horizontalalignment='center', verticalalignment='center', color='b', transform=fig.transFigure)
# Figure x0, y0
ax.text(0,0,'x', horizontalalignment='center', verticalalignment='center', color='k', transform=fig.transFigure)
plt.show()

How to place matplotlib legend according to coordinate

I have several plots and one of these showed below:
Example plot
Problem is I have many plots and I need to put the legend differently according to the position where x=0 and line of x=0 may vary in different plots.
How can I achieve this?
besides, bbox_to_anchor just allow me locate relatively to the fig, but have no idea of the inside (x,y) coordinate.
This is the part plotting:
ax.errorbar(x=x, y=y_erd, yerr=e_erd, fmt='-o',ecolor='orange',elinewidth=1,ms=5,mfc='wheat',mec='salmon',capsize=3)
ax.errorbar(x=x, y=y_ers, yerr=e_ers, fmt='-o',ecolor='blue',elinewidth=1,ms=5,mfc='wheat',mec='salmon',capsize=3)
ax.legend(['ERD', 'ERS'], loc="upper left", bbox_to_anchor=(1, 0.85),fontsize='x-small')
ax.axhline(y=0, color='r', linestyle='--')
We have created a code to calculate the zero position of the x and y axes using a simple sample as an example. First, get the tick values for each axis. Then, use the obtained value to get the index of zero. The next step is to calculate the position of the tick marks for the difference between the minimum and maximum values. From the array, we obtain the coordinates based on the zero index we obtained earlier. Set the obtained coordinates to bbox_to_anchor=[].
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-10, 10, 500)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y, label='x=0,y=0')
xticks, yticks = ax.get_xticks(), ax.get_yticks()
xpos, ypos = 0, 0
for i,(x,y) in enumerate(zip(xticks, yticks)):
if x == 0:
xpos = i
if y == 0:
ypos = i
print(xpos, ypos)
x_min, x_max = ax.get_xlim()
xticks = [(tick - x_min)/(x_max - x_min) for tick in xticks]
y_min, y_max = ax.get_ylim()
yticks = [(tick - y_min)/(y_max - y_min) for tick in yticks]
print(xticks[xpos], yticks[ypos])
ax.legend(bbox_to_anchor=[xticks[xpos], yticks[ypos]], loc='center')
plt.show()

how to add variable error bars to scatter plot points with shared axes in python matplotlib

I have generated a plot that shows a topographic profile with points along the profile that represent dated points. However, these dated points also have symmetric uncertainty values/error bars (that typically vary for each point).
In this example, I treat non-dated locations as 'np.nan'. I would like to add uncertainty values to the y2 axis (Mean Age) with defined uncertainty values as y2err.
Everytime I use the ax2.errorbar( ... ) line, my graph is squeezed and distorted.
import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
#Longitude = x; Elevation = y
x = (-110.75696,-110.75668,-110.75640,-110.75612,-110.75584,-110.75556,-110.75528)
y = (877,879,878,873,871,872,872)
ax1.plot(x, y)
ax1.set_xlabel('Longitude')
# Make the y-axis label, ticks and tick labels match the line color.
ax1.set_ylabel('Elevation', color='b')
ax1.tick_params('y', colors='b')
ax2 = ax1.twinx()
# Mean Age, np.nan = 0.0
y2 = (np.nan,20,np.nan,np.nan,np.nan,np.nan,np.nan)
y2err = (np.nan,5,np.nan,np.nan,np.nan,np.nan,np.nan)
ax2.scatter(x, y2, color='r')
#add error bars to scatter plot points
# (??????) ax2.errorbar(x, y, y2, y2err, capsize = 0, color='black')
ax2.set_ylim(10,30)
ax2.set_ylabel('Mean Age', color='r')
ax2.tick_params('y', colors='r')
fig.tight_layout()
plt.show()
If I do not apply the ax2.errorbar... line my plot looks like the first image, which is what I want but with the points showing uncertainty values (+/- equal on both side of point in the y-axis direction).
Plot of Elevation vs Age without error bars
When I use the ax2.errorbar line it looks like the second image:
Plot when using ax2.errorbar line
Thanks!

Manipulation on vertical space in matplotlib subplots

I want to reduce the verticalspacing between subplot. Surfing along the web I just have found how to reduce the horizontal spacing, something like
import matplotlib.pyplot as plt
fig, axes = plt.subplots(nrows=4, ncols=4)
fig.tight_layout() # Or equivalently, "plt.tight_layout()"
fig.subplots_adjust(hspace=0.5)
plt.show()
The hspace thing is the one that manipulates such behaviour, but apparently there's no vspace.
EDIT:
This does not reduce the space between the y-axis, that is what I want to manipulate.
As you said in your question hspace reduces the vertical spacing between subplots. The equivalent for horizontal spacing between subplots is wspace. Below is an example:
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig, ((ax1,ax2),(ax3,ax4)) = plt.subplots(nrows=2, ncols=2)
fig.tight_layout()
ax1.plot(x, y)
ax2.scatter(x, y)
ax3.scatter(x, y)
ax4.scatter(x, y)
fig.subplots_adjust(wspace=0.2)
plt.show()
Using a value for 1 for wspace gives
Using 0.2 as the value of wspace gives
An alternative approach is to pass the gridspec_kw argument a dict with keys wspace / hspace:
Example
fig, axes = plt.subplots(nrows=2, ncols=2, gridspec_kw={'hspace': 0.2, 'wspace': 0.9})
plt.tight_layout()
for ax, color in zip(axes.ravel(), list('rgbk')):
ax.scatter(np.arange(100), np.random.randn(100), color=color)
If I understood your question correctly, you want to reduce the vertical spacing, which is not what I have seen in all of the answers above.
If I am correct, you should reduce the hspace from 0.5 to 0.2, for instance. That's because hspace does not stand for horizontal spacing, it stands for height spacing, which is what you need.

How to shift the colorbar position to right in matplotlib?

I draw a scatter chart as below :
The code is :
sc = plt.scatter(x, y, marker='o', s=size_r, c=clr, vmin=lb, vmax=ub, cmap=mycm, alpha=0.65)
cbar = plt.colorbar(sc, shrink=0.9)
And I want to shift the colorbar to right a little bit to extend the drawing area. How to do that ?
Use the pad attribute.
cbar = plt.colorbar(sc, shrink=0.9, pad = 0.05)
The documentation of make_axes() describes how to use pad: "pad: 0.05 if vertical, 0.15 if horizontal; fraction of original axes between colorbar and new image axes".
Actually you can put the colorbar anywhere you want.
fig1=figure()
sc = plt.scatter(x, y, marker='o', s=size_r, c=clr, vmin=lb, vmax=ub, cmap=mycm, alpha=0.65)
position=fig1.add_axes([0.93,0.1,0.02,0.35]) ## the parameters are the specified position you set
fig1.colorbar(sc,cax=position) ##