我怎样才能用我当前的数据拟合这个正弦波?
How can I fit this sinusoidal wave with my current data?
我收集了一些数据来分析加速度随时间的变化。但是当我编写下面的代码以很好地拟合正弦波时,结果就是这样。这是因为我没有足够的数据还是我在这里做错了什么?
在这里你可以看到我的图表:
直接绘制的测量值(不适合)
适合水平和垂直移位(curve_fit)
linspace增加了数据
手动操作振幅
编辑:我通过使用 linspace 函数增加了数据大小并绘制了它,但我不确定为什么振幅不匹配,是因为要分析的数据很少吗? (我可以手动控制振幅,但我不明白为什么不能)
我用于拟合的代码
def model(x, a, b):
return a * np.sin(b * x)
param, parav_cov = cf(model, time, z_values)
array_x = np.linspace(800, 1400, 1000)
fig = plt.figure(figsize = (9, 4))
plt.scatter(time, z_values, color = "#3333cc", label = "Data")
plt.plot(array_x, model(array_x, param[0], param[1], param[2], param[3]), label = "Sin Fit")
我会使用 FFT 来初步猜测参数,因为这种事情是高度非线性的,否则 curve_fit
不太可能走得太远。使用 FFT 的原因是为了初步了解所涉及的频率,仅此而已。 3Blue1Brown 有一个很棒的 video on FFTs 如果你不觉得它
我使用 web plot digitizer 从绘图中获取数据,然后进入 Python 并确保它看起来正常:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('sinfit2.csv')
print(df.head())
给我:
x y
0 809.3 0.3
1 820.0 0.3
2 830.3 19.6
3 839.9 19.6
4 849.6 0.4
我首先用 NumPy 做了一个基本的 FFT(SciPy 有更完整的 fftpack
,但这里不需要):
import numpy as np
from numpy.fft import fft
d = fft(df.y)
plt.plot(np.abs(d)[:len(d)//2], '.')
np.abs(d)
是因为你得到一个包含相位和振幅的复数,而 [:len(d)//2]
是因为(对于实值输入)输出关于中点对称,即 d[5]
== d[-5]
.
这表示最大的分量是 18,我试着手工绘制它,看起来还不错:
x = np.linspace(0, np.pi * 2, len(df))
plt.plot(df.x, df.y, '.-', lw=1)
plt.plot(df.x, np.sin(x * 18) * 10 + 10)
我乘以 10 并加 10 是因为正弦的范围是 (-1, +1),我们需要将它取到 (0, 20)。
接下来我将这些传递给 curve_fit
使用简化模型来帮助它:
from scipy.optimize import curve_fit
def model(x, a, b):
return np.sin(x * a + b) * 10 + 10
(a, b), cov = curve_fit(model, x, df.y, [18, 0])
我再次对 * 10 + 10
进行硬编码以获得与您的数据匹配的范围,这给了我 a=17.8
和 b=2.97
最后我绘制了以更高频率采样的函数以确保一切正常:
plt.plot(df.x, df.y)
plt.plot(
np.linspace(810, 1400, 501),
model(np.linspace(0, np.pi*2, 501), a, b)
)
给我:
看起来还不错。请注意,您可能想要更改这些参数以使其适合您的原始 X,并注意我的 df.x
从 810 开始,所以我可能错过了第一点。
我收集了一些数据来分析加速度随时间的变化。但是当我编写下面的代码以很好地拟合正弦波时,结果就是这样。这是因为我没有足够的数据还是我在这里做错了什么?
在这里你可以看到我的图表:
直接绘制的测量值(不适合)
适合水平和垂直移位(curve_fit)
linspace增加了数据
手动操作振幅
编辑:我通过使用 linspace 函数增加了数据大小并绘制了它,但我不确定为什么振幅不匹配,是因为要分析的数据很少吗? (我可以手动控制振幅,但我不明白为什么不能)
我用于拟合的代码
def model(x, a, b):
return a * np.sin(b * x)
param, parav_cov = cf(model, time, z_values)
array_x = np.linspace(800, 1400, 1000)
fig = plt.figure(figsize = (9, 4))
plt.scatter(time, z_values, color = "#3333cc", label = "Data")
plt.plot(array_x, model(array_x, param[0], param[1], param[2], param[3]), label = "Sin Fit")
我会使用 FFT 来初步猜测参数,因为这种事情是高度非线性的,否则 curve_fit
不太可能走得太远。使用 FFT 的原因是为了初步了解所涉及的频率,仅此而已。 3Blue1Brown 有一个很棒的 video on FFTs 如果你不觉得它
我使用 web plot digitizer 从绘图中获取数据,然后进入 Python 并确保它看起来正常:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('sinfit2.csv')
print(df.head())
给我:
x y
0 809.3 0.3
1 820.0 0.3
2 830.3 19.6
3 839.9 19.6
4 849.6 0.4
我首先用 NumPy 做了一个基本的 FFT(SciPy 有更完整的 fftpack
,但这里不需要):
import numpy as np
from numpy.fft import fft
d = fft(df.y)
plt.plot(np.abs(d)[:len(d)//2], '.')
np.abs(d)
是因为你得到一个包含相位和振幅的复数,而 [:len(d)//2]
是因为(对于实值输入)输出关于中点对称,即 d[5]
== d[-5]
.
这表示最大的分量是 18,我试着手工绘制它,看起来还不错:
x = np.linspace(0, np.pi * 2, len(df))
plt.plot(df.x, df.y, '.-', lw=1)
plt.plot(df.x, np.sin(x * 18) * 10 + 10)
我乘以 10 并加 10 是因为正弦的范围是 (-1, +1),我们需要将它取到 (0, 20)。
接下来我将这些传递给 curve_fit
使用简化模型来帮助它:
from scipy.optimize import curve_fit
def model(x, a, b):
return np.sin(x * a + b) * 10 + 10
(a, b), cov = curve_fit(model, x, df.y, [18, 0])
我再次对 * 10 + 10
进行硬编码以获得与您的数据匹配的范围,这给了我 a=17.8
和 b=2.97
最后我绘制了以更高频率采样的函数以确保一切正常:
plt.plot(df.x, df.y)
plt.plot(
np.linspace(810, 1400, 501),
model(np.linspace(0, np.pi*2, 501), a, b)
)
给我:
看起来还不错。请注意,您可能想要更改这些参数以使其适合您的原始 X,并注意我的 df.x
从 810 开始,所以我可能错过了第一点。