How to plot in figure coordinates in matplotlib? - python

I want to retrieve the figure coordinates of one scatter point and set a second point exactly at that point - solely based on figure coordinates and not on axes or data coordinates.
My approach, to plot the red cross (last line) on top of the blue point, does not work yet:
import matplotlib.pyplot as plt
fig, ax0 = plt.subplots()
point0 = ax0.scatter(1, 1)
ax0.set_xlim(0, 2)
ax0.set_ylim(0, 2)
bbox = ax0.get_position()
ax1 = fig.add_axes(bbox)
ax1.set_xlim(0, 2)
ax1.set_ylim(0, 2)
ax1.axis('off')
point1 = ax1.scatter(1, 0.5, marker='x')
x, y = ax0.transData.transform((1, 1))
print('x={}'.format(x), 'y={}'.format(y))
ax1.scatter(x, y, marker='x', color='r', transform=fig.transFigure)
prints: x=221.4 y=144.72
I would be thankful for any hints how to solve this.

It turns out that transformation operations solve this problem:
import matplotlib.pyplot as plt
fig, ax0 = plt.subplots()
point0 = ax0.scatter(1, 1)
bbox = ax0.get_position()
ax0.set_xlim(0, 2)
ax0.set_ylim(0, 2)
mytrans = ax0.transData + ax0.transAxes.inverted()
x, y = mytrans.transform((1, 1))
ax1 = fig.add_axes(bbox)
ax1.axis('off')
ax1.set_xlim(0, 2)
ax1.set_ylim(0, 2)
print('x={}'.format(x), 'y={}'.format(y))
ax1.scatter(x, y, marker='x', color='r', transform=ax1.transAxes)
The point needs to be transformed from data to axes coordinates.

Related

Matplotlib: Scatter plot in a loop over set of arrays with consistent scatter point size and color bar

I am trying to generate a scatter plot using dataframe series x & y and the size of the scatter data point using dataframe series z.
I should mention that I iterate through a set of each x,y, and z arrays and add the color plot outside the loop.
I see that the scatter sizes and color bar are generated at each iteration therefore scatter sizes are not consistent with all data points in the plot and also with the colorbar at the end. How do I solve this?
fig, ax = plt.subplots()
for x, y, z in arrays_of_xyz:
splot = ax.scatter(x.to_numpy(), y.to_numpy(), marker= 'o', s = z.to_numpy(), cmap ='viridis_r', c = z.to_numpy())
fig.tight_layout()
plt.colorbar(splot)
plt.show()
Gautham
Can't see in which way the sizes in the plot are inconsistent.
The colorbar can be inconsistent if you do not enforce consistent vmin and vmax when calling scatter.
Can you please try with the following code and tell more about inconsistencies you got:
import numpy as np
import matplotlib.pyplot as plt
num_sets = 3
colors = ("red", "green", "blue")
num_pts_per_set = 20
xs = np.random.randn(num_sets, num_pts_per_set)
ys = np.random.randn(num_sets, num_pts_per_set)
zs = (
np.random.rand(num_sets, num_pts_per_set)
* np.arange(1, num_sets + 1).reshape(-1, 1)
* 30
)
zmin = zs.min()
zmax = zs.max()
fig, (ax1, ax2) = plt.subplots(ncols=2)
ax1.set_title("Sizes according to z\nColors according to set #")
for i, (x, y, z, clr) in enumerate(zip(xs, ys, zs, colors)):
ax1.scatter(x, y, marker="o", s=z, c=clr, label=f"Set #{i}")
ax1.legend()
ax2.set_title("Facecolors according to z\nSizes according to set #")
for i, (x, y, z, clr) in enumerate(zip(xs, ys, zs, colors)):
splot = ax2.scatter(x, y, marker="o", c=z, edgecolors=clr, s=(i+1)*30, vmin=zmin, vmax=zmax, label=f"Set #{i}")
ax2.legend()
fig.colorbar(splot)
plt.show()

How to plot a graph with logscale over a background image?

I want to plot a curve over a background image with the x and y axis in logscale. However, when I try to do so, the image is stretched by the logscale. I obtain this figure
This is the code I wrote.
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(low=0, high=10**6, size=(100,))
y = np.random.uniform(low=10**(-14), high=10**(-10), size=(100,))
x.sort()
y.sort()
xm = len(x)
ym = len(y)
img = plt.imread("quiverplot.png")
fig, ax2 = plt.subplots()
plt.plot(x, y)
ax2.set_xscale('log')
ax2.set_yscale('log')
ax1 = ax2.twinx()
img = ax1.imshow(img, zorder=0, extent=[x[0], x[xm-1], y[0], y[ym-1]], aspect='auto')
fig.tight_layout()
plt.show()
Thanks for any advices you can give me.
Don't use twinx(), but create a new axes with matplotlib.pyplot.axes().
You can do like this controlling the frame(background), x/y axis, and z-order.
fig, ax2 = plt.subplots()
ax2.plot(x, y)
ax2.set_xscale('log')
ax2.set_yscale('log')
ax2.set_frame_on(False)
ax2.zorder = 1
ax1 = plt.axes(ax2.get_position(True))
ax1.set_frame_on(False)
ax1.set_axis_off()
ax1.imshow(img, extent=[x[0], x[xm-1], y[0], y[ym-1]], aspect='auto')
...
It will be simpler if you change the order of plotting like this.
fig, ax2 = plt.subplots()
ax2.imshow(img, extent=[x[0], x[xm-1], y[0], y[ym-1]], aspect='auto')
ax2.set_axis_off()
ax1 = plt.axes(ax2.get_position(True))
ax1.set_frame_on(False)
ax1.set_xscale('log')
ax1.set_yscale('log')
ax1.plot(x, y)
...

Stream plot with varying colours in matplotlib giving 2 cmaps

A stream plot, or streamline plot, is used to display 2D vector fields. I am creating a stream plot with varying colors in Python but am getting two different cmap on the side. The code used is almost identical to the help file but I am getting multiple cmaps on the third plot. How does one remove the second cmap?
Below is the code that I used followed by the output.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
x,y = np.meshgrid(np.linspace(-5,5,20),np.linspace(-5,5,20))
xdot = y
ydot = -2*x - 3*y
# subplot2grid
fig = plt.figure(figsize=(18,10))
ax1 = plt.subplot2grid((2,2), (0, 0))
ax2 = plt.subplot2grid((2,2), (0, 1))
ax3 = plt.subplot2grid((2,2), (1, 0))
ax4 = plt.subplot2grid((2,2), (1, 1))
# Plot 1
Q = ax1.quiver(x, y, xdot, ydot, scale=500, angles='xy') # Quiver key
ax1.quiverkey(Q,-10,22.5,30,'5.1.8',coordinates='data',color='k')
ax1.set(xlabel='x', ylabel='y')
ax1.set_title('Quiver plot 6.1.1')
# Plot 2
strm = ax2.streamplot(x, y, xdot, ydot, density=1, color='k', linewidth=2) # streamplot(X,Y,u,v)
fig.colorbar(strm.lines)
ax2.set(xlabel='x', ylabel='y')
ax2.set_title('Stream plot of 6.1.1')
# Plot 4
strm = ax4.streamplot(x, y, xdot, ydot, density=1, color=xdot, linewidth=2, cmap='autumn') # streamplot(X,Y,u,v, density = 1)
fig.colorbar(strm.lines, ax=ax4)
ax4.set(xlabel='x', ylabel='y', title='Stream plot of 6.1.1 with varying color')
plt.show()
The help file for the stream plot has an example that worked to implement this problem that worked as desired. This is what I used to plot the original stream plot.
Stream plot
Constrained Layout Guide
Summary
So to summarize my question. How does one remove the two color maps at the side?
Any help would be highly appreciated.
You should specify the ax of the ax2.streamplot:
import numpy as np
import matplotlib.pyplot as plt
x,y = np.meshgrid(np.linspace(-5,5,20),np.linspace(-5,5,20))
xdot = y
ydot = -2*x - 3*y
# subplot2grid
fig = plt.figure(figsize=(18,10))
ax1 = plt.subplot2grid((2,2), (0, 0))
ax2 = plt.subplot2grid((2,2), (0, 1))
ax3 = plt.subplot2grid((2,2), (1, 0))
ax4 = plt.subplot2grid((2,2), (1, 1))
# Plot 1
Q = ax1.quiver(x, y, xdot, ydot, scale=500, angles='xy') # Quiver key
ax1.quiverkey(Q,-10,22.5,30,'5.1.8',coordinates='data',color='k')
ax1.set(xlabel='x', ylabel='y')
ax1.set_title('Quiver plot 6.1.1')
# Plot 2
strm = ax2.streamplot(x, y, xdot, ydot, density=1, color='k', linewidth=2) # streamplot(X,Y,u,v)
fig.colorbar(strm.lines, ax = ax2) # <--- TO BE DELETED
ax2.set(xlabel='x', ylabel='y')
ax2.set_title('Stream plot of 6.1.1')
# Plot 4
strm = ax4.streamplot(x, y, xdot, ydot, density=1, color=xdot, linewidth=2, cmap='autumn') # streamplot(X,Y,u,v, density = 1)
fig.colorbar(strm.lines, ax=ax4)
ax4.set(xlabel='x', ylabel='y', title='Stream plot of 6.1.1 with varying color')
plt.show()
Alternatively, you can delete the above line of code in order to remove the undesired colorbar:

Stacked horizontal plots with multiple Y axis varying in scale

Hi I am trying to create:
horizontally stacked plots
Have secondary axis on both plots
Have different scales on axis - unfortunately, both my Y axis currently have the same scale per subplot... :(
Current code:
# Create axes
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.suptitle("XYZ")
fig.set_figheight(5)
fig.set_figwidth(15)
# First graph
ax1.scatter(
df_PTA_clip_pstar["start_time"],
df_PTA_clip_pstar["pstar"],
s=5,
c="black",
label="P*",
)
plt.ylabel("P*")
ax1.scatter(df_PTA_clipkh["start_time"], df_PTA_clipkh["kh"], s=2, c="cyan", label="Kh")
ax1.secondary_yaxis("right")
plt.ylabel("Kh")
# Second graph - will add the correct data to this once first graph fixed
ax2.scatter(x, y, s=5, c="Red", label="P*")
ax2.scatter(x, z, s=5, c="Green", label="Kh")
ax2.secondary_yaxis("right")
plt.tight_layout()
plt.legend()
plt.show()
Current progress:
You can use .twinx() method on each ax object so you can have two plots on the same ax object sharing x-axis:
import matplotlib.pyplot as plt
import numpy as np
# Create axes
fig, (ax1, ax2) = plt.subplots(1, 2)
## First subplot
x = np.random.random_sample(100)
y = np.random.random_sample(100)
ax1.set_xlim(0, 2)
ax1.scatter(x, y,
s=5,
c="black")
ax11 = ax1.twinx()
x = 1 + x
y = 1 + np.random.random_sample(100)
ax11.scatter(x, y,
s=5,
c="red")
## Second subplot
x = 2 * np.random.random_sample(100) - 1
y = np.random.random_sample(100)
ax2.set_xlim(-1, 2)
ax2.scatter(x, y,
s=5,
c="blue")
ax21 = ax2.twinx()
x = 1 + x
y = 10 + np.random.random_sample(100)
ax21.scatter(x, y,
s=5,
c="orange")
plt.show()

How to keep hemisphere the correct shape in matplotlib 3D plot as I rotate the view?

I'd like to be able to rotate the view of a hemisphere in a 3D matplotlib plot and have the shape show correctly,
Answers to set matplotlib 3d plot aspect ratio? used in the first example don't help as they address the aspect ratio of the plot window.
Question: In the second example I show that if I make the scales equal lengths (-1, 1), (-1, 1), (-0.5, 1.5) I can preserve the shape as I rotate the view, but is this the only way to preserve the shape under view rotation?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = np.random.random((3, 1000)) - 0.5
points /= np.sqrt((points**2).sum(axis=0))
x, y, z = points[:, points[2] > 0.] # upper hemisphere
fig = plt.figure(figsize=plt.figaspect(0.5)) # https://stackoverflow.com/a/12371373/3904031
ax1 = fig.add_subplot(2, 1, 1, projection='3d')
ax1.plot(x, y, z, '.k')
ax1.view_init(0, 90)
ax1.set_title('view_init(0, 90)', fontsize=16)
ax2 = fig.add_subplot(2, 1, 2, projection='3d')
ax2.plot(x, y, z, '.k')
ax2.view_init(90, 0)
ax2.set_title('view_init(90, 0)', fontsize=16)
plt.show()
fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
ax1.plot(x, y, z, '.k')
ax1.view_init(0, 90)
ax1.set_title('view_init(0, 90)', fontsize=16)
ax1.set_xlim(-1.0, 1.0)
ax1.set_ylim(-1.0, 1.0)
ax1.set_zlim(-0.5, 1.5)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
ax2.plot(x, y, z, '.k')
ax2.view_init(90, 0)
ax2.set_title('view_init(90, 0)', fontsize=16)
ax2.set_xlim(-1.0, 1.0)
ax2.set_ylim(-1.0, 1.0)
ax2.set_zlim(-0.5, 1.5)
plt.show()
Finally, per this answer:
Simple fix!
I've managed to get this working in version 3.3.1.
It looks like this issue has perhaps been resolved in PR#17172; You can use the ax.set_box_aspect([1,1,1]) function to ensure the aspect is correct (see the notes for the set_aspect function).
You first make the limits in all three axes the same either by adding bounding points (invisible dots beyond your data) to define a cube of equal dimensions, or just use set_xlim, set_ylim, set_zlim as I've done here.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = np.random.random((3, 1000)) - 0.5
points /= np.sqrt((points**2).sum(axis=0))
x, y, z = points[:, points[2] > 0.] # upper hemisphere
fig = plt.figure(figsize=plt.figaspect(0.5)) # https://stackoverflow.com/a/12371373/3904031
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
ax1.plot(x, y, z, '.k')
ax1.view_init(0, 90)
ax1.set_title('view_init(0, 90)', fontsize=16)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
ax2.plot(x, y, z, '.k')
ax2.view_init(90, 0)
ax2.set_title('view_init(90, 0)', fontsize=16)
for ax in (ax1, ax2):
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-0.5, 1.5)
ax.set_box_aspect([1,1,1])
plt.show()

Categories

Resources