fpex0.baseline
1import numpy as np 2from scipy import interpolate 3from scipy import integrate 4from copy import copy 5from fpex0.linreg import LinearRegression 6 7 8class BaselineData: 9 """ 10 Stores information about the baselevel. 11 12 ## Parameters 13 **reg_L** 14 <br> Left linear part. 15 16 **reg_R** 17 <br> Right linear part. 18 19 **onset** 20 <br> Onset of phase transition. 21 22 **endset** 23 <br> Endset of the phase transition. 24 """ 25 def __init__(self, reg_L=None, reg_R=None, onset=None, endset=None): 26 self.reg_L = reg_L 27 self.reg_R = reg_R 28 self.endset = endset 29 self.onset = onset 30 31class BaselineDetectionSettings: 32 """ 33 Stores (default) baseline detection settings to be passed to `fpex0.baseline.detectLinearRange()`. 34 35 ## Parameters 36 Naming: 37 - R,L = right or left 38 - abs, rel = absolute or relative 39 - dev = deviation 40 - initfraction = initial fraction of data to interpolate. 41 42 **LabsdevA**, **LabsdevB**, **LabsdevS2** 43 44 **LreldevA**, **LreldevB**, **LreldevS2** 45 46 **Linitfraction** 47 48 **RabsdevA**, **RabsdevB**, **RabsdevS2** 49 50 **RreldevA**, **RreldevB**, **RreldevS2** 51 52 **Rinitfraction** 53 """ 54 def __init__(self): 55 self.LabsdevA = -1 56 self.LabsdevB = -1 57 self.LabsdevS2 = -1 58 self.LreldevA = np.infty 59 self.LreldevB = 0.01 60 self.LreldevS2 = 2.00 61 self.Linitfraction = 0.1 62 63 self.RabsdevA = -1 64 self.RabsdevB = 0.05 65 self.RabsdevS2 = -1 66 self.RreldevA = np.infty 67 self.RreldevB = np.infty 68 self.RreldevS2 = 2.00 69 self.Rinitfraction = 0.2 70 71 72def detectLinearRange(X, Y, side, initlen, reldevAmax, reldevBmax, reldevS2max, absdevAmax, absdevBmax, absdevS2max): 73 """ 74 Detect the stop position of the linear range. 75 76 ## Takes 77 78 **X**: x-values 79 80 **Y**: y-values 81 82 **side**: 'L' or 'R' for "from left" or "from right" 83 84 **initlen** 85 <br> Length (in samples) to calculate the initial standard deviation. 86 87 **reldevA** 88 <br> Acceptable relative deviation of initial y-axis intercept. 89 90 **reldevB** 91 <br> Acceptable relative deviation of initial slope. 92 93 **reldevS2** 94 <br> Acceptable relative deviation of initial squared standard deviation. 95 96 **absdevA** 97 <br> Acceptable absolute deviation of initial y-axis intercept. 98 99 **absdevB** 100 <br> Acceptable absolute deviation of initial. 101 102 **absdevS2** 103 <br> Acceptable absolute deviation of initial squared standard deviation. 104 105 106 ## Returns 107 **stopidx** 108 <br> Position in X, where the linear range is estimated to be left. 109 110 **reg** 111 <br> Final regression structure as returned by fpex0.linreg.LinearRegression.linReg. 112 113 **initreg** 114 <br> Initial regression structure as returned by fpex0.linreg.LinearRegression.linReg. 115 116 ### Comments 117 * Set the `reldev` to inf to disable 118 119 * Set the `absdev` to -1 to disable 120 121 * The `reldev` is only be checked if value exceeds `absdev` 122 123 """ 124 125 126 # choose loop-range 127 if side.lower() == "left": 128 start = initlen 129 stop = len(X) 130 step = 1 131 # initial regression 132 initreg = LinearRegression() 133 initreg.linReg(X[:start], Y[:start]) 134 elif side.lower() == "right": 135 start = len(X)-1 - initlen # start at position left from initial range 136 stop = -1 # exclusive index 137 step = -1 138 # initial regression 139 initreg = LinearRegression() 140 initreg.linReg(X[start+1:], Y[start+1:]) 141 142 143 reg = copy(initreg) 144 index = None 145 146 for index in range(start, stop, step): 147 # update regression 148 reg.linReg(X[index], Y[index]) 149 150 absdevA = abs(reg.a - initreg.a ) 151 reldevA = abs(absdevA / initreg.a ) 152 absdevB = abs(reg.b - initreg.b ) 153 reldevB = abs(absdevB / initreg.b ) 154 absdevS2 = abs(reg.s2 - initreg.s2 ) 155 reldevS2 = abs(absdevS2 / initreg.s2) 156 157 # check deviation 158 if (absdevA > absdevAmax ) and (reldevA > reldevAmax ): 159 break 160 if (absdevB > absdevBmax ) and (reldevB > reldevBmax ): 161 break 162 if (absdevS2 > absdevS2max) and (reldevS2 > reldevS2max): 163 break 164 stopidx = index 165 166 return stopidx, reg, initreg 167 168 169def getBaselinePrimitive(X, Y, index_l, icept_l, slope_l, index_r, icept_r, slope_r, bl_type, res=1000): 170 """ 171 Retrieves the baselevel function for specified data. 172 173 ## Takes 174 175 **X**: x-values (temperatures, time, ...) 176 177 **Y**: y-values (voltage, heat flux, ....) 178 179 **index_l** 180 <br> Index where left linear part is left ("onset") 181 182 **icept_l** 183 <br> y-intercept of left linear part. 184 185 **slope_l** 186 <br> Slope of left linear part. 187 188 **index_r** 189 <br> Index where right linear part is left ("onset"). 190 191 **icept_r** 192 <br> y-intercept of right linear part. 193 194 **slope_r** 195 <br> Slope of right linear part. 196 197 **bl_type** 198 <br> What type should the baselevel function have? "linear" or "sigmoidal". 199 200 **res** 201 <br> Number of support points for sigmoidal (default: 100). 202 203 204 ## Returns 205 206 **blfun** 207 <br> Function handle of baselevel function 208 209 """ 210 211 # calculate values at "onset" and "offset" 212 yval_l = icept_l + X[index_l] * slope_l 213 yval_r = icept_r + X[index_r] * slope_r 214 yval_delta = yval_r - yval_l 215 216 # calculate linear base level function 217 lin_bl_xvals = [ X[0] , X[index_l], X[index_r], X[-1] ] 218 lin_bl_yvals = [icept_l+X[0]*slope_l, yval_l , yval_r , icept_r+X[-1]*slope_r ] 219 lin_bl_fun = interpolate.interp1d(lin_bl_xvals, lin_bl_yvals, 'linear') # piecewise linear interpolation 220 221 222 if bl_type.lower() == 'linear': 223 return lin_bl_fun 224 225 elif bl_type.lower() in ['sigmoid', 'sigmoidal']: 226 227 # sigmoidal interpolation as in Fig. 3 of DIN 51007 228 229 # substract baseline and integrate peak part "in between" 230 Xmid = X[index_l:index_r+1] 231 Ymid = Y[index_l:index_r+1] - lin_bl_fun(Xmid) 232 Ymid = np.array([y if y >= 0 else 0 for y in Ymid]) # set negatives to zero 233 cumarea = integrate.cumtrapz(Ymid, Xmid) # cumulative integral (area) 234 sigmoidal = np.concatenate(([0], cumarea)) / max(cumarea) * yval_delta + yval_l # add a zero for consistency with matlab code 235 236 # interpolate integral at support points (res = #intervals) 237 sig_nodes = np.linspace(Xmid[0], Xmid[-1], res+1) # Here should be an error in the corresponding matlab code. It produces 1001 nodes for res=1000 238 sig_interp = interpolate.interp1d(Xmid, sigmoidal, 'linear') # this is now a function object 239 sig_nodevals = sig_interp(sig_nodes) 240 241 # generate baseline function (piecewise cubic hermite interpolant) 242 sig_x = np.concatenate( ( [X[0]], [X[index_l-1]], sig_nodes, [X[index_r+1]], [X[-1]]) ) 243 sig_y = np.concatenate( ( [lin_bl_fun( X[0] )], [lin_bl_fun( X[index_l-1] )], sig_nodevals, [lin_bl_fun( X[index_r+1] )], [lin_bl_fun( X[-1] )] ) ) 244 blfun = interpolate.PchipInterpolator(sig_x, sig_y) # this is now a function object 245 246 return blfun 247 248 249 250def getBaseline(X, Y, bl_type='linear', blds=BaselineDetectionSettings()): 251 """ 252 Retrieves the baselevel function for specified DSC data. 253 254 ## Takes 255 **X**: 256 <br> Numpy vector of x-values (e.g. temperatures) or list (!) of vectors. 257 258 **Y**: 259 <br> Numpy vector of y-values (e.g. cp-values) or list (!) of vectors. 260 261 **bl_type**: 'linear' or 'sigmoidal' 262 (default: 'linear') 263 264 **blds** 265 <br> BaselineDetectionSettings-object containing the BaseLine Detection Setting 266 (default: Default BLDS object). 267 268 269 ## Returns 270 **blfun** 271 <br> Function handle of baselevel function. 272 273 **bldata** 274 <br> Structure containing information about the baseline. 275 276 OR if list have been passed: 277 278 **baseline_list**: 279 <br> List of tuples (blfun, bldata). 280 """ 281 282 283 if type(X) is list: 284 baseline_list = [getBaseline(X[i], Y[i], bl_type, blds) for i in range(len(X))] 285 return baseline_list 286 287 print("Detecting linear ranges") 288 peakPos = np.argmax(Y) 289 initlen_L = int( np.floor(blds.Linitfraction * peakPos) ) 290 initlen_R = int( np.floor(blds.Rinitfraction * (len(X)-peakPos)) ) 291 idx_L, reg_L, _ = detectLinearRange(X,Y,'left' ,initlen_L,blds.LreldevA, blds.LreldevB, blds.LreldevS2, blds.LabsdevA, blds.LabsdevB, blds.LabsdevS2) 292 idx_R, reg_R, _ = detectLinearRange(X,Y,'right',initlen_R,blds.RreldevA, blds.RreldevB, blds.RreldevS2, blds.RabsdevA, blds.RabsdevB, blds.RabsdevS2) 293 print('Done.') 294 295 # get baselevel function 296 blfun = getBaselinePrimitive(X, Y, idx_L, reg_L.a, reg_L.b, idx_R, reg_R.a, reg_R.b, bl_type) 297 298 # small plausibility check: slope of linear parts should be small; 299 maxslope = 0.1 300 if (abs(reg_L.b) > maxslope) or (abs(reg_R.b) > maxslope): 301 print("\n") 302 print(f"Slope of linear part is large: left: {reg_L.b}, right: {reg_R.b}. There's probably something wrong. Using proposed baseline instead!") 303 if abs(reg_L.b) > maxslope: 304 reg_L.b = 0 305 reg_L.a = Y[0] 306 idx_L = 1 307 if (abs(reg_R.b) > maxslope): 308 reg_R.b = 0 309 reg_R.a = Y[-1] 310 idx_R = len[Y]-2 311 blfun = getBaselinePrimitive(X, Y, idx_L, reg_L.a, reg_L.b, idx_R, reg_R.a, reg_R.b, bl_type) 312 313 314 # build data 315 316 # classic onset/endset estimation: where the linear parts are left (poor estimation) 317 # bldata.onset = X[idx_L] 318 # bldata.endset = X[idx_R] 319 320 # better onset/endset estimation: point X where the data (X,Y) first falls below baseline (seen from peak maximum) 321 # with fallback using the ind_L or idx_R 322 bloffset = 0.02 323 temp = Y[0:peakPos+1] - blfun(X[0:peakPos+1]) 324 counter = None 325 # get index of last value below bloffset 326 for k in range(len(temp)): 327 if temp[k] < bloffset: 328 counter = k 329 idxOnset = counter 330 331 temp = Y[peakPos:] - blfun(X[peakPos:]) 332 counter = None 333 # get index of first value below bloffset 334 for k in range(len(temp)): 335 if temp[k] < bloffset: 336 counter = k 337 break 338 idxEndset = counter + peakPos - 1 339 340 if idxOnset is None: 341 idxOnset = idx_L 342 if idxEndset is None: 343 idxEndset = idx_R 344 345 # save data 346 bldata = BaselineData() # save all together at the end? 347 bldata.reg_L = reg_L # regression information left 348 bldata.reg_R = reg_R # regression information right 349 bldata.onset = X[idxOnset] 350 bldata.endset = X[idxEndset] 351 352 return blfun, bldata 353 354 355def subtractBaseline(X, Yin, blfun, onset=None, endset=None, clearzero=True, nonnegative=True): 356 """ 357 Subtracts the baseline from given points. 358 359 ## Takes 360 **X**: x values (e.g. vector temperatures) 361 362 **Y**: y values or function (e.g. vector of cp values, or function cp(T)) 363 364 **blfun** 365 <br> Function handle to baseline function. 366 367 **clearzero** 368 <br> Flag indicating to clear zeros (see DSC204_clearZeroFromMax) (default: True). 369 370 **nonnegative** 371 <br> Flag indicating to ensure nonnegativity (default: True). 372 373 **onset** 374 <br> Onset value (zero values are put below/left of this x value) [optional]. 375 376 **endset** 377 <br> Endset value (zero values are put above/right of this x value) [optional]. 378 379 380 ## Returns 381 **Yvals** 382 <br> Processed y values. 383 384 **Yfun** 385 <br> Interpolator of processed values. 386 """ 387 388 if onset is None: 389 onset = min(X) 390 if endset is None: 391 endset = max(X) 392 393 assert( len(X) == len(Yin) ) 394 Yvals = Yin 395 396 # subtract baseline from Y data 397 Yvals = Yvals - blfun(X) 398 399 # make zeros outside the interval [onset, endset] 400 idx = [i for i in range(len(X)) if (X[i] < onset or X[i] > endset)] 401 Yvals[idx] = 0 402 403 # ensure nonnegativity 404 if nonnegative: 405 idx = [i for i in range(len(Yvals)) if Yvals[i] < 0] 406 Yvals[idx] = 0 407 408 # clear zero 409 if clearzero: 410 Yvals = clearZeroFromMax(Yvals) 411 412 413 # for the interpolation function, add some zero space left and right 414 addlen = 5 415 XX = np.concatenate( ( X[0] + np.arange(-addlen,0), X , X[-1] + np.arange(1,addlen+1) ) ) 416 YY = np.concatenate( ( np.zeros(addlen) , Yvals , np.zeros(addlen) ) ) 417 418 # build function from values 419 Yfun = interpolate.PchipInterpolator(XX,YY) 420 421 return Yvals, Yfun 422 423 424def clearZeroFromMax(data): 425 """ 426 Locates the maximum value in datavec, searches the first occurances 427 of a zero value to the left and to the right from the peak position 428 and deletes the noise before and after these positions. 429 430 ## Takes 431 **data**: Data array to be cleared 432 433 ## Returns 434 **data**: Cleared data array 435 """ 436 # Find index/position of maximal value 437 maxidx = np.argmax(data) 438 439 # Find the positions of the first zeros, seen from the peak value 440 clearToIdx = max( np.where(data[:maxidx+1]==0)[0] ) 441 clearFromIdx = min( np.where(data[maxidx:] ==0)[0] ) + maxidx 442 443 # delete noise 444 if not (clearToIdx is None): 445 data[:clearToIdx] = 0 446 if not (clearFromIdx is None): 447 data[clearFromIdx:] = 0 448 449 return data
9class BaselineData: 10 """ 11 Stores information about the baselevel. 12 13 ## Parameters 14 **reg_L** 15 <br> Left linear part. 16 17 **reg_R** 18 <br> Right linear part. 19 20 **onset** 21 <br> Onset of phase transition. 22 23 **endset** 24 <br> Endset of the phase transition. 25 """ 26 def __init__(self, reg_L=None, reg_R=None, onset=None, endset=None): 27 self.reg_L = reg_L 28 self.reg_R = reg_R 29 self.endset = endset 30 self.onset = onset
Stores information about the baselevel.
Parameters
reg_L
Left linear part.
reg_R
Right linear part.
onset
Onset of phase transition.
endset
Endset of the phase transition.
32class BaselineDetectionSettings: 33 """ 34 Stores (default) baseline detection settings to be passed to `fpex0.baseline.detectLinearRange()`. 35 36 ## Parameters 37 Naming: 38 - R,L = right or left 39 - abs, rel = absolute or relative 40 - dev = deviation 41 - initfraction = initial fraction of data to interpolate. 42 43 **LabsdevA**, **LabsdevB**, **LabsdevS2** 44 45 **LreldevA**, **LreldevB**, **LreldevS2** 46 47 **Linitfraction** 48 49 **RabsdevA**, **RabsdevB**, **RabsdevS2** 50 51 **RreldevA**, **RreldevB**, **RreldevS2** 52 53 **Rinitfraction** 54 """ 55 def __init__(self): 56 self.LabsdevA = -1 57 self.LabsdevB = -1 58 self.LabsdevS2 = -1 59 self.LreldevA = np.infty 60 self.LreldevB = 0.01 61 self.LreldevS2 = 2.00 62 self.Linitfraction = 0.1 63 64 self.RabsdevA = -1 65 self.RabsdevB = 0.05 66 self.RabsdevS2 = -1 67 self.RreldevA = np.infty 68 self.RreldevB = np.infty 69 self.RreldevS2 = 2.00 70 self.Rinitfraction = 0.2
Stores (default) baseline detection settings to be passed to fpex0.baseline.detectLinearRange()
.
Parameters
Naming:
- R,L = right or left
- abs, rel = absolute or relative
- dev = deviation
- initfraction = initial fraction of data to interpolate.
LabsdevA, LabsdevB, LabsdevS2
LreldevA, LreldevB, LreldevS2
Linitfraction
RabsdevA, RabsdevB, RabsdevS2
RreldevA, RreldevB, RreldevS2
Rinitfraction
55 def __init__(self): 56 self.LabsdevA = -1 57 self.LabsdevB = -1 58 self.LabsdevS2 = -1 59 self.LreldevA = np.infty 60 self.LreldevB = 0.01 61 self.LreldevS2 = 2.00 62 self.Linitfraction = 0.1 63 64 self.RabsdevA = -1 65 self.RabsdevB = 0.05 66 self.RabsdevS2 = -1 67 self.RreldevA = np.infty 68 self.RreldevB = np.infty 69 self.RreldevS2 = 2.00 70 self.Rinitfraction = 0.2
73def detectLinearRange(X, Y, side, initlen, reldevAmax, reldevBmax, reldevS2max, absdevAmax, absdevBmax, absdevS2max): 74 """ 75 Detect the stop position of the linear range. 76 77 ## Takes 78 79 **X**: x-values 80 81 **Y**: y-values 82 83 **side**: 'L' or 'R' for "from left" or "from right" 84 85 **initlen** 86 <br> Length (in samples) to calculate the initial standard deviation. 87 88 **reldevA** 89 <br> Acceptable relative deviation of initial y-axis intercept. 90 91 **reldevB** 92 <br> Acceptable relative deviation of initial slope. 93 94 **reldevS2** 95 <br> Acceptable relative deviation of initial squared standard deviation. 96 97 **absdevA** 98 <br> Acceptable absolute deviation of initial y-axis intercept. 99 100 **absdevB** 101 <br> Acceptable absolute deviation of initial. 102 103 **absdevS2** 104 <br> Acceptable absolute deviation of initial squared standard deviation. 105 106 107 ## Returns 108 **stopidx** 109 <br> Position in X, where the linear range is estimated to be left. 110 111 **reg** 112 <br> Final regression structure as returned by fpex0.linreg.LinearRegression.linReg. 113 114 **initreg** 115 <br> Initial regression structure as returned by fpex0.linreg.LinearRegression.linReg. 116 117 ### Comments 118 * Set the `reldev` to inf to disable 119 120 * Set the `absdev` to -1 to disable 121 122 * The `reldev` is only be checked if value exceeds `absdev` 123 124 """ 125 126 127 # choose loop-range 128 if side.lower() == "left": 129 start = initlen 130 stop = len(X) 131 step = 1 132 # initial regression 133 initreg = LinearRegression() 134 initreg.linReg(X[:start], Y[:start]) 135 elif side.lower() == "right": 136 start = len(X)-1 - initlen # start at position left from initial range 137 stop = -1 # exclusive index 138 step = -1 139 # initial regression 140 initreg = LinearRegression() 141 initreg.linReg(X[start+1:], Y[start+1:]) 142 143 144 reg = copy(initreg) 145 index = None 146 147 for index in range(start, stop, step): 148 # update regression 149 reg.linReg(X[index], Y[index]) 150 151 absdevA = abs(reg.a - initreg.a ) 152 reldevA = abs(absdevA / initreg.a ) 153 absdevB = abs(reg.b - initreg.b ) 154 reldevB = abs(absdevB / initreg.b ) 155 absdevS2 = abs(reg.s2 - initreg.s2 ) 156 reldevS2 = abs(absdevS2 / initreg.s2) 157 158 # check deviation 159 if (absdevA > absdevAmax ) and (reldevA > reldevAmax ): 160 break 161 if (absdevB > absdevBmax ) and (reldevB > reldevBmax ): 162 break 163 if (absdevS2 > absdevS2max) and (reldevS2 > reldevS2max): 164 break 165 stopidx = index 166 167 return stopidx, reg, initreg
Detect the stop position of the linear range.
Takes
X: x-values
Y: y-values
side: 'L' or 'R' for "from left" or "from right"
initlen
Length (in samples) to calculate the initial standard deviation.
reldevA
Acceptable relative deviation of initial y-axis intercept.
reldevB
Acceptable relative deviation of initial slope.
reldevS2
Acceptable relative deviation of initial squared standard deviation.
absdevA
Acceptable absolute deviation of initial y-axis intercept.
absdevB
Acceptable absolute deviation of initial.
absdevS2
Acceptable absolute deviation of initial squared standard deviation.
Returns
stopidx
Position in X, where the linear range is estimated to be left.
reg
Final regression structure as returned by fpex0.linreg.LinearRegression.linReg.
initreg
Initial regression structure as returned by fpex0.linreg.LinearRegression.linReg.
Comments
Set the
reldev
to inf to disableSet the
absdev
to -1 to disableThe
reldev
is only be checked if value exceedsabsdev
170def getBaselinePrimitive(X, Y, index_l, icept_l, slope_l, index_r, icept_r, slope_r, bl_type, res=1000): 171 """ 172 Retrieves the baselevel function for specified data. 173 174 ## Takes 175 176 **X**: x-values (temperatures, time, ...) 177 178 **Y**: y-values (voltage, heat flux, ....) 179 180 **index_l** 181 <br> Index where left linear part is left ("onset") 182 183 **icept_l** 184 <br> y-intercept of left linear part. 185 186 **slope_l** 187 <br> Slope of left linear part. 188 189 **index_r** 190 <br> Index where right linear part is left ("onset"). 191 192 **icept_r** 193 <br> y-intercept of right linear part. 194 195 **slope_r** 196 <br> Slope of right linear part. 197 198 **bl_type** 199 <br> What type should the baselevel function have? "linear" or "sigmoidal". 200 201 **res** 202 <br> Number of support points for sigmoidal (default: 100). 203 204 205 ## Returns 206 207 **blfun** 208 <br> Function handle of baselevel function 209 210 """ 211 212 # calculate values at "onset" and "offset" 213 yval_l = icept_l + X[index_l] * slope_l 214 yval_r = icept_r + X[index_r] * slope_r 215 yval_delta = yval_r - yval_l 216 217 # calculate linear base level function 218 lin_bl_xvals = [ X[0] , X[index_l], X[index_r], X[-1] ] 219 lin_bl_yvals = [icept_l+X[0]*slope_l, yval_l , yval_r , icept_r+X[-1]*slope_r ] 220 lin_bl_fun = interpolate.interp1d(lin_bl_xvals, lin_bl_yvals, 'linear') # piecewise linear interpolation 221 222 223 if bl_type.lower() == 'linear': 224 return lin_bl_fun 225 226 elif bl_type.lower() in ['sigmoid', 'sigmoidal']: 227 228 # sigmoidal interpolation as in Fig. 3 of DIN 51007 229 230 # substract baseline and integrate peak part "in between" 231 Xmid = X[index_l:index_r+1] 232 Ymid = Y[index_l:index_r+1] - lin_bl_fun(Xmid) 233 Ymid = np.array([y if y >= 0 else 0 for y in Ymid]) # set negatives to zero 234 cumarea = integrate.cumtrapz(Ymid, Xmid) # cumulative integral (area) 235 sigmoidal = np.concatenate(([0], cumarea)) / max(cumarea) * yval_delta + yval_l # add a zero for consistency with matlab code 236 237 # interpolate integral at support points (res = #intervals) 238 sig_nodes = np.linspace(Xmid[0], Xmid[-1], res+1) # Here should be an error in the corresponding matlab code. It produces 1001 nodes for res=1000 239 sig_interp = interpolate.interp1d(Xmid, sigmoidal, 'linear') # this is now a function object 240 sig_nodevals = sig_interp(sig_nodes) 241 242 # generate baseline function (piecewise cubic hermite interpolant) 243 sig_x = np.concatenate( ( [X[0]], [X[index_l-1]], sig_nodes, [X[index_r+1]], [X[-1]]) ) 244 sig_y = np.concatenate( ( [lin_bl_fun( X[0] )], [lin_bl_fun( X[index_l-1] )], sig_nodevals, [lin_bl_fun( X[index_r+1] )], [lin_bl_fun( X[-1] )] ) ) 245 blfun = interpolate.PchipInterpolator(sig_x, sig_y) # this is now a function object 246 247 return blfun
Retrieves the baselevel function for specified data.
Takes
X: x-values (temperatures, time, ...)
Y: y-values (voltage, heat flux, ....)
index_l
Index where left linear part is left ("onset")
icept_l
y-intercept of left linear part.
slope_l
Slope of left linear part.
index_r
Index where right linear part is left ("onset").
icept_r
y-intercept of right linear part.
slope_r
Slope of right linear part.
bl_type
What type should the baselevel function have? "linear" or "sigmoidal".
res
Number of support points for sigmoidal (default: 100).
Returns
blfun
Function handle of baselevel function
251def getBaseline(X, Y, bl_type='linear', blds=BaselineDetectionSettings()): 252 """ 253 Retrieves the baselevel function for specified DSC data. 254 255 ## Takes 256 **X**: 257 <br> Numpy vector of x-values (e.g. temperatures) or list (!) of vectors. 258 259 **Y**: 260 <br> Numpy vector of y-values (e.g. cp-values) or list (!) of vectors. 261 262 **bl_type**: 'linear' or 'sigmoidal' 263 (default: 'linear') 264 265 **blds** 266 <br> BaselineDetectionSettings-object containing the BaseLine Detection Setting 267 (default: Default BLDS object). 268 269 270 ## Returns 271 **blfun** 272 <br> Function handle of baselevel function. 273 274 **bldata** 275 <br> Structure containing information about the baseline. 276 277 OR if list have been passed: 278 279 **baseline_list**: 280 <br> List of tuples (blfun, bldata). 281 """ 282 283 284 if type(X) is list: 285 baseline_list = [getBaseline(X[i], Y[i], bl_type, blds) for i in range(len(X))] 286 return baseline_list 287 288 print("Detecting linear ranges") 289 peakPos = np.argmax(Y) 290 initlen_L = int( np.floor(blds.Linitfraction * peakPos) ) 291 initlen_R = int( np.floor(blds.Rinitfraction * (len(X)-peakPos)) ) 292 idx_L, reg_L, _ = detectLinearRange(X,Y,'left' ,initlen_L,blds.LreldevA, blds.LreldevB, blds.LreldevS2, blds.LabsdevA, blds.LabsdevB, blds.LabsdevS2) 293 idx_R, reg_R, _ = detectLinearRange(X,Y,'right',initlen_R,blds.RreldevA, blds.RreldevB, blds.RreldevS2, blds.RabsdevA, blds.RabsdevB, blds.RabsdevS2) 294 print('Done.') 295 296 # get baselevel function 297 blfun = getBaselinePrimitive(X, Y, idx_L, reg_L.a, reg_L.b, idx_R, reg_R.a, reg_R.b, bl_type) 298 299 # small plausibility check: slope of linear parts should be small; 300 maxslope = 0.1 301 if (abs(reg_L.b) > maxslope) or (abs(reg_R.b) > maxslope): 302 print("\n") 303 print(f"Slope of linear part is large: left: {reg_L.b}, right: {reg_R.b}. There's probably something wrong. Using proposed baseline instead!") 304 if abs(reg_L.b) > maxslope: 305 reg_L.b = 0 306 reg_L.a = Y[0] 307 idx_L = 1 308 if (abs(reg_R.b) > maxslope): 309 reg_R.b = 0 310 reg_R.a = Y[-1] 311 idx_R = len[Y]-2 312 blfun = getBaselinePrimitive(X, Y, idx_L, reg_L.a, reg_L.b, idx_R, reg_R.a, reg_R.b, bl_type) 313 314 315 # build data 316 317 # classic onset/endset estimation: where the linear parts are left (poor estimation) 318 # bldata.onset = X[idx_L] 319 # bldata.endset = X[idx_R] 320 321 # better onset/endset estimation: point X where the data (X,Y) first falls below baseline (seen from peak maximum) 322 # with fallback using the ind_L or idx_R 323 bloffset = 0.02 324 temp = Y[0:peakPos+1] - blfun(X[0:peakPos+1]) 325 counter = None 326 # get index of last value below bloffset 327 for k in range(len(temp)): 328 if temp[k] < bloffset: 329 counter = k 330 idxOnset = counter 331 332 temp = Y[peakPos:] - blfun(X[peakPos:]) 333 counter = None 334 # get index of first value below bloffset 335 for k in range(len(temp)): 336 if temp[k] < bloffset: 337 counter = k 338 break 339 idxEndset = counter + peakPos - 1 340 341 if idxOnset is None: 342 idxOnset = idx_L 343 if idxEndset is None: 344 idxEndset = idx_R 345 346 # save data 347 bldata = BaselineData() # save all together at the end? 348 bldata.reg_L = reg_L # regression information left 349 bldata.reg_R = reg_R # regression information right 350 bldata.onset = X[idxOnset] 351 bldata.endset = X[idxEndset] 352 353 return blfun, bldata
Retrieves the baselevel function for specified DSC data.
Takes
X:
Numpy vector of x-values (e.g. temperatures) or list (!) of vectors.
Y:
Numpy vector of y-values (e.g. cp-values) or list (!) of vectors.
bl_type: 'linear' or 'sigmoidal' (default: 'linear')
blds
BaselineDetectionSettings-object containing the BaseLine Detection Setting
(default: Default BLDS object).
Returns
blfun
Function handle of baselevel function.
bldata
Structure containing information about the baseline.
OR if list have been passed:
baseline_list:
List of tuples (blfun, bldata).
356def subtractBaseline(X, Yin, blfun, onset=None, endset=None, clearzero=True, nonnegative=True): 357 """ 358 Subtracts the baseline from given points. 359 360 ## Takes 361 **X**: x values (e.g. vector temperatures) 362 363 **Y**: y values or function (e.g. vector of cp values, or function cp(T)) 364 365 **blfun** 366 <br> Function handle to baseline function. 367 368 **clearzero** 369 <br> Flag indicating to clear zeros (see DSC204_clearZeroFromMax) (default: True). 370 371 **nonnegative** 372 <br> Flag indicating to ensure nonnegativity (default: True). 373 374 **onset** 375 <br> Onset value (zero values are put below/left of this x value) [optional]. 376 377 **endset** 378 <br> Endset value (zero values are put above/right of this x value) [optional]. 379 380 381 ## Returns 382 **Yvals** 383 <br> Processed y values. 384 385 **Yfun** 386 <br> Interpolator of processed values. 387 """ 388 389 if onset is None: 390 onset = min(X) 391 if endset is None: 392 endset = max(X) 393 394 assert( len(X) == len(Yin) ) 395 Yvals = Yin 396 397 # subtract baseline from Y data 398 Yvals = Yvals - blfun(X) 399 400 # make zeros outside the interval [onset, endset] 401 idx = [i for i in range(len(X)) if (X[i] < onset or X[i] > endset)] 402 Yvals[idx] = 0 403 404 # ensure nonnegativity 405 if nonnegative: 406 idx = [i for i in range(len(Yvals)) if Yvals[i] < 0] 407 Yvals[idx] = 0 408 409 # clear zero 410 if clearzero: 411 Yvals = clearZeroFromMax(Yvals) 412 413 414 # for the interpolation function, add some zero space left and right 415 addlen = 5 416 XX = np.concatenate( ( X[0] + np.arange(-addlen,0), X , X[-1] + np.arange(1,addlen+1) ) ) 417 YY = np.concatenate( ( np.zeros(addlen) , Yvals , np.zeros(addlen) ) ) 418 419 # build function from values 420 Yfun = interpolate.PchipInterpolator(XX,YY) 421 422 return Yvals, Yfun
Subtracts the baseline from given points.
Takes
X: x values (e.g. vector temperatures)
Y: y values or function (e.g. vector of cp values, or function cp(T))
blfun
Function handle to baseline function.
clearzero
Flag indicating to clear zeros (see DSC204_clearZeroFromMax) (default: True).
nonnegative
Flag indicating to ensure nonnegativity (default: True).
onset
Onset value (zero values are put below/left of this x value) [optional].
endset
Endset value (zero values are put above/right of this x value) [optional].
Returns
Yvals
Processed y values.
Yfun
Interpolator of processed values.
425def clearZeroFromMax(data): 426 """ 427 Locates the maximum value in datavec, searches the first occurances 428 of a zero value to the left and to the right from the peak position 429 and deletes the noise before and after these positions. 430 431 ## Takes 432 **data**: Data array to be cleared 433 434 ## Returns 435 **data**: Cleared data array 436 """ 437 # Find index/position of maximal value 438 maxidx = np.argmax(data) 439 440 # Find the positions of the first zeros, seen from the peak value 441 clearToIdx = max( np.where(data[:maxidx+1]==0)[0] ) 442 clearFromIdx = min( np.where(data[maxidx:] ==0)[0] ) + maxidx 443 444 # delete noise 445 if not (clearToIdx is None): 446 data[:clearToIdx] = 0 447 if not (clearFromIdx is None): 448 data[clearFromIdx:] = 0 449 450 return data
Locates the maximum value in datavec, searches the first occurances of a zero value to the left and to the right from the peak position and deletes the noise before and after these positions.
Takes
data: Data array to be cleared
Returns
data: Cleared data array