Open change_point_demo.m in the Editor
Run demo

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