NAG Toolbox for MATLAB Demo for the Change Point Analysis
The NAG Toolbox routines nag_tsa_cp_pelt (g13na), nag_tsa_cp_pelt_user (g13nb), nag_tsa_cp_binary (g13nd) and nag_tsa_cp_binary_user (g13ne) perform change point detection using either the PELT algorithm or using binary detection and either with or without a user supplied cost function.
This demo illustrates the use of the routine nag_tsa_cp_pelt (g13na). The PELT algorithm is used with the choice of four model distributions form which parameter changes are detected. A different data set is used for the four distributions: Normal, Gamma, exponential and Poisson.
Contents
Model Parameter Values
For the Normal and Gamma distributions, a parameter value is required. For the Normal distribution, where change points are sought for either the mean or the standard deviation (not both) requires a fixed value for the other. For the Gamma distribution, the shape parameter is required and change points are sought for the scale parameter. Scale parameter values are plotted; multiplying these by the chosen shape parameter value gives the mean for that change point region.
For the exponential and Poisson distributions, change points in the lambda parameter are sought.
Minimum distance between change points (minss)
This is initially set to the default value of 2, but can be increased to provide (possibly) different sets of regions.
function change_point_demo % can be changed: % the distribution, % the parameter value (if any), % the minimum distance between change points. scrsz = get(0, 'ScreenSize'); % Main window size fw = 0.7*scrsz(3); fh = 0.7*scrsz(4); % other sizes l = 24; % distance from LHS of figure s = 80; % spacing between controls tw = 20; % text label width sl = 200; % slider length sh = 25; % slider height td = fh - 100; % distance from top of figure % Set up figure h1 = figure( ... 'Visible','off', ... 'Name', 'Change Point Analysis using NAG Toolbox for MATLAB', ... 'NumberTitle','off',... 'MenuBar','none',... 'Position', [scrsz(1)+100 scrsz(2)+20 fw fh] ... ); % Defaults paramdef = 1; distdef = 1; gapdef = 2; h2 = axes( ... 'Parent',h1,... 'Position', [0.216 0.148 0.738 0.798],... 'ActivePositionProperty','position',... 'DataAspectRatio',[50 3.5 1],... 'PlotBoxAspectRatio',[1 0.608 0.608], ... 'XLim',[0 100], 'YLim',[-3 4], 'ZLim',[-1 1], ... 'XTick', [0 10 20 30 40 50 60 70 80 90 100], ... 'XTickLabel',{ '0'; '10'; '20'; '30'; '40'; '50'; '60'; '70'; '80'; '90'; '100' }, ... 'YTick', [-3 -2 -1 0 1 2 3 4], ... 'YTickLabel', { '-3'; '-2'; '-1'; '0'; '1'; '2'; '3'; '4' }, ... 'SortMethod','childorder',... 'Box','on',... 'ParentMode', 'manual' ... ); h3 = get(h2,'title'); set(h3, ... 'Parent', h2, ... 'Units', 'data', ... 'FontUnits','points', ... 'Color', [0 0 0], ... 'Position', [50.0 4.0 0], ... 'FontName', 'Helvetica', ... 'FontSize', 11, ... 'FontWeight', 'bold', ... 'HorizontalAlignment','center', ... 'VerticalAlignment', 'bottom', ... 'LineStyle','-', ... 'LineWidth',0.5, ... 'Margin',3, ... 'Description','Axes Title' ... ); % Buttons % Parameter value slider uicontrol( ... 'Parent',h1,... 'Style', 'slider', ... 'Min', 0.01, 'Max', 5, ... 'Position', [l td-s sl sh], ... 'TooltipString', 'Chooses the parameter value', ... 'Value', paramdef, ... 'Callback', @parameter); uicontrol('Style','text',... 'Position',[l td-s+sh+5 sl sh],... 'String','parameter value'); param_string = uicontrol( ... 'Parent', h1, ... 'Style', 'edit', ... 'String', num2str(paramdef), ... 'Enable','inactive', ... 'Position', [l+sl+5 td-s tw sh]); % Minimum gap between change points uicontrol( ... 'Parent', h1, ... 'Style', 'slider', ... 'Min', 2, 'Max', 20, ... 'Position', [l td-2*s sl sh], ... 'TooltipString', ... 'Chooses minimum gap between cchange points', ... 'Value', gapdef, ... 'Callback', @gap); uicontrol( ... 'Parent', h1, ... 'Style','text',... 'Position',[l td-2*s+sh+5 sl sh],... 'String','minss'); gap_string = uicontrol( ... 'Parent', h1, ... 'Style', 'edit', ... 'String', num2str(gapdef), ... 'Enable','inactive', ... 'Position', [l+sl+5 td-2*s tw sh]); uicontrol( ... 'Parent',h1,... 'Style', 'popup', ... 'String', ... ['Normal mean'; 'Normal s.d.'; 'Normal both'; 'Gamma '; 'Exponential'; 'Poisson '], ... 'Position',[l td-3*s sl sh], ... 'TooltipString', 'Chooses the distribution', ... 'Value', distdef, ... 'Callback', @choosedist); uicontrol( ... 'Parent', h1, ... 'Style','text',... 'Position',[l td-3*s+sh+5 sl sh],... 'String','Distribution / characteristic'); uicontrol('Style','pushbutton',... 'String','Solve',... 'Callback',@solver,... 'Position',[l td-5*s sl sh]); % Data values [y] = get_data(distdef); d.y = y; d.reset = false; d.param = paramdef; d.param_string = param_string; d.gap = gapdef; d.gap_string = gap_string; d.dist = distdef; guidata(h1,d); solver(h1,d); % Make the UI visible. h1.Visible = 'on'; end function [y] = get_data(ctype) if ctype<4 y = [ 0.00; 0.78;-0.02; 0.17; 0.04;-1.23; 0.24; 1.70; 0.77; 0.06; 0.67; 0.94; 1.99; 2.64; 2.26; 3.72; 3.14; 2.28; 3.78; 0.83; 2.80; 1.66; 1.93; 2.71; 2.97; 3.04; 2.29; 3.71; 1.69; 2.76; 1.96; 3.17; 1.04; 1.50; 1.12; 1.11; 1.00; 1.84; 1.78; 2.39; 1.85; 0.62; 2.16; 0.78; 1.70; 0.63; 1.79; 1.21; 2.20;-1.34; 0.04;-0.14; 2.78; 1.83; 0.98; 0.19; 0.57;-1.41; 2.05; 1.17; 0.44; 2.32; 0.67; 0.73; 1.17;-0.34; 2.95; 1.08; 2.16; 2.27; -0.14;-0.24; 0.27; 1.71;-0.04;-1.03;-0.12;-0.67; 1.15;-1.10; -1.37; 0.59; 0.44; 0.63;-0.06;-0.62; 0.39;-2.63;-1.63;-0.42; -0.73; 0.85; 0.26; 0.48;-0.26;-1.77;-1.53;-1.39; 1.68; 0.43]; end if ctype==4 y = [ 13.766; 10.859; 12.693; 11.319; 12.900; 11.506; 13.457; 12.171; 12.807; 11.805; 11.538; 10.220; 11.984; 3.520; 3.313; 2.493; 2.079; 2.486; 2.504; 2.965; 1.693; 10.802; 11.274; 10.548; 11.371; 4.983; 3.913; 3.623; 4.311; 5.224; 6.346; 3.632; 2.902; 4.408; 4.725; 3.272; 4.416; 21.270; 24.844; 24.056; 22.942; 24.244; 23.211; 21.841; 20.446; 20.125; 20.794; 22.214; 20.773; 21.421; 20.776; 21.944; 20.792; 21.171; 19.412; 21.176; 21.710; 20.534; 20.844; 19.695; 20.702; 19.775; 18.881; 20.937; 20.516; 21.310; 23.591; 22.099; 23.079]; end if ctype==5 y = [ 19.414; 17.796; 21.752; 20.553; 19.245; 19.295; 19.989; 19.713; 18.362; 23.742; 22.147; 20.709; 23.191; 23.741; 21.304; 22.366; 20.972; 21.096; 21.953; 22.939; 21.490; 22.052; 33.393; 32.475; 30.114; 31.488; 31.181; 33.576; 31.361; 16.037; 16.008; 13.977; 16.042; 17.165; 13.194; 15.856; 14.469; 17.987; 16.724; 19.048; 18.028; 17.656; 14.385; 14.224; 14.184; 13.903; 15.443; 12.928; 13.890; 13.518; 14.635; 14.930; 12.460; 11.881; 12.568; 11.825; 13.272; 11.808; 12.739; 12.520; 14.516; 12.212; 10.918; 11.376; 13.649; 13.528; 11.504; 12.236; 11.590; 13.249; 12.934; 11.898; 11.425; 10.736; 11.946]; end if ctype==6 y = [8.0; 9.0; 10.0; 9.0; 10.0; 8.0; 16.0; 14.0; 10.0; 16.0; 10.0; 14.0; 14.0; 10.0; 11.0; 15.0; 13.0; 10.0; 15.0; 12.0; 9.0; 12.0; 13.0; 14.0; 8.0; 12.0; 13.0; 9.0; 11.0; 8.0; 14.0; 23.0; 9.0; 10.0; 10.0; 4.0; 16.0; 8.0; 8.0; 11.0; 1.0; 11.0; 17.0; 5.0; 17.0; 4.0; 19.0; 10.0; 8.0; 15.0; 15.0; 12.0; 11.0; 7.0; 21.0; 5.0; 15.0; 7.0; 10.0; 12.0; 12.0; 13.0; 15.0; 9.0; 10.0; 11.0; 11.0; 12.0; 12.0; 10.0; 12.0; 11.0; 12.0; 11.0; 11.0; 11.0]; end end function parameter(hObject,eventData) d = guidata(hObject); slider_val = get(hObject,'Value'); set(d.param_string,'String',num2str(slider_val)); d.param = slider_val; guidata(hObject,d); end function gap(hObject,eventData) d = guidata(hObject); slider_val = round(get(hObject,'Value')); set(d.gap_string,'String',num2str(slider_val)); d.gap = slider_val; guidata(hObject,d); end function choosedist(hObject,eventData) d = guidata(hObject); v = get(hObject, 'Value'); d.dist = v; [d.y] = get_data(d.dist); guidata(hObject,d); end function solver(hObject,eventData) gd = guidata(hObject); y = gd.y; param = zeros(1,1); param(1) = gd.param; minss = gd.gap; ctype = gd.dist; % Setup time series data ctitles = char('Normal distribution looking for changes in the mean', ... 'Normal distribution looking for changes in the s.d.', ... 'Normal distribution looking for changes in the mean and s.d.', ... 'Gamma distribution looking for changes scale parameter b', ... 'Exponential distribution looking for changes in lambda', ... 'Poisson distribution looking for changes in lambda'); n = size(y,1); if ctype==3 beta = 2*log(n); else beta = log(n); end ctitle = ctitles(ctype,:); [tau,sparam,~] = g13na( ... nag_int(ctype), y, 'beta', beta, 'minss', ... nag_int(minss), 'param',param); plot(y,'Color','red'); if numel(tau) > 1 xpos = transpose(double(tau(1:end-1))*ones(1,2)); ypos = diag(ylim)*ones(2,numel(tau)-1); line(xpos,ypos,'Color','black'); xpos = transpose(cat(2,cat(1,1,tau(1:end-1)),tau)); if ctype==2 || ctype==4 ypos = ones(2,1)*sparam(2,:); elseif ctype==6 ypos = ones(2,1)*transpose(sparam(:)); else ypos = ones(2,1)*sparam(1,:); end line(xpos,ypos,'Color','green'); if ctype==3 ypos = ones(2,1)*sparam(2,:); line(xpos,ypos,'Color','magenta'); end end title(ctitle); xlabel('{\bf Time}'); ylabel('{\bf Value}'); gd.reset = true; guidata(hObject,gd); end
