在 Python 中对具有随时间陡度的 S 型曲线进行建模

问题描述 投票:0回答:1

我有一个遵循 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,因此它与图表中显示的数据并不完全相同。

python numpy scipy curve-fitting sigmoid
1个回答
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
© www.soinside.com 2019 - 2024. All rights reserved.