我有一个遵循 sigmoid 曲线的数据集,并且我观察到 sigmoid 函数随着时间的推移变得更加陡峭。我正在寻找有关使用 Python 创建模型来拟合该曲线的指导。这个问题与这篇文章类似,期望数据曲线随着时间的推移变得更陡。我尝试使用逻辑回归来拟合数据(正如另一篇文章所建议的那样),但我需要结合一个与时间相关的陡度参数。
数据为单日数据。在下图中,我按小时对数据进行了时间分段。也就是说,每种颜色代表一天中特定时间的数据。首先,观察到对于每种颜色,数据遵循 S 型曲线(这对于橙色数据尤其明显,并且是我正在使用的数据的属性)。其次,请注意不同的颜色遵循不同的 S 形陡度。例如,蓝色曲线遵循比橙色曲线更陡峭的 S 形曲线。根据我的观察,数据来源越晚,S 形曲线就越陡。我试图通过在第二张图片中绘制 sigmoid 曲线来展示此属性。
这是我尝试拟合这些数据的尝试:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# Define a logistic function with a time-dependent parameter
def time_dependent_logistic(x, k, t):
return 1 / (1 + np.exp(-k * (x - t)))
# TODO: I replaced the variables below with my actual data
X = ... # Example: np.random.rand(100) * 10
time = ... # Example: np.linspace(0, 1, 100)
y = time_dependent_logistic(X, k=5 * time, t=2) + 0.05 * np.random.randn(100)
# Fit the time-dependent logistic function to the data
popt, pcov = curve_fit(time_dependent_logistic, X, y, bounds=([0, 0], [np.inf, np.inf]))
# Generate predictions using the fitted parameters
X_test = np.linspace(min(X), max(X), 300)
y_pred = time_dependent_logistic(X_test, *popt)
# Plot the original data and the fitted logistic curve
plt.scatter(X, y, label='Original data')
plt.plot(X_test, y_pred, label='Fitted time-dependent logistic curve', color='red', linewidth=2)
plt.xlabel('Input')
plt.ylabel('Output')
plt.legend()
plt.show()
但是,图表并未正确拟合数据。它只是产生一条平坦的线:
我已经研究了正确拟合数据的方法,以便曲线不会产生平坦的线,但这是我第一次尝试对这样的数据进行建模,所以我不确定我是否采取了正确的方法。任何指导都会很棒。预先非常感谢您的帮助。很长一段时间以来,我一直被这个问题困扰,并且我也对其他拟合数据的方法持开放态度。
编辑:
我将我的数据上传到 Google 云端硬盘,以防有人想使用它:https://drive.google.com/file/d/1aDB8U6Cn8lFo1TWFSRXWX-X0aEpCQ4sT/view?usp=sharing。请注意,我移动了 X 和时间列以使最小值为 0,因此它与图表中显示的数据并不完全相同。
嗯,我尝试将“开关”曲线 y=y0+A.tanh(c(x-x0)) 拟合到每个小时的数据(单独)。参数 x0、y0、A、c 似乎取决于小时,但很难说出它们对应于一天中的哪个小时,因为您已将时间轴重新归零。
为了让优化例程发挥作用,我必须根据每小时的数据提供合适的初始猜测 (p0) 和界限。
代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
# Curve to be fitted (for ONE dataset)
def func( xval, x0, y0, A, c ):
return y0 + A * np.tanh( c * ( xval - x0 ) )
# Read data from csv file
x, t, y = np.loadtxt( "data.csv", delimiter=',', skiprows=1, unpack=True )
hours_to_plot = [ 0, 1, 2, 3, 4, 5 ]
colours = [ "black", "red", "orange", "yellow", "magenta", "blue" ]
xmin, xmax = 0.0, 200.0
fmthead = "{:4}" + 4 * " {:>11}"
fmtdata = "{:4}" + 4 * " {:>11.6}"
print( fmthead.format( "hour", "x0", "y0", "A", "c" ) )
for hour in range( 0, int( max( t ) / 3600 ) + 1 ):
xhour = []
yhour = []
for i in range( len( t ) ):
if hour <= t[i] / 3600.0 < hour + 1: # 3600 seconds in an hour
xhour.append( x[i] )
yhour.append( y[i] )
if len( xhour ) == 0: continue
# Fit a switch curve to this hour's data
x1, x2, y1, y2 = min( xhour ), max( xhour ), min( yhour ), max( yhour )
p0 = [ 0.5*(x1+x2), 0.5*(y1+y2), 0.5*(y2-y1), 1.0 ]
params, cv = curve_fit( func, xhour, yhour, p0=p0, bounds=( [ x1, y1, 0.0, 0.0 ], [ x2, y2, (y2-y1), np.inf ] ) )
print( fmtdata.format( hour, *params ) )
# Generate predictions using the fitted parameters
xtest = np.linspace( xmin, xmax, 201 )
ytest = func( xtest, *params )
if hour in hours_to_plot:
# Plot the original data and the fitted logistic curve for selected hours
plt.scatter( xhour, yhour, s=4, color=colours[hour%6] )
plt.plot( xtest, ytest, linewidth=2, label=f"hour {hour}", color=colours[hour%6] )
plt.xlabel('Input')
plt.ylabel('Output')
plt.xlim( xmin, xmax )
plt.legend()
plt.show()
参数:
hour x0 y0 A c
0 35.9892 32.7941 8.3595 0.0310962
1 138.025 56.978 30.2371 0.0108192
2 185.048 76.6886 14.0 0.013368
3 102.521 47.7351 29.0443 0.0152189
4 102.606 47.2297 13.29 0.0394483
5 103.609 45.9815 26.7875 0.0283607
6 109.932 56.2847 31.2823 0.044357