Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何将分段(线性段和常数段交替)函数拟合到抛物线函数?_Python_Algorithm_Math_Curve Fitting_Approximation - Fatal编程技术网

Python 如何将分段(线性段和常数段交替)函数拟合到抛物线函数?

Python 如何将分段(线性段和常数段交替)函数拟合到抛物线函数?,python,algorithm,math,curve-fitting,approximation,Python,Algorithm,Math,Curve Fitting,Approximation,例如,我有一个函数,但也可以是其他函数,比如二次函数或对数函数。我只对这个领域感兴趣。函数的参数(本例中为a和k)也是已知的 我的目标是将一个连续分段函数与此相匹配,其中包含线性函数的交替段(即倾斜直线段,每个段的截距为0)和常数(即将倾斜段连接在一起的水平段)。第一段和最后一段都是倾斜的。分段数量应在9-29之间预先选择(即5-15个线性台阶+4-14个恒定高原) 正式 输入功能: 拟合分段函数: 如果事先指定了段号(n),我将寻找最佳结果参数(c、r、b)(以最小二乘法表示)。

例如,我有一个函数,但也可以是其他函数,比如二次函数或对数函数。我只对这个领域感兴趣。函数的参数(本例中为a和k)也是已知的

我的目标是将一个连续分段函数与此相匹配,其中包含线性函数的交替段(即倾斜直线段,每个段的截距为0)和常数(即将倾斜段连接在一起的水平段)。第一段和最后一段都是倾斜的。分段数量应在9-29之间预先选择(即5-15个线性台阶+4-14个恒定高原)

正式

  • 输入功能:

  • 拟合分段函数:

如果事先指定了段号(n),我将寻找最佳结果参数(c、r、b)(以最小二乘法表示)。 结果常数(c)和断点(r)应为全自然数,斜率(b)四舍五入两个小数点

我尝试使用分段常数模型进行数值拟合,并以图形直观的方式进一步处理得到的常数模型,以“切片”斜率的常数步长。它在某种程度上是有效的,但我确信从拟合角度和计算效率来看,这都是次优的。生成具有8个坡度(范围为1-50000)的管件需要几分钟的时间。我相信一定有更好的办法

我的想法是只使用数值方法/ML,我们有输入函数的代数形式这一事实可以在某种程度上被利用,至少可以使用代数变换(积分)来得到一个更简单的优化问题

import numpy as np
import matplotlib.pyplot as plt
import pwlf

# The input function
def input_func(x,k,a):
    return np.power(x,1/a)*k

x = np.arange(1,5e4)
y = input_func(x, 1.8, 1.3)

plt.plot(x,y);

def pw_fit(函数、x_r、无seg、*fparams):
#在指定范围内工作
x=np.arange(1,x_r)
y_输入=func(x,*fparams)
my_pwlf=pwlf.分段线性拟合(x,y_输入,度=0)
res=我的自我适合度(无分段)
yHat=my_pwlf.predict(x)
#断点处的函数值
y_isec=func(res,*fparams)
#断点处的坡度值
斜率=np.四舍五入(y_isec/res,小数=2)
坡度=坡度[1:]
#对于第一个坡度值,我使用第一个恒定平台和输入函数的交点
slopes=np.insert(slopes,0,np.round(y_输入[np.argwhere(np.diff(np.sign(y_输入-yHat))).flatten()[0]/np.argwhere(np.diff(np.sign(y_输入-yHat)).flatten()[0],小数=2))
高原=np.唯一(np.圆形(yHat))
#如果由于舍入斜率值(到两个小数),在后续步骤中没有变化,我只删除这些段
to_del=np.argwhere(np.diff(slopes)==0).flatten()
坡度=np.删除(坡度,至+1)
高原=np.删除(高原,至删除)
断点=[np.ceil(高原[0]/坡度[0])]
对于idx,枚举中的j(斜率[1:-1]):
断点追加(np.floor(高原[idx]/j))
断点.append(np.ceil(平台[idx+1]/j))
附加断点(np.楼层(高原[-1]/坡度[-1]))
返回坡度、高原、断点
slo、平台、断裂=pw_拟合(输入函数,50000,8,1.8,1.3)
#分段函数本身
def pw_计算(x、坡度、高原、断裂):
x=x.astype('float')
条件列表=[x<中断[0]]
对于idx,枚举中的j(中断符[:-1]):

cond_list.append((j)(不重要,但我认为拟合的分段函数不是连续的。间隔应该是
x。我们可以将线性和常量片段的平方误差函数进行集成,并让SciPy对其进行优化。Python 3:

import matplotlib.pyplot as plt
import numpy as np
import scipy.optimize

xl = 1
xh = 50000
a = 1.3
p = 1 / a
n = 8


def split_b_and_c(bc):
    return bc[::2], bc[1::2]


def solve_for_r(b, c):
    r = np.empty(2 * n)
    r[0] = xl
    r[1:-1:2] = c / b[:-1]
    r[2::2] = c / b[1:]
    r[-1] = xh
    return r


def linear_residual_integral(b, x):
    return (
        (x ** (2 * p + 1)) / (2 * p + 1)
        - 2 * b * x ** (p + 2) / (p + 2)
        + b ** 2 * x ** 3 / 3
    )


def constant_residual_integral(c, x):
    return x ** (2 * p + 1) / (2 * p + 1) - 2 * c * x ** (p + 1) / (p + 1) + c ** 2 * x


def squared_error(bc):
    b, c = split_b_and_c(bc)
    r = solve_for_r(b, c)
    linear = np.sum(
        linear_residual_integral(b, r[1::2]) - linear_residual_integral(b, r[::2])
    )
    constant = np.sum(
        constant_residual_integral(c, r[2::2])
        - constant_residual_integral(c, r[1:-1:2])
    )
    return linear + constant


def evaluate(x, b, c, r):
    i = 0
    while x > r[i + 1]:
        i += 1
    return b[i // 2] * x if i % 2 == 0 else c[i // 2]


def main():
    bc0 = (xl + (xh - xl) * np.arange(1, 4 * n - 2, 2) / (4 * n - 2)) ** (
        p - 1 + np.arange(2 * n - 1) % 2
    )
    bc = scipy.optimize.minimize(
        squared_error, bc0, bounds=[(1e-06, None) for i in range(2 * n - 1)]
    ).x
    b, c = split_b_and_c(bc)
    r = solve_for_r(b, c)
    X = np.linspace(xl, xh, 1000)
    Y = [evaluate(x, b, c, r) for x in X]
    plt.plot(X, X ** p)
    plt.plot(X, Y)
    plt.show()


if __name__ == "__main__":
    main()

我自己也试着提出了一个新的解决方案,基于这样的想法,我已经划分了域,曲线拟合了一个双常数和线性段(在np.maximum的帮助下)。我使用了1/f(x)'作为指定断点的函数,但我知道这是任意的,并且不提供全局优化。可能有一些用于这些断点的优化函数。但是这个解决方案对我来说是可行的,因为它可能适合在第一个段进行更好的拟合,而以牺牲后面段的错误为代价。(任务本身实际上是基于成本的零售利润计算{供应价格->增加利润},因为零售POS软件只能使用这种分段利润功能)。
如果允许参数为浮点数,则得出的答案是正确的最优解。不幸的是,POS软件不能使用浮点数。之后可以将c-s和r-s四舍五入。但b-s应四舍五入到两位小数,因为这些小数被输入为百分比,此约束将破坏具有长浮点数的最优解。我将尝试to通过Amo和David的宝贵意见进一步改进我的解决方案。谢谢

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# The input function f(x)
def input_func(x,k,a):
    return np.power(x,1/a) * k

# 1 / f(x)'
def one_per_der(x,k,a):
    return a / (k * np.power(x, 1/a-1))

# 1 / f(x)' inverted
def one_per_der_inv(x,k,a):
    return np.power(a / (x*k), a / (1-a))

def segment_fit(start,end,y,first_val):
    b, _ = curve_fit(lambda x,b: np.maximum(first_val, b*x), np.arange(start,end), y[start-1:end-1])
    b = float(np.round(b, decimals=2))
    bp = np.round(first_val / b)
    last_val = np.round(b * end)
    return b, bp, last_val

def pw_fit(end_range, no_seg, **fparams):    

    y_bps = np.linspace(one_per_der(1, **fparams), one_per_der(end_range,**fparams) , no_seg+1)[1:]
    x_bps = np.round(one_per_der_inv(y_bps, **fparams))

    y = input_func(x, **fparams)
    
    slopes = [np.round(float(curve_fit(lambda x,b: x * b, np.arange(1,x_bps[0]), y[:int(x_bps[0])-1])[0]), decimals = 2)]
    plats = [np.round(x_bps[0] * slopes[0])]

    bps = []

    for i, xbp in enumerate(x_bps[1:]):
        b, bp, last_val = segment_fit(int(x_bps[i]+1), int(xbp), y, plats[i])
        slopes.append(b); bps.append(bp); plats.append(last_val)

    breaks = sorted(list(x_bps) + bps)[:-1]
    
    # If due to rounding slope values (to two decimals), there is no change in a subsequent step, I just remove those segments
    to_del = np.argwhere(np.diff(slopes) == 0).flatten()
    breaks_to_del = np.concatenate((to_del * 2, to_del * 2 + 1))

    slopes = np.delete(slopes,to_del + 1)
    plats = np.delete(plats[:-1],to_del)
    breaks = np.delete(breaks,breaks_to_del)
    
    return slopes, plats, breaks

def pw_calc(x, slopes, plateaus, breaks):
    x = x.astype('float')
    
    cond_list = [x < breaks[0]]
    
    for idx, j in enumerate(breaks[:-1]):
        cond_list.append((j <= x) & (x < breaks[idx+1]))
        
    cond_list.append(breaks[-1] <= x)
    
    func_list = [lambda x: x * slopes[0]]
    
    for idx, j in enumerate(slopes[1:]):
        func_list.append(plateaus[idx])
        func_list.append(lambda x, j=j: x * j)
          
    return np.piecewise(x, cond_list, func_list)

fparams = {'k':1.8, 'a':1.2}
end_range = 5e4
no_steps = 10

x = np.arange(1, end_range)
y = input_func(x, **fparams)

slopes, plats, breaks = pw_fit(end_range, no_steps, **fparams)

y_output = pw_calc(x, slopes, plats, breaks)

plt.plot(x,y_output,y);
将numpy导入为np
将matplotlib.pyplot作为plt导入
从scipy.optimize导入曲线\u拟合
#输入函数f(x)
def输入函数(x,k,a):
返回np.功率(x,1/a)*k
#一楼(x)'
定义一个(x,k,a):
返回a/(k*np.幂(x,1/a-1))
#1/f(x)“倒置”
定义每个库存的一个库存(x、k、a):
返回np.幂(a/(x*k),a/(1-a))
def段拟合(开始、结束、y、首个值):
b、 曲线拟合(λx,b:np.最大值(第一个值,b*x),np.arange(开始,结束),y[start-1:end-1])
b=浮点(np.四舍五入(b,小数=2))
bp=np.四舍五入(第一值)
最后一轮=np轮(b*结束)
返回b,bp,最后一个值
def pw_配合(端部范围、无seg、**F参数):
y_bps=np.linspace(1,**fparams),1_/der(end_range,**fparams),无seg+1)[1:]
x_bps=np.轮(每个存货一个(y_bps,**fparams))
y=输入函数(x,**F参数)
斜率=[np.圆形(浮点(曲线拟合(λx,b:x*b,np.arange(1,x_bps[0]),y[:int(x_bps[0])-1]),小数
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# The input function f(x)
def input_func(x,k,a):
    return np.power(x,1/a) * k

# 1 / f(x)'
def one_per_der(x,k,a):
    return a / (k * np.power(x, 1/a-1))

# 1 / f(x)' inverted
def one_per_der_inv(x,k,a):
    return np.power(a / (x*k), a / (1-a))

def segment_fit(start,end,y,first_val):
    b, _ = curve_fit(lambda x,b: np.maximum(first_val, b*x), np.arange(start,end), y[start-1:end-1])
    b = float(np.round(b, decimals=2))
    bp = np.round(first_val / b)
    last_val = np.round(b * end)
    return b, bp, last_val

def pw_fit(end_range, no_seg, **fparams):    

    y_bps = np.linspace(one_per_der(1, **fparams), one_per_der(end_range,**fparams) , no_seg+1)[1:]
    x_bps = np.round(one_per_der_inv(y_bps, **fparams))

    y = input_func(x, **fparams)
    
    slopes = [np.round(float(curve_fit(lambda x,b: x * b, np.arange(1,x_bps[0]), y[:int(x_bps[0])-1])[0]), decimals = 2)]
    plats = [np.round(x_bps[0] * slopes[0])]

    bps = []

    for i, xbp in enumerate(x_bps[1:]):
        b, bp, last_val = segment_fit(int(x_bps[i]+1), int(xbp), y, plats[i])
        slopes.append(b); bps.append(bp); plats.append(last_val)

    breaks = sorted(list(x_bps) + bps)[:-1]
    
    # If due to rounding slope values (to two decimals), there is no change in a subsequent step, I just remove those segments
    to_del = np.argwhere(np.diff(slopes) == 0).flatten()
    breaks_to_del = np.concatenate((to_del * 2, to_del * 2 + 1))

    slopes = np.delete(slopes,to_del + 1)
    plats = np.delete(plats[:-1],to_del)
    breaks = np.delete(breaks,breaks_to_del)
    
    return slopes, plats, breaks

def pw_calc(x, slopes, plateaus, breaks):
    x = x.astype('float')
    
    cond_list = [x < breaks[0]]
    
    for idx, j in enumerate(breaks[:-1]):
        cond_list.append((j <= x) & (x < breaks[idx+1]))
        
    cond_list.append(breaks[-1] <= x)
    
    func_list = [lambda x: x * slopes[0]]
    
    for idx, j in enumerate(slopes[1:]):
        func_list.append(plateaus[idx])
        func_list.append(lambda x, j=j: x * j)
          
    return np.piecewise(x, cond_list, func_list)

fparams = {'k':1.8, 'a':1.2}
end_range = 5e4
no_steps = 10

x = np.arange(1, end_range)
y = input_func(x, **fparams)

slopes, plats, breaks = pw_fit(end_range, no_steps, **fparams)

y_output = pw_calc(x, slopes, plats, breaks)

plt.plot(x,y_output,y);