I want to make a graph with more than one function. My goal is at the point that function a ends, function b start but with an angle.
Code:
import numpy as np
import matplotlib.pyplot as plt
h = 5
l = 5
vnp1 = 3
h1 = np.arange(0, h+1, 1)
a = (vnp1*1000) * h1 / 1000
l1 = np.arange(0, l+1, 1)
b = (vnp1*1000) ** 2 * l1 / 1000
plt.plot(h1, a)
plt.plot(l1, b)
From what I understand, you would like the x-axis of the second function to be modified. Try this:
plt.plot(h1, a)
plt.plot(np.arange(h1[-1], h1[-1]+l+1, 1), b)
Related
I'm trying to generate a sine wave that matches my requirements for amplitude, number of phases and start and end values.
I can accurately control amplitude and number of phases, but I don't know how to set the start and end values accurately. I believe there is a mathematical approach to this, but I don't know where to start.
I also tried actually "cropping" the wave by cutting elements of y before start and after end, but beside being not very elegant, I encountered issues with accuracy.
Thanks for any help!
import numpy as np
import math
import matplotlib.pylab as plt
min=0
max=180
f = 2
start = 45
stop = 60
t = np.linspace(0, 2*np.pi, 1000)
y = ((max+min)/2) + (((max-min)/2) * np.sin(f*t + 100)) # "100" set manually by experimenting
actual_min, actual_max = np.min(y), np.max(y)
actual_start, actual_stop = y[0], y[-1]
print(f"min: {actual_min}, max: {actual_max}")
print(f"start: {actual_start}, stop: {actual_stop}")
plt.plot(t, y)
min: 0.00019246157938823671, max: 179.99994783571566
start: 44.42709230012171, stop: 44.42709230012167
You need arcsin to get the t for a given y:
import numpy as np
import matplotlib.pylab as plt
min = 0
max = 180
f = 2
start = 45
stop = 60
a = (max + min) / 2
b = (max - min) / 2
t0 = np.arcsin((start - a) / b) / f
t1 = np.arcsin((stop - a) / b) / f + 2 * np.pi
t = np.linspace(t0, t1, 1000)
y = a + b * np.sin(f * t)
assert np.allclose([start, stop], [y[0], y[-1]])
I want to plot a graph in PyCharm, using Matplotlib and a parametric equation. In this equation x and y are defined by some constant variables (a, b & c) and another variable (θ) which is in a range between 0 - 10. Python doesn't seem to handle the range because it's not a string.
This is an example code with an example equation:
import math
import matplotlib.pyplot as plt
a = 2
b = 3
c = 4
θ = range(0, 10)
x = (a + b * c) / θ
y = ((a / b) + c) * θ
plt.plot(x, y)
plt.show()
I would like to have something like this (2d) and eventually 3d:
I have tried to use the for loop and updating the graph, but that resulted in a very slow PyCharm and the graph to shut down.
Does any of you know how to do this?
Found the answer:
import math
import matplotlib.pyplot as plt
a = 2
b = 3
c = 4
θ = np.linspace(0, 10) #this sets an array between 0 and 10
x = (a + b * c) / θ
y = ((a / b) + c) * θ
plt.plot(x, y)
plt.show()
For the rest as an advice: np.linspace doesn't work with math.fabs, math.cos, math.pow. You have to use np.abs, np.cos or np.power instead ;)
Is is possible to have a plot, something like This random triangle, but instead of . we have Roses?
I mean, How can we plot the below Rose in random locations??
import numpy as np
import matplotlib.pyplot as plt
t,k = np.linspace(0,2*np.pi,1000),5
x = np.cos(k*t)*np.cos(t)
y = np.cos(k*t)*np.sin(t)
plt.plot(x,y,'r')
plt.axis('off')
plt.axis('square')
plt.show()
yes! you just plot your rose at lots of random points as sampled from your linked out question.
first I've refactored the method so it returns size points uniformly sampled within the given triangle:
import numpy as np
import matplotlib.pyplot as plt
def trisample(A, B, C, size=1):
r1 = np.random.rand(size)
r2 = np.random.rand(size)
s1 = np.sqrt(r1)
p1 = 1 - s1
p2 = (1 - r2) * s1
p3 = r2 * s1
x = A[0] * p1 + B[0] * p2 + C[0] * p3
y = A[1] * p1 + B[1] * p2 + C[1] * p3
return x, y
next we calculate a few of your roses:
t, k, z = np.linspace(0, np.pi, 5*5+1), 5, 0.1
x_r = np.cos(k*t) * np.sin(t) * z
y_r = np.cos(k*t) * np.cos(t) * z
note that your pi*2 meant that it was orbited twice so I've dropped that, also I only use 5 points per "petal" to speed things up. z scales the roses down so they fit into the triangle
finally we sample some points in the triangle, and plot them as you did:
for x_t, y_t in zip(*trisample([1,1], [5,3], [2,5], 100)):
plt.plot(x_r + x_t, y_r + y_t, lw=1)
plt.axis('off')
plt.axis('square');
which gives something like the following:
You need a random start point for the rose. I guess you could something like thatin this manner:
import numpy as np
import matplotlib.pyplot as plt
import random
t,k = np.linspace(0,2*np.pi,1000),5
x_start = random.randint(0, 10)
y_start = random.randint(0, 10)
x = x_start + np.cos(k*t)*np.cos(t)
y = y_start + np.cos(k*t)*np.sin(t)
plt.plot(x,y,'r')
plt.axis('off')
plt.axis('square')
plt.show()
To do some simulations in Python, I'm trying to generate numbers a,b,c such that a^2 + b^2 + c^2 = 1. I think generating some a between 0 and 1, then some b between 0 and sqrt(1 - a^2), and then c = sqrt(1 - a^2 - b^2) would work.
Floating point values are fine, the sum of squares should be close to 1. I want to keep generating them for some iterations.
Being new to Python, I'm not really sure how to do this. Negatives are allowed.
Edit: Thanks a lot for the answers!
According to this answer at stats.stackexchange.com, you should use normally distributed values to get uniformly distributed values on a sphere. That would mean, you could do:
import numpy as np
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
Just in case your interested in the probability densities I decided to do a comparison between the different approaches:
import numpy as np
import random
import math
def MSeifert():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
def VBB():
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
return x[0], x[1], x[2]
def user3684792():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
def JohanL():
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
return a, b, c
def SeverinPappadeux():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
And plotting the distributions:
%matplotlib notebook
import matplotlib.pyplot as plt
f, axes = plt.subplots(3, 4)
for func_idx, func in enumerate([MSeifert, JohanL, user3684792, VBB]):
axes[0, func_idx].set_title(str(func.__name__))
res = [func() for _ in range(50000)]
for idx in range(3):
axes[idx, func_idx].hist([i[idx] for i in res], bins='auto')
axes[0, 0].set_ylabel('a')
axes[1, 0].set_ylabel('b')
axes[2, 0].set_ylabel('c')
plt.tight_layout()
With the result:
Explanation: The rows show the distributions for a, b and c respectively while the columns show the histograms (distributions) of the different approaches.
The only approaches that give a uniformly random distribution in the range (-1, 1) are JohanLs and Severin Pappadeux's approach. All other approaches have some features like spikes or a functional behavior in the range [0, 1). Note that these two solution currently gives values between -1 and 1 while all other approaches give values between 0 and 1.
I think it is actually a cool problem, and a nice way to do this is to just use spherical polar coordinates and generate the angles at random.
import random
import numpy as np
def random_pt():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
You could do it like this:
import random
import math
def three_random_numbers_adding_to_one():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
a, b, c = three_random_numbers_adding_to_one()
print(a**2 + b**2 + c**2)
However floats have only limited precision so these won't add to exactly 1, just approximately.
You may need to check if the numbers generated with this function are "random enough". It could be that this setup biases the "randomness".
The "right" answer depends on whether you are looking for a uniform random distribution in space, or on the surface of a sphere, or something else. If you are looking for points on the surface of a sphere, you still have to worry about the cos(theta) factor which will cause points to appear "bunched up" near the poles of the sphere. Since exact nature is not clear from your question, here is a "totally random" distribution that should work:
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
Another advantage here is that since we are using numpy arrays, you can quickly scale to large sets of points too, by using x = np.random.uniform(0, 1, (3, n)) for any n.
Time to add another solution, heh...
This time it is truly uniform on the unit sphere point picking - check http://mathworld.wolfram.com/SpherePointPicking.html for details
import math
import random
def random_pt():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
for k in range(0, 100):
a, b, c = random_pt()
print("{0} {1} {2} {3}".format(a, b, c, a*a + b*b + c*c))
I am using the below codes to plot a line with two slopes as shown in the picture.The slope should should decline after certain limit [limit=5]. I am using vectorisation method to set the slope values.Is there any other method to set the slope values.Could anyone help me in this?
import matplotlib.pyplot as plt
import numpy as np
#Setting the condition
L=5 #Limit
m=1 #Slope
c=0 #Intercept
x=np.linspace(0,10,1000)
#Calculate the y value
y=m*x+c
#plot the line
plt.plot(x,y)
#Set the slope values using vectorisation
m[(x<L)] = 1.0
m[(x>L)] = 0.75
# plot the line again
plt.plot(x,y)
#Display with grids
plt.grid()
plt.show()
You may be overthinking the problem. There are two line segments in the picture:
From (0, 0) to (A, A')
From (A, A') to (B, B')
You know that A = 5, m = 1, so A' = 5. You also know that B = 10. Given that (B' - A') / (B - A) = 0.75, we have B' = 8.75. You can therefore make the plot as follows:
from matplotlib import pyplot as plt
m0 = 1
m1 = 0.75
x0 = 0 # Intercept
x1 = 5 # A
x2 = 10 # B
y0 = 0 # Intercept
y1 = y0 + m0 * (x1 - x0) # A'
y2 = y1 + m1 * (x2 - x1) # B'
plt.plot([x0, x1, x2], [y0, y1, y2])
Hopefully you see the pattern for computing y values for a given set of limits. Here is the result:
Now let's say you really did want to use vectorization for some obscure reason. You would want to compute all the y values up front and plot once, otherwise you will get weird results. Here are some modifications to your original code:
from matplotlib import pyplot as plt
import numpy as np
#Setting the condition
L = 5 #Limit
x = np.linspace(0, 10, 1000)
lMask = (x<=L) # Avoid recomputing this mask
# Compute a vector of slope values for each x
m = np.zeros_like(x)
m[lMask] = 1.0
m[~lMask] = 0.75
# Compute the y-intercept for each segment
b = np.zeros_like(x)
#b[lMask] = 0.0 # Already set to zero, so skip this step
b[~lMask] = L * (m[0] - 0.75)
# Compute the y-vector
y = m * x + b
# plot the line again
plt.plot(x, y)
#Display with grids
plt.grid()
plt.show()
Following your code, you should modify the main part like this:
x=np.linspace(0,10,1000)
m = np.empty(x.shape)
c = np.empty(x.shape)
m[(x<L)] = 1.0
c[x<L] = 0
m[(x>L)] = 0.75
c[x>L] = L*(1.0 - 0.75)
y=m*x+c
plt.plot(x,y)
Note that c needs to change as well for the line to be continuous. This is the result: