Can't see the result of a matrix figure using python - python

Would you help me fix this code?
I am using Jupiter and can't see the result. Maybe there are issues with xLim or yLim.
The entire code is below, and I want to see the 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]]
theta = np.pi
a = 0.7120
b = 0.4320
shiftrotateMatrix = np.array([[np.cos(theta),-np.sin(theta),-a*np.cos(theta)+b*np.sin(theta)+a],\
[np.cos(theta),-np.sin(theta),-a*np.sin(theta)-b*np.cos(theta)+b],\
[0,0,1]])
points3d = points
for x in points3d:
x.append(1)
pointsab = []
for x in points3d:
pointsab.append(np.dot(shiftrotateMatrix,x))
finalpoints = [np.array([x[0],x[1]]) for x in pointsab]
fig = plt.figure()
finalbird = matplotlib.patches.Polygon(finalpoints, facecolor='yellow')
fig, ax = plt.subplots()
ax.set_aspect("auto")
ax.add_patch(finalbird)
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
plt.show()

Do this at the end of your code.
for point in finalpoints:
print(point[0] - point[1])
Every single (x, y) point has the property x - y = 0.56. Your Polygon is being "shown", but it's a straight line.

Related

How to plot multiple lines from a loop on one 3d plot in Python?

Basically, I am looping generation of rays in Python and I'm trying to plot them all on the same graph. They should all be on a circle of radius 0.1. Each ray should be at a position on the circle that is varied by the arg which is in this case the theta. Also, just to mention (although I don't think it's that relevant) I am doing OOP here.
I get correct rays but I can't get them on the same 3d graph and I'm not sure how I'm supposed to do it. I thought using plt.show() would give me a graph with all 24 rays but it just plots 24 graphs.
Here is the relevant bit of code for reference:
r = 0.1
arg = 0
for i in range (0,24):
arg += np.pi/12
x = r*np.sin(arg)
y = r*np.cos(arg)
l = ray.Ray(r=np.array([x,y,0]),v=np.array([0.5,0,5]))
c = ray.SphericalRefraction(z0 = 100, curv = 0.0009, n1 = 1.0, n2 = 1.5, ar = 5)
c.propagate_ray(l)
o = ray.OutputPlane(250)
o.outputintercept(l)
points = np.array(l.vertices())
fig = plt.figure()
ax = plt.axes(projection='3d')
#ax = fig.add_subplot(1,2,1,projection='3d')
#plt.plot(points[:,2],points[:,0])
ax.plot3D(points[:,0],points[:,1],points[:,2])
plt.show()
Expanding on the comment by Mercury, the figure and also axes object must be created outside the loop.
import matplotlib.pyplot as plt
import numpy as np
r = 0.1
arg = 0
fig = plt.figure()
ax = plt.axes(projection='3d')
for i in range(0,24):
arg += np.pi/12 * i
v1 = r*np.sin(arg)
v2 = r*np.cos(arg)
# ...
# using sample data
x = []
y = []
z = []
for j in range(2):
x.append(j*v1)
y.append(j*v2)
z.append(j)
# add vertex to the axes object
ax.plot3D(x, y, z)
plt.show()

How to display multiple graphs with overlapping data in the same figure thank to matplotlib?

I'm searching to plot multiple graphs (here is 2) from colormap or contourf functions with different axis X, Y and data Z in the same figure. However I only want to display the maximum of each data with a single color bar for all of the graphs.
In this example, I create a single figure in which I add each graph but the second graph overwrite the first one, regardless of whether its data are lower or higher.
import matplotlib.pyplot as plt
import numpy as np
a = [1,0.25]
fig = plt.figure(1)
ax = fig.gca()
for i in range(2):
x = np.linspace(-3, 3, 51)
y = np.linspace(-2*a[i], 2*a[i], 41)
X, Y = np.meshgrid(x, y)
if i == 0:
Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2)
else:
Z = 0.5*np.ones((41,51))
graph = ax.contourf(X,Y,Z)
bar = fig.colorbar(graph)
plt.show()
Figure 1 displayed by the code
Here is what I want to display :
Figure 2 desired
Thank you a lot,
Tristan
According to the discussion we had in the comments to your post, I think you can edit your code to achieve what you want as below.
First, as a general comment, I suggest that you move your variables to the top of the script.
Second, and this is the main part, you can make do with plotting only one graph if you use comparisons to test which value to fill in your Z-array. You can chain several comparisons using np.logical_and and then use np.where to fill a Z-array with either the function values or the constant value, based on whether you are inside your desired box of x- and y-values and whether the function value or the desired constant value is largest.
fig = plt.figure()
ax = fig.gca()
xmin, xmax, nx = -3, 3, 51
ymin, ymax, ny = -2, 2, 41
# box values
xbmin, xbmax = -3, 3
ybmin, ybmax = -0.5, 0.5
zlevel = 0.5
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
X, Y = np.meshgrid(x,y)
Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2)
test1 = Z<zlevel
test2 = np.logical_and(X>=xbmin, X<=xbmax)
test3 = np.logical_and(Y>=ybmin, Y<=ybmax)
mask = np.logical_and(np.logical_and(test1, test2), test3)
Z = np.where(mask, zlevel*np.ones(Z.shape), Z)
graph = ax.contourf(X,Y,Z)
bar = fig.colorbar(graph)
plt.show()

Extend a 2D plot to 3D

I'm trying to show my 2D data on a 3D space.
Here is my code below:
import numpy as np
import matplotlib.pyplot as plt
i = 60
n = 1000
r = 3.8
eps = 0.7
y = np.ones((n, i))
# random numbers on the first row of array x
np.random.seed(1)
x = np.ones((n+1, i))
x[0, :] = np.random.random(i)
def logistic(r, x):
return r * x * (1 - x)
present_indi = np.arange(i)
next_indi = (present_indi + 1) % i
prev_indi = (present_indi - 1) % i
for n in range(1000):
y[n, :] = logistic(r, x[n, :])
x[n+1, :] = (1-eps)*y[n, present_indi] + 0.5*eps*(y[n, prev_indi] + y[n, next_indi])
#print(x)
# the above logic generates a 2D array 'x'. with i columns and n rows.
fig, ax = plt.subplots()
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot(i,x[n,i],'*k',ms=0.9)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
The above code perfectly displays i and x graph. I have plotted all the elements of 1st column of X, then all elements of second column, then the third and so on....., using the nested for loop logic (refer to the code)
Now what I need to do is, extend the plotting to 3D, i.e use Xaxis = i, Yaxis= n, Zaxis= array 'x'
I want to plot something like this:
I know I have to use mplot3D
But doing the following won't give me any result:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot_wireframe(i,n,x[n,i],rstride=1,cstride=1)
Plotting 3d images in matplotlib is a little tricky. Generally you plot whole surfaces at once instead of plotting one line at a time. You do so by passing three 2d arrays, one for each position dimension (x, y, z). But you can't just pass any old 2d arrays either; the points themselves have to be in a precise order!
Sometimes you can do something that just works, but I find it easier to explicitly parameterize plots using u and v dimensions. Here's what I was able to get working here:
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(x.shape[1])
v_plt = np.arange(x.shape[0])
# The outer products here produce 2d arrays. We multiply by
# ones in this case for an identity transformation, but in
# general, you could use any broadcasted operation on `u`
# and `v`.
x_plt = np.outer(np.ones(np.size(v_plt)), u_plt)
y_plt = np.outer(v_plt, np.ones(np.size(u_plt)))
# In this case, our `x` array gives the `z` values directly.
z_plt = x
fig = plt.figure(figsize=(16, 10))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1) # Add a bit more space around the plot.
ax.plot_wireframe(x_plt, y_plt, z_plt,
rstride=1, cstride=1, # "Resolution" of the plot
color='blue', linewidth=1.0,
alpha=0.7, antialiased=True)
# Tilt the view to match the example.
ax.view_init(elev = 45, azim = -45)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
And here's the resulting image. I had to reduce n to 80 to make this comprehensible at all, and I have no idea what I am looking at, so I am not sure it's correct. But I think it looks broadly similar to the example you gave.
Just to illustrate the power of this approach, here's a nautilus shell. It uses a two-stage parameterization, which could be compressed, but which I find conceptually clearer:
n_ticks = 100
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(n_ticks // 2) * 2
v_plt = np.arange(n_ticks)
# theta is the angle along the leading edge of the shell
# phi is the angle along the spiral of the shell
# r is the distance of the edge from the origin
theta_plt = np.pi * ((u_plt / n_ticks) * 0.99 + 0.005)
phi_plt = np.pi * v_plt / (n_ticks / 5)
r_plt = v_plt / (n_ticks / 5)
# These formulas are based on the formulas for rendering
# a sphere parameterized by theta and phi. The only difference
# is that r is variable here too.
x_plt = r_plt[:, None] * np.cos(phi_plt[:, None]) * np.sin(theta_plt[None, :])
y_plt = r_plt[:, None] * np.sin(phi_plt[:, None]) * np.sin(theta_plt[None, :])
z_plt = r_plt[:, None] * \
(np.ones(np.shape(phi_plt[:, None])) * np.cos(theta_plt[None, :]))
# This varies the color along phi
colors = cm.inferno(1 - (v_plt[:, None] / max(v_plt))) * \
np.ones(np.shape(u_plt[None, :, None]))
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1)
ax.plot_surface(x_plt, y_plt, z_plt,
rstride=1, cstride=1,
facecolors=colors, linewidth=1.0,
alpha=0.3, antialiased=True)
ax.view_init(elev = 45, azim = -45)
plt.show()

Plot points overlapping when rotating points on xy-plane

The point of the spot marked (comment says "marked spot") is to rotate the existing points in a 3D plot by one spot. Basically I'm moving all points 60 degrees. For some reason when moving the points I am having two points overlap. I have tried changing my if statement, and have messed around with he indices, but have not been successful. The only reason I included the entire function is so you can plot and see the problem I am having. Here it is; let me know if you have any questions:
def transform_3d_trig(a,b,c):
q=2*math.pi/(360)
d=c*q
a2 = a+b
n = 12
sin=math.sin
cos=math.cos
sqrt=math.sqrt
x = []
y = []
z = []
for i in range(n):
if i <= 5:
x.append(a*np.cos(q*(60*(i-1))))
y.append(a*np.sin(q*(60*(i-1))))
z.append(0)
else:
x.append(a2*np.cos(q*(60*(i-1))))
y.append(a2*np.sin(q*(60*(i-1))))
z.append(0)
x_new = x #new lists
y_new = y
z_new = z
for i in range(n):
y_new[i] = y[i]*cos(d)
z_new[i] = y_new[i]*np.tan(d)
# plot points at this stage (before rotation); no overlapping points
fig = plt.figure(figsize=(12,12))
ax3 = fig.add_subplot(211, projection='3d')
bond2 = [x_new[4],x_new[10],y_new[4],y_new[10],z_new[4],z_new[10]]
ax3.plot(bond2[:2],bond2[2:4],bond2[4:6], color='r')
ax3.scatter(x_new, y_new, z_new)
ax3.set_xlabel('\nX')
ax3.set_ylabel('\nY')
ax3.set_zlabel('\nZ')
x_dummy = x_new #dummy variables to not screw up list
y_dummy = y_new
for i in range(n): #marked spot
if (i == 5 or i == 11):
x_new[i] = x_dummy[i-5]
y_new[i] = y_dummy[i-5]
else:
x_new[i] = x_dummy[i+1]
y_new[i] = y_dummy[i+1]
print(x_new[i], y_new[i]) #to track what point are overlapping
# plot points at this stage (after rotation); overlapping points
ax3 = fig.add_subplot(212, projection='3d')
bond2 = [x_new[4],x_new[10],y_new[4],y_new[10],z_new[4],z_new[10]]
ax3.plot(bond2[:2],bond2[2:4],bond2[4:6], color='r')
ax3.scatter(x_new, y_new, z_new)
ax3.set_xlabel('\nX')
ax3.set_ylabel('\nY')
ax3.set_zlabel('\nZ')
plt.show()
return x, y, z, x_new, y_new, z_new
The problem is probably that these lines don't do what you expect:
x_dummy = x_new
y_dummy = y_new
They don't create copies of your arrays but just duplicate the references. As a result you are actually 'screwing up' your original _new arrays when modifying the _dummy 'versions' because they are (references to) the same object.
To achieve the behaviour you are looking for (I assume), you would need to force the actual copy of the data:
x_dummy = x_new.copy()
y_dummy = y_new.copy()

How to space overlapping annotations

I want to annotate the bars in a graph with some text but if the bars are close together and have comparable height, the annotations are above ea. other and thus hard to read (the coordinates for the annotations were taken from the bar position and height).
Is there a way to shift one of them if there is a collision?
Edit: The bars are very thin and very close sometimes so just aligning vertically doesn't solve the problem...
A picture might clarify things:
I've written a quick solution, which checks each annotation position against default bounding boxes for all the other annotations. If there is a collision it changes its position to the next available collision free place. It also puts in nice arrows.
For a fairly extreme example, it will produce this (none of the numbers overlap):
Instead of this:
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from numpy.random import *
def get_text_positions(x_data, y_data, txt_width, txt_height):
a = zip(y_data, x_data)
text_positions = y_data.copy()
for index, (y, x) in enumerate(a):
local_text_positions = [i for i in a if i[0] > (y - txt_height)
and (abs(i[1] - x) < txt_width * 2) and i != (y,x)]
if local_text_positions:
sorted_ltp = sorted(local_text_positions)
if abs(sorted_ltp[0][0] - y) < txt_height: #True == collision
differ = np.diff(sorted_ltp, axis=0)
a[index] = (sorted_ltp[-1][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[-1][0] + txt_height
for k, (j, m) in enumerate(differ):
#j is the vertical distance between words
if j > txt_height * 2: #if True then room to fit a word in
a[index] = (sorted_ltp[k][0] + txt_height, a[index][1])
text_positions[index] = sorted_ltp[k][0] + txt_height
break
return text_positions
def text_plotter(x_data, y_data, text_positions, axis,txt_width,txt_height):
for x,y,t in zip(x_data, y_data, text_positions):
axis.text(x - txt_width, 1.01*t, '%d'%int(y),rotation=0, color='blue')
if y != t:
axis.arrow(x, t,0,y-t, color='red',alpha=0.3, width=txt_width*0.1,
head_width=txt_width, head_length=txt_height*0.5,
zorder=0,length_includes_head=True)
Here is the code producing these plots, showing the usage:
#random test data:
x_data = random_sample(100)
y_data = random_integers(10,50,(100))
#GOOD PLOT:
fig2 = plt.figure()
ax2 = fig2.add_subplot(111)
ax2.bar(x_data, y_data,width=0.00001)
#set the bbox for the text. Increase txt_width for wider text.
txt_height = 0.04*(plt.ylim()[1] - plt.ylim()[0])
txt_width = 0.02*(plt.xlim()[1] - plt.xlim()[0])
#Get the corrected text positions, then write the text.
text_positions = get_text_positions(x_data, y_data, txt_width, txt_height)
text_plotter(x_data, y_data, text_positions, ax2, txt_width, txt_height)
plt.ylim(0,max(text_positions)+2*txt_height)
plt.xlim(-0.1,1.1)
#BAD PLOT:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(x_data, y_data, width=0.0001)
#write the text:
for x,y in zip(x_data, y_data):
ax.text(x - txt_width, 1.01*y, '%d'%int(y),rotation=0)
plt.ylim(0,max(text_positions)+2*txt_height)
plt.xlim(-0.1,1.1)
plt.show()
Another option using my library adjustText, written specially for this purpose (https://github.com/Phlya/adjustText). I think it's probably significantly slower that the accepted answer (it slows down considerably with a lot of bars), but much more general and configurable.
from adjustText import adjust_text
np.random.seed(2017)
x_data = np.random.random_sample(100)
y_data = np.random.random_integers(10,50,(100))
f, ax = plt.subplots(dpi=300)
bars = ax.bar(x_data, y_data, width=0.001, facecolor='k')
texts = []
for x, y in zip(x_data, y_data):
texts.append(plt.text(x, y, y, horizontalalignment='center', color='b'))
adjust_text(texts, add_objects=bars, autoalign='y', expand_objects=(0.1, 1),
only_move={'points':'', 'text':'y', 'objects':'y'}, force_text=0.75, force_objects=0.1,
arrowprops=dict(arrowstyle="simple, head_width=0.25, tail_width=0.05", color='r', lw=0.5, alpha=0.5))
plt.show()
If we allow autoalignment along x axis, it gets even better (I just need to resolve a small issue that it doesn't like putting labels above the points and not a bit to the side...).
np.random.seed(2017)
x_data = np.random.random_sample(100)
y_data = np.random.random_integers(10,50,(100))
f, ax = plt.subplots(dpi=300)
bars = ax.bar(x_data, y_data, width=0.001, facecolor='k')
texts = []
for x, y in zip(x_data, y_data):
texts.append(plt.text(x, y, y, horizontalalignment='center', size=7, color='b'))
adjust_text(texts, add_objects=bars, autoalign='xy', expand_objects=(0.1, 1),
only_move={'points':'', 'text':'y', 'objects':'y'}, force_text=0.75, force_objects=0.1,
arrowprops=dict(arrowstyle="simple, head_width=0.25, tail_width=0.05", color='r', lw=0.5, alpha=0.5))
plt.show()
(I had to adjust some parameters here, of course)
One option is to rotate the text/annotation, which is set by the rotation keyword/property. In the following example, I rotate the text 90 degrees to guarantee that it wont collide with the neighboring text. I also set the va (short for verticalalignment) keyword, so that the text is presented above the bar (above the point that I use to define the text):
import matplotlib.pyplot as plt
data = [10, 8, 8, 5]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(range(4),data)
ax.set_ylim(0,12)
# extra .4 is because it's half the default width (.8):
ax.text(1.4,8,"2nd bar",rotation=90,va='bottom')
ax.text(2.4,8,"3nd bar",rotation=90,va='bottom')
plt.show()
The result is the following figure:
Determining programmatically if there are collisions between various annotations is a trickier process. This might be worth a separate question: Matplotlib text dimensions.
Just thought I would provide an alternative solution that I just created textalloc that makes sure that text-boxes avoids overlap with both each other and lines when possible, and is fast.
For this example you could use something like this:
import textalloc as ta
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(2017)
x_data = np.random.random_sample(100)
y_data = np.random.random_integers(10,50,(100))
f, ax = plt.subplots(dpi=200)
bars = ax.bar(x_data, y_data, width=0.002, facecolor='k')
ta.allocate_text(f,ax,x_data,y_data,
[str(yy) for yy in list(y_data)],
x_lines=[np.array([xx,xx]) for xx in list(x_data)],
y_lines=[np.array([0,yy]) for yy in list(y_data)],
textsize=8,
margin=0.004,
min_distance=0.005,
linewidth=0.7,
textcolor="b")
plt.show()
This results in this

Categories