I am trying to rotate a given figure 90 degree.
fig = plt.figure()
points = [[0.3036, 0.1960], [0.6168, 0.2977], [0.7128, 0.4169], [0.7120, 0.1960],[0.9377,0.2620],\
[0.7120,0.5680],[0.3989,0.6697],[0.3028,0.7889],[0.3036,0.5680],[0.5293,0.5020]]
bird = matplotlib.patches.Polygon(points, facecolor='blue')
fig, ax = plt.subplots()
ax.set_aspect("equal")
ax.add_patch(bird)
ax.set_xlim(0.2,1)
ax.set_ylim(0.2,0.9)
plt.show()
To rotate a matrix, you basically multiply your coordinates with a rotation matrix, that is given by
[[cos(theta), -sin(theta)], [sin(theta), cos(theta)]]
theta being the angle of rotation (so in your case [[0, -1], [1, 0]]).
So you just calculate the dot product like this:
points = np.array(points)
rotation_matrix = np.array([[0, -1], [1, 0]])
new_points = points.dot(rotation_matrix)
and then you can plot your new set of coordinates. This is the results (after adding (0, 1) to the coordinates so that the bird is in the frame...
Related
I would like to draw a parallelepiped in Python using matplotlib centred in (0,0,0), with the top face of a different color (or each face of a different parametrized color), and with these dimensions:
L = 1
l = 0.7
s = 0.4
This is the code I developed to draw a cube with the same face color.
import matplotlib.pyplot as plt
import numpy as np
# Create axis
axes = [5, 5, 5]
# Create Data
data = np.ones(axes, dtype = np.bool)
# Control Tranperency
alpha = 0.9
# Control colour
colors = np.empty(axes + [4], dtype = np.float32)
colors[:] = [1, 0, 0, alpha] # red
# Plot figure
fig2 = plt.figure()
ax = fig2.add_subplot(111, projection='3d')
ax.voxels(data, facecolors=colors)
Any suggestion to modify it? Considering that I would like to rotate it with a rotation matrix/quaternion operator in a second time, it would be useful to define the coordinates of the vertices or of some ley points of the parallelepiped.
thank you all!
I'm trying to generate a right triangle with hypotenuse = 1, interior angle 25, with its base rotated 30 degrees. I've entered the trig formulas correctly to my understanding, and I suspect some kind of rounding error. Because the triangle produced in matplot is slightly off from a right triangle.
import math
import matplotlib.pyplot as plt
from annotation import label
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle base and the x axis.
point_A = (0,0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
# Label our points
label(plt, 'A', point_A)
label(plt, 'B', point_B)
label(plt, 'C', point_C)
# Draw the right triangle between our points.
plt.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
As you can see, the angle ACB is not a right angle as trigonometry would predict.
#Mad Physicist pointed out a the comment that ABC does appear to be a right triangle. I would like to get ACB to be a right angle, like this example.
Here is with the right triangle formed by angle_a below the previous one:
point_D = (math.cos(angle_a) * math.cos(angle_b), 0)
plt.plot(*zip(point_A, point_C, point_D, point_A), marker='o', color='black')
label(plt, 'D', point_D)
Now solved. I finally got it working thanks to the line ax.set_aspect('equal')
The problem is that by default, matplotlib doesn't use the same distances in the x and in the y direction. Instead, matplotlib tries to fit everything nicely into the given bounds.
These uneven distances distort angles, and also deforms circles.
You can force an equal aspect ratio via ax.set_aspect('equal').
To calculate the positions via the angles, and have the right corner at point B, you need to take into account that the length AC is cos(b) times the length of AC. AC can be chosen to have 1 as length. Alternatively, you could divide both B and C by cos(b) to have a larger triangle, where the length of AB would be 1.
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5), sharey=True)
for ax in (ax1, ax2):
point_A = (0, 0)
if ax == ax1:
point_B = (math.cos(angle_a + angle_b) * math.cos(angle_b), math.sin(angle_a + angle_b) * math.cos(angle_b))
point_C = (math.cos(angle_a), math.sin(angle_a))
ax.set_title('length AC is 1')
else:
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) / math.cos(angle_b), math.sin(angle_a) / math.cos(angle_b))
ax.set_title('length AB is 1')
point_M = ((point_A[0] + point_C[0]) / 2, (point_A[1] + point_C[1]) / 2)
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax.set_aspect('equal', 'datalim')
plt.show()
The same calculation works for any angle. Here is how 12 rotations in steps of 30 degrees look together:
The following code shows the effect of ax.set_aspect('equal') for the original points.
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
point_A = (0, 0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
point_M = ((point_A[0] + point_B[0]) / 2, (point_A[1] + point_B[1]) / 2)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(16, 4), gridspec_kw={'width_ratios': [2, 1]})
for ax in (ax1, ax2):
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax1.set_title('Default aspect ratio, deforms the angles')
ax2.set_aspect('equal') # or plt.axis('equal')
ax2.set_title('Equal aspect ratio')
plt.tight_layout()
plt.show()
You appear to have jumbled your trigonometry. I'm going to suggest moving slowly step-by-step, and using variable names that help you remember what is going on.
Let's start with the original triangle. If interior_angle = np.deg2rad(25) and the hypotenuse has length 1, then the right angle on the x-axis is at (np.cos(interior_angle), 0) and the other interior angle is at (np.cos(interior_angle), np.sin(interior_angle)). Neither of the points in your diagram corresponds.
Now let's express the triangle as a matrix whose columns are the vertices:
interior_angle = np.deg2rad(25)
vertices = np.array([
[0, np.cos(interior_angle), np.cos(interior_angle), 0],
[0, 0, np.sin(interior_angle), 0],
])
The last vertex is a repeat of the origin to make plotting easier.
Now let's look at the rotation. For rotation_angle = np.deg2rad(30), point (x, y) rotates to (np.cos(rotation_angle) * x - np.sin(rotation_angle) * y, np.sin(rotation_angle) * x + np.cos(rotation_angle) * y). This can be expressed as a matrix equation:
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
p_out = rotation_matrix # p_in
The array vertices is so constructed that it can be directly multiplied by a rotation matrix. You can therefore write
rotation_angle = np.deg2rad(30)
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
rotated_vertices = rotation_matrix # vertices
The plotted image should make more sense now:
plt.plot(*vertices)
plt.plot(*rotated_vertices)
plt.axis('equal')
plt.show()
I am trying to plot a polar colour heat map for a set of coordinates (coor) and corresponding phi values. I have used the tripcolor function as follows:
plt.tripcolor(coor[:, 0], coor[:, 1], phi, shading=shading, cmap=cmap, edgecolor="face")
plt.show()
This is the result that I got:
PlotMesh
Is there a way to make the outer grid circular?
I would try to create a triangulation from the coordinates:
import matplotlib.tri as tri
triang = tri.Triangulation(coor[:, 0], coor[:, 1])
plt.tripcolor(triang, phi, shading=shading, cmap=cmap, edgecolor="face")
I have sparse scatter plot to visualize the comparison of predicted vs actual values. The range of the values are 1-4 and there are no decimal points.
I have tried plotly so far with hte following code (but I can also use a matplotlib solution):
my_scatter = go.Scatter(
x = y_actual, y = y_pred, mode = 'markers',
marker = dict(color = 'rgb(240, 189, 89)', opacity=0.5)
)
This prints the graph nicely (see below). I use opacity to see the density at each point. I.e. if two points lie on top of each other, the point will be shown in darker color. However, this is not explanatory enough. Is it possible to add the counts at each point as a label? There are some overlaps at certain intersections. I want to display how many points intersects. Can this be done automatically using matplotlib or plotly?
This answer uses matplotlib.
To answer the initial question first: You need to find out how often the data produces a point at a given coordinate to be able to annotate the points. If all values are integers this can easily be done using a 2d histogram. Out of the hstogram one would then select only those bins where the count value is nonzero and annotate the respective values in a loop:
x = [3, 0, 1, 2, 2, 0, 1, 3, 3, 3, 4, 1, 4, 3, 0]
y = [1, 0, 4, 3, 2, 1, 4, 0, 3, 0, 4, 2, 3, 3, 1]
import matplotlib.pyplot as plt
import numpy as np
x = np.array(x)
y = np.array(y)
hist, xbins,ybins = np.histogram2d(y,x, bins=range(6))
X,Y = np.meshgrid(xbins[:-1], ybins[:-1])
X = X[hist != 0]; Y = Y[hist != 0]
Z = hist[hist != 0]
fig, ax = plt.subplots()
ax.scatter(x,y, s=49, alpha=0.4)
for i in range(len(Z)):
ax.annotate(str(int(Z[i])), xy=(X[i],Y[i]), xytext=(4,0),
textcoords="offset points" )
plt.show()
You may then decide not to plot all points but the result from the histogramming which offers the chance to change the color and size of the scatter points,
ax.scatter(X,Y, s=(Z*20)**1.4, c = Z/Z.max(), cmap="winter_r", alpha=0.4)
Since all values are integers, you may also opt for an image plot,
fig, ax = plt.subplots()
ax.imshow(hist, cmap="PuRd")
for i in range(len(Z)):
ax.annotate(str(int(Z[i])), xy=(X[i],Y[i]), xytext=(0,0), color="w",
ha="center", va="center", textcoords="offset points" )
Without the necesity to calculate the number of occurances, another option is to use a hexbin plot. This gives slightly inaccurate positions of the dots, du to the hexagonal binning, but I still wanted to mention this option.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
x = np.array(x)
y = np.array(y)
fig, ax = plt.subplots()
cmap = plt.cm.PuRd
cmaplist = [cmap(i) for i in range(cmap.N)]
cmaplist[0] = (1.0,1.0,1.0,1.0)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list('mcm',cmaplist, cmap.N)
ax.hexbin(x,y, gridsize=20, cmap=cmap, linewidth=0 )
plt.show()
I'm attempting to generate a model PSF from a set of observed stars. I'm following the great example provided by ali_m in this answer (MCVE below)
The 5 stars I'm using look like this:
where the center (peak intensity) is at bins [9, 9]. The results of their combination via numpy's hitsogram2d is this:
showing a peak density at bins [8, 8]. To center it at [9, 9], I have to obtain the centroids (see below) as:
cx, cy = np.array([1.] * len(stars)), np.array([1.] * len(stars))
instead. Why is this?
import numpy as np
from matplotlib import pyplot as plt
stars = # Uploaded here: http://pastebin.com/tjLqM9gQ
fig, ax = plt.subplots(2, 3, figsize=(5, 5))
for i in range(5):
ax.flat[i].imshow(
stars[i], cmap=plt.cm.viridis, interpolation='nearest',
origin='lower', vmin=0.)
ax.flat[i].axhline(9., ls='--', lw=2, c='w')
ax.flat[i].axvline(9., ls='--', lw=2, c='w')
fig.tight_layout()
# (nstars, ny, nx) pixel coordinates relative to each centroid
# pixel coordinates (integer)
x, y = np.mgrid[:20, :20]
# centroids (float)
cx, cy = np.array([0.] * len(stars)), np.array([0.] * len(stars))
dx = cx[:, None, None] + x[None, ...]
dy = cy[:, None, None] + y[None, ...]
# 2D weighted histogram
bins = np.linspace(0., 20., 20)
h, xe, ye = np.histogram2d(dx.ravel(), dy.ravel(), bins=bins,
weights=stars.ravel())
fig, ax = plt.subplots(1, 1, subplot_kw={'aspect': 'equal'})
ax.hold(True)
ax.imshow(h, cmap=plt.cm.viridis, interpolation='nearest',
origin='lower', vmin=0.)
ax.axhline(8., ls='--', lw=2, c='w')
ax.axvline(8., ls='--', lw=2, c='w')
plt.show()
The reason, the histogram is not centered at the point (9,9) where the single star intensity distribution is centered, is that the code to generate it shifts around the bins of the histogram.
As I already suggested in the comments, keep things simple. E.g. we do not need plots to see the problem. Also, I do not understand what those dx dy are, so let's avoid them.
We can then calculate the histogram by
import numpy as np
stars = # Uploaded here: http://pastebin.com/tjLqM9gQ
# The argmax of a single star results in (9,9)
single_star_argmax = np.unravel_index(np.argmax(stars[0]), stars[0].shape)
# Create a meshgrid of coordinates (0,1,...,19) times (0,1,...,19)
y,x = np.mgrid[:len(stars[0,:,0]), :len(stars[0,0,:])]
# duplicating the grids
xcoord, ycoord = np.array([x]*len(stars)), np.array([y]*len(stars))
# compute histogram with coordinates as x,y
# and [20,20] bins
h, xe, ye = np.histogram2d(xcoord.ravel(), ycoord.ravel(),
bins=[len(stars[0,0,:]), len(stars[0,:,0])],
weights=stars.ravel())
# The argmax of the combined stars results in (9,9)
combined_star_argmax = np.unravel_index(np.argmax(h), h.shape)
print single_star_argmax
print combined_star_argmax
print single_star_argmax == combined_star_argmax
# prints:
# (9, 9)
# (9, 9)
# True
The only problem in the original code really was the line bins = np.linspace(0., 20., 20) which creates 20 points between 0 and 20,
0. 1.05263158 2.10526316 ... 18.94736842 20.
This scales the bin size to ~1.05 and lets your argmax occur already "earlier" then expected.
What you really want are 20 points between 0 and 19, np.linspace(0,19,20) or
np.arange(0,20)
To avoid such mistakes, one can simply give the length of the original array as argument, bins=20.