Skip to content
Snippets Groups Projects
S8b_cpnts.m 18.82 KiB
%% S8_cpnts
% visualisation of the lying bouts + changepoints for development purposes

clear variables
close all
clc

% results directory
init_.resdir = ['C:\Users\adria036\OneDrive - Wageningen '...
                'University & Research\iAdriaens_doc\Projects\' ...
                'eEllen\B4F_indTracking_cattle\B4F_results\'];
% load data
load([init_.resdir 'D3_sdata_IQ_UWB.mat'])
load([init_.resdir 'D2_data_meta.mat'])


%% set constants
% delete cow 280
sdata = rmfield(sdata,'cow_280');
fields_ = fieldnames(sdata);
for f = 1:length(fields_)
% set cow
cow = fields_{f};
% cow = 'cow_99';

% set technique
cpts.Zstat = 'mean';       % technique based on which cpts are found for z
cpts.CDstat = 'std';      % technique based on which cpts are found for CD

% gapsize for new segment for changepoint analysis
cpts.gapsize = 300;         % 5 minutes 

% segment length
cpts.segmentlength = 600;   % 10 minutes

% set windowsize in days
cpts.window = 600/(60*60*24);      % windowsize in which cpts are detected

% set settings
cpts.MinDistance = 1200;     % 20 min 
cpts.NumChanges = 2;        % number of changes per hour of data

% figure settings
cpts.Units = 'centimeters';     % set units
cpts.figPos = [-35 1 30 22];      % set position


%% select data and plot lying bouts
% select data of one day per cow

clear data
close all
                                     
% data contains "z" and "CD" and runs from first lying to last lying bout
data = sdata.(cow)(:,[2 3 7 13 17 18 15]);                                      
  
% reference date for numeric time
refdate = datenum(2019,7,3);

% summary of the lying bouts
data.islying_prev(1) = 0;
data.islying_prev(2:end) = data.islying(1:end-1);
data.lychange = ((data.islying == 1 & data.islying_prev == 0) | ...
                 (data.islying == 0 & data.islying_prev == 1));
data_ld = data(data.lychange == 1,:);
data_ld.dateup(:,1) = NaT;           
data_ld.dateup(1:2:end-1) = data_ld.date(2:2:end);
data_ld(isnat(data_ld.dateup),:) = [];
data_ld = movevars(data_ld,'dateup','After','date');
data_ld.numdatedown = datenum(data_ld.date) - refdate;
data_ld.numdateup = datenum(data_ld.dateup) - refdate;

% visualise the data and the lying bouts (data_meta.selection)
h = figure('Units',cpts.Units,...
           'OuterPosition',cpts.figPos);            % figure
subplot(2,1,1); hold on; box on;title('Z');         % prepare z plot
ylim([0 2.5])
subplot(2,1,2); hold on; box on;title('CD');        % prepare CD plot
ylim([0 12])

% plot lying bouts 
for i = 1:2
    subplot(2,1,i);
    
    for j = 1:height(data_ld)
        x = [datenum(data_ld.date)-refdate ...
             datenum(data_ld.dateup)-refdate];      % numeric time
        y = [0.1 0.1];               % y variable = thick line
        plot(x,y,'LineWidth',4.5,'Color',[204/255 0 102/255])
        x = [datenum(data_ld.date)-refdate ...    % numeric time
             datenum(data_ld.date)-refdate];
        y = [0 15];                 % vertical line begin
        plot(x,y,'LineWidth',0.8,'Color',[204/255 0 102/255])
        x = [datenum(data_ld.dateup)-refdate ...
             datenum(data_ld.dateup)-refdate]; % vertical line end
        plot(x,y,'LineWidth',0.8,'Color',[204/255 0 102/255])
    end
end

% plot z data and implement changepoint analysis
%       stap 1: determine data with gaps not too long and plot gap
%       step 2: split data according to gaps
%       step 3: determine max amount of CP based on data
%       step 4: CP analysis
%       step 5: visualise result

data2 = data(isnan(data.avg_z_sm)~= 1 & isnan(data.centerdist) ~= 1,:);
data2.numdate = datenum(data2.date)-refdate;

% plot z data
subplot(2,1,1); plot(data2.numdate,data2.avg_z_sm, ...
                     'LineWidth',0.9,'Color',[60/255 179/255 113/255]);
xlim([min(data2.numdate) max(data2.numdate)])

% plot cd data
subplot(2,1,2); plot(data2.numdate,data2.centerdist, ...
                     'LineWidth',0.9,'Color',[60/255 179/255 113/255]);
xlim([min(data2.numdate) max(data2.numdate)])

% calculate gapsize
data2.gapsize(end) = 0;
data2.gapsize(1:end-1) = diff(data2.numdate)*60*60*24;

% select data segments and characterize the segments
ind = find(data2.gapsize > cpts.gapsize);
segments = data2(ind,:);                                    % segments
segments.segstart(1) = data2.numdate(1);                    % segment start
segments.segstart(2:end) = data2.numdate(ind(1:end-1)+1);   % segment start
segments.dur = (segments.numdate - segments.segstart)*60*60*24;  % dur in s

% find the segment where transitions happens
for i = 1:height(data_ld)
    % find whether the lie down event is in a segment
    ind = find(segments.segstart < data_ld.numdatedown(i) & ...
               segments.numdate > data_ld.numdatedown(i));
    if ~isempty(ind)
        data_ld.segdown(i) = ind;
    else
        data_ld.segdown(i) = NaN;
    end
    
    % find whether the get up event is in a segment
    ind = find(segments.segstart < data_ld.numdateup(i) & ...
               segments.numdate > data_ld.numdateup(i));
    if ~isempty(ind)
        data_ld.segup(i) = ind;
    else
        data_ld.segup(i) = NaN;
    end
end

% implement changepoint analysis on the segments that are long enough, and
% that have at least one getup/liedown event

% segment number
segments.number = (1:height(segments))';

% segments with an event get up or lie down
seglist = unique([data_ld.segdown;data_ld.segup]);
seglist(isnan(seglist)) = [];

% add to segments whether there is a get up or lie down event
segments.liedown = ismember(segments.number, seglist);

%%
%---------------------------------------------------------------------
% changepoint analysis - ATTEMPT 2 - take full day independent of segment
% length and a fixed number of max changepoints
%       -->  evaluate validity of detection only
%            if there is a getup or liedown event
days = unique(floor(data2.numdate));
for i = 1:length(days)
    % select daydata 
    data_day = data2(floor(data2.numdate) == days(i),:);
    
    
    
    if height(data_day) > 12*60*60
        % prepare results
        day = sprintf('day_%d',days(i));
        
        % prepare figure
        k = figure('Units',cpts.Units,...
           'OuterPosition',cpts.figPos);            % figure
        subplot(2,1,1); hold on; box on;            % prepare z plot
        title([cow ', day = ' num2str(days(i)) ', Z'], ...
               'Interpreter','none');         
        ylim([0 2.5])
        subplot(2,1,2); hold on; box on;title('CD');        % prepare CD plot
        ylim([0 12])

        % plot z and cd of the segment
        subplot(2,1,1);
        plot(data_day.numdate, data_day.avg_z_sm, ...
             'LineWidth',0.9,'Color',[60/255 179/255 113/255])
        subplot(2,1,2);
        plot(data_day.numdate, data_day.centerdist, ...
             'LineWidth',0.9,'Color',[60/255 179/255 113/255])
        
        % select events
        downevents = data_ld(floor(data_ld.numdatedown) == days(i),:);   % all down
        upevents = data_ld(floor(data_ld.numdatedown) == days(i),:);       % all up
        
        % add detection window
        downevents.winstart = downevents.numdatedown - cpts.window/2;
        downevents.winend = downevents.numdatedown + cpts.window/2;
        upevents.winstart = upevents.numdateup - cpts.window/2;
        upevents.winend = upevents.numdateup + cpts.window/2;
        
        % plot events as ground thruth + window
        for j = 1:height(downevents)
            % z subplot
            subplot(2,1,1);
            plot([downevents.numdatedown(j) downevents.numdatedown(j)], ...
                 [0 2.5],'LineWidth',0.8,'Color',[204/255 0 102/255])
            plot([downevents.winstart(j) downevents.winend(j)],...
                  [1 1],'LineWidth',4.5,'Color',[204/255 0 102/255])
            % cd subplot
            subplot(2,1,2);
            plot([downevents.numdatedown(j) downevents.numdatedown(j)], ...
                 [0 15],'LineWidth',0.8,'Color',[204/255 0 102/255])
            plot([downevents.winstart(j) downevents.winend(j)],...
                  [6 6],'LineWidth',4.5,'Color',[204/255 0 102/255])
        end
        for j = 1:height(upevents)
            % z subplot
            subplot(2,1,1); xlim([days(i) days(i)+1])
            plot([upevents.numdateup(j) upevents.numdateup(j)], ...
                 [0 2.5],'LineWidth',0.8,'Color',[204/255 0 102/255])
            plot([upevents.winstart(j) upevents.winend(j)],...
                 [1 1],'LineWidth',4.5,'Color',[204/255 0 102/255])
            
            % cd subplot
            subplot(2,1,2);
            xlim([days(i) days(i)+1])
            plot([upevents.numdateup(j) upevents.numdateup(j)], ...
                 [0 15],'LineWidth',0.8,'Color',[204/255 0 102/255]) 
            plot([upevents.winstart(j) upevents.winend(j)],...
                  [6 6],'LineWidth',4.5,'Color',[204/255 0 102/255])
        end
        
        % changepoints avg_z
        [cptsr.(cow).(day).ipoints, cptsr.(cow).(day).resid] = ...
             findchangepts(data_day.avg_z_sm,...
                           'Statistic',cpts.Zstat,...
                           'MinDistance',cpts.MinDistance,...
                           'MaxNumChanges',cpts.NumChanges*24);
        cptsr.(cow).(day).ipoints = [[1;...
                    cptsr.(cow).(day).ipoints(1:end-1);...
                    cptsr.(cow).(day).ipoints(end)],...
                    [cptsr.(cow).(day).ipoints;height(data_day)]];

        % calculate mean value + std
        for j = 1:size(cptsr.(cow).(day).ipoints,1)
            cptsr.(cow).(day).ipoints(j,3) = ...
                mean(data_day.avg_z_sm(...
                        cptsr.(cow).(day).ipoints(j,1):...
                        cptsr.(cow).(day).ipoints(j,2)));
            cptsr.(cow).(day).ipoints(j,4) = ...
                std(data_day.avg_z_sm(...
                        cptsr.(cow).(day).ipoints(j,1):...
                        cptsr.(cow).(day).ipoints(j,2)));
            % plot changepoints avg_z_sm
            subplot(2,1,1)
            plot([data_day.numdate(cptsr.(cow).(day).ipoints(j,2)) ...
                  data_day.numdate(cptsr.(cow).(day).ipoints(j,2))],...
                  [0 3],'b','LineWidth',1)
            plot([data_day.numdate(cptsr.(cow).(day).ipoints(j,1)) ...
                  data_day.numdate(cptsr.(cow).(day).ipoints(j,2))],...
                 [cptsr.(cow).(day).ipoints(j,3) ...
                  cptsr.(cow).(day).ipoints(j,3)],'b','LineWidth',2) 
        end
        
        % changepoints cd
        [cptsr.(cow).(day).ipointsCD, cptsr.(cow).(day).residCD] = ...
            findchangepts(data_day.centerdist,...
                           'Statistic',cpts.CDstat,...
                           'MinDistance',cpts.MinDistance,...
                           'MaxNumChanges',cpts.NumChanges*24);
        cptsr.(cow).(day).ipointsCD = [[1;...
                  cptsr.(cow).(day).ipointsCD(1:end-1);...
                  cptsr.(cow).(day).ipointsCD(end)],...
                  [cptsr.(cow).(day).ipointsCD;height(data_day)]];

        % calculate mean value + std
        for j = 1:size(cptsr.(cow).(day).ipointsCD,1)
            cptsr.(cow).(day).ipointsCD(j,3) = mean(data_day.centerdist(...
                                cptsr.(cow).(day).ipointsCD(j,1):...
                                cptsr.(cow).(day).ipointsCD(j,2)));
            cptsr.(cow).(day).ipointsCD(j,4) = std(data_day.centerdist(...
                                cptsr.(cow).(day).ipointsCD(j,1):...
                                cptsr.(cow).(day).ipointsCD(j,2)));
            % plot changepoints centerdist
            subplot(2,1,2)
            plot([data_day.numdate(cptsr.(cow).(day).ipointsCD(j,2)) ...
                  data_day.numdate(cptsr.(cow).(day).ipointsCD(j,2))],...
                  [0 15],'b','LineWidth',1)
            plot([data_day.numdate(cptsr.(cow).(day).ipointsCD(j,1)) ...
                  data_day.numdate(cptsr.(cow).(day).ipointsCD(j,2))],...
                 [cptsr.(cow).(day).ipointsCD(j,3) 
                  cptsr.(cow).(day).ipointsCD(j,3)],'b',...
                  'LineWidth',2) 
        end
        saveas(k,[init_.resdir 'figb_cpts_' cow '_' day '.fig'])
        saveas(k,[init_.resdir 'figb_cpts_' cow '_' day '.tif'])
    end
end
end
% save results
save([init_.resdir 'D5b_cpts_res.mat'],'cptsr','cpts')

% clear variables
clear ans cow data data_day daat_ld day days downevents f j i h ind
clear x y upevents seglist data2 k


%% Postprocessing - filter and combine results
% >>> SENSITIVITY

% >>> SPECIFICITY
%       - change = in segment
%       - difference in mean value (~std of the signal)
%       - CD: difference at changepoints in level > 0.25m
%       - CD: difference at changepoints in std > 
% load results
% load([init_.resdir 'D4_cpts_res.mat'])

% cows
cows = fieldnames(cptsr);

for i = 1:length(fields_) % all cows with results
    T=1;    % count cpts for cd
    T2=1;   % count cpts for z
    % all days for this cow
    days = fieldnames(cptsr.(cows{i}));
      
    close all
    % add to changepoints the information for postprocessing
    for j = 1:length(days)
        % ref day numeric
        day = days{j}; day = str2double(day(5:end)); % num day (~refday)
        
        % select and store/split data
        data.(cows{i}).(days{j}) = sdata.(cows{i})(:,[2 3 7 13 17 18 15]);                                      
        data.(cows{i}).(days{j})(isnan(data.(cows{i}).(days{j}).avg_z_sm) | ...
                                 isnan(data.(cows{i}).(days{j}).centerdist),:) = [];
        data.(cows{i}).(days{j}).numdate = ...
               datenum(data.(cows{i}).(days{j}).date)-refdate; % numeric date
        data.(cows{i}).(days{j}) = data.(cows{i}).(days{j})(...
               floor(data.(cows{i}).(days{j}).numdate) == day,:);

        % load figure and plot
%         k = openfig([init_.resdir 'fig_cpts_' cows{i} '_' days{j} '.fig']);

        % results z
        rzdata = array2table(cptsr.(cows{i}).(days{j}).ipoints,...
                            'VariableNames',{'istart','iend','avg','std'});
        % difference in mean for CD
        rzdata.avgdif(1) = NaN;
        rzdata.avgdif(2:end) = abs(diff(rzdata.avg));
%         idx = find(rzdata.avgdif <= 0.25); % less than 0.25 meter diff
%         if ~isempty(idx)
%             rzdata.iend(idx-1) = rzdata.iend(idx);
%             rzdata(idx,:) = [];
%         end
        % calculate mean value + std
        for l = 1:height(rzdata)
            rzdata.avgnew(l) = mean(...
                                data.(cows{i}).(days{j}).avg_z_sm(...
                                    rzdata.istart(l):...
                                    rzdata.iend(l)));
            rzdata.stdnew(l) = std(...
                                data.(cows{i}).(days{j}).avg_z_sm(...
                                    rzdata.istart(l):...
                                    rzdata.iend(l)));
            rzdata.numdatestart(l) = data.(cows{i}).(days{j}).numdate(...
                                            rzdata.istart(l)); % start
            rzdata.numdateend(l) = data.(cows{i}).(days{j}).numdate(...
                                            rzdata.iend(l)); % end
                                
            % plot changepoints centerdist
%             subplot(2,1,1)
%             plot([data.(cows{i}).(days{j}).numdate(rzdata.iend(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rzdata.iend(l))],...
%                   [0 2.5],'Color',[1 185/255 0],'LineWidth',1)
%             plot([data.(cows{i}).(days{j}).numdate(rzdata.istart(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rzdata.istart(l))],...
%                   [0 2.5],'Color',[1 185/255 0],'LineWidth',1)
%             plot([data.(cows{i}).(days{j}).numdate(rzdata.istart(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rzdata.iend(l))],...
%                  [rzdata.avgnew(l)  rzdata.avgnew(l)],... 
%                   'Color',[1 185/255 0],'LineWidth',2.5)
%               
        end
        % add that it's a changepoint to data
        data.(cows{i}).(days{j}).ischangeZ(1) = T2;
        data.(cows{i}).(days{j}).ischangeZ(rzdata.iend) = ...
                                    (T2+1:T2+height(rzdata))';
        % save new changepoints
        fie = ['z_' days{j}];
        data.(cows{i}).(fie) = rzdata;
        T2 = T2+height(rzdata);
                                            
        % results cd
        rcpdata = array2table(cptsr.(cows{i}).(days{j}).ipointsCD,...
                            'VariableNames',{'istart','iend','avg','std'});

        % difference in mean for CD
        rcpdata.avgdif(1) = NaN;
        rcpdata.avgdif(2:end) = abs(diff(rcpdata.avg));
%         idx = find(rcpdata.avgdif <= 0.5);% less than 0.5 meter diff
%         if ~isempty(idx)
%             rcpdata.iend(idx-1) = rcpdata.iend(idx);
%             rcpdata(idx,:) = [];
%         end
        % calculate mean value + std
        for l = 1:height(rcpdata)
            rcpdata.avgnew(l) = mean(...
                                data.(cows{i}).(days{j}).centerdist(...
                                    rcpdata.istart(l):...
                                    rcpdata.iend(l)));
            rcpdata.stdnew(l) = std(...
                                data.(cows{i}).(days{j}).centerdist(...
                                    rcpdata.istart(l):...
                                    rcpdata.iend(l)));
            rcpdata.numdatestart(l) = data.(cows{i}).(days{j}).numdate(...
                                            rcpdata.istart(l)); % start
            rcpdata.numdateend(l) = data.(cows{i}).(days{j}).numdate(...
                                            rcpdata.iend(l)); % end
                                
            % plot changepoints centerdist
%             subplot(2,1,2)
%             plot([data.(cows{i}).(days{j}).numdate(rcpdata.iend(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rcpdata.iend(l))],...
%                   [0 15],'Color',[1 185/255 0],'LineWidth',1)
%             plot([data.(cows{i}).(days{j}).numdate(rcpdata.istart(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rcpdata.istart(l))],...
%                   [0 15],'Color',[1 185/255 0],'LineWidth',1)
%             plot([data.(cows{i}).(days{j}).numdate(rcpdata.istart(l)) ...
%                   data.(cows{i}).(days{j}).numdate(rcpdata.iend(l))],...
%                  [rcpdata.avgnew(l)  rcpdata.avgnew(l)],... 
%                   'Color',[1 185/255 0],'LineWidth',2.5)
              
        end
        % add that it's a changepoint to data
        data.(cows{i}).(days{j}).ischangeCD(1) = T;
        data.(cows{i}).(days{j}).ischangeCD(rcpdata.iend) = ...
                                    (T+1:T+height(rcpdata))';
        T = T+height(rcpdata);
                                
        % save new changepoints
        fie = ['cd_' days{j}];
        data.(cows{i}).(fie) = rcpdata;
        
        % save figure
%         saveas(k, [init_.resdir 'nfig_cpts_' cows{i} '_' days{j} '.fig'])
%         saveas(k, [init_.resdir 'nfig_cpts_' cows{i} '_' days{j} '.tif'])
    
    end
end

save([init_.resdir 'D5b_dataday.mat'],'data', '-v7.3')