I'm doing a Python test to draw some functions.
The problem is that the points that both functions intersect at the same X are not correct.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
x = np.arange(-10,10,1)
def f(x):
return x+30
def z(x):
return x*x
plt.figure(figsize=(5,5))
plt.plot(x, f(x).astype(np.int))
plt.plot(x, z(x).astype(np.int))
plt.title("Gráfico de función" )
plt.xlabel("X")
plt.ylabel("Y")
idx = np.argwhere(np.diff(np.sign(f(x) - z(x)))).flatten()
plt.plot(x[idx], f(x[idx]), 'ro')
plt.legend(["F","Z"])
plt.show()
I expect only two points, but in the plot appeared four. Two of them are incorrect.
This error is not with the plot itself but with your methods of getting the points for this case when the intersection is an integer value. When taking np.diff of np.sign you go from -1 to 0 to 1 at the intersection points giving you 1 at 4 locations. If the intersection was not an int, you would get -1 to 1 and get the correct answer. If you try this instead you can find the integer intersection points:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
x = np.arange(-10,10,1)
def f(x):
return x+30.
def z(x):
return x*x
plt.figure(figsize=(5,5))
plt.plot(x, f(x).astype(np.int))
plt.plot(x, z(x).astype(np.int))
plt.xlabel("X")
plt.ylabel("Y")
#Say only get args where there is no sign (i.e. zero).
idx = np.argwhere((np.sign(f(x) - z(x))==0))
plt.plot(x[idx], f(x[idx]), 'ro')
plt.legend(["F","Z"])
plt.show()
EDIT:
The above code only works if you have perfect integer intersections. To arbitrarily do both you need to check to see if a perfect integer intersection exists before deciding which method to use. I just used a simple for loop to do this but I am sure there are more elegant ways to do this.
for v in np.sign(f(x) - z(x)):
if v==0:
idx = np.argwhere(np.sign(f(x) - z(x))==0)
break
else:
idx = np.argwhere(np.diff(np.sign(f(x) - z(x))))
Related
I would like to create a multivariate function that takes the max value of 2 functions and then to plot it. However by using the max function there is an error when applying the function on the meshgrid. I have tried this on other multivariate function without the max function and it worked.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
def f(x,y):
return max(np.cos(x),np.sin(y))
x=np.linspace(0,5,20)
y=np.linspace(-3,2,20)
X, Y = np.meshgrid(x, y)
Z=f(X,Y)
fig = go.Figure(data=[go.Surface(x=X, y=Y, z=Z)])
fig.show()
The error I get is : The truth value of an array with more than one element is ambiguous. Use a.any() or a.all(). However, I don't think that the suggestion is adapted to my case. I also tried by defining the max function with if statement but as I expected I get the same error. Does anyone could help?
The np.sin and np.cos functions work with arrays and the max function would produce an ambiguous answer (do you want the maximum of both functions or a comparison - numpy doesn't know). I recommend doing the built-in math.sin, math.cos on each of the values in the arrays and compare to get the desired max value .
def f(x,y):
max_values = []
for x_value, y_value in zip(x,y): #(iterate both together)
max_values.append(max(math.cos(x_value), math.sin(y_value)))
This may run slower than before, but does this help?
try using ax.scatter3D with
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
x=np.linspace(0,5,20)
y=np.linspace(-3,2,20)
def f(x,y):
max_values = []
for x_value, y_value in zip(x,y): #(iterate both together)
max_values.append(max(math.cos(x_value), math.sin(y_value)))
X, Y = np.meshgrid(x, y)
max_values = []
for x_value, y_value in zip(X,Y):
Z=zip([math.cos(item) for item in x_value],[math.sin(item) for item in y_value])
max_values.append([max(x,y) for x,y in Z])
fig = plt.figure()
ax = plt.axes(projection="3d")
ax.scatter3D(X, Y,max_values, c=max_values, cmap='Greens');
plt.show()
I have found a very simple alternative without using np.meshgrid, and put it here in case it could help someone later.
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
x=np.linspace(0,5,20)
y=np.linspace(-3,2,20)
S=np.zeros([x.shape[0],y.shape[0]])
for i in range(x.shape[0]):
for j in range(y.shape[0]):
S[i,j]=f(x[i],y[j])
fig = go.Figure(data=[go.Surface(x=x, y=y, z=S, colorscale='Temps')])
fig.show()
Can someone explain why I get this strange output when running this code:
import matplotlib.pyplot as plt
import numpy as np
def x_y():
return np.random.randint(9999, size=1000), np.random.randint(9999, size=1000)
plt.plot(x_y())
plt.show()
The output:
Your data is a tuple of two 1000 length arrays.
def x_y():
return np.random.randint(9999, size=1000), np.random.randint(9999, size=1000)
xy = x_y()
print(len(xy))
# > 2
print(xy[0].shape)
# > (1000,)
Let's read pyplot's documentation:
plot(y) # plot y using x as index array 0..N-1
Thus pyplot will plot a line between (0, xy[0][i]) and (1, xy[1][i]), for i in range(1000).
You probably try to do this:
plt.plot(*x_y())
This time, it will plot 1000 points joined by lines: (xy[0][i], xy[1][i]) for i in range 1000.
Yet, the lines don't represent anything here. Therefore you probably want to see individual points:
plt.scatter(*x_y())
Your function x_y is returning a tuple, assigning each element to a variable gives the correct output.
import matplotlib.pyplot as plt
import numpy as np
def x_y():
return np.random.randint(9999, size=1000), np.random.randint(9999, size=1000)
x, y = x_y()
plt.plot(x, y)
plt.show()
I have a handful of data points that cluster along a line in 3d space. I have the x,y,z data in a csv file that I want to import. I would like to find an equation that represents that line, or the plane perpendicular to that line, or whatever is mathematically correct. These data are independent of each other. Maybe there are better ways to do this than what I tried to do but...
I attempted to replicate an old post here that seemed to be doing exactly what I'm trying to do
Fitting a line in 3D
but it seems that maybe updates over the past decade have left the second part of the code not working? Or maybe I'm just doing something wrong. I've included the entire thing that I frankensteined together from this at the bottom. There are two lines that seem to be giving me a problem.
I've snippeted them out here...
import numpy as np
pts = np.add.accumulate(np.random.random((10,3)))
x,y,z = pts.T
# this will find the slope and x-intercept of a plane
# parallel to the y-axis that best fits the data
A_xz = np.vstack((x, np.ones(len(x)))).T
m_xz, c_xz = np.linalg.lstsq(A_xz, z)[0]
# again for a plane parallel to the x-axis
A_yz = np.vstack((y, np.ones(len(y)))).T
m_yz, c_yz = np.linalg.lstsq(A_yz, z)[0]
# the intersection of those two planes and
# the function for the line would be:
# z = m_yz * y + c_yz
# z = m_xz * x + c_xz
# or:
def lin(z):
x = (z - c_xz)/m_xz
y = (z - c_yz)/m_yz
return x,y
#verifying:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
zz = np.linspace(0,5)
xx,yy = lin(zz)
ax.scatter(x, y, z)
ax.plot(xx,yy,zz)
plt.savefig('test.png')
plt.show()
They return this, but no values...
FutureWarning: rcond parameter will change to the default of machine precision times max(M, N) where M and N are the input matrix dimensions.
To use the future default and silence this warning we advise to pass rcond=None, to keep using the old, explicitly pass rcond=-1.
m_xz, c_xz = np.linalg.lstsq(A_xz, z)[0]
FutureWarning: rcond parameter will change to the default of machine precision times max(M, N) where M and N are the input matrix dimensions.
To use the future default and silence this warning we advise to pass rcond=None, to keep using the old, explicitly pass rcond=-1.
m_yz, c_yz = np.linalg.lstsq(A_yz, z)[0]
I don't know where to go from here. I don't even actually need the plot, I just needed an equation and am ill-equipped to move forward. If anyone knows an easier way to do this, or can point me in the right direction, I'm willing to learn, but I'm very, very lost. Thank you in advance!!
Here is my entire frankensteined code in case that is what is causing the issue.
import pandas as pd
import numpy as np
mydataset = pd.read_csv('line1.csv')
x = mydataset.iloc[:,0]
y = mydataset.iloc[:,1]
z = mydataset.iloc[:,2]
data = np.concatenate((x[:, np.newaxis],
y[:, np.newaxis],
z[:, np.newaxis]),
axis=1)
# Calculate the mean of the points, i.e. the 'center' of the cloud
datamean = data.mean(axis=0)
# Do an SVD on the mean-centered data.
uu, dd, vv = np.linalg.svd(data - datamean)
# Now vv[0] contains the first principal component, i.e. the direction
# vector of the 'best fit' line in the least squares sense.
# Now generate some points along this best fit line, for plotting.
# we want it to have mean 0 (like the points we did
# the svd on). Also, it's a straight line, so we only need 2 points.
linepts = vv[0] * np.mgrid[-100:100:2j][:, np.newaxis]
# shift by the mean to get the line in the right place
linepts += datamean
# Verify that everything looks right.
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as m3d
ax = m3d.Axes3D(plt.figure())
ax.scatter3D(*data.T)
ax.plot3D(*linepts.T)
plt.show()
# this will find the slope and x-intercept of a plane
# parallel to the y-axis that best fits the data
A_xz = np.vstack((x, np.ones(len(x)))).T
m_xz, c_xz = np.linalg.lstsq(A_xz, z)[0]
# again for a plane parallel to the x-axis
A_yz = np.vstack((y, np.ones(len(y)))).T
m_yz, c_yz = np.linalg.lstsq(A_yz, z)[0]
# the intersection of those two planes and
# the function for the line would be:
# z = m_yz * y + c_yz
# z = m_xz * x + c_xz
# or:
def lin(z):
x = (z - c_xz)/m_xz
y = (z - c_yz)/m_yz
return x,y
print(x,y)
#verifying:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
zz = np.linspace(0,5)
xx,yy = lin(zz)
ax.scatter(x, y, z)
ax.plot(xx,yy,zz)
plt.savefig('test.png')
plt.show()
As was proposed in the old post you refer to, you could also make use of principal component analysis instead of a least squares approach. For that I suggest sklearn.decomposition.PCA from the sklearn package.
An example can be found below using the csv-file you provided.
import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
mydataset = pd.read_csv('line1.csv')
x = mydataset.iloc[:,0]
y = mydataset.iloc[:,1]
z = mydataset.iloc[:,2]
coords = np.array((x, y, z)).T
pca = PCA(n_components=1)
pca.fit(coords)
direction_vector = pca.components_
print(direction_vector)
# Create plot
origin = np.mean(coords, axis=0)
euclidian_distance = np.linalg.norm(coords - origin, axis=1)
extent = np.max(euclidian_distance)
line = np.vstack((origin - direction_vector * extent,
origin + direction_vector * extent))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(coords[:, 0], coords[:, 1], coords[:,2])
ax.plot(line[:, 0], line[:, 1], line[:, 2], 'r')
You can get rid of the complaint from leastsquares by adding rcond=None like this:
m_xz, c_xz = np.linalg.lstsq(A_xz, z, rcond=None)[0]
Is this the right decision for your situation? I have no idea. But there's more about it in the docs.
When I run your code with your inputs it seems to run just fine and I get values assigned to m_xz, c_xz, etc. If you don't call them explicitly with print('m_xz') (or whatever) then you won't see them.
m_xz
Out[42]: 5.186132604596112
c_xz
Out[43]: 62.5764694106141
Also, you reference your data in kind of two different ways. You get x, y, and z from your csv, but also put it into a numpy array. You can get rid of the duplication and pandas by just using numpy:
data = np.genfromtxt('line1.csv', delimiter=',', skip_header=1)
x = data[:,0]
y = data[:,1]
z = data[:,2]
I'm new to python and i am trying to plot 3 exponential functions on the same axis without using NumPy (not allowed). MatPlotLib, SymPy etc are allowed.
Question answered with broad answers below.
Code removed for privacy - this is not needed to understand the answers below as they are broad, or to answer any future questions on this topic
Of course, and I hope that you can understand my scruples, I prefer to leave my answer as generic as possible while trying to help you.
If you cannot use Numpy1, you have to use the math module and old good lists.
You start importing math from the standard library and the pyplot module from Matplotlib:
import math
import matplotlib.pyplot as plt
You decide the interval in which you plot your function and how many points you need for your plot
x_0, x_N = 0, 12
N =120
N is best intended as the number of intervals between N+1 points, so that we write
dx = (x_N-x_0)/N
Now we can say x_i = x_0 + dx × i but we have to store our results so that they are reusable. It's now that we have to use a list and we have two options, ① start with an empty list and append to it all the x_i using a for loop
xs = []
for i in range(N+1): xs.append(x_0+dx*i)
and ② a list comprehension
xs = [ x_0+dx*i for i in range(N+1) ]
(the result is identical).
You now have fixed the problem of the abscissae, it's the turn of the ordinates; again, we can use the append or the list comprehension
ys = []
for i in range(N+1):
x = xs[i]
y = math.sin(x/3.805)
ys.append(y)
or
ys = [ math.sin(xs[i]/3.805) for i in range(N+1) ]
Now you can plot the function
plt.plot(xs, ys, label='sin(%.3fx)'%(1/3.805))
plt.legend()
plt.grid()
plt.show()
(1) You cannot use Numpy but, but Matplotlib will use Numpy behind the scenes...
The lists that you pass to plt.plot are immediately converted to Numpy arrays! and only later are processed by the complex machinery of the plotting module.
Here is an approach using sympy, Python's package for symbolic math. It first solves x* to be log(delta/2)/a. Then, for some given values, a plot is drawn.
Note that sympy has a very simplified plotting function, with limited control over legend placement etc. If you need more control, the function values need to be calculated in an array.
from sympy import *
from sympy.abc import x, a, b
delta = symbols('delta', real=True)
x_star = symbols('x*', real=True)
f = exp(a*x)
g = -exp(a*x)
h = exp(a*x)*sin(b*x)
eq = Eq(delta, f.subs(x, x_star) - g.subs(x, x_star))
sol = solve(eq, x_star) # [log(delta/2)/a]
values = {a: 0.5, b: 1.5, delta:4.0}
x_star = sol[0].subs(values)
p = plot(f.subs(values), g.subs(values), h.subs(values), (x, 0, x_star),
show=False, legend=True, ylabel='', ylim=(-2,3.5))
p[0].line_color = 'r'
p[1].line_color = 'g'
p[2].line_color = 'b'
p.show()
Resulting plot:
Here is your version with some small adaptions to make it work. Note that matplotlib's plot functions work a little bit different than sympy's.
import matplotlib.pyplot as plt
from math import exp, sin
a = 5.0
b = 10.0
d = 0.1
x_star = 6.0
#x_star =(1/a)*float((math.log1p(d/2)))# #x* evenually needs to be in this form*#
print('x*= ',x_star)
steps = 200; r = [i*x_star/steps for i in range(steps)] # similar to np.linspace
f_r = []
g_r = []
h_r = []
for x in r:
y = exp(a*x)
f = y
f_r.append(f)
print('f(x)= ',f)
g = -1*y
g_r.append(g)
print('g(x)= ',g)
h = y*sin(b*x)
h_r.append(h)
print('h(x)= ',h)
plt.plot(r, f_r, 'b--', linewidth=1, color='r', label='f(x)=exp(a*x)')
plt.plot(r, g_r, 'b--', linewidth=2, color='g', label='g(x)=-exp(a*x)')
plt.plot(r, h_r, 'b--', linewidth=3, color='b', label='h(x)=exp(a*x)*sin(b*x)')
plt.ylabel('values')
plt.xlabel('x')
plt.legend(title='functions')
plt.show()
Right, so I've got a list of x values, y values and z values (which I think I converted into arrays?) which I want to make a surface plot, but it's not working.
Here's what I'm trying to do, you can ignore most of the code as it is pretty irrelevant - just look at the end where I have xdis, ydis and dist and where I'm trying to plot atm I'm getting ValueError: need more than 1 value to unpack :(. Help much appreciated.
from math import *
from numpy import *
import pylab
def sweep (v,p,q,r,s):
a=.98
for i in range (1, len(v)-1):
for j in range (1, len(v)-1):
c =0.0
if i==p and j==q: c =1.0
if i==r and j==s: c= -1.0
v[i,j]=(v[i -1,j]+v[i+1,j]+v[i,j -1]+v[i,j+1]+c-a*v[i,j])/(4-a)
def main():
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
ydis=[]
xdis=[]
resis=[]
for j in range(2,18):
for i in range(2,18):
v= zeros ((20,20),float )
p=q=9
r=i
s=j
dv =1.0e10
lastdv =0
count =0
while (fabs(dv - lastdv)>1.0e-7*fabs(dv)):
lastdv =dv
sweep(v,p,q,r,s)
dv=v[p,q]-v[r,s]
resis.append(dv)
xdis.append(r-p)
ydis.append(s-q)
X=array(xdis)
Y=array(ydis)
Z=array(resis)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X,Y,Z)
plt.show()
main()
plot_wireframe expects three 2D-arrays (X,Y,Z) as input. So,
after:
X=np.array(xdis)
Y=np.array(ydis)
Z=np.array(resis)
add:
X=X.reshape((-1,16))
Y=Y.reshape((-1,16))
Z=Z.reshape((-1,16))
It doesn't seem like the "sweep" function is modifying 'v' so you're getting an empty list.