I am trying to plot the fall of an object (an optical fork to be precise) as a function of time in order to verify that the law of gravity is indeed 9.81. The different data are supposed to represent the passage at each slot. The different slits are spaced 1 centimeter apart and there are 11 slits in all. I measured these data with an Arduino setup and I plot the graph and fit with Python. I have the data in a CSV file but when I run my code, I get an error "Unable to coerce to Series, length must be 1: given 11". However, when I enter the values manually one by one instead of reading the file, the code works and I get this graph (which is what I expect).
Here is the instruction I use (I added the values at each iteration by entering them manually and I thought that by doing the same thing in my CSV file the code would work but unfortunately it doesn't work either)
t = 1e-3 * np.array([3.524,7.06,10.608,14.17,17.744,21.326,24.918,28.518,32.128,35.746,39.372])
instead of
t = pd.read_csv("Fall.csv") # Opening the data file
Do you know where the error can come from? I mean why does the code work when I enter the values manually but not when I try to read a file with exactly the same values? I specify that I have 11 data in my CSV file.
Here is my initial CSV file (i.e. without having added the values at each iteration): Fall.csv.
And here is my whole code :
import numpy as np # For the calculation
import pandas as pd # To read files
import matplotlib.pyplot as plt # To draw curves
import scipy.optimize as opt # For the adjustment
# Raw data
t = pd.read_csv("Fall.csv") # Opening the data file
z = -0.01 * np.linspace(1, 11, 11)
# Definition of the free fall function
g = 9.81 # the acceleration of gravity
def f(t,t0,h0): # Definition of the fitting function
return -0.5*g*(t-t0)**2 h0
# Data adjustment
init_param = [0 , 0] # Initial values t0=0, h0=0
final_param , var = opt.curve_fit(f,t,z)
# Optimal function
tt = np.linspace(final_param[0], 100e-3,100)
hh = f(tt, *final_param) # Reconstruction of the fitted curve
# Plot of analyzed data
plt.clf() # Plot of data and fit
plt.xlabel("Time (s)")
plt.ylabel("Height (m)")
legend = "t0 = %f ms, h0 = %f centimeter " % (final_param[0]*1000,final_param[1]*100)
plt.plot(tt,hh,"r--",label=legend) # The adjustment
plt.plot(t,z,"bo", label="Data") # The data
plt.legend()
CodePudding user response:
Your problem arises from the shape of t
.
Scipy curve_fit
documentation specifies that, in your case, xdata
should a Sequence, ie a 1D array (more or less).
However, as your csv has one value by row, pd.read_csv()
reads it as a DataFrame, which basically is a 2D array.
You can check it by printing t.shape
, which outputs (11,1)
, while z.shape
is (11,)
.
There are multiple solutions to this problem, either rewrite your csv on one line or call opt_curve
with t[0]
to pick only the first column of t
. Careful here, 0
is the name of the first and only column of your DataFrame, and not an index. This gives would give : final_param, var = opt.curve_fit(f, t[0], z)
I would advise for the first solution though, to directly get the desired shape when reading the data.
CodePudding user response:
In the parenthesis of opt.curve_fit(f, t, z)
, just replace f, t, z
with (f, t[0], z)
, so the difference is [0]
of t. With [0]
you select the first column of t
individually.
Read the official Scipy documentation for more information.
Your solution should be like this:
import numpy as np # For the calculation
import pandas as pd # To read files
import matplotlib.pyplot as plt # To draw curves
import scipy.optimize as opt # For the adjustment
# Raw data
t = pd.read_csv("Fall.csv") # Opening the data file
z = -0.01 * np.linspace(1, 11, 11)
# Definition of the free fall function
g = 9.81 # the acceleration of gravity
def f(t,t0,h0): # Definition of the fitting function
return -0.5*g*(t-t0)**2 h0
# Data adjustment
init_param = [0 , 0] # Initial values t0=0, h0=0
final_param, var = opt.curve_fit(f, t[0], z)
# Optimal function
tt = np.linspace(final_param[0], 100e-3,100)
hh = f(tt, *final_param) # Reconstruction of the fitted curve
# Plot of analyzed data
plt.clf() # Plot of data and fit
plt.xlabel("Time (s)")
plt.ylabel("Height (m)")
legend = "t0 = %f ms, h0 = %f centimeter " % (final_param[0]*1000,final_param[1]*100)
plt.plot(tt,hh,"r--",label=legend) # The adjustment
plt.plot(t,z,"bo", label="Data") # The data
plt.legend()