Это будет моей отправной точкой:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit
### to generate test data
def temp( t , low, high, period, ramp ):
tRed = t % period
dwell = period / 2. - ramp
if tRed < dwell:
out = high
elif tRed < dwell + ramp:
out = high - ( tRed - dwell ) / ramp * ( high - low )
elif tRed < 2 * dwell + ramp:
out = low
elif tRed <= period:
out = low + ( tRed - 2 * dwell - ramp)/ramp * ( high -low )
else:
assert 0
return out + np.random.normal()
### A continuous function that somewhat fits the data
### but definitively gets the period and levels.
### The ramp is less well defined
def fit_func( t, low, high, period, s, delta):
return ( high + low ) / 2. + ( high - low )/2. * np.tanh( s * np.sin( 2 * np.pi * ( t - delta ) / period ) )
time1List = np.arange( 300 ) * 16
time2List = np.linspace( 0, 300 * 16, 7213 )
tempList = np.fromiter( ( temp(t - 6.3 , 41, 155, 63.3, 2.05 ) for t in time1List ), np.float )
funcList = np.fromiter( ( fit_func(t , 41, 155, 63.3, 10., 0 ) for t in time2List ), np.float )
sol, err = curve_fit( fit_func, time1List, tempList, [ 40, 150, 63, 10, 0 ] )
print sol
fittedLow, fittedHigh, fittedPeriod, fittedS, fittedOff = sol
realHigh = fit_func( fittedPeriod / 4., *sol)
realLow = fit_func( 3 / 4. * fittedPeriod, *sol)
print "high, low : ", [ realHigh, realLow ]
print "apprx ramp: ", fittedPeriod/( 2 * np.pi * fittedS ) * 2
realAmp = realHigh - realLow
rampX, rampY = zip( *[ [ t, d ] for t, d in zip( time1List, tempList ) if ( ( d < realHigh - 0.05 * realAmp ) and ( d > realLow + 0.05 * realAmp ) ) ] )
topX, topY = zip( *[ [ t, d ] for t, d in zip( time1List, tempList ) if ( ( d > realHigh - 0.05 * realAmp ) ) ] )
botX, botY = zip( *[ [ t, d ] for t, d in zip( time1List, tempList ) if ( ( d < realLow + 0.05 * realAmp ) ) ] )
fig = plt.figure()
ax = fig.add_subplot( 2, 1, 1 )
bx = fig.add_subplot( 2, 1, 2 )
ax.plot( time1List, tempList, marker='x', linestyle='', zorder=100 )
ax.plot( time2List, fit_func( time2List, *sol ), zorder=0 )
bx.plot( time1List, tempList, marker='x', linestyle='' )
bx.plot( time2List, fit_func( time2List, *sol ) )
bx.plot( rampX, rampY, linestyle='', marker='o', markersize=10, fillstyle='none', color='r')
bx.plot( topX, topY, linestyle='', marker='o', markersize=10, fillstyle='none', color='#00FFAA')
bx.plot( botX, botY, linestyle='', marker='o', markersize=10, fillstyle='none', color='#80DD00')
bx.set_xlim( [ 0, 800 ] )
plt.show()
, обеспечивающей:
>> [155.0445024 40.7417905 63.29983807 13.07677546 -26.36945489]
>> high, low : [155.04450237880076, 40.741790521444436]
>> apprx ramp: 1.540820542195840
Есть несколько вещейотметить.Моя функция подгонки работает лучше, если рампа мала по сравнению со временем ожидания.Более того, здесь можно найти несколько постов, где обсуждается подгонка пошаговых функций.В целом, поскольку для подгонки требуется значимая производная, дискретные функции являются проблемой.Есть как минимум два решения.а) сделать непрерывную версию, подогнать и сделать результат дискретным по своему вкусу или б) предоставить дискретную функцию и ручную непрерывную производную.
РЕДАКТИРОВАТЬ
Таквот что я получаю, работая с вашим недавно опубликованным набором данных:
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit, minimize
def partition( inList, n ):
return zip( *[ iter( inList ) ] * n )
def temp( t, low, high, period, ramp, off ):
tRed = (t - off ) % period
dwell = period / 2. - ramp
if tRed < dwell:
out = high
elif tRed < dwell + ramp:
out = high - ( tRed - dwell ) / ramp * ( high - low )
elif tRed < 2 * dwell + ramp:
out = low
elif tRed <= period:
out = low + ( tRed - 2 * dwell - ramp)/ramp * ( high -low )
else:
assert 0
return out
def chi2( params, xData=None, yData=None, verbose=False ):
low, high, period, ramp, off = params
th = np.fromiter( ( temp( t, low, high, period, ramp, off ) for t in xData ), np.float )
diff = ( th - yData )
diff2 = diff**2
out = np.sum( diff2 )
if verbose:
print '-----------'
print th
print diff
print diff2
print '-----------'
return out
# ~ return th
def fit_func( t, low, high, period, s, delta):
return ( high + low ) / 2. + ( high - low )/2. * np.tanh( s * np.sin( 2 * np.pi * ( t - delta ) / period ) )
inData = np.loadtxt('SOF2.csv', skiprows=1, delimiter=',' )
inData2 = inData[ :, 2 ]
xList = np.arange( len(inData2) )
inData480 = partition( inData2, 480 )
xList480 = partition( xList, 480 )
inDataMean = np.fromiter( (np.mean( x ) for x in inData480 ), np.float )
xMean = np.arange( len( inDataMean) ) * 16
time1List = np.linspace( 0, 16 * len(inDataMean), 500 )
sol, err = curve_fit( fit_func, xMean, inDataMean, [ -40, 150, 60, 10, 10 ] )
print sol
# ~ print chi2([-49,155,62.5,1 , 8.6], xMean, inDataMean )
res = minimize( chi2, [-44.12, 150.0, 62.0, 8.015, 12.3 ], args=( xMean, inDataMean ), method='nelder-mead' )
# ~ print res
print res.x
# ~ print chi2( res.x, xMean, inDataMean, verbose=True )
# ~ print chi2( [-44.12, 150.0, 62.0, 8.015, 6.3], xMean, inDataMean, verbose=True )
fig = plt.figure()
ax = fig.add_subplot( 2, 1, 1 )
bx = fig.add_subplot( 2, 1, 2 )
for x,y in zip( xList480, inData480):
ax.plot( x, y, marker='x', linestyle='', zorder=100 )
bx.plot( xMean, inDataMean , marker='x', linestyle='' )
bx.plot( time1List, fit_func( time1List, *sol ) )
bx.plot( time1List, np.fromiter( ( temp( t , *res.x ) for t in time1List ), np.float) )
bx.plot( time1List, np.fromiter( ( temp( t , -44.12, 150.0, 62.0, 8.015, 12.3 ) for t in time1List ), np.float) )
plt.show()
>> [-49.53569904 166.92138068 62.56131027 1.8547409 8.75673747]
>> [-34.12188737 150.02194584 63.81464913 8.26491754 13.88344623]
Как видите, точка данных нарампа не подходит. Так, может быть, время 16 минут не является таким постоянным?Это было бы проблемой, поскольку это не локальная x-ошибка, а эффект накопления.