Matplotlib regression scattered plot using Python? - python

My Question
I tried regression using curve_fit function from scipy module, and now I am getting a scattered kind of plot, I can't figure it out, Why I am getting a scattered plot here ?
My Code
def scipyFunction(x,y):
plot(x, y, 'o', label='Original data', markersize=10)
x = [float(xn) for xn in x] #every element (xn) in x becomes a float
y = [float(yn) for yn in y] #every element (yn) in y becomes a float
x = np.array(x) #tranform data into numpy array
y = np.array(y) #transform data into numpy array
def functionForScipy(x,a,b,c,d):
return a*x**3 + b*x**2 + c*x + d
#make the curve_fit
popt,pcov = curve_fit(functionForScipy,x,y)
'''
The result is :
popt[0] = a, popt[1] = b, popt[2] = d of the function,
so f(x) = popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3].
'''
print(popt)
plt.plot(x, popt[0]*x**3 + popt[1]*x**2 + popt[2]*x + popt[3], label="Fitted Curve") # same as lin eabove
plt.legend(loc='upper left')
plt.show()
x and y plot is like this :

I suspect this occurs because the values in your x array are not monotonically increasing (that is to say that the each subsequent number is larger than the last).
You need to sort your x values before you plot them otherwise they will be all over the place, as shown in the example below.
import numpy as np
import matplotlib.pyplot as plt
def func(x):
return x**2
x = np.array([0, 5, 2, 1, 3, 4])
y = func(x)
plt.plot(x, y, 'b-', label='Unsorted')
x.sort()
y = func(x)
plt.plot(x, y, 'r-', label='Sorted')
plt.legend()
plt.show()

Probably your x and y are not sorted.
Don't forget the apply the same sorting you do on x on y as well. To achieve this zip is very handy. You could add the following at the beginning of your function:
comb = zip(x,y)
comb.sort(key=lambda x:x[0]) #sort according to x
x, y = zip(*comb) #both x and y are sorted now according to x

Related

3D graphing the complex values of a function in Python

This is the real function I am looking to represent in 3D:
y = f(x) = x^2 + 1
The complex function would be as follows:
w = f(z) = z^2 + 1
Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing.
We get:
f(x + iy) = x^2 + 2xyi - y^2 + 1
So:
u = x^2 - y^2 + 1
and v = 2xy
This u is what is being used in the code below.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
X, Y = np.meshgrid(x,y)
U = (X**2) - (Y**2) + 1
fig = plt.figure(dpi = 300)
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike.
Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well.
I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes.
The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with.
I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students.
Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(z):
return z**2 + 1
def freal(x, y):
return x**2 - y**2 + 1
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
yproj = 0 # value of y for which to project xu axes
xproj = 0 # value of x to project onto yu axes
X, Y = np.meshgrid(x,y)
Z = X + 1j * Y
W = f(Z)
U = W.real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, alpha=0.7)
# xu projection
xuproj = freal(x, yproj)
ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5)
ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5)
# yu projection
yuproj = freal(xproj, y)
ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5)
ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5)
# partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s
x = np.linspace(-3, 3, 150)
y = np.linspace(0, 3, 150)
X, Y = np.meshgrid(x,y)
U = f(X + 1j*Y).real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, cmap=cm.jet)
ax.set_box_aspect( (np.diff(ax.get_xlim())[0],
np.diff(ax.get_ylim())[0],
np.diff(ax.get_zlim())[0]))
#ax.set_aspect('equal')
plt.show()
gives this result:
and
The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this.
There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.

Euler method python

I have some problems with plot. Exatlct solution here is true, but eulerbmethod gives the same curve, but much lower
import numpy as np
import matplotlib.pyplot as plt
# Define parameters
f = lambda x, y: 2*x
h = 0.1
x = np.arange(-10, 10, h)
x0 = 0
y0 = 2
# Explicit Euler Method
y = np.zeros(len(x))
y[x0] = y0
for i in range(0, len(x) - 1):
y[i + 1] = y[i] + h*f(x[i], y[i])
plt.figure(figsize=(12, 8))
plt.plot(x, y, 'b--', label='Euler')
plt.plot(x, 2+x**2, 'g', label='Exact')
plt.title('Numerical integration methods')
plt.xlabel('x')
plt.ylabel('y')
plt.grid()
plt.legend()
plt.show()
That's because your "exact solution" is not correct.
When you integrate, you have to consider that you have a non-zero value for x:

Surface Plot of 3D Arrays using matplotlib

I have a function of the form f(x,y,z) and want to create a surface plot for it (level sets) using matplotlib. The problem I have is that plot_surface only accepts 3 arguments, whereas the type of plot I want to do is create a grid of x,y,z values and then plot the value of my function f at each of those points.
Here is a minimal example:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
bounds = [1,1,1]
numpoints = 25
x = np.linspace(-bounds[0], bounds[0], numpoints)
y = np.linspace(-bounds[1], bounds[1], numpoints)
z = np.linspace(-bounds[2], bounds[2], numpoints)
X, Y, Z = np.meshgrid(x, y, z)
s = X.shape
Ze = np.zeros(s)
Zp = np.zeros(s)
DT = np.zeros((numpoints**3,3))
# convert mesh into point vector for which the model can be evaluated
c = 0
for i in range(s[0]):
for j in range(s[1]):
for k in range(s[2]):
DT[c,0] = X[i,j,k]
DT[c,1] = Y[i,j,k]
DT[c,2] = Z[i,j,k]
c = c+1;
# this could be any function that returns a shape (numpoints**3,)
Ep = np.square(DT)[:,0]
c = 0
for i in range(s[0]):
for j in range(s[1]):
for k in range(s[2]):
Zp[i,j,k] = Ep[c]
c = c+1;
Now I would like to plot Zp as level sets in matplotlib. Is this possible?
The only way to represent 4 variables (x, y, x, f(x, y, z)) I could think in matplotlib is scatter the grid of x, y, z and give a color to the points that is proportional to f(x, y, z):
bounds = [1,1,1]
numpoints = 11
x = np.linspace(-bounds[0], bounds[0], numpoints)
y = np.linspace(-bounds[1], bounds[1], numpoints)
z = np.linspace(-bounds[2], bounds[2], numpoints)
X, Y, Z = np.meshgrid(x, y, z)
For exaple let's say taht f(x,y,z)=sin(x+y)+cos(y+z):
f_xyz = np.sin(X+Y)+np.cos(Y+Z)
Now let's scatter:
plt.figure(figsize=(7,7))
ax = plt.subplot(projection="3d")
ax.scatter(X, Y, Z, s=10, alpha=.5, c=f_xyz, cmap="RdBu")
plt.show()
As you can see the result is a bit confusing and not very clear, but it strongly depends on what function you want to plot. I hope you could find a better way

Inverse the spline interpolation SciPy

I have an array of X and Y data, and I also have a spline that outputs Y values based on X values.
I need to get a spline that will perform the reverse operation, i.e. output the X value over the Y value.
(interpolate.splrep(y, x, s=0) do not suggest)
def f(x):
return math.sin(x)
def compare_func(func, a, b, eps):
x_for_plot = list(np.arange(a, b, eps / 10))
x = list(np.arange(a, b, eps))
y_for_plot = [func(i) for i in x_for_plot]
y = [func(i) for i in x]
plt.plot(x_for_plot, y_for_plot, label='Orig')
plt.scatter(x, y, label='Points', color='blue')
spline = interpolate.splrep(x, y, s=0)
y_interpolated = interpolate.splev(x_for_plot, spline, der=0)
spline_inv = ???
x_calc = interpolate.splev(y_interpolated, spline_inv, der=0)
print(x_calc)
#x_calc and x_for_plot must be equal
plt.plot(x_for_plot, y_interpolated, label='Interpolated')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()
compare_func(f, -3, 3, 0.5)

curve_fit not optimizing one of the parameters

I need to fit with scipy.optimize.curve_fit some data that look like the points in the figure. I use a function y(x) (see def below) which gives a constant y(x)=c for x<x0, otherwise a polynomial (eg a second tilted line y1 = mx+q).
I give a reasonable initial guess for the parameters (x0, c, m, q), as show in the figure. The result from the fit shows that all the parameters are optimized except for the first one x0.
Why so?
Is it how I define the function testfit(x, *p), where x0 (=p[0]) appears within another function?
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# generate some data:
x = np.linspace(0,100,1000)
y1 = np.repeat(0, 500)
y2 = x[500:] - 50
y = np.concatenate((y1,y2))
y = y + np.random.randn(len(y))
def testfit(x, *p):
''' piecewise function used to fit
it's a constant (=p[1]) for x < p[0]
or a polynomial for x > p[0]
'''
x = x.astype(float)
y = np.piecewise(x, [x < p[0], x >= p[0]], [p[1], lambda x: np.poly1d(p[2:])(x)])
return y
# initial guess, one horizontal and one tilted line:
p0_guess = (30, 5, 0.3, -10)
popt, pcov = curve_fit(testfit, x, y, p0=p0_guess)
print('params guessed : '+str(p0_guess))
print('params from fit : '+str(popt))
plt.plot(x,y, '.')
plt.plot(x, testfit(x, *p0_guess), label='initial guess')
plt.plot(x, testfit(x, *popt), label='final fit')
plt.legend()
Output
params guessed : (30, 5, 0.3, -10)
params from fit : [ 30. 0.04970411 0.80106256 -34.17194401]
OptimizeWarning: Covariance of the parameters could not be estimated category=OptimizeWarning)
As suggested by kazemakase, I solved the problem with a smooth transition between the two functions I use to fit (one horizontal line followed by a polynomial). The trick was to multiply one function by sigmoid(x) and the other by 1-sigmoid(x), (where sigmoid(x) is defined below).
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
x = np.linspace(0,100,1000)
y1 = np.repeat(0, 500)
y2 = x[500:] - 50
y = np.concatenate((y1,y2))
y = y + np.random.randn(len(y))
def testfit(x, *p):
''' function to fit the indentation curve
p = [x0,c, poly1d_coeffs ]'''
x = x.astype(float)
y = p[1]*(1-sigmoid(x-p[0],k=1)) + np.poly1d(p[2:])(x) * sigmoid(x-p[0],k=1)
return y
def sigmoid(x, k=1):
return 1/(1+np.exp(-k*x))
p0_guess = (30, 5, 0.3, -10 )
popt, pcov = curve_fit(testfit, x, y, p0=p0_guess)
print('params guessed : '+str(p0_guess))
print('params from fit : '+str(popt))
plt.figure(1)
plt.clf()
plt.plot(x,y, 'y.')
plt.plot(x, testfit(x, *p0_guess), label='initial guess')
plt.plot(x, testfit(x, *popt), 'k', label='final fit')
plt.legend()
I had a similar problem. I ended up using np.gradient and a convolution to smooth the curve, then plotting it. Something like:
def mov_avg(n, data):
return np.convolve(data, np.ones((n,))/n, mode='valid')
If you want a more direct approach, you can try this:
def find_change(data):
def test_flag(pos):
grad = np.gradient(data) - np.gradient(data).mean()
return (grad[:pos]<0).sum() + (grad[pos:]>0).sum()
return np.vectorize(test_flag)(np.arange(len(data)-1)).argmax()
def find_gradient(pos, data):
return np.gradient(data[:pos]).mean(), np.gradient(data[pos:]).mean()
pos=find_change(x2)
print(pos, find_gradient(pos, data))
The first function calculates the point at which the gradient change by comparing the point gradient against the mean gradient, and finds the point from which the gradients are "mostly positive".
Hope it helps

Categories

Resources