I am trying to curve fit a sinusoidal shaped data set, but I a getting an error saying 'Only size-1 arrays can be converted to Python scalars'. How do I correctly pass my two series for X and Y values to fit the curve?
def objective(x, a, b, c, d):
return a * math.sin(b - x) + c * x**2 + d
# choose the input and output variables
x = moon_data["Full Moon"].values.squeeze()
y = moon_data["Full Moon Price"].values.squeeze()
plt.scatter(x,y) # This works!
# curve fit
popt, _ = curve_fit(objective, x, y) # This is the line causing the error
Moon Data is a Dataframe that I turned into a Series using .squeeze(). The original data looks like this (first 3 rows):
Full Moon
Full Moon Price
1488
2020-05-07
10001.0
1489
2020-06-05
9617.17
1490
2020-07-05
9083.8
Only Size 1 Arrays Error is a TypeError that gets triggered when you enter an array as a parameter in a function or method which accepts a single scalar value. So, the problem in your function is here: math.sin(b - x)
x can only be a single scalar value but what you are assigning is moon_data["Full Moon"] as x which is an array. Change it to np.sin(b*x) it will work but I guess you want something else so you will have to change the function accordingly.
You can't put datetime object inside sin(). Math.sin() takes a single number, not array. So, use np.sin() instead. Convert the moon_data["Full Moon"] column to datetime then toordinal so that you can put it into sin.
import pandas as pd
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
def objective(x, a, b, c,d):
return a * np.sin(b*x) + c * x**2+d
# choose the input and output variables
x = moon_data["Full Moon"]
y = moon_data["Full Moon Price"]
X=pd.to_datetime(x).apply(lambda x:x.toordinal())
plt.scatter(x,y) # This works!
# curve fit
popt, _ = curve_fit(objective, X, y)
Related
I'm trying to fit a parameter eta_H in function TGp_xx to some data (x_data, data_num_xx) using curve_fit. Now, the code below is a reduced version of what I'm using and it won't work by itself, but I hope the issue is conceptual enough to be understandable even from this
from scipy.optimize import curve_fit
Lx = 150
y_cut = 20
data = np.loadtxt("../dump/results.dat")
ux = data[:,3]
ux = np.reshape(ux , (Ly, Lx))
def Par_x(x,y,vec):
fdx = vec[(x+1)%Lx , y]
fsx = vec[(x-1+Lx)%Lx , y]
return (fdx - fsx) / 2.0
def TGp_xx(x, eta_H): return 2*eta_H*Par_x(x,y_cut,ux)
x_data = np.arange(Lx, dtype=np.int)
data_num_xx = np.empty(Lx, dtype='float64') #this is just a placeholder
popt_xx, pcov_xx = curve_fit(TGp_xx, x_data, data_num_xx)
I get an IndexError raised within Par_x:
fdx = vec[(x+1)%Lx , y]
IndexError: arrays used as indices must be of integer (or boolean) type
I tried something simpler like calling TGp_xx(x_data, some_constant) outside curve_fit, and it works. I don't really get why inside curve_fit i get the IndexError, as if I'm passing a float value (or an array of floats) as x, that can't be used as an index.
I am attempting to append a value #y that is calculated using the ODEINT onto a list called #y_list. i created a new function called #saves_vals which does this at every iteration point. The list is growing as the iterations run(which is good) however the new value of #y replaces all the values previously generated. i have attempted to create a copy of the #y values using #y[:] however this continues to fail. i am fairly new to python. please assist
from matplotlib import pyplot as plt
import numpy as np
from scipy.integrate import odeint
y_list =[]
def saves_vals(list_y,y):
print (" the value of y being used now is " +str(y))
list_y.append(y[:])
print ( " this is the y_list, as it grows " + str(list_y))
return (list_y)
def model(y,t):
k= saves_vals(y_list,y)
dydt = - 2.0 *y
return dydt
t = np.linspace(0, 10, 10)
y_0 = 10
y = odeint(model,y_0,t)
plt.figure(figsize =(4,4))
plt.plot(t,y)
plt.show()
i am expecting the list of the y values to grow as the iterations progress however to keep the results of the past iterations for later comparison
Below is a distance calculated (column y) based on values from sensor (column x).
test.txt - contents
x y
----------
-51.61 ,1.5
-51.61 ,1.5
-51.7 ,1.53
-51.91 ,1.55
-52.28 ,1.62
-52.35 ,1.63
-52.49 ,1.66
-52.78 ,1.71
-52.84 ,1.73
-52.90 ,1.74
-53.21 ,1.8
-53.43 ,1.85
-53.55 ,1.87
-53.71 ,1.91
-53.99 ,1.97
-54.13 ,2
-54.26 ,2.03
-54.37 ,2.06
-54.46 ,2.08
-54.59 ,2.11
-54.89 ,2.19
-54.94 ,2.2
-55.05 ,2.23
-55.11 ,2.24
-55.17 ,2.26
I would like to curve fit to find the constants a and b for the data in test.txt based on this function:
Function y = 10^((a-x)/10*b)
I use the following code:
import math
from numpy import genfromtxt
from scipy.optimize import curve_fit
inData = genfromtxt('test.txt',delimiter=',')
rssi_data = inData[:,0]
dist_data= inData[:,1]
print rssi_data
print dist_data
def func(x, a,b):
exp_val = (x-a)/(10.0*b)
return math.pow(10,exp_val)
coeffs, matcov = curve_fit(func,rssi_data,dist_data)
print(coeffs)
print(matcov)
The code does not execute successfully. Also I'm not sure if I'm passing the right parameters to curve_fit().
The function will need to process numpy-arrays but currently it can't because math.pow expects a scalar value. If I execute your code I get this Exception:
TypeError: only length-1 arrays can be converted to Python scalars
If you change your function to:
def func(x, a, b):
return 10 ** ((a - x) / (10 * b)) # ** is the power operator
It should work without exceptions:
>>> print(coeffs)
[-48.07485338 2.00667587]
>>> print(matcov)
[[ 3.59154631e-04 1.21357926e-04]
[ 1.21357926e-04 4.25732516e-05]]
Here the complete code:
def func(x, a, b):
return 10 ** ((a - x) / (10 * b))
coeffs, matcov = curve_fit(func, rssi_data, dist_data)
# And some plotting for visualization
import matplotlib.pyplot as plt
%matplotlib notebook # only works in IPython notebooks
plt.figure()
plt.scatter(rssi_data, dist_data, label='measured')
x = np.linspace(rssi_data.min(), rssi_data.max(), 1000)
plt.plot(x, func(x, coeffs[0], coeffs[1]), label='fitted')
plt.legend()
I upvoted the previous answer, as it is the correct one for the programming problem. But looking closer, you don't need to do the power law fitting:
y = 10^((a-x)/10*b) <=> log10(y) = log10(10^((a-x)/10*b))
<=> log10(y) = (a-x)/10*b
Use new variables:
z = log10(y), c = a/10*b and d = -1/10*b
And you have to fit now the following:
z = dx + c
Which is a straight line. Well, you just need to apply the above transformations to 2 points (x,y) => (x,log10(y)) in your table and fit a straight line to get c,d and therefore a,b.
I'm writing this because maybe you have to do this many times and this is much simpler (and precise) to do than fitting a power function.It has consequences too when you plan your experiment. You essentially need just 2 points to get the general behavior if you know this is the correct fitting function.
I hope this helps. Cheers!
I am trying to come up with a code that will allow me to plot a diagram for period doubling bifurcation.
I am using the equation x = rx − 1(1 − x), and am trying to model it with r values from 0.5 to 4. Here is code that I am working with
startr = 0.5
finalr = 4
max_time = 200
x = [0.1]
r= np.linspace(.5,4,200)
for n in range(0,200):
x = np.append(r * x[n] * (1-x[n]))
plt.plot(x, label='x');
plt.xlabel('t');
This keeps getting kicked out
TypeError: append() missing 1 required positional argument: 'values'
The are the two absolutely necessary arguments for numpy.append(), taken from the Numpy reference.
arr : array_like Values are appended to a copy of this array.
values :
array_like These values are appended to a copy of arr. It must be of
the correct shape (the same shape as arr, excluding axis). If axis is
not specified, values can be any shape and will be flattened before
use.
Therefore, try using
np.append(x, r * x[n] * (1-x[n]))
inside your loop.
Logistic Map
Save file and run, png image file of graph will save in the same folder
import numpy as np
import matplotlib.pyplot as plt
Many =50000
x = np.random.rand(Many)
r = np.linspace(0,4.0, num= Many)
for i in range(1, 54):
x_a = 1-x
Data= np.multiply(x,r)
Data= np.multiply(Data, x_a)
x = Data
plt.title(r'Logistic map: $x_{n+1} = r x_{n} (1-x_{n}).$ n = '+ str(i) )
plt.ylabel('x-Random number')
plt.xlabel('r-Rate')
plt.scatter(r, Data, s=0.1, c='k')
plt.show()
plt.savefig(str(i) + " Logistic Map.png", dpi = 300)
plt.clf()
I have done a point filter programme in a 3D plane, but I need to do a loop along a known 3D normal vector with a known length. Many thanks for the help.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
point = sta[10] #starting points
normal = axe[10] #normal vector
d = -point.dot(normal)
# create x,y
xx, yy = np.meshgrid(np.linspace(-3.,-2.,101), np.linspace(-11.,-10.,101))
# calculate corresponding z
z = (-normal[0] * xx - normal[1] * yy - d) * 1. /normal[2]
f=[]
for i in xrange(len(xx)-1):
for j in xrange(len(xx)-1):
if (xx[i][j]-sta[10][0])**2 + (yy[i][j]-sta[10][1])**2 + (z[i][j]-sta[10][2])**2 > float(rad[0])**2:
xx[i][j]=NaN
yy[i][j]=NaN
z[i][j]=NaN
Since you're using meshgrid and xx, yy and z have the same shape, numpy's broadcasting policy will automatically do what you need. Try this:
invalid = (xx-sta[10,0])**2 + (yy-sta[10,1])**2 + (z-sta[10,2])**2 > float(rad[0])**2
xx[invalid]=np.NaN
yy[invalid]=np.NaN
z[invalid]=np.NaN
It creates a boolean mask invalid which contains True for all entries that satisfy the condition. You can then use this mask to set the corresponding values to NaN.
Note that you can use tuples to index numpy arrays. I.e. myArray[a][b] is equivalent to myArray[a, b].
Also note that I assumed you excluded the last entries by accident. If it was on purpose that you used xrange(len(xx)-1) rather than xrange(len(xx)), it is getting a bit uglier and you have to do it like this:
invalid = (xx[:-1,:-1]-sta[10,0])**2 + (yy[:-1,:-1]-sta[10,1])**2 + (z[:-1,:-1]-sta[10,2])**2 > float(rad[0])**2
xx[:-1,:-1][invalid]=np.NaN
yy[:-1,:-1][invalid]=np.NaN
z[:-1,:-1][invalid]=np.NaN