Let's say i have an object which I have to destroy by shooting (projectile motion). The position of the object is random (as for now, just integers, to make it easier). Even when my 'bullet' looks to be just in place, the loop doesn't break. Probably the program doesn't consider graph 1 and graph 2 as equal at any point.
I tried few things about that if condition but it nothing worked.
Can anyone tell me what I must add/change?
import matplotlib.pylab as plt
import numpy as np
import random
g = 10
c = []
d = []
fig = plt.figure()
L = random.randint(5.0,18.0)
while True:
try:
#velocity
v = float(input("What is the velocity?\n>"))
#angle
a = np.radians(float(input("What is the angle?\n>")))
z = np.sin(2*a)*v**2/g #max range
h = ((v**2*(np.sin(a))**2)/(2*g)) #max. height
x= np.linspace(0, z, 1000)
#y values
y = (x*np.tan(a) - (g*x**2)/(2*v**2*((np.cos(a))**2)))
ax = plt.axes(xlim=(0, 1.5*L), ylim=(0, 1.2*h))
plt.ion() #interactive graph
#previous tries
plt.plot(c,d, '.', color = 'lightgrey')
plt.pause(0.01)
#enemy object
graph1 = plt.plot(L, 0, 'o', color = 'r', markersize=30)
plt.pause(0.01)
#actual shoot
graph2 = plt.plot(x,y, '.', color = 'b', ms = 7)
plt.pause(0.01)
if np.any(graph2) == np.any(graph1):
print("You have destroyed the enemy object!")
plt.show()
break
else:
print("You've missed. Keep shooting!")
c.append(x)
d.append(y)
plt.show()
continue
except ValueError:
print("Sorry, I can't understand.")
You don't need to plot at all to find the intersection. In fact, I don't think matplotlib can help here. The returned value of plt.plot is a list containing a single Line2D object - you could get back your original x and y values by doing
x_new = graph1[0].get_xdata()
y_new = graph1[0].get_ydata()
However, you'd only do that if your code was for some reason analyzing plots generated by a completely different function and wasn't allowed to have the original data. In your case, just use your x and y directly and find the intersection. It looks like you might be trying to do
if np.any((y == 0) * (x == L)):
print "You have destroyed the enemy object!"
Which should check if any point (x,y) is located at (0,L). The * acts as an element-by-element 'and' operator for boolean arrays. Here are some more comprehensive answers about finding intersections of two arrays.
If you're trying to build a game, look at pygame. It has all kinds of methods to detect collisions much more easily than in numpy.
Related
So I'm making a roulette game, here are the guidelines:
Roulette is a casino game that involves a steel ball spinner around a wheel. There are 38 slots where the ball might drop. 18 of those slots are colored red, another 18 are colored black and 2 are colored green. When you bet on either red or black and win, you get back twice the amount of your original bet. For example, if you bet $1, you would get an extra $1 for winning, but you would lose that original $1 if you lost.
Simulate 200 people playing roulette, each betting $1 on red 100 times.
Create a line chart and an appropriate histogram that describes these results.
Make sure to label your line chart and histogram.
So I believe my code for the game is right, but the histogram is showing up as a blank graph without anything on it.
Am I missing something? Is there something else I need to add or incorporate?
Here's what I have written for this.
import math
import random
import numpy as np
from bokeh.plotting import figure, show
def roulette(x):
results = []
count = 0
for i in range(1, x):
roll = random.randint(1, 38)
if roll <= 18:
count += 1
results.append(count/i)
else:
count -= 1
results.append(count/i)
return results
def listf(x):
for i in range(x):
return(roulette(100)[-1])
roulette_list = listf(200)
hist = np.histogram(roulette_list, bins = 10, range = (0, 200))
yval = hist[0]
xval = hist[1]
xbarwidth = xval[1] - xval[0]
xval = xval + xbarwidth/2
xval = xval[0:-1]
print(xval)
f = figure()
for x, y in zip(xval, yval):
f.vbar(x, xbarwidth, y, line_color = "white")
show(f)
Your bokeh code works perfectly, here you can see a minimal example with self defined xval and yval.
xval = [1,2,3,4]
yval = [1,2,3,4]
xbarwidth = 1
f = figure()
for x, y in zip(xval, yval):
f.vbar(x, xbarwidth, y, line_color = "white")
show(f)
You have to ckeck your implementation for the roulette() and listf. If you call
roulette_list = listf(200)
this returns only one value right now and than np.histogram doesn't work as intended.
Edit
My you can substitute your call roulette_list = listf(200) function with roulette_list =np.random.randint(0, 38, size=200, dtype=int). If you do so for a test, you will see some output in your figure.
To give y'all some context, I'm doing this inversion technique where I am trying to reproduce a profile using the integrated values. To do that I need to find the value within an array along a certain line(s). To exemplify my issue I have the following code:
fig, ax = plt.subplots(1, figsize = (10,10))
#Create the grid (different grid spacing):
X = np.arange(0,10.01,0.25)
Y = np.arange(0,10.01,1.00)
#Create the 2D array to be plotted
Z = []
for i in range(np.size(X)):
Zaux = []
for j in range(np.size(Y)):
Zaux.append(i*j + j)
ax.scatter(X[i],Y[j], color = 'red', s = 0.25)
Z.append(Zaux)
#Mesh the 1D grids:
Ymesh, Xmesh = np.meshgrid(Y, X)
#Plot the color plot:
ax.pcolor(Y,X, Z, cmap='viridis', vmin=np.nanmin(Z), vmax=np.nanmax(Z))
#Plot the points in the grid of the color plot:
for i in range(np.size(X)):
for j in range(np.size(Y)):
ax.scatter(Y[j],X[i], color = 'red', s = 3)
#Create a set of lines:
for i in np.linspace(0,2,5):
X_line = np.linspace(0,10,256)
Y_line = i*X_line*3.1415-4
#Plot each line:
ax.plot(X_line,Y_line, color = 'blue')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
plt.show()
That outputs this graph:
I need to find the closest points in Z that are being crossed by each of the lines. The idea is to integrate the values in Z that are crossed by the blue lines and plot that as a function of slope of the lines. Anyone has a good solution for it? I've tried a set of for loops, but I think it's kind of clunky.
Anyway, thanks for your time...
I am not sure about the closest points thing. That seems "clunky" too. What if it passes exactly in the middle between two points? Also I already had written code that weighs the four neighbor pixels by their closeness for an other project so I am going with that. Also I take the liberty of not rescaling the picture.
i,j = np.meshgrid(np.arange(41),np.arange(11))
Z = i*j + j
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [1-(x % 1), x % 1]
weights_y = [1-(y % 1), y % 1]
start_x = np.floor(x).astype('int')
start_y = np.floor(y).astype('int')
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
And a little sanity check it returns the picture if we give it it's coordinates.
image_model = Image_knn()
image_model.fit(Z)
assert np.allclose(image_model.predict(*np.where(np.ones(Z.shape, dtype='bool'))).reshape((11,41)), Z)
I generate m=100 lines and scale the points on them so that they are evenly spaced. Here is a plot of every 10th of them.
n = 1000
m = 100
slopes = np.linspace(1e-10,10,m)
t, slope = np.meshgrid(np.linspace(0,1,n), slopes)
x_max, y_max = Z.shape[0]-1, Z.shape[1]-1
lines_x = t
lines_y = t*slope
scales = np.broadcast_to(np.stack([x_max/lines_x[:,-1], y_max/lines_y[:,-1]]).min(axis=0), (n,m)).T
lines_x *= scales
lines_y *= scales
And finally I can get the "points" consisting of slope and "integral" and draw it. You probably should take a closer look at the "integral" it's just a ruff guess of mine.
%%timeit
points = np.array([(slope, np.mean(image_model.predict(lines_x[i],lines_y[i]))
*np.linalg.norm(np.array((lines_x[i,-1],lines_y[i,-1]))))
for i,slope in enumerate(slopes)])
plt.scatter(points[:,0],points[:,1])
Notice the %%timeit in the last block. This takes ~38.3 ms on my machine and therefore wasn't optimized. As Donald Knuth puts it "premature optimization is the root of all evil". If you were to optimize this you would remove the for loop, shove all the coordinates for line points in the model at once by reshaping and reshaping back and then organize them with the slopes. But I saw no reason to put myself threw that for a few ms.
And finally we get a nice cusp as a reward. Notice that it makes sense that the maximum is at 4 since the diagonal is at a slope of 4 for our 40 by 10 picture. The intuition for the cusp is a bit harder to explain but I guess you probably have that already. For the length it comes down to the function (x,y) -> sqrt(x^2+y^2) having different directional differentials when going up and when going left on the rectangle.
I have a random walk on a 2D grid with randomly initialised positions. I am looking for a condition, where if the random walk is within some range of the initial positions of the other random walks, it will stop.
While I found this easy to implement in the simple case, I am having trouble implementing it in the case of N random walks. This is due to the fact, that the code needs to check for a range of values around every initial position, except for the one, which is around the current random walk.
P.S This is my first post on stack overflow. Please, let me know if I was being too vague or did not follow the guidelines for asking questions on here.
import numpy.random as rd #importing libraries
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import numpy.ma as ma
lsize=100
T=1000 #n of steps
N=5 #n of walks
xvec=np.zeros(N)
yvec=np.zeros(N)
xstore=np.zeros(T+1)
ystore=np.zeros(T+1)
xmat=np.zeros((T+1,N))
ymat=np.zeros((T+1,N))
for i in range(N): #randomly assigns initial position
xcor=rd.randint(1,lsize)
ycor=rd.randint(1,lsize)
xvec[i]=xcor
yvec[i]=ycor
for i in range(N):
xstore[0]=xvec[i]
ystore[0]=yvec[i]
for j in range(T):
A=[0,1,2,3]
temp=rd.choice(A)
if temp==0:
ystore[j+1]=ystore[j]+1 #up
xstore[j+1]=xstore[j]
elif temp==1:
xstore[j+1]=xstore[j]+1 #right
ystore[j+1]=ystore[j]
elif temp==2:
ystore[j+1]=ystore[j]-1 #down
xstore[j+1]=xstore[j]
elif temp==3:
xstore[j+1]=xstore[j]-1 #left
ystore[j+1]=ystore[j]
xstore[j+1]=np.mod(xstore[j+1], lsize+1)
ystore[j+1]=np.mod(ystore[j+1], lsize+1)
xmat[:,i]=xstore
ymat[:,i]=ystore
plt.plot(xmat,ymat)
plt.show()
It's a well-asked question apart from the fact that you could have defined "within some range of the initial positions of the other random walks" better. I will assume that you mean some distance in x or y or some distance criterion in the x,y plane. The following outlines a solution for a distance criterion in x only, but the extension to other criteria is straightforward.
Basically you want a checking criterion at the end of you inner for-loop:
distance_in_x = np.mod(np.abs(xvec - xstore[j+1]), lsize)
distance_in_x[i] = np.inf # effectively mask that position
if np.any(distance_in_x <= min_distance_in_x):
break
This assumes that you have defined min_distance_in_x somewhere above.
The basic trick is that you mask the distance to origin of the walk itself by adding infinity to it. Similarly, you could also just add min_distance_in_x to ensure that the check in the following line never picks up that origin.
Edit
For a square around the origin of starting points, the script becomes:
import numpy.random as rd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import numpy.ma as ma
lsize=100
T=1000 #n of steps
N=10 #n of walks
xvec=np.zeros(N)
yvec=np.zeros(N)
xmat=np.full((T+1,N), np.nan)
ymat=np.full((T+1,N), np.nan)
min_distance_in_x = 3
min_distance_in_y = 3
# randomly assigns initial position
for i in range(N):
xcor=rd.randint(1,lsize)
ycor=rd.randint(1,lsize)
xvec[i]=xcor
yvec[i]=ycor
# walk
for i in range(N):
xstore=np.full(T+1, np.nan)
ystore=np.full(T+1, np.nan)
xstore[0]=xvec[i]
ystore[0]=yvec[i]
for j in range(T):
A=[0,1,2,3]
temp=rd.choice(A)
if temp==0:
ystore[j+1]=ystore[j]+1 #up
xstore[j+1]=xstore[j]
elif temp==1:
xstore[j+1]=xstore[j]+1 #right
ystore[j+1]=ystore[j]
elif temp==2:
ystore[j+1]=ystore[j]-1 #down
xstore[j+1]=xstore[j]
elif temp==3:
xstore[j+1]=xstore[j]-1 #left
ystore[j+1]=ystore[j]
xstore[j+1]=np.mod(xstore[j+1], lsize+1)
ystore[j+1]=np.mod(ystore[j+1], lsize+1)
distance_in_x = np.abs(xvec - xstore[j+1])
distance_in_x[i] = np.inf # effectively mask that position
distance_in_y = np.abs(yvec - ystore[j+1])
distance_in_y[i] = np.inf # effectively mask that position
if np.any(np.logical_and(distance_in_x <= min_distance_in_x,
distance_in_y <= min_distance_in_y)):
print("Got too close on run #{}!".format(i))
break
xmat[:,i]=xstore
ymat[:,i]=ystore
for x, y in zip(xmat.T, ymat.T):
# break the line by inserting NaNs where the boundary is crossed (i.e. a step size > 1)
linebreaks, = np.where((np.abs(np.diff(x)) > 1) | (np.abs(np.diff(y)) > 1))
if linebreaks.size > 0 :
x = np.insert(x, linebreaks+1, np.nan)
y = np.insert(y, linebreaks+1, np.nan)
# plot lines
plt.plot(x, y)
# plot start points
plt.gca().set_prop_cycle(None) # resets color cycle
for x, y in zip(xmat[0,:], ymat[0,:]):
plt.plot(x, y, 'o', ms=10)
# plot end points
plt.gca().set_prop_cycle(None) # resets color cycle
for x, y in zip(xmat.T, ymat.T):
# select last non-nan entry
x = x[~np.isnan(x)][-1]
y = y[~np.isnan(y)][-1]
plt.plot(x, y, '^', ms=10)
plt.show()
I wrote a function that calculates relativistic kinetic energy but I'm having some problems graphing it. I'm working in Python Jupyter.
I'm using Taylor for very small values of v (so I don't end up with E=0 because of cancellation) and the original expression for the biggest values.
I have to calculate it for v between 1e-6 and 2.88e8.
m=1
c=3e8
def E(v):
if (v/c) <= 0.8860:
return ((m*v**2)/2) + ((3*m*v**4)/(8*c**2)) + ((5*m*v**6)/(16*c**4)) + ((35*m*v**8)/(128*c**6))
else:
return (m*c**2)*((1/sp.sqrt(1-(v**2/c**2)))-1)
This gives me very accurate results.
Now the problem is graphing E(v). I have to use matplotlib (+ numpy and scipy for everything else). Here is what I wrote for a log-log graph.
xs = np.logspace(-6, 8.5)
fig, ax = plt.subplots(figsize=[10,10])
ax.plot(xs, E(xs))
ax.set_xscale('log')
ax.set_yscale('log')
I am getting a value error :
"ValueError: The truth value of an array with more than one element
is ambiguous. Use a.any() or a.all()", and it points at "if (v/c) <= 0.8860:".
what's this error means and what I did wrong. I'd be very grateful if you could help me graph or change my function.
You need to create a loop rather than just a condition:
m=1
c=3e8
def E(v):
for i in range(len(v)):
if (v[i]/c) <= 0.8860:
return ((m*v**2)/2) + ((3*m*v**4)/(8*c**2)) + ((5*m*v**6)/(16*c**4)) + ((35*m*v**8)/(128*c**6))
else:
return (m*c**2)*((1/sp.sqrt(1-(v**2/c**2)))-1)
xs = np.logspace(-6, 8.5)
fig, ax = plt.subplots(figsize=[10,10])
ax.plot(xs, E(xs))
ax.set_xscale('log')
ax.set_yscale('log')
The condition is expected a value for 'v' to compare to the 0.8860.
when presented with an array that has multiple values it says that it can't give True or False for an array.
all() and any() are methods to extract True or False from a list or an array:
any() will give True if any is True and all() will only if all is True.
The statement if (v/c) <= 0.8860 is not well defined, because for some values in v this is True for others it is False. This is a bit like asking "Are people taller than 1.8 meters?" There is no definitive answer; some are, some are not. The error tells you to specify exactly that: Do you want to know if all people are bigger or if any one person is bigger than 1.8m? Here you want to actually know something else: You want for each entry a different result, depending on the condition.
You may use numpy.piecewise to define a function differently for certain conditions. Also mind that no entry of v should be larger than c as this would result in an infinite energy.
import numpy as np
import matplotlib.pyplot as plt
m=1
c=3.e8
lowv = lambda v:((m*v**2)/2) + ((3*m*v**4)/(8*c**2)) + ((5*m*v**6)/(16*c**4)) + ((35*m*v**8)/(128*c**6))
highv = lambda v:(m*c**2)*(1./np.sqrt(1-(v/c)**2)-1)
E = lambda v : np.piecewise(v, [(v/c) <= 0.8860, (v/c) > 0.8860], [lowv,highv])
xs = 2.9999*np.logspace(-6, 8, num=1000)
fig, ax = plt.subplots(figsize=[10,10])
ax.plot(xs, E(xs))
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
Alternatively you may use numpy.vectorize to make the existing function evaluated for each entry in v individually. This is of course less efficient and should not be used for large arrays. Here is does not matter too much.
import numpy as np
import matplotlib.pyplot as plt
m=1
c=3.e8
def E(v):
if (v/c) <= 0.8860:
return ((m*v**2)/2) + ((3*m*v**4)/(8*c**2)) + ((5*m*v**6)/(16*c**4)) + ((35*m*v**8)/(128*c**6))
else:
return (m*c**2)*((1/np.sqrt(1-(v**2/c**2)))-1)
Ev = np.vectorize(E)
xs = 2.9999*np.logspace(-6, 8, num=1000)
fig, ax = plt.subplots(figsize=[10,10])
ax.plot(xs, Ev(xs))
ax.set_xscale('log')
ax.set_yscale('log')
plt.show()
I have angles that form a complete turn in an array x, from -90 to 270 e.g. (it may be defined otherwise, like from 0 to 360 or -180 to 180) with step 1 or whatever.
asin function is valid only between -90 and +90.
Thus, angles < -90 or > 90 would be "mapped" between these values.
E.g. y = some_asin_func(over_sin(x)) will end up in an y value that is always between -90 and +90. So y is stuck between -90 and +90.
I do need to retrieve to which x-input is y related, because it's ambiguous yet: for example, the function over (x) will give the same y values for x = 120 and x = 60, or x = -47 and x = 223. Which is not what I want.
Put an other way; I need y making a complete turn as x does, ranging from where x starts up to where x ends.
An image will be better:
Here, x ranges between -90 (left) to 270 (right of the graph).
The valid part of the curve is between x=-90 and x=+90 (left half of the graph).
All other values are like mirrored about y=90 or y=-90.
For x=180 for example, I got y=0 and it should be y=180.
For x=270, I have y=-90 but it should be y=270, thus +360.
Here's a code sample:
A = 50 # you can make this value vary to have different curves like in the images, when A=0 -> shape is triangle-like, when A=90-> shape is square-like.
x = np.linspace(-90,270,int(1e3))
u = np.sin(math.pi*A/180)*np.cos(math.pi*x/180)
v = 180*(np.arcsin(u))/math.pi
y = 180*np.arcsin(np.sin(math.pi*x/180)/np.cos(math.pi*v/180))/math.pi
plt.plot(x,y)
plt.grid(True)
Once again, first left half of the graph is completely correct.
The right half is also correct in its behavior, but in final, here, it must be mirrored about an horizontal axis at position y=+90 when x>90, like this:
That is, it's like the function is mirrored about y=-90 and y=+90 for y where x is out of the range [-90,+90] and only where where x is out of the range [-90,+90].
I want to un-mirror it outside the valid [-90,+90] range:
about y=-90 where y is lower than -90
about y=+90 where y is greater than +90
And of course, modulo each complete turn.
Here an other example where x ranges from -180 to 180 and the desired behavior:
Yet:
Wanted:
I have first tested some simple thing up now:
A = 50
x = np.linspace(-180,180,int(1e3))
u = np.sin(math.pi*A/180)*np.cos(math.pi*x/180)
v = 180*(np.arcsin(u))/math.pi
y = 180*np.arcsin(np.sin(math.pi*x/180)/np.cos(math.pi*v/180))/math.pi
for i,j in np.ndenumerate(x):
xval = (j-180)%180-180
if (xval < -90):
y[i] = y[i]-val
elif (xval > 90):
y[i] = y[i]+val
plt.plot(x,y);
plt.grid(True)
plt.show()
which doesn't work at all but I think the background idea is there...
I guess it may be some kind of modulo trick but can't figure it out.
Here a solution that fixes the periodicity of the cos function 'brute force' by calculating an offset and a sign correction based on the x value. I'm sure there is something better out there, but I would almost need a drawing with the angles and distances involved.
from matplotlib import pyplot as plt
import numpy as np
fig, ax = plt.subplots(1,1, figsize=(4,4))
x = np.linspace(-540,540,1000)
sign = np.sign(np.cos(np.pi*x/180))
offset = ((x-90)//180)*180
for A in range(1,91,9):
u = np.sin(np.pi*A/180)*np.cos(np.pi*x/180)
v = 180*(np.arcsin(u))/np.pi
y = 180*np.arcsin(np.sin(np.pi*x/180)/np.cos(np.pi*v/180))/np.pi
y = sign*y + offset
ax.plot(x,y)
ax.grid(True)
plt.show()
The result for the interval [-540, 540] looks like this:
Note that you can get pi also from numpy, so you don't need to import math -- I altered the code accordingly.
EDIT:
Apparently I first slightly misunderstood the OP's desired output. If the calculation of offset is just slightly changed, the result is as requested:
from matplotlib import pyplot as plt
import numpy as np
fig, ax = plt.subplots(1,1, figsize=(4,4))
x = np.linspace(-720,720,1000)
sign = np.sign(np.cos(np.pi*x/180))
offset = ((x-90)//180 +1 )*180 - ((x-180)//360+1)*360
for A in range(1,91,9):
u = np.sin(np.pi*A/180)*np.cos(np.pi*x/180)
v = 180*(np.arcsin(u))/np.pi
y = 180*np.arcsin(np.sin(np.pi*x/180)/np.cos(np.pi*v/180))/np.pi
y = sign*y + offset
ax.plot(x,y)
ax.grid(True)
plt.show()
The result now looks like this:
Thank you #Thomas Kühn, it seems fine except I wanted to restrict the function in a single same turn in respect to y-values. Anyway, it's only aesthetics.
Here's what I found by my side. It's maybe not perfect but it works:
A = 50
u = np.sin(math.pi*A/180)*np.cos(math.pi*x/180)
v = 180*(np.arcsin(u))/math.pi
y = 180*np.arcsin(np.sin(math.pi*x/180)/np.cos(math.pi*v/180))/math.pi
for i,j in np.ndenumerate(x):
val = (j-180)%360-180
if (val < -90):
y[i] = -180-y[i]
elif (val > 90):
y[i] = 180-y[i]
Here are some expected results:
Range from -180 to +180
Range from 0 to +360
Range from -720 to +720
Range from -360 to +360 with some different A values.
Funny thing is that it reminds me some electronics diagrams as well.
Periodic phenomenons are everywhere!