Plot an equation with matplotlib and numpy - python

I want to plot an equation where the x axis represents time t and the y axis represents a variable n. Now the formula for n is n =((np.log(t)*10**6)/np.log(2)) + 1 and the maximum time on my graph is the time since big bang theory in seconds which is 4.35 x 10**17 seconds.
I tried this:
import matplotlib.pyplot as plt
import numpy as np
def graph(formula, x_range):
y = np.array(x_range)
x = eval(formula)
plt.plot(x,y)
plt.show()
graph(((np.log(x)*10**6)/np.log(2)) + 1, range(0, 4.35*10**17 ))
which doesn't seem to work. Any ideas on how I can achieve this?

There are several problems here:
The range is supposed to be defined over integers;
The number of items the range would generate is too large, you have to take huge steps;
The formula takes x as a variable, but you seem to define a np.array(x_range) in y; and more importantly
you use eval(..), but eval(..) usually a string or another object that can be parsed;
you do not give graph(..) a formula as first element: before Python calls graph(..) it first evaluates the operands.
In my opinion, the best way to achieve this is using a lambda-expression:
import matplotlib.pyplot as plt
import numpy as np
def graph(formula, x_range):
x = np.array(x_range)
#^ use x as range variable
y = formula(x)
#^ ^call the lambda expression with x
#| use y as function result
plt.plot(x,y)
plt.show()
graph(lambda x : ((np.log(x)*10**6)/np.log(2)) + 1, range(0, 435*10**15,10**12))
# ^use a lambda expression ^range over integers
# take huge steps
This generates the following image:
EDIT:
based on your comment you want time on the y-axis and the function on the x-axis, this can simply be achieved by assigning to the other variables like:
import matplotlib.pyplot as plt
import numpy as np
def graph(formula, x_range):
y = np.array(x_range)
x = formula(y)
plt.plot(x,y)
plt.show()
graph(lambda x : ((np.log(x)*10**6)/np.log(2)) + 1, range(0,435*10**15,10**12))
Note that you do not need to change the name of the variable in the lambda-expression: indeed when you call the lambda expression, the local x will simply be the y of the caller.

Related

matplotlib doesn't display the correct data

I am new to Python. For some reason when I look at the plot it displays all the data as if Y = 0 but the last one, which is weird since when I ask it to print Y it displays the right values. What am I doing wrong?
import math
import numpy as np
import matplotlib.pyplot as plt
y0=2 # [m]
g=9.81 # [m/s^2]
v=20 # initial speed [m/s]
y_target=1 # [m]
x=35 # [m]
n_iter=50
theta=np.linspace(0,0.5*math.pi,n_iter) # theta input [rad]
Y=np.zeros(n_iter) # y output [m]
for i in range(n_iter):
Y[i]=math.tan(theta[i])*x-g/(2*(v*math.cos(theta[i]))**2)*x**2+y0
plt.plot(theta,Y)
plt.ylabel('y [m]')
plt.xlabel('theta [rad]')
plt.ylim(top=max(Y),bottom=min(Y))
plt.show()
The problem is that the function blows up a bit as theta approaches π/2. Notice the little 1e33 at the top of the y-axis in the plot: the scale of that axis is huge, because the last value of y is essentially minus infinity (because of dividing by almost zero). If you change the limits of the y-axis, e.g. to (-1000, +1000), the plot looks correct.
But I can't resist helping you with something you didn't ask for help on... You are not using NumPy correctly. NumPy gives you two things: n-dimensional arrays as a data structure, and fast, optimized code for 'vectorized' computing with those arrays. In essence, you never need a loop in NumPy — you just compute with everything at once. Try doing 10 * np.array([1, 2, 3]) and you will get the idea.
So I would write your code like this:
import numpy as np
import matplotlib.pyplot as plt
# Problem parameters.
y0 = 2 # [m]
g = 9.81 # [m/s^2]
v = 20 # initial speed [m/s]
x = 35 # [m]
# Make theta [rad].
steps = 50
theta = np.linspace(0, 0.5*np.pi, steps)
# Compute y.
y = np.tan(theta) * x - g / (2 * (v * np.cos(theta))**2) * x**2 + y0
# Plot.
plt.plot(theta, y)
plt.ylabel('y [m]')
plt.xlabel('theta [rad]')
plt.ylim(-1000, 1000)
plt.show()
Notice that there's no loop — you just use the vector theta as if it were a scalar. And the math library (which can't handle NumPy's arrays, only scalars) is not needed at all when you're using NumPy.

How can I find the x-corresponding value of a function?

I have a function (Moorse potential in case anybody cares), and I want to find the coordinates of the minimum value in X. I can very easily find the minimum value in y with min(y), but, how can I find the x value asociated witgh the minimum y coordinate?
Copy of my code:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
from math import e
#para niquel
D= 0.4205 #profundidad de pozo
f = 2.7540 #distancia de equilibrio
a = 1.4199 #ancho de potencial
x = np.arange(-100,100,0.01) #Distancia interatómica / eje X
y = -D + D*(1-e**(-a*(x-f)))**2
plt.xlabel('Distancia interatómica [$\AA$]')
plt.ylabel('Energía [eV]')
plt.plot(x, y, color = 'mediumturquoise')
plt.xlim(2,5.5)
plt.ylim(-0.5, 0.75)
#plt.annotate('a$_{0}$', xy = (X, min(y)) ) HERE'S WHERE I'D NEED THE X COORDINATE!!
plt.show()
Thanks in advance!
You can use an argmin to find the minimum index of y, then based on how you generated the range, you can work backwards to get the actual value. You can do something like
x[np.argmin(y)]
Note that this might not be equivalent to
np.argmin(y) * 0.01 + -100
due to floating point rounding issues.
With some algebra (or a graphing calculator...) you can resolve the Morse Potential equation in terms of y and get an accurate and quick answer.
I went ahead and did this (using symbolab, you can confirm here) given your constants, and then plotted the graphs here to confirm they looked right. There are two mathematical functions due to the two cases arising when taking the square root.
Here's some code that should compute the x value based off the y value:
import math
def getXfromY(y):
if(y <= 2.754):
return -(math.ln(-math.sqrt(y/0.4205) + 1))/1.4199
elif(y > 2.754):
return -(math.ln(math.sqrt(y/0.4205) + 1))/1.4199

I am beginner, and have a question related to plotting in Python

I am new to python.
I wanted to know the syntax for a problem
Suppose I want to plot a quantity x = (constant with a fixed given value) * ln (1+z) versus z (which varies from c to d)
How do I define the variables x and z, how do I input an 'ln' function
I have imported numpy, scipy and matplotlib, but do not know how to proceed thereafter
Since you already imported numpy, here is just another answer:
import numpy as np
import matplotlib.pyplot as plt
x_coeff = 10
c = 0
d = 100
z = [i for i in range(c, d)]
x = [x_coeff * np.log(1+v) for i, v in enumerate(z)]
plt.plot(z, x)
plt.show()
It's always better to check the documents, and give out your first try:
https://docs.scipy.org/doc/numpy/reference/generated/numpy.log.html
You might also need to understand "list comprehension".
It's a beautiful and convenient way to create list in python.
For plotting a curve, you need two lists, one of them is domain on x axis and the other is range points on y-axis. first we take a constant as input,using python inbuilt input function and make sure that it is int, use math library and log function to do log as need.
import math
import matplotlib.pyplot as plt
a = int(input("enter a value for constant : "))
c,d = 0,100
xvals = list(range(c,d,1)) # start,end,step
print(xvals)
yvals = [a*math.log(1+x) for x in xvals]
print(yvals)
plt.plot(xvals,yvals)
plt.show()

Equation return different values for the same variable

I'm trying to create a chart but it looks incorrect.
For the range(0, 1000000) the chart should be starts at 0 and ends at 1 at x-axis, but it has negative values. In the begging it's OK, but after some value, it gets wrong.
I tried to manually calculate specific values and found out that there is a different result for the same value in the equation. Here is an example:
import numpy as np
import matplotlib.pyplot as plt
def graph(formula, x_range):
x = np.array(x_range)
y = eval(formula)
print(y)
plt.plot(x, y)
plt.show()
formula = '1-((2**32-1)/2**32)**(x*(x-1)/2)'
graph(formula, range(80300, 80301))
x = 80300
print(eval(formula))
There is a different result for the same value, here is the console output:
[-0.28319476]
0.5279390283223464
I have no idea why there is a different result for the same formula and the value. The correct is 0.5279390283223464.
To make your code work correctly use bigger datatype i.e (dtype="float64"), edit your code to:
x = np.array(x_range, dtype="float64")
or if you want the 2 results to match in precision add [0]
x = np.array(x_range, dtype="float64")[0]
x = np.array(x_range, dtype="float32")[0]
to understand why, read below:
if you change formula in your code to simple one for example (formula = "x + 100") you will get correct results
what does this mean?
it means that your formula which is '1-((232-1)/232)**(x*(x-1)/2)' cause an overflow in numpy "numpy built in C not python"
i tried the following code to narrow problem possibilities:
formula = '1-((2**32-1)/2**32)**(x*(x-1)/2)'
x = 80300
print(eval(formula))
x = np.array(range(80300, 80301))[0]
print(eval(formula))
output from sublime Text>>>
0.5279390283223464
RuntimeWarning: overflow encountered in long_scalars
import numpy as np
-0.28319476138546906
which support my point of view

Plot arbitrary 2-D function in python/pyplot like Matlab's Ezplot

I'm looking for a way to generate a plot similar to how ezplot works in MATLAB in that I can type:
ezplot('x^2 + y^2 = y + 5')
and get a graph ready to go for any arbitrary function. I'm only worrying about the case where I have both a x and a y.
I only have the function, and I'd really rather not go about trying to calculate all the y values for some given x range if I didn't have to.
The few solutions I've seen suggested are either about decision boundaries (which this is not. There is no test data or anything, just an arbitrary function) or are all for functions already defined as y = some x equation which doesn't really help me.
I would somewhat accept if there was a good way to mimic Wolfram|Alpha in their solve functionality("solve x^2 + y^2 = y + 5 for y" will give me two functions I could then graph separately), but rather prefer the ezplot as that's more or less instant within MATLAB.
I think you could use sympy plotting and parse_expr for this For your example, this would work as follows
from sympy.plotting import plot_implicit
from sympy.parsing.sympy_parser import parse_expr
def ezplot(s):
#Parse doesn't parse = sign so split
lhs, rhs = s.replace("^","**").split("=")
eqn_lhs = parse_expr(lhs)
eqn_rhs = parse_expr(rhs)
plot_implicit(eqn_lhs-eqn_rhs)
ezplot('x^2 + y^2 = y + 5')
This can be made as general as needed
You could use sympy to solve the equation and then use the resulting functions for plotting y over x:
import sympy
x=sympy.Symbol('x')
y=sympy.Symbol('y')
f = sympy.solve(x**2 + y**2 - y - 5, [y])
print f
xpts = (numpy.arange(10.)-5)/10
ypts = sympy.lambdify(x, f, 'numpy')(xpts)
# then e.g.: pylab.scatter(xpts, ypts)
#EdSmith solution works fine. Nevertheless, I have another suggestion. You can use plot a contour. You can rewrite your function as f(x, y)=0, and then use this code
from numpy import mgrid, pi
import matplotlib.pyplot as plt
def ezplot(f):
x, y = mgrid[-2*pi:2*pi:51, -2*pi:2*pi:51]
z = f(x, y)
ezplt = plt.contour(x, y, f, 0, colors='k')
return ezplt
That's the main idea. Of course, you can generalize it as the function in MATLAB, like general intervals of x and y, passing the function as a string, etc.

Categories

Resources