From 5c6feb30b0f3d251e9f712d0bffaefe3cd476ca3 Mon Sep 17 00:00:00 2001 From: "Nes, Egbert van" <egbert.vannes@wur.nl> Date: Fri, 26 Mar 2021 09:13:51 +0000 Subject: [PATCH] Update adjustplot.m, ebisuzaki.m, fishercombine.m, generic_ews.m, generic_ews_sens.m, run_regime.m, violinplot.m, README.md files --- README.md | 2 +- adjustplot.m | 383 +++++++++++++++++ ebisuzaki.m | 68 +++ fishercombine.m | 20 + generic_ews.m | 1008 ++++++++++++++++++++++++++++++++++++++++++++ generic_ews_sens.m | 454 ++++++++++++++++++++ run_regime.m | 81 ++++ violinplot.m | 110 +++++ 8 files changed, 2125 insertions(+), 1 deletion(-) create mode 100644 adjustplot.m create mode 100644 ebisuzaki.m create mode 100644 fishercombine.m create mode 100644 generic_ews.m create mode 100644 generic_ews_sens.m create mode 100644 run_regime.m create mode 100644 violinplot.m diff --git a/README.md b/README.md index f50402b..b67d4e7 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ebisuzaki.m - generate null models according to the Ebisuzaki (1997) fishercombine.m - combine independent p values according to the Fisher method generic_ews.m - the rolling window method for early warning signals (Dakos et al. 2008) generic_ews_sens.m - sensitivity analysis of generic_ews -run_regime.m - the main figures are created with this code, usage: +run_regime.m - the main figures of Scheffer et al. 2021 are created with this code, usage: run_regime(data,regime), where data should be a MATLAB table with (at least) two fields: trees diff --git a/adjustplot.m b/adjustplot.m new file mode 100644 index 0000000..1546515 --- /dev/null +++ b/adjustplot.m @@ -0,0 +1,383 @@ +function adjustplot(hfig, comm, varargin) + %handy to adjust subplot positions or default appearance + %adjustplot(gcf,'-d'): set defaults + %adjustplot(gcf,'-1',orientation): make axes the same of subplots in a orientation + %('x','y','b' (both) + %adjustplot(gcf,'-t','tr') add text right (tr), text left (tl), bottom + %left (bl), bottom right (bl) + %adjustplot(gcf,'-r',orientation): remove axes of a subplot in a + %orientation 't'=remove titles only, 'tx'=remove x titles, 'ty' remove + %y titles + %adjustplot(gcf,'-p',spacing): shift subplots closer together (spacing = [spacingx + %spacingy]) + %adjustplot(gcf,'-b') remove ticks from the axes (does not + %automatically update with scaling of the figure) + if nargin < 1 + hfig = gcf; + elseif ~ishandle(hfig) + if nargin == 2 + varargin = [{comm} varargin]; + comm = hfig; + hfig = gcf; + elseif nargin == 1 + varargin{1} = []; + comm = hfig; + hfig = gcf; + end + end + % if nargin < 3 + % varargin{1} = []; + % end + %if nargin < 2 + % comm = '-d'; + %end + switch comm + case '-b' %box without ticks, does not work if Inf is in xlim or ylim + %not updated (could be done with listener) + %ax=get(hfig,'currentaxes'); + axs = get(hfig, 'children'); + drawnow() + for i = 1:length(axs) + if strcmp(get(axs(i), 'Type'), 'axes') + yylim = get(axs(i), 'ylim'); + xxlim = get(axs(i), 'xlim'); + + if strcmp(get(axs(i), 'YDir'), 'reverse') + iy = [2 1]; + else + iy = [1 2]; + end + if strcmp(get(axs(i), 'XDir'), 'reverse') + ix = [2 1]; + else + ix = [1 2]; + end + h = findobj(axs(i), 'tag', 'plot-box'); + if isempty(h) + %first use only!! (else listener causes problem) + set(axs(i), 'box', 'off') + set(axs(i), 'ylim', yylim); + set(axs(i), 'xlim', xxlim); + oldhold = ~strcmp(get(axs(i), 'nextplot'), 'add'); + hold(axs(i), 'on'); + h = plot(axs(i), [xxlim(ix(1)), xxlim(ix(2)), xxlim(ix(2))], [yylim(iy(2)), yylim(iy(2)), yylim(iy(1))], 'k-', 'tag', 'plot-box', 'LineWidth', get(axs(i), 'LineWidth')); + set(get(get(h, 'Annotation'), 'LegendInformation'), 'IconDisplayStyle', 'off'); + if ~oldhold + hold(axs(i), 'off'); + end + addlistener(axs(i), 'YLim', 'PostSet', @(src,evnt)adjustplot(hfig,'-b')); + addlistener(axs(i), 'XLim', 'PostSet', @(src,evnt)adjustplot(hfig,'-b')); + else + set(h, 'xdata', [xxlim(ix(1)), xxlim(ix(2)), xxlim(ix(2))], 'ydata', [yylim(iy(2)), yylim(iy(2)), yylim(iy(1))]); + end + end + end + return; + case '-d' + if nargin < 3 + apen = struct('pen', '-', 'pen2', '-', 'markersize', 5, 'markerfacecolor', 'none', 'cycle', 0, ... + 'i', 1, 'color', [0 0 1], 'color2', [0 0 1], 'drawcolor', [0.7500 0 0], 'colormap', [0 1 1; 1 0 1], ... + 'linewidth', 1, 'fontsize', 16, 'tickdir', 'out', 'box', 'on'); + else + apen = varargin{1}; + end + + try + hax = get(hfig, 'CurrentAxes'); + catch + hax = findobj(hfig, 'type', 'axes'); + end + if isempty(hax) + hax = gca; + end + + Cbar = findobj(hfig, 'tag', 'Colorbar'); + if ~isempty(Cbar) + set(Cbar, 'FontSize', apen.fontsize); + end + set(hax, 'FontSize', apen.fontsize); + set(hax, 'LineWidth', apen.linewidth); + set(hax, 'TickDir', 'out'); + set(hax, 'TickLength', [0.015 0.025]) + if isempty(findobj(hfig, 'tag', 'plot-box')) + set(hax, 'Box', apen.box); + else + adjustplot(hfig, '-b'); + end + %set(H, 'TickDir', apen.tickdir); + % set(hax, 'Box', apen.box); + t = findobj(hax, 'type', 'text'); + if ~isempty(t) + if ~isfield(apen, 'fontsize') + apen.fontsize = 14; + end + + set(t, 'fontsize', apen.fontsize); + end + + t1 = [findobj(hfig, 'tag', 'conteq-text'); findobj(hfig, 'tag', 'contbif-text'); findobj(hfig, 'tag', 'label-text')]; + set(t1, 'fontsize', 10); + if isempty(Cbar) || ~any(hax == Cbar) + View = get(hax, 'view'); + if (apen.fontsize >= 14) && min(View == [0 90]) + set(hax, 'units', 'normalized'); + if ~isempty(Cbar) + P = [0.1500 0.1900 0.6455 0.7350]; + else + P = [0.1300 0.1100 0.7750 0.8150]; + end + + pos1 = get(hax, 'position'); + if (pos1(3) > 0.6) && (pos1(4) > 0.6) + set(hax, 'position', transform(P, apen.fontsize)); + if ~isempty(Cbar) + P = [0.831375 0.13 0.058125 0.795]; + set(Cbar, 'position', transform(P, apen.fontsize)) + end + + end + + end + end + case '-t' + if length(varargin) == 2 + atext = varargin{2}; + pos = varargin{1}; + else + atext = varargin{1}; + pos = 'tl'; + end + hax = get(hfig, 'CurrentAxes'); + if ischar(pos) + switch pos + case 'tl' + pos = [0.05 0.95]; + case 'tr' + pos = [0.95 0.95]; + case 'bl' + pos = [0.05 0.05]; + case 'br' + pos = [0.95 0.05]; + end + end + xlims = get(hax, 'xlim'); + ylims = get(hax, 'ylim'); + text(hax, xlims(1) + pos(1) * (xlims(2) - xlims(1)), ylims(1) + pos(2) * (ylims(2) - ylims(1)), atext); + case '-l' + hs = getsubplotgrid(hfig); + if isempty(varargin{1}) + orientation = 'b'; + else + orientation = varargin{1}; + end + [rows, cols] = size(hs); + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'x', 1) + for i = 1:rows - 1 + for j = 1:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + adjustticklabels(hs(i, j), 'X') + end + end + end + end + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'y', 1) + for i = 1:rows + for j = 1:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + adjustticklabels(hs(i, j), 'Y') + end + end + end + end + case '-r' + if isempty(varargin{1}) + orientation = 'b'; + else + orientation = varargin{1}; + end + hs = getsubplotgrid(hfig); + if numel(hs) < 2 + disp('Too few axes'); + return; + end + removeticklabel = true; + if strncmpi(orientation, 't', 1) + removeticklabel = false; + if strfind(orientation, 'x') + orientation = 'x'; + elseif strfind(orientation, 'y') + orientation = 'y'; + else + orientation = 'b'; + end + end + [rows, cols] = size(hs); + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'x', 1) + for i = 1:rows - 1 + for j = 1:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + set(get(hs(i, j), 'xlabel'), 'string', '') + if removeticklabel + set(hs(i, j), 'XTickLabel', []); + end + end + end + end + end + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'y', 1) + for i = 1:rows + for j = 2:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + set(get(hs(i, j), 'ylabel'), 'string', '') + if removeticklabel + set(hs(i, j), 'YTickLabel', []); + end + end + end + end + end + ndx = hs ~= 0; + set(hs(ndx), 'fontsize', 12) + aa = get(hs(ndx), 'xlabel'); + if iscell(aa) + aa = [aa{:}]; + end + set(aa, 'fontsize', 12); + aa = get(hs(ndx), 'ylabel'); + if iscell(aa) + aa = [aa{:}]; + end + set(aa, 'fontsize', 12); + case '-p' + if isempty(varargin{1}) + spacing = 0.02; + else + spacing = varargin{1}; + if ischar(spacing) + spacing = str2double(spacing); + end + end + set(hfig, 'PaperPositionMode', 'auto') + set(hfig, 'units', 'normalized'); + figpos = get(hfig, 'position'); + vert = 0.5469; %normal size of figure + hor = 0.41; + [hs, colwidths] = getsubplotgrid(hfig); + if isempty(hs) + disp('No axes to adjust'); + return; + elseif length(hs) == 1 + return; + end + + if length(spacing) == 1 + spacing = spacing + zeros(2, 1); + end + [rows, cols] = size(hs); + if cols <= 4 && rows <= 4 + newfigpos = [figpos(1:2) hor * cols / 2 vert * rows / 2]; + else + rat = rows / cols; + if rat < 1 + newfigpos = [figpos(1:2) hor vert * rat]; + else + newfigpos = [figpos(1:2) hor / rat vert]; + end + end + if newfigpos(4) > 1 + newfigpos(2) = - 0.2; + end + set(hfig, 'position', newfigpos); + for i = 1:rows + for j = 1:cols + h = findobj(hfig, 'tag', sprintf('subplot(%d,%d)', i, j)); + if any(ishandle(h)) && any(h ~= 0) + set(h, 'position', subplotpos(rows, cols, j, i, spacing, colwidths(i, j))); + end + end + end + movegui(gcf, 'center') + otherwise + error('adjustsubplots:unknown', 'Unknown command'); + end +end + +function [hs, colwidths] = getsubplotgrid(h) + ch = get(h, 'children'); + hs = []; + colwidths = []; + if ~isempty(ch) + tags = get(ch, 'tag'); + ch = ch(~strcmp(tags, 'legend')); + types = get(ch, 'type'); + if ~iscell(types) + types = {types}; + end + poss = get(ch, 'position'); + if ~iscell(poss) + poss = {poss}; + end + ipos = zeros(length(ch), 4); + for i = length(ch): - 1:1 + if strcmp(types{i}, 'axes') + ipos(i, :) = poss{i}; + else + ipos(i, :) = []; + ch(i) = []; + types(i) = []; + end + end + %we actually need to account for the spacing between the columns, a + %bit complex? + colwidth = floor(ipos(:, 3) ./ min(ipos(:, 3)) + 0.01); %the columns each figure spans + colpos = sort(unique(sort(ipos(:, 1))), 'ascend'); + rowpos = sort(unique(sort(ipos(:, 2))), 'descend'); + hs = zeros(length(rowpos), length(colpos)); + colwidth(colwidth > length(colpos)) = length(colpos); + colwidths = zeros(length(rowpos), length(colpos)); + for i = 1:length(ch) + if strcmp(types{i}, 'axes') + arow = find(rowpos == ipos(i, 2)); + acol = find(colpos == ipos(i, 1)); + hs(arow, acol) = ch(i); + colwidths(arow, acol) = colwidth(i); + set(ch(i), 'tag', sprintf('subplot(%d,%d)', arow, acol)); + end + end + end +end + +function adjustticklabels(hax, orient) + axis(hax, 'tight') + newtick = get(hax, [orient 'tick']); + tickdiff = (newtick(2) - newtick(1)); + newlim = [newtick(1) - tickdiff newtick(end) + tickdiff]; + axis(hax, 'manual') + set(hax, [orient 'lim'], newlim); + set(hax, [orient 'tick'], [newtick(1) - tickdiff newtick]); +end +function P = transform(P, fontsize) + %P = [left, bottom, width, height] + %where left and bottom define the distance from the lower-left corner of the screen + if fontsize <= 18 + P(1) = P(1) + 0.01; + P(2) = P(2) + 0.03; + P(3) = P(3) - 0.02; + P(4) = P(4) - 0.04; + elseif fontsize <= 22 + P(2) = P(2) + 0.04; + P(4) = P(4) - 0.04; + elseif fontsize <= 28 + P(1) = P(1) + 0.02; + P(3) = P(3) - 0.02; + P(2) = P(2) + 0.08; + P(4) = P(4) - 0.08; + else + P(1) = P(1) + 0.08; + P(3) = P(3) - 0.08; + P(2) = P(2) + 0.16; + P(4) = P(4) - 0.16; + end + + return +end diff --git a/ebisuzaki.m b/ebisuzaki.m new file mode 100644 index 0000000..97ffb93 --- /dev/null +++ b/ebisuzaki.m @@ -0,0 +1,68 @@ +% X=ebisuzaki(x,nsim,value); +% +% This function creates 'nsim' random time series that have the same power +% spectrum as the original time series 'x' but random phases. The power +% spectrum of 'x' and 'X' is the same (see abs(fft(x)) and abs(fft(X)); +% The 'X' time series has also the same variance as 'x'. (see Ebisuzaki W. (1997) ; +% A method to estimate the statistical significance of a correlation when +% the data are serially correlated. J Climate, 10, 2147-2153). +% +% Input +% 'x' : vector of real number containing the time series to match +% 'nsim' : integer number giving the number of simulation +% 'value' : real number to initiate the random sequence (if > 0, the seeds +% is initiated to the value; otherwise, it is changed from the clock) +% +% Output +% 'X' : matrix of real number (the same number of rows as length of 'x' and +% 'nsim' columns. +% +% fixed by Hao Ye, 2012 +% +% original version by +% Vincent MORON +% March 2002 +function X = ebisuzaki(x,nsim,value) +if nargin<2 + nsim=1; +end +if nargin<3 + value=-1; +end + +if(value>0) + rand('seed',value); +else + rand('seed',sum(100*clock)); +end +if any(isnan(x)) + error('stats:ebisizaki','This procedure cannot handle NaNs'); +end + +n = length(x); +n2 = floor(n/2); +x = x(:); +a = fft(x); % discrete fourier transform of the original +amplitudes = abs(a); % power of the original spectrum +amplitudes(1) = 0; + +for i = 1:nsim + if (mod(n, 2) == 0) % series has even length + thetas = 2*pi * rand(n2-1,1); + angles = [0; thetas(:); 0; flipud(-thetas(:))]; + recf = amplitudes .* exp(1i*angles); % adjust the phases + recf(n2,1) = sqrt(2) * recf(n2,1) * cos(rand()*2*pi); % adjust n/2 power + else % series has odd length + thetas = 2*pi * rand(n2, 1); + angles = [0; thetas(:); flipud(-thetas(:))]; + recf = amplitudes .* exp(1i*angles); % adjust the phases + end + X(:,i) = real(ifft(recf)); +end + +% adjust variance of the surrogate time series to match the original +X = X * diag(std(x)./std(X)); + +% adjust mean of the surrogate time series to match the original +% X = X - repmat(mean(X), n, 1); +% X = X + mean(x); diff --git a/fishercombine.m b/fishercombine.m new file mode 100644 index 0000000..ff6dd06 --- /dev/null +++ b/fishercombine.m @@ -0,0 +1,20 @@ +function [res]=fishercombine(ps) +% In statistics, Fisher's method,[1][2] also known as Fisher's combined probability test, +% is a technique for data fusion or "meta-analysis" (analysis of analyses). +% It was developed by and named for Ronald Fisher. In its basic form, it is used +% to combine the results from several independent tests bearing upon the same overall hypothesis (H0). +if nargin==0 + disp('usage: p=fishercombine(ps) %add vector with ps of the independent tests'); +end +chi2=-2*sum(log(ps)); +df=2*length(ps); +p=1- chi2cdf(chi2,df); +if nargout==0 + fprintf('Chi2 = %g df=%d p = %g\n',chi2,df,p); +else + res.p=p; + res.chi=chi2; + res.df=df; +end +end + diff --git a/generic_ews.m b/generic_ews.m new file mode 100644 index 0000000..e2a23d9 --- /dev/null +++ b/generic_ews.m @@ -0,0 +1,1008 @@ +%function generic_ews +% generic_ews is used to estimate statistical moments within rolling +% windows along a timeserie (based on the R early warning signals toolbox, +% but simplified) +% +% Usage +% +% generic_ews(timeseries, option pairs) +% generic_ews(time,timeseries, option pairs) +% for example: +% generic_ews(timeseries, 'winsize', 50, 'detrending', 'no', ... +% 'bandwidth', [], 'logtransform', false, 'interpolate', false) +% +% Arguments +% +% timeseries - a numeric vector of the observed univariate timeseries values or a numeric +% matrix of observed timeseries values. If you use a tabel or dataset +% you can add time as a column named t or time +% +% time - a numeric vector of the time index (numeric) +% +% datacolumn - column number with the data (default first column (excluding time)) +% +% winsize - is the size of the rolling window expressed as percentage of the timeseries length +% (must be numeric between 0 and 100). Default is 50%. +% +% bandwidth - is the bandwidth used for the Gaussian kernel when gaussian filtering is applied. +% It is expressed as percentage of the timeseries length (must be numeric between 0 and 100) +% Alternatively it can be given by the optimal bandwidth suggested by Bowman and Azzalini (1997) Default). +% +% detrending - the timeseries can be detrended/filtered prior to analysis (can be a cell for each data column). +% There are four options: 'no'= no detrending, 'gaussian' = +% gaussian filtering, 'movmean' = moving average (statistics +% toolbox), 'poly' = polynomical (default degree=4, see +% polydegree), 'linear' = linear detrending, or 'first-diff' = first-differencing. Default is 'gaussion' detrending. +% +% indicators - cell of strings with the names of the indicators can be one of the following strings: +% 'AR' - autocorrelation of residuals +% 'acf' - autocorrelation function of residuals +% 'std' - standard deviation of residuals +% 'skewness' - skewness of residuals +% 'cv' - coefficient of variation of original data +% 'abscorr' - the average of the absolute correlation with all other columns +% default: {'AR', 'acf', 'std', 'skewness'} +% +% figures - cell of strings with figures of the combined figure {'original data', 'residuals'; +% 'indicator 1'...'indicator 4'} 'original data 2' gives the +% original data of column 2, 'residuals 2' the residuals of +% col 2, no number = first datacolumn +% +% logtransform - if TRUE data are logtransformed prior to analysis as log(X+1). Default is FALSE. +% +% interpolate - If TRUE linear interpolation is applied to produce a timeseries of equal length as the original. Default is FALSE (assumes there are no gaps in the timeseries). +% polydegree - degree of polynomial for detrending (default = 4) +% arlag - The lag for autoregression. Default = 1 +% silent - If silent=true then the figure is not drawn, default = false +% ebisuzaki - number of runs for a null model (using the Ebisuzaki method), default = 0 +% violinplot - make violin plot of the taus of the null model, default = true +% title - use text as title of the figure, default ='' +% corrcolumns - column number with the variable for cross correlation +% bandwidth_unit - unit of bandwidth (see units of winsize) +% winsize_unit - units of the winsize - if different from '%' the absolute +% units of the time series are assumed. If % the real size +% is calculated from the length of the time series +% cv - (depreciated) if true use cv instead of standard deviation default = false (use now indicators field) +% +% +%not yet supported in the MATLAB version: +% AR_n - If TRUE the best fitted AR(n) model is fitted to the data. Default is FALSE. +% +% powerspectrum -If TRUE the power spectrum within each rolling window is plotted. Default is FALSE. +% +% Options: +% s=generic_ews('-defaultopts') - create a struct s with the default +% options (used internally) +% generic_ews('-f',res) determine the Fisher combined probability plots of +% the results res. res should be a struct array or cell array of earlier results +% +% generic_ews returns a structure with the following fields: +% timeseries - original time series +% EWSopt - structure with all used options (including the time index) +% trend - trend in the time series (depending on the detrending option) +% colnames - names of columns of the results (indicator names made unique, e.g. if +% two AR indicators are saved they are named AR and AR_1) +% indicators - the resulting +% taus - Kendall tau values for each indicator +% pvalues - p values of the tau values (only if an ebizuzsaki analysis was +% done) +% ebitaus - the tau values for each of the ebizuzsaki iterations +% done) +% description - string with main options +% +% +% In addition, generic_ews returns a plots. The plot contains the original data, +% the detrending/filtering applied and the residuals (if selected), and all the moment statistics. +% For each statistic trends are estimated by the nonparametric Kendall tau correlation (if statistics toolbox +% is present). +% Not supported: The second plot, if asked, quantifies resilience indicators fitting AR(n) selected by the +% Akaike Information Criterion. The third plot, if asked, is the power spectrum estimated +% by spec.ar for all frequencies within each rolling window. +% +% Author(s) +% +% Vasilis Dakos vasilis.dakos@gmail.com +% MATLAB version by Egbert van Nes +% +% References +% +% Ives, A. R. (1995). "Measuring resilience in stochastic systems." Ecological Monographs 65: 217-233 +% +% Dakos, V., et al (2008). "Slowing down as an early warning signal for abrupt climate change." Proceedings of the National Academy of Sciences 105(38): 14308-14312 +% +% Dakos, V., et al (2012)."Methods for Detecting Early Warnings of Critical Transitions in Time Series Illustrated Using Simulated Ecological Data." PLoS ONE 7(7): e41010. doi:10.1371/journal.pone.0041010 +% +% +function result = generic_ews(timeseries, varargin) + if nargin == 0 + error('grind:generic_ews:notenoughargs', 'Not enough input arguments, at least the timeseries is needed (see <a href="matlab:help(''generic_ews'')">help generic_ews</a>)'); + end + if ischar(timeseries) + if strcmp(timeseries, '-defaultopts') + %the default options + result = struct('winsize', 50, ... + 'detrending', 'gaussian', ... % = c("no", "gaussian", "linear", "poly", "first-diff", "movmean", + 'bandwidth', 10, ... + 'time', [], ... + 'datacolumn', 1, ... %per indicator or one for all + 'corrcolumns', [], .... %empty is all (except datacolumn) + 'silent', false, ... + 'logtransform', false, ... + 'arlag', 1, ... + 'indicators', {{'AR', 'acf', 'std', 'skewness'}}, ... %other options cv, abscorr, crosscorr, mean + 'figures', {{'original data', 'residuals'; 'indicator 1', 'indicator 2'; 'indicator 3', 'indicator 4'; 'indicator 5', 'indicator 6'}}, ... + 'cv', false, ... + 'polydegree', 4, ... + 'interpolate', false, ... + 'AR_n', false, ... + 'powerspectrum', false, ... + 'bandwidth_unit', '%', ... + 'winsize_unit', '%', ... + 'ebisuzaki', 0, ... %number of nullmodel (Ebisuzaki) runs (0=none) + 'violinplot', true, ... + 'title', ''); + return; + elseif strncmpi(timeseries, '-f', 2) + %table with fisher combined probabilities + res = varargin{1}; + if ~iscell(res) + %if not a cell make a cell for convenience + res1 = varargin{1}; + res = cell(size(res)); + for i = 1:numel(res1) + res{i} = res1(i); + end + end + %ebitaus = zeros(res{1}.EWSopt.ebisuzaki, length(res)); + titles = cell(length(res), 1); + colnames = res{1}.colnames; + pvalues = zeros(length(res), length(res{1}.colnames)); + fisher(1, length(res{1}.colnames)) = struct('p', [], 'chi', [], 'df', []); + % fisher = cell(1, length(res{1}.colnames)); + taus = zeros(length(res), length(res{1}.colnames)); + silent = res{1}.EWSopt.silent | nargout > 0; + for j = 1:length(res{1}.colnames) + if ~silent + fprintf( '\n======= %s ========\n', res{1}.colnames{j}); + fprintf( 'Title\tKendall-tau %s\tp-value %s\n', res{1}.colnames{j}, res{1}.colnames{j}); + end + for i = 1:numel(res) + %ebitaus(:, i) = res{i}.ebitaus(j, :)'; + titles{i} = res{i}.EWSopt.title; + if isempty(titles{i}) + titles{i} = sprintf('data set %d', i); + end + taus(i, j) = res{i}.taus(j); + pvalues(i, j) = res{i}.pvalues(j); %(sum(taus(i) < ebitaus(:, i)) + 1) / size(ebitaus, 1); + if ~silent + fprintf( '%s\t%.3g\t%.3g\n', titles{i}, taus(i, j), pvalues(i, j)); + end + end + fisher1 = fishercombine(pvalues(:, j)); + if ~silent + regimes = 'Fisher combined'; %sprintf('%s, ', titles{:}); + fprintf( '%s\tX2(%d)=%.3g\t%.3g\n', regimes, fisher1.df, fisher1.chi, fisher1.p); + end + fisher(1, j) = fisher1; + end + if nargout > 0 + result.titles = titles; + result.colnames = colnames; + result.taus = taus; + result.pvalues = pvalues; + % result.fisherp = fisherp; + result.fisher = fisher; + end + return; + end + end + if ~((isfield(timeseries, 'timeseries') || isfield(timeseries, 'inds')) && isfield(timeseries, 'taus')) % if these fields exist it is a previously saved run + [timeseries, EWSopt] = initialize_settings(timeseries, varargin); + + taus = zeros(length(EWSopt.indicators), 1); + trend = zeros(size(timeseries)); + for i = 1:size(timeseries, 2) + detrending = getcolumn(EWSopt.detrending, i, true); + if strcmpi(detrending, 'first-diff') + timeseries(:, i) = [nan; diff(timeseries(:, i))]; + elseif strcmpi(detrending, 'linear') + p = polyfit(EWSopt.time, timeseries(:, i), 1); + trend(:, i) = polyval(p, EWSopt.time); + elseif strcmpi(detrending, 'poly') + p = polyfit(EWSopt.time, timeseries(:, i), EWSopt.polydegree); + trend(:, i) = polyval(p, EWSopt.time); + elseif strcmpi(detrending, 'movmean') + bw = getcolumn(EWSopt.bandwidth, i); + if isempty(bw) + bw = [4 0]; + end + try + trend(:, i) = movmean( timeseries(:, i), bw); + catch + trend(:, i) = movmean1( timeseries(:, i), bw); + end + elseif strcmpi(detrending, 'gaussian') + bw = getcolumn(EWSopt.bandwidth, i); + if strcmp(EWSopt.bandwidth_unit{i}, '%') + if ~isempty(bw) + EWSopt.absbandwidth = bw / 100 * (max(EWSopt.time) - min(EWSopt.time)); + else + EWSopt.absbandwidth = []; + end + else + if iscell(EWSopt.bandwidth) + EWSopt.absbandwidth = EWSopt.bandwidth{i}; + else + EWSopt.absbandwidth=EWSopt.bandwidth; + end + end + trend(:, i) = ksmooth(EWSopt.time, timeseries(:, i), getcolumn(EWSopt.absbandwidth, i), detrending); + end + end + + + % ndx = ~isnan(trend(:, 1)); + if strcmp(EWSopt.winsize_unit, '%') + if EWSopt.winsize > 100 + EWSopt.winsize = 100; + end + if EWSopt.winsize < 0.1 + EWSopt.winsize = 0.1; + end + EWSopt.avwin = EWSopt.winsize / 100 * (max(EWSopt.time) - min(EWSopt.time)); + else + EWSopt.avwin = EWSopt.winsize; + end + + indicators = cell(size(EWSopt.indicators)); + for i1 = 1:length(indicators) + indicators{i1} = zeros(size(timeseries, 1), 1) .* NaN; + end + ts = EWSopt.time; + winsize = EWSopt.avwin; + tstart = ts - winsize; + f = find(tstart >= EWSopt.time(1), 1); + if isempty(f) + error('not enough data') + end + tstart = tstart - (tstart(f) - EWSopt.time(1)); %allign with time lags + tstart(tstart < EWSopt.time(1)) = NaN; + notrend = false; + for i1 = 1:length(ts) + if ~isnan(tstart(i1)) && ~isnan(trend(i1, 1)) + ndx = ts >= tstart(i1) & ts < ts(i1) & ~isnan(trend(i1, 1)); + tim = timeseries(ndx, :); + if isempty(trend) + tren = zeros(size(tim)); + else + tren = trend(ndx, :); + end + for j = 1:length(EWSopt.indicators) + switch EWSopt.indicators{j} + case 'AR' + indicators{j}(i1) = autoregression(tim(:, getcolumn(EWSopt.datacolumn, j)) - tren(:, getcolumn(EWSopt.datacolumn, j)), getcolumn(EWSopt.arlag, j)); + case 'acf' + indicators{j}(i1) = acf(tim(:, getcolumn(EWSopt.datacolumn, j)) - tren(:, getcolumn(EWSopt.datacolumn, j)), getcolumn(EWSopt.arlag, j)); + case 'cv' + indicators{j}(i1) = std(tim(:, getcolumn(EWSopt.datacolumn, j))) / mean(tim(:, getcolumn(EWSopt.datacolumn, j))); + case 'std' + indicators{j}(i1) = std(tim(:, getcolumn(EWSopt.datacolumn, j)) - tren(:, getcolumn(EWSopt.datacolumn, j))); + case 'skewness' + indicators{j}(i1) = skewness(tim(:, getcolumn(EWSopt.datacolumn, j)) - tren(:, getcolumn(EWSopt.datacolumn, j))); + % case 'abscorr' + % indicators{j}(i1) = meancorr(tim, getcolumn(EWSopt.datacolumn, j), getcolumn(EWSopt.corrcolumns, j, true), true); + % case 'crosscorr' + % indicators{j}(i1) = meancorr(tim, getcolumn(EWSopt.datacolumn, j), getcolumn(EWSopt.corrcolumns, j, true), false); + % case 'mean' + % indicators{j}(i1) = mean(tim(:, getcolumn(EWSopt.datacolumn, j))); + case {'abscorr' 'crosscorr', 'mean'} + notrend = true; + otherwise + error('grind:generic_ews:unknown', 'Unknown indicator : %s', EWSopt.indicators{j}); + end + end + % fprintf('%d-%d %g %g %g\n',find(ndx,1),find(ndx,1,'last'),ts(find(ndx,1)),ts(find(ndx,1,'last')),indicators{1}(i1)) + end + end + if notrend + for i1 = 1:length(ts) + if ~isnan(tstart(i1)) + ndx = ts >= tstart(i1) & ts < ts(i1) ; + tim = timeseries(ndx, :); + for j = 1:length(EWSopt.indicators) + switch EWSopt.indicators{j} + case 'abscorr' + indicators{j}(i1) = meancorr(tim, getcolumn(EWSopt.datacolumn, j), getcolumn(EWSopt.corrcolumns, j, true), true); + case 'crosscorr' + indicators{j}(i1) = meancorr(tim, getcolumn(EWSopt.datacolumn, j), getcolumn(EWSopt.corrcolumns, j, true), false); + case 'mean' + indicators{j}(i1) = mean(tim(:, getcolumn(EWSopt.datacolumn, j))); + end + end + % fprintf('%d-%d %g %g %g\n',find(ndx,1),find(ndx,1,'last'),ts(find(ndx,1)),ts(find(ndx,1,'last')),indicators{1}(i1)) + end + end + end + hasstats = exist('corr', 'file') ~= 0; + if hasstats + %we cannot use p values as the data are not independent + taus = zeros(size(EWSopt.indicators)); + for i = 1:length(taus) + taus(i) = corr(EWSopt.time, indicators{i}, 'type', 'Kendall', 'rows', 'complete'); + end + end + ebitaus = []; + else %plot previous data + hasstats = exist('corr', 'file') ~= 0; + res = timeseries; + EWSopt = res.EWSopt; + EWSopt = mergeoptions(EWSopt, varargin); + if ~isfield(EWSopt, 'indicators') + EWSopt.indicators = {'AR', 'acf', 'std', 'skewness'}; + if EWSopt.cv + EWSopt.indicators{3} = 'cv'; + end + end + % if isa(res.inds, 'table') + % inds = table2array(res.inds(:, makeunique(EWSopt.indicators))); + % else + % inds = res.inds(:, 4:end); + % end + taus = res.taus; + ebitaus = res.ebitaus; + if ~isfield(res, 'inds') + trend = res.trend; + if isempty(trend) + trend = zeros(size(res.timeseries)); + end + timeseries = res.timeseries; + indicators = num2cell(res.indicators, 1); + elseif isa(res.inds, 'table') %old version + if ~any(strcmp(res.inds.Properties.VariableNames, 'trend')) + trend = zeros(size(res.inds.Variable)); + else + trend = res.inds.trend; + end + timeseries = res.inds.Variable; + inds = table2array(res.inds(:, makeunique(EWSopt.indicators))); + indicators = num2cell(inds, 1); + end + end + + + pvalues = []; + + if EWSopt.ebisuzaki > 0 && isempty(ebitaus) + nulldata = zeros(size(timeseries, 1), size(timeseries, 2), EWSopt.ebisuzaki); + for i = 1:size(timeseries, 2) + nulldata(:, i, :) = ebisuzaki(timeseries(:, i), EWSopt.ebisuzaki); + end + %nulldata = ebisuzaki(timeseries(:, EWSopt.datacolumn), EWSopt.ebisuzaki); + EWSopt1 = EWSopt; + EWSopt1.ebisuzaki = 0; + EWSopt1.silent = true; + if isempty(ebitaus) + ebitaus = zeros(numel(taus), EWSopt.ebisuzaki); + for i = 1:EWSopt.ebisuzaki + res = generic_ews(nulldata(:, :, i), EWSopt1); + ebitaus(:, i) = res.taus; + end + end + end + if ~isempty(ebitaus) + pvalues = zeros(size(EWSopt.indicators)); + nebisuzaki = size(ebitaus, 2); + for j = 1:length(pvalues) + pvalues(j) = (sum(taus(j) < ebitaus(j, :)) + 1) / nebisuzaki; + end + fprintf('Surrogate data analysis %s\n', EWSopt.title); + fprintf('Number of surrogate data sets: %d\n', nebisuzaki); + for i = 1:length(pvalues) + fprintf('Tau %s = %g, pvalue = %g\n', EWSopt.indicators{i}, taus(i), pvalues(i)); + end + if ~EWSopt.silent && (~isfield(EWSopt, 'violinplot') || EWSopt.violinplot) + oldfig = get(0, 'currentfigure'); + figure; + try + violinplot(ebitaus', (1:length(pvalues)), 'bandwidth', 0.1, 'categorynames', EWSopt.indicators); + catch + end + adjustplot('-d') + hold on + plot((1:length(pvalues)), taus, 'ro'); + ylabel('Kendall \tau') + title('Surrogate data') + if ~isempty(oldfig) + figure(oldfig); + end + end + end + if ~EWSopt.silent && ~isempty(EWSopt.figures) + % if ~isfield(EWSopt, 'figures') + % EWSopt.figures = {'original data', 'residuals'; 'indicator 1', 'indicator 2'; 'indicator 3', 'indicator 4'}; + % end + xlims = [min(EWSopt.time), max(EWSopt.time)]; + figure; + figs = EWSopt.figures'; %strange of MATLAB that this is different + for i = 1:numel(EWSopt.figures) + h = subplot(size(EWSopt.figures, 1), size(EWSopt.figures, 2), i); + if strncmp(figs{i}, 'original data', 13) + col = str2double(regexp(figs{i}, '[0-9]*', 'match', 'once')); + if isnan(col) + col = getcolumn(EWSopt.datacolumn, 1); + end + plot(EWSopt.time, timeseries(:, col), 'k-', 'Tag', figs{i}); + xlim(xlims) + adjustticklabels(gca, 'Y'); + if ~isempty(trend) + hold on; + plot(EWSopt.time, trend(:, col), 'r-', 'Tag', 'trend'); + hold off; + end + if ~isempty(EWSopt.title) + text(0.1, 0.9, EWSopt.title, 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'toptext') + set(gcf, 'name', EWSopt.title) + end + xlabel('Time'); + % ylabel('variable and trend'); + elseif strncmp(figs{i}, 'residuals', 9) + col = str2double(regexp(figs{i}, '[0-9]*', 'match', 'once')); + if isnan(col) + col = getcolumn(EWSopt.datacolumn, 1); + end + if ~isempty(trend) + h = stem(EWSopt.time, timeseries(:, col) - trend(:, col), 'k.', 'Tag', figs{i}); + adjustticklabels(gca, 'Y'); + text(0.1, 0.9, 'residuals', 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'toptext') + set(h, 'markersize', 1); + else + plot(EWSopt.time, timeseries(:, col), 'k-', 'Tag', figs{i}); + text(0.1, 0.9, 'No residuals - no detrending', 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'text1') + end + xlabel('Time'); + xlim(xlims); + else + uinds = makeunique(EWSopt.indicators); + iind = find(strcmp(figs{i}, uinds)); + if isempty(iind) + iind = str2double(regexp(figs{i}, '[0-9]*', 'match', 'once')); + end + if iind <= length(EWSopt.indicators) + sindicator = uinds{iind}; + if any(strcmp(EWSopt.indicators{iind}, {'AR', 'acf'})) + sindicator = sprintf('%s(%d)', lower(sindicator), EWSopt.arlag); + elseif strcmp(EWSopt.indicators{iind}, 'std') + sindicator = 'standard deviation'; + end + plot(EWSopt.time, indicators{iind}, 'k-', 'tag', uinds{iind}); + adjustticklabels(gca, 'Y'); + text(0.1, 0.9, sindicator, 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'toptext') + if hasstats + if ~isempty(pvalues) + text(0.1, 0.1, sprintf('{\\tau} = %5.3g (p =%5g)', taus(iind), pvalues(iind)), 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'bottomtext'); + else + text(0.1, 0.1, sprintf('{\\tau} = %5.3g', taus(iind)), 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'bottomtext'); + end + end + xlabel('Time'); + xlim(xlims); + else + delete(h); + end + end + movegui(gcf, 'center'); + end + ch = findobj(gcf, 'type', 'axes'); + set(ch, 'TickDir', 'out'); + if numel(figs) > 1 + adjustpos([0, 0.07]); + removeaxis('x'); + end + xlim(xlims); + end + if nargout > 0 + % indic = makeunique(EWSopt.indicators); + % if ~isoctave && verLessThan('matlab', '8.4.0') + % properties = struct('VarNames', indic , 'Description', sprintf('data generated with generic_ews\nwinsize = %g, detrending = %s, bandwidth = %g, logtransform = %d, arlag = %d, interpolate = %d', EWSopt.winsize, getcolumn(EWSopt.detrending, 1), getcolumn(EWSopt.bandwidth, 1), EWSopt.logtransform, EWSopt.arlag, EWSopt.interpolate)); + % inds = struct('time', EWSopt.time, 'timeseries', timeseries, 'trend', trend, 'indicators', [indicators{:}], 'Properties', properties); + % else + % % [~,ndx]=unique(EWSopt.indicators); + % if ~isempty(trend) + % ndx = ~isnan(trend(:, 1)); + % for i = 1:length(indicators) + % inds = zeros(size(ndx)) + NaN; + % if numel(indicators{i}) == numel(ndx) + % inds = indicators{i}; + % else + % inds(ndx) = indicators{i}; + % end + % indicators{i} = inds; + % end + % inds = table(EWSopt.time, timeseries, trend, indicators{:}); + % inds.Properties.VariableNames = [{'Time', 'Variable', 'trend'}, indic]; + % else + % inds = table(EWSopt.time, timeseries, indicators{:}); + % inds.Properties.VariableNames = [{'Time', 'Variable'}, indic]; + % end + % + % inds.Properties.Description = sprintf('data generated with generic_ews\nwinsize = %g, detrending = %s, bandwidth = %g, logtransform = %d, arlag = %d, interpolate = %d', EWSopt.winsize, getcolumn(EWSopt.detrending, 1), getcolumn(EWSopt.bandwidth, 1), EWSopt.logtransform, EWSopt.arlag, EWSopt.interpolate); + % end + if isempty(getcolumn(EWSopt.bandwidth, 1)) + % optimal bandwidth suggested by Bowman and Azzalini (1997) p.31 + n = size(timeseries, 1); + hx = median(abs(EWSopt.time - median(EWSopt.time))) / 0.6745 * (4 / 3 / n)^0.2; + hy = median(abs(timeseries(:, getcolumn(EWSopt.datacolumn, 1)) - median(timeseries(:, getcolumn(EWSopt.datacolumn, 1))))) / 0.6745 * (4 / 3 / n)^0.2; + EWSopt.bandwidth = sqrt(hy * hx); + end + if nargout == 1 + descr = sprintf('data generated at %s with generic_ews\nwinsize = %g, detrending = %s, bandwidth = %g, logtransform = %d, arlag = %d, interpolate = %d', ... + datestr(now()), EWSopt.winsize, getcolumn(EWSopt.detrending, 1), getcolumn(EWSopt.bandwidth, 1), EWSopt.logtransform, EWSopt.arlag, EWSopt.interpolate); + result = struct('timeseries', timeseries, ... + 'EWSopt', EWSopt, ... + 'trend', trend, ... + 'colnames', {makeunique(EWSopt.indicators)} , ... + 'indicators', [indicators{:}], ... + 'taus', taus, ... + 'pvalues', pvalues, ... + 'ebitaus', ebitaus, ... + 'description', descr); + end + end + + %end +end +function [timeseries, EWSopt] = initialize_settings(timeseries, args) + if isa(timeseries, 'table') + %the time series may be a table , it can then have a column with + %time, the name of that column should be t or Time or time + ndx = ~(strcmp('t', timeseries.Properties.VariableNames) | strcmpi('time', timeseries.Properties.VariableNames)); + if any(ndx) + % the time is added to the options + args = [args, 'time', timeseries.t]; + timeseries = timeseries{:, ndx}; + else + timeseries = timeseries{:, :}; + end + elseif isa(timeseries, 'dataset') + %the time series may be a dataset, it can then have a column with + %time, the name of that column should be t or Time or time + ndx = ~(strcmp('t', timeseries.Properties.VarNames) | strcmpi('time', timeseries.Properties.VarNames)); + if any(ndx) + % the time is added to the options + args = [args, 'time', timeseries.t]; + timeseries = double(timeseries(:, ndx)); + else + timeseries = double(timeseries(:, :)); + end + else + timeseries = double(timeseries); + end + +% if ~exist('i_use', 'file') +% addpath([grindpath, filesep, 'sys2']); +% end + if isempty(timeseries) + error('grind:generic_ews:empty', 'Empty time series'); + end + EWSopt = generic_ews('-defaultopts'); + EWSopt = mergeoptions(EWSopt, args); + if ischar(EWSopt.bandwidth_unit) + EWSopt.bandwidth_unit={EWSopt.bandwidth_unit}; + end + if numel(EWSopt.bandwidth_unit)==1&&size(timeseries,2)>1 + EWSopt.bandwidth_unit=repmat(EWSopt.bandwidth_unit,1,size(timeseries,2)); + end + + if EWSopt.powerspectrum || EWSopt.AR_n + error('Option not yet supported'); + end + if isempty(EWSopt.time) + EWSopt.time = transpose(linspace(1, size(timeseries, 1), size(timeseries, 1))); + end + + if EWSopt.interpolate + ts = transpose(linspace(EWSopt.time(1), EWSopt.time(end), size(timeseries, 1))); + timeseries = interp1(EWSopt.time, timeseries, ts); + EWSopt.time = ts; + end + for i = 1:size(timeseries, 2) + if getcolumn(EWSopt.logtransform, i) + timeseries(:, i) = log(timeseries(:, i) + 1); + end + end + + EWSopt.datalength = max(EWSopt.time) - min(EWSopt.time); + if EWSopt.cv + %depreciated + f = strcmp(EWSopt.indicators, 'std'); + EWSopt.indicators(f) = {'cv'}; + end +end +function EWSopt = mergeoptions(EWSopt, arg) + f = fieldnames(EWSopt); + if ~isempty(arg) && isstruct(arg{1}) + %all options can be entered as struct + if exist('mergestructs', 'file') == 0 + %initgrind; + end + %replace common fields in EWSopt + EWSopt = mergestructs(EWSopt, arg{1}); + else + for i = 1:2:length(arg) + if ~any(strcmp(f, arg{i})) + s = sprintf('"%s", ', f{:}); + error('Unknown option for generic_ews: %s\nValid options are: %s', arg{i}, s(1:end - 1)); + end + EWSopt.(arg{i}) = arg{i + 1}; + end + end +end + + +%merge two structs +%astruct=mergestructs(astruct,newstruct) +% +%fields of a astruct will be replaced by newstruct, new fields will be +%added if necessary +%nested for substructures +function fullstruct = mergestructs(fullstruct, substruct) + if isempty(fullstruct) + fullstruct = substruct; + elseif ~isempty(substruct) && numel(substruct) == numel(fullstruct) + fnew = fieldnames(substruct); + fold = fieldnames(fullstruct); + for i = 1:length(fnew) + for j = 1:numel(substruct) + if isstruct(substruct(j).(fnew{i})) && any(strcmp(fold, fnew{i})) && isstruct(fullstruct(j).(fnew{i})) + fullstruct(j).(fnew{i}) = mergestructs(fullstruct(j).(fnew{i}), substruct(j).(fnew{i})); + else + fullstruct(j).(fnew{i}) = substruct(j).(fnew{i}); + end + + end + + end + + else + error('grind:mergestructs', 'Cannot merge strucs of different sizes') + end +end + + +function res = getcolumn(col, i, multcell) + if nargin < 3 + multcell = false; + end + if multcell && ~iscell(col) + col = {col}; + end + if isempty(col) + res = col; + elseif iscell(col) + if numel(col) == 1 + res = col{1}; + else + res = col{i}; + end + else + if numel(col) == 1 + res = col(1); + else + res = col(i); + end + end +end + +function res = meancorr(x, datacolumm, corrcolumns, doabs) + if size(x, 2) == 1 + res = NaN; + return; + end + res = corr(x); + res = res(datacolumm, :); + if isempty(corrcolumns) + res(:, datacolumm) = []; + else + res = res(:, corrcolumns); + end + if doabs + res = mean(abs(res)); + else + res = mean(res); + end +end +function indic = makeunique(indic) + uindic = unique(indic); + if length(uindic) < length(indic) + for i = 1:length(uindic) + f = find(strcmp(uindic{i}, indic)); + for j = 2:length(f) + indic{f(j)} = sprintf('%s_%d', indic{f(j)}, j - 1); + end + end + end +end +function b = linregres(x, y) %#ok<DEFNU> + %b=(sum(x.*y)-1/length(x)*sum(x)*sum(y))/(sum(x.^2)-1/length(x)*sum(x).^2); + b = sum(x .* y) ./ (sum(x.^2)); %without intercept + %intercept + %a=mean(y)-b*mean(x); +end +function res = skewness(x, flag) + if nargin < 2 + flag = 0; + end + x = x - mean(x); + s2 = mean(x.^2); % this is the biased variance estimator + m3 = mean(x.^3); + res = m3 ./ s2.^(1.5); + if flag == 0 + n = length(x); + if n > 3 + n(n < 3) = NaN; % bias correction is not defined for n < 3. + res = res .* sqrt((n - 1) ./ n) .* n ./ (n - 2); + end + end +end +function res = acf(X, alag) + if nargin < 2 + alag = 1; + end + %autocorrelation function like in R + n = length(X); + s = var(X); + mu = mean(X); + Xt = X(1:end - alag); + Xtk = X(1 + alag:end); + res = 1 / (n - 1) / s * sum((Xt - mu) .* (Xtk - mu)); +end +function res = ksmooth(x, y, bandwidth, method) + if nargin < 3 + bandwidth = []; + end + if nargin < 4 + method = 'g'; + end + if isempty(x) + res = x; + return + end + n = length(x); + if isempty(bandwidth) + % optimal bandwidth suggested by Bowman and Azzalini (1997) p.31 + hx = median(abs(x - median(x))) / 0.6745 * (4 / 3 / n)^0.2; + hy = median(abs(y - median(y))) / 0.6745 * (4 / 3 / n)^0.2; + h = sqrt(hy * hx); + fprintf('bandwidth smoothing set to %g\n', h); + if h < sqrt(eps) * n + error('GRIND:outf:LittleVar', 'There is not enough variation in the data. Regression is meaningless.') + end + else + h = bandwidth; + end + switch lower(method(1)) + case 'g' %"Normal" = Gaussian kernel function + h = h * 0.36055512754640; %variance of 0.13 to get quartiles at + / - 0.25 * h (comparable with ksmooth (R)) + res = ones(size(y, 1), 1); + for k = 1:n + xx = abs(x(k) - x) / h; + z = exp( - xx(xx < 4).^2 / 2); %x < 4 is more efficient for long time series (negligible effect) + res(k) = sum(z .* y(xx < 4)) / sum(z); + end + %remove tails + x1 = (x(end) - x) > h / 2 & (x - x(1)) > h / 2; + res(~x1) = NaN; + case 'b' %"box" = moving average + d = h / 2; % 0.25 quartiles + res = cell(1, n); + for k = 1:n + xx = abs(x(k) - x) / h; + z = xx < d; + res(k) = sum(z .* y) / sum(z); + end + end +end + +function res = movmean1( timeseries, bw) + %simple remake of movmean (for older MAT + if length(bw) == 1 + if mod(bw, 2) == 0 + bw = [bw / 2 bw / 2 - 1]; + else + bw = [(bw - 1) / 2 (bw - 1) / 2]; + end + end + starti = [1:length(timeseries)] - bw(1); + endi = [1:length(timeseries)] + bw(2); + starti(starti < 1) = 1; + endi(endi > length(timeseries)) = length(timeseries); + res = zeros(size(timeseries)); + for i1 = 1:length(timeseries) + res(i1) = mean(timeseries(starti(i1):endi(i1))); + end +end + +function removeaxis(orientation) + hs = getsubplotgrid(gcf); + if nargin == 0 + orientation = 'b'; + end + [rows, cols] = size(hs); + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'x', 1) + for i = 1:rows - 1 + for j = 1:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + set(get(hs(i, j), 'xlabel'), 'string', '') + set(hs(i, j), 'XTickLabel', []); + end + end + end + for i = 2:rows + for j = 1:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + set(get(hs(i, j), 'title'), 'string', '') + end + end + end + end + if strncmpi(orientation, 'b', 1) || strncmpi(orientation, 'y', 1) + for i = 1:rows + for j = 2:cols + if ishandle(hs(i, j)) && (hs(i, j) ~= 0) + set(get(hs(i, j), 'ylabel'), 'string', '') + set(hs(i, j), 'YTickLabel', []); + end + end + end + end + set(hs(hs ~= 0), 'fontsize', 12) + aa = get(get(gcf, 'children'), 'xlabel'); + if ~iscell(aa) + aa = {aa}; + end + for i = 1:length(aa) + set(aa{i}, 'fontsize', 12); + end + aa = get(get(gcf, 'children'), 'ylabel'); + if ~iscell(aa) + aa = {aa}; + end + for i = 1:length(aa) + set(aa{i}, 'fontsize', 12); + end +end +function adjustpos(spacing) + set(gcf, 'PaperPositionMode', 'auto') + set(gcf, 'units', 'normalized'); + figpos = get(gcf, 'position'); + vert = 0.5469; %normal size of figure + hor = 0.41; + hs = getsubplotgrid(gcf); + if nargin == 0 + spacing = 0.02; + end + if length(spacing) == 1 + spacing = spacing + zeros(2, 1); + end + [rows, cols] = size(hs); + if cols < 4 && rows <= 4 + set(gcf, 'position', [figpos(1:2) hor * cols / 4 vert * rows / 4]); + else + rat = rows / cols; + if rat < 1 + set(gcf, 'position', [figpos(1:2) hor vert * rat]); + else + set(gcf, 'position', [figpos(1:2) hor / rat vert]); + end + end + for i = 1:rows + for j = 1:cols + h = findobj(gcf, 'tag', sprintf('subplot(%d,%d)', i, j)); + if any(ishandle(h)) && any(h ~= 0) + set(h, 'position', subplotpos(rows, cols, j, i, spacing)); + end + end + end +end + +function pos = subplotpos(rows, cols, x1, y1, spacing, colwidth) + %tight position for subplots, you can only set the spacing between the + %figures (simplified from subaxis) + % s=subplot('position',subplotpos(rows,cols,x1,y1),'tag',sprintf('subplot(%d,%d)',x1,y1)); + % + if nargin < 5 + spacing = [0.02 0.02]; + end + if nargin<6 + colwidth=1; + end + if length(spacing) == 1 + spacing = spacing + zeros(2, 1); + end + + Args = struct('Holdaxis', 0, ... + 'SpacingVertical', spacing(1), 'SpacingHorizontal', spacing(2), ... + 'MarginLeft', .2, 'MarginRight', .1, 'MarginTop', 0.05, 'MarginBottom', .1, ... + 'rows', rows, 'cols', cols); + + + cellwidth = ((1 - Args.MarginLeft - Args.MarginRight) - (Args.cols - 1) * Args.SpacingHorizontal) / Args.cols; + if colwidth>1 + cellwidth=cellwidth*colwidth+(colwidth-1)*Args.SpacingHorizontal; + end + cellheight = ((1 - Args.MarginTop - Args.MarginBottom) - (Args.rows - 1) * Args.SpacingVertical) / Args.rows; + xpos1 = Args.MarginLeft + cellwidth * (x1 - 1) + Args.SpacingHorizontal * (x1 - 1); + xpos2 = Args.MarginLeft + cellwidth * x1 + Args.SpacingHorizontal * (x1 - 1); + ypos1 = Args.MarginTop + cellheight * (y1 - 1) + Args.SpacingVertical * (y1 - 1); + ypos2 = Args.MarginTop + cellheight * y1 + Args.SpacingVertical * (y1 - 1); + + pos = [xpos1 1 - ypos2 xpos2 - xpos1 ypos2 - ypos1]; +end + +function hs = getsubplotgrid(h) + ch = get(h, 'children'); + tags = get(ch, 'tag'); + ch = ch(~strcmp(tags, 'legend')); + types = get(ch, 'type'); + poss = get(ch, 'position'); + if length(ch) == 1 + poss = {poss}; + types = {types}; + end + ipos = zeros(length(ch), 4); + for i = length(ch): - 1:1 + if iscell(types) && strcmp(types{i}, 'axes') + ipos(i, :) = poss{i}; + elseif ischar(types) && strcmp(types, 'axes') + ipos(i, :) = poss{i}; + else + ipos(i, :) = []; + end + end + colpos = sort(unique(sort(ipos(:, 1))), 'ascend'); + rowpos = sort(unique(sort(ipos(:, 2))), 'descend'); + hs = zeros(length(rowpos), length(colpos)); + for i = 1:length(ch) + if strcmp(types{i}, 'axes') + arow = find(rowpos == ipos(i, 2)); + acol = find(colpos == ipos(i, 1)); + hs(arow, acol) = ch(i); + set(ch(i), 'tag', sprintf('subplot(%d,%d)', arow, acol)); + end + end +end + +%autoregression ols (without intercept and with subtraction the mean) +% +function rc = autoregression(Ys, alag) + if nargin < 2 + alag = 1; + end + + rc = zeros(length(alag), size(Ys, 2)); + Ys = Ys - mean(Ys(2:end)); %subtracting the mean + % (important if you do regression without intercept!) + for j = 1:length(alag) + for i = 1:size(Ys, 2) + res = linregres(Ys(1:end - alag, i), Ys(1 + alag:end, i)); + rc(j, i) = res(1); + end + + end +end + + + +function adjustticklabels(hax, orient) + % axis(hax, 'tight') + % newtick = get(hax, [orient 'tick']); + % tickdiff = (newtick(2) - newtick(1)); + % newlim = [newtick(1) - tickdiff newtick(end) + tickdiff]; + % axis(hax, 'manual') + % set(hax, [orient 'lim'], newlim); + % set(hax, [orient 'tick'], [newtick(1) - tickdiff newtick]); +end + + + + diff --git a/generic_ews_sens.m b/generic_ews_sens.m new file mode 100644 index 0000000..ce9d45f --- /dev/null +++ b/generic_ews_sens.m @@ -0,0 +1,454 @@ +%function generic_ews_sens +% make heatmaps of the sensitivity of the tau values of generic_ews of the +% window size and bandwithd +% +% Usage +% +% generic_ews_sens(timeseries, option pairs) +% +% all options pairs: (with defaults) +% res= generic_ews_sens(timeseries, 'winsize', (5:5:90), 'detrending', 'no', ... +% 'bandwidth', (1:1:20), 'logtransform', false, 'interpolate', false) +% To draw plots: +% generic_ews_sens('-p',res) plot all results. +% generic_ews_sens('-p',res,'indicators',{'AR'},'units',unit) - some options +% can be changed for plotting: +% - 'indicators' the indicators to be plotted +% - 'defaultpos' position of the default settings +% - 'units' the units for the bandwidth and window size in the plot +% - 'title' title of the figure, default the indicator +% other changed settings are ignored +% +% generic_ews_sens('-f',res) determine the Fisher combined probability plots of +% the results res. res should be a struct array or cell array of earlier results +% s=generic_ews_sens('-defaultopts') - create a struct s with the default +% settings (used internally) +% +% +% Arguments (only those different from generic_ews) +% +% winsize - an array with the window sizes to be tested (in % of the +% size of the time series) +% +% bandwidth - an array with the bandwidths to be tested (in % of the +% size of the time series) +% defaultpos - position of a star with the default settings (bandwidth,winsize) (optional) +% bandwidth_unit - unit of bandwidth (see units of winsize) +% winsize_unit - units of the winsize - if different from '%' the absolute +% units of the time series are assumed. If % the real size +% is calculated from the length of the time series +% distribution - way of selecting bandwidth/winsize (regular/uniform) +% (default regular) +% ndraws - number of draws from distribution (ignored when regular) +% (default = 1000) +% +% +% generic_ews_sens returns a struct with the fields: +% +% bandwidths: matrix with all bandwidth tested +% winsizes: matrix with the window sizes +% taus: 3D matix with the taus (taus(:,:,1)=AR1, +% taus(:,:,2)=acf, taus(:,:,3)=standard dev/CV, taus(:,:,4)=skewness) +% EWSopt the options used +% inds the timeseries and the lastly calculated indicators (see generic_ews) +% colnames the names of the columns (see generic_ews) +% +% +% +% Author(s) +% +% Vasilis Dakos vasilis.dakos@gmail.com +% MATLAB version by Egbert van Nes +% +% References +% +% Dakos, V., et al (2008). "Slowing down as an early warning signal for abrupt climate change." Proceedings of the National Academy of Sciences 105(38): 14308-14312 +% +% Dakos, V., et al (2012)."Methods for Detecting Early Warnings of Critical Transitions in Time Series Illustrated Using Simulated Ecological Data." PLoS ONE 7(7): e41010. doi:10.1371/journal.pone.0041010 +% +% +function [res] = generic_ews_sens(timeseries, varargin) + if isfield(timeseries, 'timeseries') + varargin = [{timeseries} varargin]; + timeseries = '-p'; + end + if nargin > 0 && ischar(timeseries) + if strncmp(timeseries, '-p', 2) + %plot the figures + if numel(varargin{1}) > 1 + res = varargin{1}; + for i = 1: numel(varargin{1}) + generic_ews_sens('-p', res(i), varargin{2:end}); + end + return; + end + EWSopt = varargin{1}.EWSopt; + if length(varargin) > 1 + EWSopt = mergeoptions(EWSopt, varargin(2:end)); + end + results = varargin{1}; + %bandwidths = varargin{1}.bandwidths; + %winsizes = varargin{1}.winsizes; + taus = varargin{1}.taus; + ebitaus = varargin{1}.ebitaus; + colnames = varargin{1}.colnames; + %EWSopt.indicators = varargin{1}.colnames; + if ischar( EWSopt.indicators) + EWSopt.indicators = {EWSopt.indicators}; + end + figs = EWSopt.indicators; + if ischar(figs) + figs = {figs}; + elseif isnumeric(figs) + figs = EWSopt.indicators(figs); + end + if ischar(EWSopt.bandwidth_unit) + EWSopt.bandwidth_unit = {EWSopt.bandwidth_unit}; + end + if numel(EWSopt.bandwidth_unit) == 1 && size(timeseries, 2) > 1 + EWSopt.bandwidth_unit = repmat(EWSopt.bandwidth_unit, 1, size(timeseries, 2)); + end + % bw_unit = EWSopt.bandwidth_unit; + % win_unit = EWSopt.winsize_unit; + % if isfield(EWSopt, 'units') + % units = EWSopt.units; + % else + % units = {'%', '%'}; + % end + % if ischar(units) + % units = {units, units}; + % end + % if length(units) == 1 + % units = [units units]; + % end + % if any(strcmp(bw_unit{1}, '%')) + % bandwidths = bandwidths .* EWSopt.datalength ./ 100; + % end + % if strcmp(win_unit, '%') + % winsizes = winsizes .* EWSopt.datalength ./ 100; + % end + if isfield(EWSopt, 'defaultpos') + defaultpos = EWSopt.defaultpos; + if iscell(defaultpos) + defaultpos = [defaultpos{:}]; + end + else + defaultpos = []; + end + titles = colnames; + for i = 1:numel(figs) + fignr = find(strcmp(colnames, figs{i})); + if isempty(fignr) + error('grind:generic_ews_sens:unknowindicator', 'indicator %s unknown', figs{i}); + end + if ~isempty(EWSopt.title) + atitle = EWSopt.title; + else + atitle = titles{fignr}; + end + plot_sensitiv(results, taus(:, :, fignr), fignr, [], [], defaultpos, atitle, EWSopt); + end + if ~isempty(ebitaus) + if ~isfield(EWSopt,'distribution')||strcmp(EWSopt.distribution, 'regular') + titles = colnames; + levels = [0:0.05:0.3]; + for i = 1:numel(figs) + fignr = find(strcmp(colnames, figs{i})); + if ~isempty(EWSopt.title) + atitle = ['p-value''s ' EWSopt.title ]; + else + atitle = ['p-value''s ' titles{fignr}]; + end + pvalues = getpvalues(taus(:, :, fignr), ebitaus(:, :, fignr, :)); + plot_sensitiv(results, pvalues, fignr, [0 0.2], levels, defaultpos, atitle, EWSopt); + end + else + for i = 1:numel(figs) + fignr = find(strcmp(colnames, figs{i})); + if ~isempty(EWSopt.title) + atitle = ['p-value''s ' EWSopt.title ]; + else + atitle = ['p-value''s ' titles{fignr}]; + end + pvalues = getpvalues(taus(:, :, fignr), ebitaus(:, :, fignr, :)); + figure; + violinplot(pvalues, ones(size(pvalues))); + title(atitle) + end + end + end + return; + elseif strcmp(timeseries, '-setcolor') + c1 = findobj(gcf, 'tag', 'perc_fill'); + if ~isempty(c1) + %something wrong with this code... (it works only once) + colors = get(c1, 'FaceColor'); + colors = vertcat(colors{:}); + [~, i] = max(var(colors, 1)); + rangecolors = 1 - (colors - max(colors(:, i))) ./ (min(colors(:, i) - max(colors(:, i)))); + mincolor = varargin{1}; + if ischar(mincolor) + colorcodes = {[1 1 0], 'y'; ... + [1 0 1], 'm'; ... + [0 1 1], 'c'; ... + [1 0 0], 'r'; ... + [0 1 0], 'g'; ... + [0 0 1], 'b'; ... + [1 1 1], 'w'; ... + [0 0 0], 'k'}; + ndx = strcmp(mincolor, colorcodes(:, 2)); + mincolor = colorcodes{ndx,1}; + end + maxcolor = ones(1, 3); + for i = 1:length(c1) + set(c1(i), 'facecolor', (mincolor + (maxcolor - mincolor) * (rangecolors(i)))); + end + else + error('generic_ews_sens:notfound', 'series not found'); + end + return; + elseif strncmpi(timeseries, '-f', 2) %fisher combine previous results + inputs1 = varargin{1}; + inputs1 = generic_ews_sens('-add_pvalues', inputs1); + EWSopt = inputs1(1).EWSopt; + if length(varargin) > 1 + EWSopt = mergeoptions(EWSopt, varargin(2:end)); + end + ind_ndx = find(ismember(inputs1(1).colnames, EWSopt.indicators)); + if isempty(ind_ndx) + error('generic_ews_sens:fisher', 'None of the indicators were found'); + end + res1.pvalues = {inputs1(:).pvalues}; + res1.fisher_pvalues = ones(size(inputs1(1).taus, 1), size(inputs1(1).taus, 2), length(ind_ndx)); + res1.fisher_chi = ones(size(res1.fisher_pvalues)); + titles = inputs1(1).colnames; + levels = [0:0.05:0.3]; + if iscell(EWSopt.defaultpos) + defaultpos = [EWSopt.defaultpos{:}]; + else + defaultpos = EWSopt.defaultpos; + end + for k = 1:length(ind_ndx) %loop the different indicators + for f1 = 1:size(inputs1(1).taus, 1) + for f2 = 1:size(inputs1(1).taus, 2) + ps = zeros(size(inputs1)); + for i = 1:numel(inputs1) + ps(i) = inputs1(i).pvalues(f1, f2, ind_ndx(k)); + end + fisher1 = fishercombine(ps); + res1.fisher_pvalues(f1, f2, k) = fisher1.p; + res1.fisher_chi(f1, f2, k) = fisher1.chi; + end + end + res1.fisher_df = fisher1.df; + end + res1.hfigs = []; + for k = 1:length(ind_ndx) + if ~isempty(EWSopt.title) + atitle = ['Fisher p ' EWSopt.title ]; + else + atitle = ['Fisher p ' titles{ind_ndx(k)}]; + end + res1.hfigs(k) = plot_sensitiv(inputs1(1), res1.fisher_pvalues(:, :, k), ... + k, [0 0.3], levels, defaultpos, atitle, EWSopt); + end + for i = 1:length(res1.pvalues) + res1.pvalues{i} = res1.pvalues{i}(:, :, ind_ndx); + end + if nargout > 0 + res = res1; + end + return; + elseif strcmp(timeseries, '-add_pvalues') %get the p-values + res = varargin{1}; + res(1).pvalues = []; + if numel(res) > 1 + for i = numel(res): -1:1 + res(i) = generic_ews_sens('-add_pvalues', res(i)); + end + else + res.pvalues = ones(size(res.taus)); + for k = 1:size(res(1).taus, 3) + res.pvalues(:, :, k) = getpvalues(res.taus(:, :, k), res.ebitaus(:, :, k, :)); + end + end + return + elseif strcmp(timeseries, '-defaultopts') + res = generic_ews('-defaultopts'); + %default winsizes and bandwidths + res.winsize = (5:5:90); + res.bandwidth = (1:1:20); + res.defaultpos = {[]}; + res.distribution = 'regular'; + res.ndraws = 1000; + return; + end + end + EWSopt = generic_ews_sens('-defaultopts'); + EWSopt = mergeoptions(EWSopt, varargin); + sens_opts = EWSopt; + + %override the value of silent to suppress too many figures + EWSopt.silent = true; +% if exist('i_waitbar', 'file') == 0 +% initgrind; +% end + if strcmp(EWSopt.distribution, 'regular') + inds = []; + taus = zeros(numel(EWSopt.winsize), numel(EWSopt.bandwidth), numel(EWSopt.indicators)); + ebitaus = zeros(numel(EWSopt.winsize), numel(EWSopt.bandwidth), numel(EWSopt.indicators), EWSopt.ebisuzaki); + [bandwidths, winsizes] = meshgrid(EWSopt.bandwidth, EWSopt.winsize); + % wb = i_parfor_waitbar(0, size(winsizes, 1), 'Calculating sensitivity'); + parfor i = 1:size(winsizes, 1) + taus_loc = taus(i, :, :); + ebitaus_loc = ebitaus(i, :, :, :); + for j = 1:size(winsizes, 2) + EWSopt1 = EWSopt; + EWSopt1.winsize = winsizes(i, j); + EWSopt1.bandwidth = bandwidths(i, j); + res2 = generic_ews(timeseries, EWSopt1); + taus_loc(1, j, :) = res2.taus; + if ~isempty(res2.ebitaus) + ebitaus_loc(1, j, :, :) = res2.ebitaus; + end + end + % i_parfor_waitbar(wb, i); + taus(i, :, :) = taus_loc; + ebitaus(i, :, :, :) = ebitaus_loc; + end + elseif strcmp(EWSopt.distribution, 'uniform') + taus = zeros(1, EWSopt.ndraws, numel(EWSopt.indicators)); + inds = zeros(EWSopt.ndraws, size(timeseries, 1), numel(EWSopt.indicators)); + ebitaus = zeros(1, EWSopt.ndraws, numel(EWSopt.indicators), EWSopt.ebisuzaki); + bandwidths = min(EWSopt.bandwidth) + rand(EWSopt.ndraws, 1) * (max(EWSopt.bandwidth) - min(EWSopt.bandwidth)); + winsizes = min(EWSopt.winsize) + rand(EWSopt.ndraws, 1) * (max(EWSopt.winsize) - min(EWSopt.winsize)); + %wb = i_parfor_waitbar(0, EWSopt.ndraws, 'Calculating sensitivity'); + for i = 1:EWSopt.ndraws + EWSopt1 = EWSopt; + EWSopt1.winsize = winsizes(i); + EWSopt1.bandwidth = bandwidths(i); + res2 = generic_ews(timeseries, EWSopt1); + taus(1, i, :) = res2.taus; + if ~isempty(res2.ebitaus) + ebitaus(1, i, :, :) = res2.ebitaus; + end + inds(i, :, :) = res2.indicators; + %i_parfor_waitbar(wb, i); + end + end + EWSopt1 = EWSopt; + EWSopt1.winsize = winsizes(1); + EWSopt1.bandwidth = bandwidths(1); + EWSopt1.ebisuzaki = 0; + + inputs1 = generic_ews(timeseries, EWSopt1); + EWSopt1.ebisuzaki = EWSopt.ebisuzaki; + %i_parfor_waitbar([]); + %save the basic data per run + if ~isempty(inds) + inputs1.indicators = inds; + end + inputs1.bandwidths = bandwidths; + inputs1.winsizes = winsizes; + inputs1.taus = taus; + if EWSopt.ebisuzaki > 0 + inputs1.ebitaus = ebitaus; + else + inputs1.ebitaus = []; + end + inputs1.EWSopt = inputs1.EWSopt; + inputs1.EWSopt.winsize = sens_opts.winsize; + inputs1.EWSopt.bandwidth = sens_opts.bandwidth; + inputs1.EWSopt.silent = sens_opts.silent; + % res1.indicators = res1.indicators; + % res1.colnames = res1.colnames; + if nargout > 0 + res = inputs1; + end +end +function hfig = plot_sensitiv(results, x, fignr, climit, levels, defaultpos, atitle, EWSopt) + hfig = figure; + % pcolor(bandwidths, winsizes, x); + % shading flat + if isfield(EWSopt,'distribution')&&strcmp(EWSopt.distribution, 'uniform') + hold on + percentiles = [0.05:0.025:0.95]; + maxcolor = [0.1 0.1 0.1]; + mincolor = [0.9 0.9 0.9]; + cs = 0:find(abs(percentiles - 0.5) < 0.01) - 1; + cs = cs ./ (max(cs(:))); + cs = [cs(1:end - 1), fliplr(cs)]; + data = quantile(results.indicators(:, :, fignr), percentiles)'; + ndx = ~isnan(data(:, 1)); + for i = 1:length(percentiles) - 1 + c = mincolor + cs(i) .* (maxcolor - mincolor); + fill([EWSopt.time(ndx); flipud(EWSopt.time(ndx))], [data(ndx, i); flipud(data(ndx, i + 1))], c); + end + set(get(gca, 'children'), 'EdgeColor', 'none', 'tag', 'perc_fill') + plot(EWSopt.time, quantile(results.indicators(:, :, fignr), 0.5), 'color', maxcolor, 'tag', 'median'); + xlabel('Time'); + + xlims = [min(EWSopt.time), max(EWSopt.time)]; + xlim(xlims) + text(0.1, 0.9, results.colnames{fignr}, 'units', 'normalized', 'fontsize', 11, 'fontname', 'Arial', 'tag', 'toptext') + adjustplot('-d'); + %plot(EWSopt.time,results.indicators(:, :, fignr)) + else + if ~isempty(levels) + contourf(results.bandwidths, results.winsizes, x, levels); + else + contourf(results.bandwidths, results.winsizes, x); + end + if ~isempty(climit) + set(gca, 'clim', climit) + end + if ~isempty(defaultpos) + hold on + h1 = plot(defaultpos(1), defaultpos(2), 'k*'); + set(h1, 'markersize', 6); + hold off + end + adjustplot('-d'); + xlim([min(results.bandwidths(:)) max(results.bandwidths(:))]); + ylim([min(results.winsizes(:)) max(results.winsizes(:))]); + xlabel(sprintf('filtering bandwidth (%s)', EWSopt.bandwidth_unit{1})) + ylabel(sprintf('sliding window (%s)', EWSopt.winsize_unit)); + h = colorbar; + adjustplot('-d'); + ylabel(h, atitle); + end + % title(atitle); + set(hfig, 'tag', atitle); +end +function pvalues = getpvalues(taus, ebitaus) + nebisuzaki = size(ebitaus, 4); + pvalues = zeros(size(taus)); + for i1 = 1:size(taus, 1) + for j1 = 1:size(taus, 2) + pvalues(i1, j1) = (sum(taus(i1, j1) < ebitaus(i1, j1, 1, :)) + 1) / nebisuzaki; + end + end +end +function EWSopt = mergeoptions(EWSopt, arg) + f = fieldnames(EWSopt); + if ~isempty(arg) && isstruct(arg{1}) + %all options can be entered as struct + if exist('mergestructs', 'file') == 0 + initgrind; + end + %replace common fields in EWSopt + EWSopt = mergestructs(EWSopt, arg{1}); + else + for i = 1:2:length(arg) + if ~any(strcmp(f, arg{i})) + s = sprintf('"%s", ', f{:}); + error('Unknown option for generic_ews_sens: %s\nValid options are: %s', arg{i}, s(1:end - 1)); + end + if iscell(arg{1}) + EWSopt.(arg{1}{i}) = arg{1}{i + 1}; + else + EWSopt.(arg{i}) = arg{i + 1}; + end + end + end +end diff --git a/run_regime.m b/run_regime.m new file mode 100644 index 0000000..20e6439 --- /dev/null +++ b/run_regime.m @@ -0,0 +1,81 @@ +function res=run_regime(tree,regimename) + %how to run different regimes + + %regimes, [start year, end year] + regimes = { + 'BM III', [501 684]; ... + 'P I', [705 871]; ... + 'P II', [873 1118]; ... + 'Early P III', [1157 1215]; ... + 'Late P III', [1217 1283]; ... + 'P III', [1157 1283]}; + + regimendx=find(strcmp(regimes(:,1),regimename)); + if isempty(regimendx) + error('regime "%s" does not exist, check the spelling',regimename); + end + + ndx=regimes{regimendx,2}; + ndx=ndx(1):ndx(2); + %ndx= the range of data points for each regime + data = [tree.trees(ndx) tree.tot_hab_cell(ndx)]; + + if ~any(strcmp(regimename, {'Early P III', 'Late P III'})) + %settings for large data sets (all regimes except 'Early PIII' and 'Late PIII'): + res = generic_ews(data, 'ebisuzaki', 1000, ... + 'detrending' , {'gaussian' 'movmean'}, ... + 'bandwidth', {[30] [5 0]}, ... + 'winsize', 60, ... + 'winsize_unit', 'yr', ... + 'bandwidth_unit', {'yr' 'yr'}, ... + 'datacolumn', [1 2 1 1 2 2], ... + 'arlag', 1, ... + 'indicators', {'AR' 'AR' 'crosscorr' 'std' 'std' 'mean'}... + ); + + res2 = generic_ews_sens(data, 'bandwidth', linspace(5, 40, 15),... + 'winsize', linspace(20, 90, 15),... + 'winsize_unit', 'yr' ,... + 'bandwidth_unit', 'yr',... + 'ebisuzaki', 0, ... %put to 1000 for p plot (takes long) + 'detrending', 'gaussian', ... + 'datacolumn', 1, ... + 'arlag', 1, ... + 'defaultpos', {[30,60]}, ... + 'indicators', {'AR', 'crosscorr', 'std'}); + figure + generic_ews_sens('-p',res2); + else + + %settings for small data sets (Early PIII and Late PIII): + res = generic_ews(data, 'ebisuzaki', 1000, ... + 'detrending' , {'gaussian' ,'movmean'}, ... + 'bandwidth', {[15] [5 0]}, ... + 'winsize', 20, ... + 'winsize_unit', 'yr', ... + 'bandwidth_unit', {'yr' 'yr'}, ... + 'datacolumn', [1 2 1 1 2 2], ... + 'arlag', 1, ... + 'indicators', {'AR','AR','crosscorr','std','std','mean'}... + ); + + res2 = generic_ews_sens(data, 'bandwidth', linspace(5, 40, 15)/2,... + 'winsize', linspace(20, 90, 15)/2,... + 'winsize_unit', 'yr' ,... + 'bandwidth_unit', 'yr',... + 'ebisuzaki', 0, ... %put to 1000 for p plot (takes long) + 'detrending', 'gaussian', ... + 'datacolumn', 1, ... + 'arlag', 1, ... + 'defaultpos', {[15,20]}, ... + 'indicators', {'AR', 'crosscorr', 'std'}); + figure + generic_ews_sens('-p',res2); + end + + + + + +end + diff --git a/violinplot.m b/violinplot.m new file mode 100644 index 0000000..cc6ac70 --- /dev/null +++ b/violinplot.m @@ -0,0 +1,110 @@ +%violinplot - create a violinplot +% +%Usage: +% violinplot(x,cat) make violinplot of x, where cat are the values of the +% catagories +% violinplot(x,cat,'propertery',...) +% You can use all properties of the function ksdensity (like 'bandwidth') and all +% properties of patches (like 'facecolor') +% additionally: +% 'percentiles' list of percentiles to indicate in the violins for +% instance: [0.05 0.5 0.95] +% 'violinwidth' relative width of the violins relative to the smallest gap +% in the categories, default 2/3 +% 'categorynames' optionnally you can put strings for categories +% + +% (c) Egbert van Nes +function violinplot(x, cat, varargin) + if nargin > 2 && ischar(cat) + varargin = [{cat} varargin]; + cat = []; + end + if iscell(x) + if isempty(cat) + cat = 1:numel(x); + end + x1 = []; + cat1 = []; + if iscell(cat) + for i = 1:numel(x) + x1 = [x1; x{i}]; %#ok<AGROW> + cat1 = [cat1; cat{i}]; %#ok<AGROW> + end + else + for i = 1:numel(x) + x1 = [x1; x{i}]; %#ok<AGROW> + cat1 = [cat1; cat(i) + zeros(size(x{i}))]; %#ok<AGROW> + end + end + x = x1; + cat = cat1; + end + ksdensityopts = {'Kernel' , 'Support' , 'Weights', 'Bandwidth', 'Function', 'BoundaryCorrection', 'Censoring' , 'NumPoints'}; + if nargin == 1 || isempty(cat) + cat = repmat(1:size(x, 2), size(x, 1), 1); + end + if size(x, 1) > 1 && numel(cat) == size(x, 2) + cat = repmat(cat, size(x, 1), 1); + end + percentiles = 0.5; + violinwidth = 2 / 3; + categorynames = {}; + used = false(size(varargin)); + ksdensityndx = false(size(varargin)); + for i = 2:2:length(varargin) + if strcmpi(varargin{i - 1}, 'percentiles') + percentiles = varargin{i}; + used(i - 1:i) = true; + elseif strcmpi(varargin{i - 1}, 'violinwidth') + violinwidth = varargin{i}; + used(i - 1:i) = true; + elseif strcmpi(varargin{i - 1}, 'categorynames') + categorynames = varargin{i}; + used(i - 1:i) = true; + elseif any(strcmpi(varargin{i - 1}, ksdensityopts)) + ksdensityndx(i - 1:i) = true; + end + end + ksdensityargs = varargin(ksdensityndx); + varargin = varargin(~used & ~ksdensityndx); + x = x(:); + cat = cat(:); + ucat = unique(cat); + diffcat = min(diff(ucat)); + if isempty(diffcat) + diffcat = 1; + end + oldhold = ishold; + hold on; + for i = 1:length(ucat) + x1 = x(cat == ucat(i)); + try + [dens, xdens] = ksdensity(x1, ksdensityargs{:}); + dens = dens / max(dens) * diffcat * violinwidth / 2; + xperc = quantile(x1', percentiles); + yperc = interp1(xdens, dens, xperc); + fill([ucat(i) - dens fliplr(dens + ucat(i))], [xdens fliplr(xdens)], [0.7, 0.7, 1], 'tag', sprintf('violin(%g)', ucat(i)), varargin{:}) + for j = 1:length(percentiles) + if percentiles(j) == 0.5 + linestyle = 'k-'; + else + linestyle = 'k:'; + end + plot([ucat(i) - yperc(j), ucat(i) + yperc(j)], [xperc(j), xperc(j)], linestyle); + end + catch err + if ~strcmp(err.identifier, 'stats:mvksdensity:NanX') + rethrow(err); + end + end + end + if ~isempty(categorynames) + set(gca, 'xtick', 1:length(categorynames), 'xticklabel', categorynames) + end + if oldhold + hold on + else + hold off + end +end -- GitLab