scipy 奇怪的意外行为 curve_fit 正弦波的大数据集
scipy weird unexpected behavior curve_fit large data set for sin wave
出于某种原因,当我尝试将大量数据转换为正弦波时,它失败了,并使其适合水平线。有人可以解释一下吗?
最小工作代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Seed the random number generator for reproducibility
import pandas
np.random.seed(0)
# Here it work as expected
# x_data = np.linspace(-5, 5, num=50)
# y_data = 2.9 * np.sin(1.05 * x_data + 2) + 250 + np.random.normal(size=50)
# With this data it breaks
x_data = np.linspace(0, 2500, num=2500)
y_data = -100 * np.sin(0.01 * x_data + 1) + 250 + np.random.normal(size=2500)
# And plot it
plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data)
def test_func(x, a, b, c, d):
return a * np.sin(b * x + c) + d
# Used to fit the correct function
# params, params_covariance = optimize.curve_fit(test_func, x_data, y_data)
# making some guesses
params, params_covariance = optimize.curve_fit(test_func, x_data, y_data,
p0=[-80, 3, 0, 260])
print(params)
plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data, label='Data')
plt.plot(x_data, test_func(x_data, *params),
label='Fitted function')
plt.legend(loc='best')
plt.show()
有谁知道如何解决这个问题。我应该使用不同的拟合方法而不是最小二乘法吗?或者我应该减少数据点的数量?
鉴于您的数据,您可以使用更可靠的 lmfit
而不是 scipy
。
特别是,您可以使用 SineModel
(有关详细信息,请参阅 here)。
lmfit
中的 SineModel
不适用于“移位”正弦波,但您可以轻松处理移位
y_data_offset = y_data.mean()
y_transformed = y_data - y_data_offset
plt.scatter(x_data, y_transformed)
plt.axhline(0, color='r')
现在可以拟合正弦波了
from lmfit.models import SineModel
mod = SineModel()
pars = mod.guess(y_transformed, x=x_data)
out = mod.fit(y_transformed, pars, x=x_data)
您可以使用 print(out.fit_report())
检查结果并使用
绘制结果
plt.plot(x_data, y_data, lw=7, color='C1')
plt.plot(x_data, out.best_fit+y_data_offset, color='k')
# we add the offset ^^^^^^^^^^^^^
或使用内置绘图方法 out.plot_fit()
,请参阅 here 了解详细信息。
请注意,在 SineModel
中,所有参数“都被限制为非负数”,因此您定义的负幅度 (-100) 在参数拟合结果中将为正 (+100)。所以相位也不会是 1,而是 π+1(PS:他们称 shift
相位)
print(out.best_values)
{'amplitude': 99.99631403054289,
'frequency': 0.010001193681616227,
'shift': 4.1400215410836605}
出于某种原因,当我尝试将大量数据转换为正弦波时,它失败了,并使其适合水平线。有人可以解释一下吗?
最小工作代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Seed the random number generator for reproducibility
import pandas
np.random.seed(0)
# Here it work as expected
# x_data = np.linspace(-5, 5, num=50)
# y_data = 2.9 * np.sin(1.05 * x_data + 2) + 250 + np.random.normal(size=50)
# With this data it breaks
x_data = np.linspace(0, 2500, num=2500)
y_data = -100 * np.sin(0.01 * x_data + 1) + 250 + np.random.normal(size=2500)
# And plot it
plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data)
def test_func(x, a, b, c, d):
return a * np.sin(b * x + c) + d
# Used to fit the correct function
# params, params_covariance = optimize.curve_fit(test_func, x_data, y_data)
# making some guesses
params, params_covariance = optimize.curve_fit(test_func, x_data, y_data,
p0=[-80, 3, 0, 260])
print(params)
plt.figure(figsize=(6, 4))
plt.scatter(x_data, y_data, label='Data')
plt.plot(x_data, test_func(x_data, *params),
label='Fitted function')
plt.legend(loc='best')
plt.show()
有谁知道如何解决这个问题。我应该使用不同的拟合方法而不是最小二乘法吗?或者我应该减少数据点的数量?
鉴于您的数据,您可以使用更可靠的 lmfit
而不是 scipy
。
特别是,您可以使用 SineModel
(有关详细信息,请参阅 here)。
lmfit
中的 SineModel
不适用于“移位”正弦波,但您可以轻松处理移位
y_data_offset = y_data.mean()
y_transformed = y_data - y_data_offset
plt.scatter(x_data, y_transformed)
plt.axhline(0, color='r')
现在可以拟合正弦波了
from lmfit.models import SineModel
mod = SineModel()
pars = mod.guess(y_transformed, x=x_data)
out = mod.fit(y_transformed, pars, x=x_data)
您可以使用 print(out.fit_report())
检查结果并使用
plt.plot(x_data, y_data, lw=7, color='C1')
plt.plot(x_data, out.best_fit+y_data_offset, color='k')
# we add the offset ^^^^^^^^^^^^^
或使用内置绘图方法 out.plot_fit()
,请参阅 here 了解详细信息。
请注意,在 SineModel
中,所有参数“都被限制为非负数”,因此您定义的负幅度 (-100) 在参数拟合结果中将为正 (+100)。所以相位也不会是 1,而是 π+1(PS:他们称 shift
相位)
print(out.best_values)
{'amplitude': 99.99631403054289,
'frequency': 0.010001193681616227,
'shift': 4.1400215410836605}