diff --git a/IQM Tools/IQMlite/Contents.m b/IQM Tools/IQMlite/Contents.m index 4e9614b26812ad9ec926e882415458ed674a093f..ef8c5395750ba5d1519ae71d1dfe96de488bb59f 100644 --- a/IQM Tools/IQMlite/Contents.m +++ b/IQM Tools/IQMlite/Contents.m @@ -1,5 +1,5 @@ % IQM Tools Lite -% Version 1.2.1 (R2015B) 30.04.2016 +% Version 1.2.2 (R2015B) 02.01.2017 % % IQMlite % ======= @@ -171,6 +171,7 @@ % IQMplot - IQMplot - plots given data. % IQMplot2 - IQMplot2 - plots bar diagrams for given data. % IQMplotCatCat - This function plots a "bubble" plot for categorical data to assess correlations +% IQMplotKM - Kaplan-Meier plot % % functions % These functions can be used on IQMmodels but on command line as well. When used in models then these are also working when converting the IQMmodel to C-code @@ -301,7 +302,16 @@ % kin_uncomp_inihib_irr - Uncompetitive inhibition (irreversible) kinetics % kin_uncomp_inihib_rev - Uncompetitive inhibition (reversible) kinetics % kin_uni_uni_rev - Uni uni reversible kinetics -% +% +% survival +% IQMcoxExt - Parameter estimation for the extended Cox model +% IQMcoxPH - Parameter estimation for the Cox proportional hazards model +% IQMcoxStratPH - Parameter estimation for the Stratified Cox proportional hazards model +% IQMhr - Hazard ratio estimate for Cox models +% IQMkm - Kaplan-Meier curves estimation and plot (see also IQMplotKM) +% IQMphTest - Schoenfeld residuals test for the proportional hazards assumption +% IQMxext - Extension to time varying covariates (input to IQMcoxExt) +% % Auxiliary % ========= % getDefaultIntegratorOptionsIQM - Inside this function/file default values for integrator options are set. This function also retrieves them diff --git a/IQM Tools/IQMlite/auxiliary/compliance/IQMoutputTable.m b/IQM Tools/IQMlite/auxiliary/compliance/IQMoutputTable.m new file mode 100644 index 0000000000000000000000000000000000000000..4fbd284b7b9c601306b385c84db777e09822001d --- /dev/null +++ b/IQM Tools/IQMlite/auxiliary/compliance/IQMoutputTable.m @@ -0,0 +1,61 @@ +function [text] = IQMoutputTable(xtable,xtitle,xfooter,filename) +% This function allows to convert a MATLAB table to a report table +% formatted to be compatible with IQReport. +% +% [SYNTAX] +% [text] = IQMoutputTable(xtable,xtitle,xfooter,filename) +% +% [INPUT] +% xtable: MATLAB table +% xtitle: String with the table caption (default: '') +% xfooter: String with the table footer (default: '') +% filename: If a filename is provided then the table is exported to +% filename.txt. (default: ''); +% +% [OUTPUT] +% The text is written to a file. + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS LITE>>> + +% Handle variable input arguments +if nargin<4, + error('Please provide all input arguments.'); +end + +% Check table +if ~istable(xtable), + error('First input argument is not a MATLAB table.'); +end + +% Get info +Ncols = size(xtable,2)+1; +headerNames = xtable.Properties.VariableNames; + +% Generate table-content as cell-array +tableCells = table2cell(xtable); + +% Add <TR> info +tableRows = cell(size(tableCells,1),1); +tableRows(1:end) = {'<TR>'}; +tableCells = [tableRows tableCells]; + +% Add <TH> info +tableCells = [ [{'<TH>'} headerNames]; tableCells]; + +% Add <TT> info +tableTitle = cell(1,Ncols); +tableTitle{1} = '<TT>'; +tableTitle{2} = xtitle; +tableCells = [tableTitle; tableCells]; + +% Add <TF> info +if ~isempty(xfooter), + tableFooter = cell(1,Ncols); + tableFooter{1} = '<TF>'; + tableFooter{2} = xfooter; + tableCells = [tableCells; tableFooter]; +end + +% Convert to report table +IQMconvertCellTable2ReportTable(tableCells,'report',filename); + diff --git a/IQM Tools/IQMlite/auxiliary/graphics/ps/gs/fonts/EMPTY.txt b/IQM Tools/IQMlite/auxiliary/graphics/ps/gs/fonts/EMPTY.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/IQM Tools/IQMlite/auxiliary/graphics/ps/gs/lib/EMPTY.txt b/IQM Tools/IQMlite/auxiliary/graphics/ps/gs/lib/EMPTY.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/IQM Tools/IQMlite/auxiliary/output/IQMgetcolors.m b/IQM Tools/IQMlite/auxiliary/output/IQMgetcolors.m index c2923b4c9b516823c98f5dad3dee61483e113f55..c9e5d2051d705c244c85e8be576229b3e9c423be 100644 --- a/IQM Tools/IQMlite/auxiliary/output/IQMgetcolors.m +++ b/IQM Tools/IQMlite/auxiliary/output/IQMgetcolors.m @@ -86,7 +86,7 @@ colors = [ % 0.85 0.7 1 % 1 0.6 0.78]; -lines = {'o-','x-','+-','*-','s-','d-','v-','^-','<-','>-','p-','h-', 'o--','x--','+--','*--','s--','d--','v--','^--','<--','>--','p--','h--', 'o-.','x-.','+-.','*-.','s-.','d-.','v-.','^-.','<-.','>-.','p-.','h-.', 'o:','x:','+:','*:','s:','d:','v:','^:','<:','>:','p:','h:' }; +lines = {'-' '--' ':' '-.' 'o-','x-','+-','*-','s-','d-','v-','^-','<-','>-','p-','h-', 'o--','x--','+--','*--','s--','d--','v--','^--','<--','>--','p--','h--', 'o-.','x-.','+-.','*-.','s-.','d-.','v-.','^-.','<-.','>-.','p-.','h-.', 'o:','x:','+:','*:','s:','d:','v:','^:','<:','>:','p:','h:' }; dots = {'o','x','+','*','s','d','v','^','<','>','p','h'}; diff --git a/IQM Tools/IQMlite/auxiliary/usernameIQM.m b/IQM Tools/IQMlite/auxiliary/usernameIQM.m index fdc03d5e019c617e378aae9f97b376c2ed77a165..d1d87296b2180e6b80ae0cb81b9697dbbf7acde7 100644 --- a/IQM Tools/IQMlite/auxiliary/usernameIQM.m +++ b/IQM Tools/IQMlite/auxiliary/usernameIQM.m @@ -1,4 +1,4 @@ -function [username] = usernameIQM(text) +function [username] = usernameIQM() % usernameIQM: Get the name of the current user % <<<COPYRIGHTSTATEMENT - IQM TOOLS LITE>>> diff --git a/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextBCToModelIQM.m b/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextBCToModelIQM.m index 54955e19de5775ffea240e712b56a3b4bff050c2..e7f0b4a5f912030a05a8fbd64c6a71c385463d06 100644 --- a/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextBCToModelIQM.m +++ b/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextBCToModelIQM.m @@ -698,7 +698,7 @@ for k=1:length(stateConstraintInfo), highbound = stateConstraintInfo(k).highbound; ODE = stateConstraintInfo(k).ODE; % add a factor "constraints_factor_statename" to the ODE for the state - IQMstructure.states(k).ODE = ['constraints_factor_' statename ' * (' ODE ')']; + IQMstructure.states(stateindex).ODE = ['constraints_factor_' statename ' * (' ODE ')']; % define the "constraints_factor_statename" in the reactions IQMstructure.reactions(end+1).name = ['constraints_factor_' statename]; % construct the piecewise expression diff --git a/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextToModelIQM.m b/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextToModelIQM.m index 74dbccf81cbea321789a2a7389543bcc3bbad148..aa5dbe8bc52e35232bdfa6f5c60eb51e31bd9976 100644 --- a/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextToModelIQM.m +++ b/IQM Tools/IQMlite/classeshandling/modelhandling/auxiliary/text2IQMmodel/convertTextToModelIQM.m @@ -132,7 +132,7 @@ for k=1:length(stateConstraintInfo), highbound = stateConstraintInfo(k).highbound; ODE = stateConstraintInfo(k).ODE; % add a factor "constraints_factor_statename" to the ODE for the state - IQMstructure.states(k).ODE = ['constraints_factor_' statename ' * (' ODE ')']; + IQMstructure.states(stateindex).ODE = ['constraints_factor_' statename ' * (' ODE ')']; % define the "constraints_factor_statename" in the reactions IQMstructure.reactions(end+1).name = ['constraints_factor_' statename]; % construct the piecewise expression diff --git a/IQM Tools/IQMlite/release.txt b/IQM Tools/IQMlite/release.txt index 2fbaf0c5ef2f71678c6d0cf65d8a7c5cac1ccaf4..4144d8a335062e61f86d1351a4a7e3cbbaa8d779 100644 --- a/IQM Tools/IQMlite/release.txt +++ b/IQM Tools/IQMlite/release.txt @@ -1,6 +1,12 @@ Release Notes IQM Tools Lite ============================ +Version 1.2.2 (02.01.2017) +-------------------------- +- Minor big fixes +- Improved graphical output +- Survival analysis functions + Version 1.2.1 (30.04.2016) ------------------------ - Minor big fixes diff --git a/IQM Tools/IQMlite/tools/dataset/IQMloadCSVdataset.m b/IQM Tools/IQMlite/tools/dataset/IQMloadCSVdataset.m index 9bdc8430a41cdeab2bb4bb562fb54ec071a6d7ce..016ff07ee416ecaeb16aa3d7688f744481fcff92 100644 --- a/IQM Tools/IQMlite/tools/dataset/IQMloadCSVdataset.m +++ b/IQM Tools/IQMlite/tools/dataset/IQMloadCSVdataset.m @@ -190,11 +190,25 @@ if FLAG_HANDLE_SPECIAL_COLUMNS, end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - % NaN values in DURATION exchange to 0 + % If DATEDAY is of class datetime it should be converted to cellarray + % of strings %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% try - data.DURATION(isnan(data.DURATION)) = 0; + if strcmp(class(data.DATEDAY),'datetime'), + data.DATEDAY = cellstr(data.DATEDAY); + end + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % If VISNAME is numeric, convert to cell with empty + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + try + if isnumeric(data.VISNAME), + data.VISNAME = cell(height(data),1); + data.VISNAME(1:end) = {''}; + end end + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotCatCat.m b/IQM Tools/IQMlite/tools/plots/IQMplotCatCat.m index db88c468ef09a4cb3866f18482aa0fdb4a3515bc..7f35f3058d2da729bc2a0b99bf30d1d2db7601c8 100644 --- a/IQM Tools/IQMlite/tools/plots/IQMplotCatCat.m +++ b/IQM Tools/IQMlite/tools/plots/IQMplotCatCat.m @@ -1,10 +1,16 @@ -function [] = IQMplotCatCat(data,catNames) +function [] = IQMplotCatCat(data,catNames,compareNames,options) % This function plots a "bubble" plot for categorical data. This allows to % at least graphically explore correlations between different categorical % covariates. Areas of circles are proportional to the fraction. % +% There are 2 cases. Either all categorical covariates are compared to each +% other (only catNames provided). Or categorical covariates are compared to +% categorical covariates, defined by compareNames. +% % [SYNTAX] % [] = IQMplotCatCat(data,catNames) +% [] = IQMplotCatCat(data,catNames,compareNames) +% [] = IQMplotCatCat(data,catNames,compareNames,options) % % [INPUT] % data: Matlab dataset. Each column corresponds to a variable @@ -12,7 +18,21 @@ function [] = IQMplotCatCat(data,catNames) % defined in "catNames" need to be present in % the dataset and contain numerical categorical data. % catNames: Cell-array with names of categorical variables -% options: MATLAB structure with optional arguments +% compareNames: Cell-array with names of categorical variables to compare +% with the categorical covariates in catNames +% options: Matlab structure with additional options +% options.percent: Show percentages instead of numbers. Percentages +% are calculated relative to the number in the bins +% of the catNames and only displayed if compared to +% compareNames. =0 do show numbers and percent. =1 show +% percent (default) =2 show numbers and percent. +% options.bubbleSize: Factor to change size of bubbles (default: 500) +% options.fontSizeText: Fontsize for the number and percent text +% (default: 10) +% options.hideFirstRow: Hides first row with bars in case +% compareNames is used. =0: not hide (default), +% =1: hide (default) +% options.color: Color of bubbles default: 0.4*[1 1 1] % % [OUTPUT] % Plot @@ -24,74 +44,253 @@ if ~istable(data), error('Input data needs to be provided as MATLAB table.'); end -% Define plot color -color = 0.4*[1 1 1]; - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Check if dataset contains defined columns +% Check which case it is and handle each case completely separately %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -for k=1:length(catNames), - try - data.(catNames{k}); - catch - error(sprintf('Please check if "%s" is a column in the dataset!',catNames{k})); - end +if nargin==2, + compareNames = {}; + options = []; end - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Keep only selected columns -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -data = data(:,catNames); - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% If is table then convert to double -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -if istable(data), - data = table2array(data); +if nargin==3, + options = []; end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Keep only selected columns -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - -% % Default settings -% % Flag to take log of values -% LogFlag = 0; -% % Coloring -% CorrThres = 0.3; % Pearson correlation coefficient threshold for different background color -% AxisColor = [1 .2 .2]; %color of axis when -% -% % Get optional values if defined -% try names = OPTIONS.names; catch, end; -% try LogFlag = OPTIONS.LogFlag; catch, end; -% try CorrThres = OPTIONS.CorrThres; catch, end; -% try AxisColor = OPTIONS.AxisColor; catch, end; +try percentFlag = options.percent; catch, percentFlag = 1; end +try bubbleSize = options.bubbleSize; catch, bubbleSize = 500; end +try fontSizeText = options.fontSizeText; catch, fontSizeText = 10; end +try hideFirstRow = options.hideFirstRow; catch, hideFirstRow = 0; end +try color = options.color; catch, color = 0.4*[1 1 1]; end % Subindex properties -Spacing = 0; -Padding = 0; +Spacing = 0.0005; +Padding = 0.001; Margin = .1; % Define fontsize if length(catNames)<=6, FONTSIZE = 10; else - FONTSIZE = 8; + FONTSIZE = 10; end -clf; -n = length(catNames); -for ir=1:n - ystr = catNames{ir}; - y = data(:,ir); - for ic=1:ir - xstr = catNames{ic}; - x = data(:,ic); - ip = (ir-1)*n+ic; - subaxis(n,n,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); - if ir==ic % plot "histogram" - +if isempty(compareNames), + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Handle first case (compare all catNames with each other) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Check if dataset contains defined columns + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + for k=1:length(catNames), + try + data.(catNames{k}); + catch + error(sprintf('Please check if "%s" is a column in the dataset!',catNames{k})); + end + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Keep only selected columns + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + data = data(:,catNames); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % If is table then convert to double + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if istable(data), + data = table2array(data); + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Keep only selected columns + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + clf; + n = length(catNames); + for ir=1:n + ystr = catNames{ir}; + y = data(:,ir); + for ic=1:ir + xstr = catNames{ic}; + x = data(:,ic); + ip = (ir-1)*n+ic; + subaxis(n,n,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); + if ir==ic % plot "histogram" + + xu = unique(x); + xu = xu(~isnan(xu)); + b = zeros(size(xu)); + nu = length(xu); + for i=1:nu + b(i) = sum(x==xu(i)); + end + h = bar(1:nu,b,0.5); + + % Write out numbers + for knr=1:length(b), + aaa = get(gca,'YLim'); + text(knr,b(knr)+max(aaa)*0.02,sprintf('%d',b(knr)),'VerticalAlign','bottom','HorizontalAlign','center','FontSize',fontSizeText+2); + end + + set(h,'FaceColor',color) + set(gca,'XLim',[0.5 nu+.5]); + title(xstr,'Interpreter','none') + set(gca,'YTick',[]); + set(gca,'XTick',[]); + set(gca,'YLim',[0 max(b)*1.3]); + set(gca,'FontSize',fontSizeText); + + if ir==1, + ylabel('#','FontSize',8); + end + + if ir==n + xlabel(xstr,'Interpreter','none','FontSize',fontSizeText); + set(gca,'XTick',1:length(xu)) + set(gca,'XTickLabel',xu); + end + + else % plot bubble plot + % Exchange x categories for numbers 1:N + xu = unique(x); + iu = {}; + for k=1:length(xu), + iu{k} = find(x==xu(k)); + end + xn = NaN(size(x)); + for k=1:length(iu), + xn(iu{k}) = k; + end + + % Exchange y categories for numbers 1:N + yu = unique(y); + iu = {}; + for k=1:length(yu), + iu{k} = find(y==yu(k)); + end + yn = NaN(size(y)); + for k=1:length(iu), + yn(iu{k}) = k; + end + + % Find unique pairs of xn and yn (these will be plotted in the + % bubble plot + [pairs,~,b] = unique([xn yn],'rows'); + + % Determine number of rows in [xn yn] with these pairs + nr_pairs = NaN(size(pairs,1),1); + + for k=1:size(pairs,1), + nr_pairs(k) = sum(b==k); + end + + % Determine relative number of pairs in fraction + nr_pairs_fraction = nr_pairs/sum(nr_pairs); + + % Determine size based on fraction + use_size = bubbleSize*nr_pairs_fraction; + + % Determine default color + color_use = color(ones(1,length(nr_pairs)),:); + + % Plot + scatter(pairs(:,1),pairs(:,2), use_size, color_use, 'filled', 'MarkerEdgeColor', 'none'); + + % Annotate + set(gca,'XLim',[min(xn)-0.5 max(xn)+0.5]) + set(gca,'YLim',[min(yn)-0.5 max(yn)+0.5]) + + % Write out percentage as text + for knr=1:size(pairs,1), + text(pairs(knr,1)+0.025,pairs(knr,2)+0.2,sprintf('%d',nr_pairs(knr)),'FontSize',FONTSIZE); + end + + set(gca,'XTick',1:length(xu)) + set(gca,'YTick',1:length(yu)) + set(gca,'FontSize',fontSizeText); + + if ic==1 + ylabel(ystr,'Interpreter','none','FontSize',fontSizeText); + set(gca,'YTickLabel',yu); + else + set(gca,'YTickLabel',[]); + end + + if ir==n + xlabel(xstr,'Interpreter','none','FontSize',fontSizeText); + set(gca,'XTickLabel',xu); + else + set(gca,'XTickLabel',[]); + end + + grid on; + + end + end + end + +else + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Handle second case (compare all catNames with compareNames) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Check if dataset contains defined columns + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + for k=1:length(catNames), + try + data.(catNames{k}); + catch + error(sprintf('Please check if "%s" is a column in the dataset!',catNames{k})); + end + end + + for k=1:length(compareNames), + try + data.(compareNames{k}); + catch + error(sprintf('Please check if "%s" is a column in the dataset!',compareNames{k})); + end + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Keep only selected columns + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + dataCompare = data(:,compareNames); + data = data(:,catNames); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % If is table then convert to double + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if istable(data), + data = table2array(data); + end + if istable(dataCompare), + dataCompare = table2array(dataCompare); + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Options / fontsizes + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + clf; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % First row is frequencies of catNames + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if hideFirstRow==0, + ncols = length(catNames); + nrows = 1+length(compareNames); + for ir=1:ncols + ystr = catNames{ir}; + y = data(:,ir); + xstr = catNames{ir}; + x = data(:,ir); + ip = ir; + subaxis(nrows,ncols,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); + % Plot frequencies xu = unique(x); xu = xu(~isnan(xu)); b = zeros(size(xu)); @@ -100,24 +299,54 @@ for ir=1:n b(i) = sum(x==xu(i)); end h = bar(1:nu,b,0.5); - set(h,'FaceColor',0.4*[1 1 1]) + grid on + set(gca,'FontSize',fontSizeText); + + % Write out numbers + for knr=1:length(b), + aaa = get(gca,'YLim'); + text(knr,b(knr)+max(aaa)*0.02,sprintf('%d',b(knr)),'VerticalAlign','bottom','HorizontalAlign','center','FontSize',fontSizeText+2); + end + + set(h,'FaceColor',color) set(gca,'XLim',[0.5 nu+.5]); title(xstr,'Interpreter','none') - set(gca,'YTick',[]); +% set(gca,'YTick',[]); + if ir>1, + set(gca,'YTickLabel',''); + end set(gca,'XTick',[]); - set(gca,'YLim',[0 length(x)]); + set(gca,'YLim',[0 max(b)*1.3]); if ir==1, ylabel('#','FontSize',8); end + end + offset = 1; + else + ncols = length(catNames); + nrows = length(compareNames); + offset = 0; + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Next rows is correlation of catNames with compareNames + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Cycle through compareNames + for kcompare=1:length(compareNames), + ir = kcompare+offset; + ystr = compareNames{kcompare}; + y = dataCompare(:,kcompare); + + % Cycle through covNames + for kcov=1:length(catNames), + ic = kcov; + xstr = catNames{ic}; + x = data(:,ic); + ip = (ir-1)*ncols+ic; + subaxis(nrows,ncols,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); - if ir==n - xlabel(xstr,'Interpreter','none','FontSize',FONTSIZE); - set(gca,'XTick',1:length(xu)) - set(gca,'XTickLabel',xu); - end - - else % plot bubble plot + % plot bubble plot % Exchange x categories for numbers 1:N xu = unique(x); iu = {}; @@ -128,7 +357,7 @@ for ir=1:n for k=1:length(iu), xn(iu{k}) = k; end - + % Exchange y categories for numbers 1:N yu = unique(y); iu = {}; @@ -138,7 +367,7 @@ for ir=1:n yn = NaN(size(y)); for k=1:length(iu), yn(iu{k}) = k; - end + end % Find unique pairs of xn and yn (these will be plotted in the % bubble plot @@ -155,31 +384,38 @@ for ir=1:n nr_pairs_fraction = nr_pairs/sum(nr_pairs); % Determine size based on fraction - use_size = 500*nr_pairs_fraction; + use_size = bubbleSize*nr_pairs_fraction; % Determine default color color_use = color(ones(1,length(nr_pairs)),:); % Plot scatter(pairs(:,1),pairs(:,2), use_size, color_use, 'filled', 'MarkerEdgeColor', 'none'); - + % Annotate set(gca,'XLim',[min(xn)-0.5 max(xn)+0.5]) set(gca,'YLim',[min(yn)-0.5 max(yn)+0.5]) + % Write out numbers as text + for knr=1:size(pairs,1), + textShow = sprintf('%d',nr_pairs(knr)); + text(pairs(knr,1)+0.025,pairs(knr,2)+0.2,textShow,'FontSize',FONTSIZE); + end + set(gca,'XTick',1:length(xu)) - set(gca,'YTick',1:length(yu)) - + set(gca,'YTick',1:length(yu)) + set(gca,'FontSize',fontSizeText); + if ic==1 - ylabel(ystr,'Interpreter','none','FontSize',FONTSIZE); + ylabel(ystr,'Interpreter','none','FontSize',10); set(gca,'YTickLabel',yu); else set(gca,'YTickLabel',[]); - end - - if ir==n - xlabel(xstr,'Interpreter','none','FontSize',FONTSIZE); - set(gca,'XTickLabel',xu); + end + + if ir==nrows + xlabel(xstr,'Interpreter','none','FontSize',fontSizeText); + set(gca,'XTickLabel',xu); else set(gca,'XTickLabel',[]); end @@ -188,4 +424,21 @@ for ir=1:n end end + + % Adjust YLim on each row to same values + if length(catNames) > 1, + for krow=1:length(compareNames)+1, + YLimALL = []; + for kcol=1:length(catNames), + subaxis(length(compareNames)+1,length(catNames),(krow-1)*length(catNames)+kcol); + YLimALL = [YLimALL; get(gca,'YLim')]; + end + YLimMin = min(YLimALL); + YLimMax = max(YLimALL); + for kcol=1:length(catNames), + subaxis(length(compareNames)+1,length(catNames),(krow-1)*length(catNames)+kcol); + set(gca,'YLim',[YLimMin(1) YLimMax(2)]); + end + end + end end \ No newline at end of file diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotCovarianceCat.m b/IQM Tools/IQMlite/tools/plots/IQMplotCovarianceCat.m index 9d6207b393a5d5e3e91a1ee22203379ee941f996..b654e36c88015bba572f5ddf4d9b181325b3dcd0 100644 --- a/IQM Tools/IQMlite/tools/plots/IQMplotCovarianceCat.m +++ b/IQM Tools/IQMlite/tools/plots/IQMplotCovarianceCat.m @@ -1,7 +1,7 @@ function [] = IQMplotCovarianceCat(data,contNames,catNames,options) -% This function plots the covariance relationship between a list of +% This function plots the covariance relationship between a list of % continuous variables (contNames) and a list of categorical variables -% (catNames), passed in "data". +% (catNames), passed in "data". % % [SYNTAX] % [] = IQMplotCovarianceCat(data,contNames,catNames) @@ -11,13 +11,15 @@ function [] = IQMplotCovarianceCat(data,contNames,catNames,options) % data: Matlab dataset. Each column corresponds to a variable % and each row to a sample. The columns with the names % defined in "contNames" and "catNames" need to be present in -% the dataset. +% the dataset. % contNames: Cell-array with names of continuous variables % catNames: Cell-array with names of categorical variables % options: MATLAB structure with optional arguments % % options.LogFlag: =1 do log transform the variables, % =0 do not transform (default: 0) +% options.fontSizeText: Fontsize for the number and percent text +% (default: 10) % % [OUTPUT] % Plot @@ -26,21 +28,22 @@ function [] = IQMplotCovarianceCat(data,contNames,catNames,options) LogFlag = 0; try LogFlag = options.LogFlag; catch, end +try fontSizeText = options.fontSizeText; catch, fontSizeText = 10; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Check if dataset contains defined columns %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for k=1:length(contNames), - try - data.(contNames{k}); - catch, + try + data.(contNames{k}); + catch, error(sprintf('Please check if "%s" is a column in the dataset!',contNames{k})); end end for k=1:length(catNames), - try - data.(catNames{k}); - catch, + try + data.(catNames{k}); + catch, error(sprintf('Please check if "%s" is a column in the dataset!',catNames{k})); end end @@ -57,7 +60,7 @@ Margin = .1; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ncat = length(catNames); %rows are the categorical covariates ncts = length(contNames); -for icat=1:ncat +for icat=1:ncat xstr = catNames{icat}; x = data.(xstr); @@ -70,6 +73,7 @@ for icat=1:ncat ip=icat; subaxis(ncts+1,ncat,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); + grid on xu = unique(x); xu = xu(~isnan(xu)); @@ -79,17 +83,28 @@ for icat=1:ncat b(i) = sum(x==xu(i)); end h = bar(1:nu,b,0.5); - set(h,'FaceColor',0.4*[1 1 1]) + set(h,'FaceColor',0.6*[1 1 1]) set(gca,'XLim',[0.5 nu+.5]); + set(gca,'FontSize',fontSizeText); + + grid on + + % Write out numbers + for knr=1:length(b), + aaa = get(gca,'YLim'); + text(knr,b(knr)+max(aaa)*0.02,sprintf('%d',b(knr)),'VerticalAlign','bottom','HorizontalAlign','center','FontSize',fontSizeText+2); + end + title(xstr,'Interpreter','none') if icat==1 ylabel('#','Interpreter','none'); else - set(gca,'YTick',[]); + % set(gca,'YTick',[]); + set(gca,'YTickLabel',''); end set(gca,'XTick',[]); - set(gca,'YLim',[0 length(x)]); + set(gca,'YLim',[0 max(b)*1.3]); for icts=1:ncts ystr = contNames{icts}; @@ -112,7 +127,7 @@ for icat=1:ncat M(x==xx(i),i) = y(x==xx(i)); end OPTIONSbox.NumFlag = 0; - OPTIONSbox.BoxColor = 0.4*[1 1 1]; + OPTIONSbox.BoxColor = 0.6*[1 1 1]; OPTIONSbox.BoxWidth = .5; OPTIONSbox.MedianWidth = .7; OPTIONSbox.OutlierColor = 0.4*[1 1 1]; @@ -120,6 +135,8 @@ for icat=1:ncat OPTIONSbox.MedianColor = [0 0 0]; plotboxIQM(M,1:length(xx),OPTIONSbox); set(gca,'XLim',[.5 length(xx)+.5]); + grid on + set(gca,'FontSize',fontSizeText); set(gca,'XTick',1:length(xx)); if icts<ncts @@ -132,8 +149,11 @@ for icat=1:ncat end if icat==1 ylabel(ystr,'Interpreter','none'); + else + % set(gca,'YTick',[]); + set(gca,'YTickLabel',''); end - set(gca,'YTick',[]); + % set(gca,'YTick',[]); if min(y)~=max(y), set(gca,'YLim',[min(y) max(y)]); else @@ -141,6 +161,21 @@ for icat=1:ncat end end end - set(gca,'YTick',[]); + % set(gca,'YTick',[]); + end +end + +% Adjust YLim on each row to same values +for krow=1:ncts+1, + YLimALL = []; + for kcol=1:ncat, + subaxis(ncts+1,ncat,(krow-1)*ncat+kcol); + YLimALL = [YLimALL; get(gca,'YLim')]; end + YLimMin = min(YLimALL); + YLimMax = max(YLimALL); + for kcol=1:ncat, + subaxis(ncts+1,ncat,(krow-1)*ncat+kcol); + set(gca,'YLim',[min(YLimMin) max(YLimMax)]); + end end diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotKM.m b/IQM Tools/IQMlite/tools/plots/IQMplotKM.m new file mode 100644 index 0000000000000000000000000000000000000000..200b517a248d53976ccfad91737f7847eecf6709 --- /dev/null +++ b/IQM Tools/IQMlite/tools/plots/IQMplotKM.m @@ -0,0 +1,101 @@ +function [ST50_output_cellTable,ST50_output] = IQMplotKM(TIME,CENS,color,line,type,marksFlag,CIflag) +% Plots a Kaplan Meier curve. +% +% [SYNTAX] +% [ST50_output] = IQMplotKM(TIME,CENS) +% [ST50_output] = IQMplotKM(TIME,CENS,color) +% [ST50_output] = IQMplotKM(TIME,CENS,color,line) +% [ST50_output] = IQMplotKM(TIME,CENS,color,line,type) +% [ST50_output] = IQMplotKM(TIME,CENS,color,line,type,marksFlag) +% [ST50_output] = IQMplotKM(TIME,CENS,color,line,type,marksFlag,CIflag) +% +% [INPUT] +% TIME: vector with time information for time to event +% CENS: vector with censoring information (0: uncensored, 1: right censored) +% color: [r g b] color default: black +% line: Line style (default: "-") +% type: "Survivor" (default) or "cumulative hazard" or "cdf" +% marksFlag: =1: censoring marks are plotted (default). =0 not plotted +% CIflag: =1: plots 95% CI. =0 not plotted (default) +% +% [OUTPUT] +% ST50_output: [50% survival time, 95% CI - lower and upper bound] + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS LITE>>> + +if nargin<3, + color = [0 0 0]; +end +if nargin<4, + line = '-'; +end +if nargin<5, + type = 'survivor'; +end +if nargin<6, + marksFlag = 1; +end +if nargin<7, + CIflag = 0; +end + +% Remove NaN values from TIME and CENS (paired) +ixNAN = find(isnan(TIME)); +TIME(ixNAN) = []; +CENS(ixNAN) = []; + +% Plot KM curve +[f,x,flo,fup] = ecdf(TIME,'censoring',CENS,'function',type); +stairs(x,f,line,'LineWidth',1,'Color',color,'LineWidth',2); hold on; + +% Plot marks for censored data +if marksFlag, + xi = TIME(logical(CENS)); + [~,~,step] = histcounts(xi,x); + step(xi>max(TIME)) = length(x); + ixstep0 = find(step==0); + xi(ixstep0) = []; + step(ixstep0) = []; + fi = f(step); + plot(xi,fi, 's','MarkerEdgeColor','white','MarkerFaceColor','white','MarkerSize',8) + plot(xi,fi, 'b+','color',color,'MarkerSize',8,'LineWidth',1) +end + +% Plot 95% confidence interval +if CIflag, + IQMplotfill(x,flo,fup,color,0.1); hold on +end + +% Annotate +grid on +set(gca,'FontSize',12); +xlabel('Time','FontSize',14); +ylabel('Probability','FontSize',14); +set(gca,'YLim',[-0.05 1.05]); + +% Determine 50% survival time and 95% CI +ix = find(f-0.5<0); +if isempty(ix), + ST50 = NaN; +else + ST50 = x(ix(1)); +end + +ix = find(flo-0.5<0); +if isempty(ix), + ST50_CI_lo = NaN; +else + ST50_CI_lo = x(ix(1)); +end + +ix = find(fup-0.5<0); +if isempty(ix), + ST50_CI_up = NaN; +else + ST50_CI_up = x(ix(1)); +end + +ST50_output = [ST50, ST50_CI_lo, ST50_CI_up]; + +ST50_output_cellTable(1,1:3) = {'<TH>' '50% Survival Time' '95% CI'}; +ST50_output_cellTable(2,1:3) = {'<TR>' sprintf('%1.3g',ST50) sprintf('[%1.3g, %1.3g]',ST50_CI_lo,ST50_CI_up)}; diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotPercentStratified.m b/IQM Tools/IQMlite/tools/plots/IQMplotPercentStratified.m new file mode 100644 index 0000000000000000000000000000000000000000..4e51adcc17ad93076c9e98b7fc2c2f3b28d4cd3b --- /dev/null +++ b/IQM Tools/IQMlite/tools/plots/IQMplotPercentStratified.m @@ -0,0 +1,200 @@ +function [yn_all_percent] = IQMplotPercentStratified(data,catNames,compareNames,options) +% This function plots column plots / stacked bar plots for categorical outcomes (percent +% of occurrence) stratified by categorical compare variables in +% compareNames. +% +% Works for one catName and one compareName for now. +% +% [SYNTAX] +% [] = IQMplotPercentStratified(data,catNames,compareNames) +% +% [INPUT] +% data: Matlab dataset. Each column corresponds to a variable +% and each row to a sample. The columns with the names +% defined in "catNames" need to be present in +% the dataset and contain numerical categorical data. +% catNames: Cell-array with names of categorical variables +% compareNames: Cell-array with names of categorical variables to compare +% with the categorical covariates in catNames +% options: Matlab structure with additional options +% options.percent: Show percentages instead of numbers. Percentages +% are calculated relative to the number in the bins +% of the catNames and only displayed if compared to +% compareNames. =0 do show numbers and percent. =1 show +% percent (default) =2 show numbers and percent. +% options.fontSizeText: Fontsize for the number and percent text +% (default: 10) +% options.color: Color of bubbles default: 0.4*[1 1 1] +% +% [OUTPUT] +% Plot + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS LITE>>> + +% Check if data is table +if ~istable(data), + error('Input data needs to be provided as MATLAB table.'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Check which case it is and handle each case completely separately +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if nargin==3, + options = []; +end + +try fontSizeText = options.fontSizeText; catch, fontSizeText = 10; end +try hideFirstRow = options.hideFirstRow; catch, hideFirstRow = 0; end +try color = options.color; catch, color = 0.4*[1 1 1]; end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Check if dataset contains defined columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +for k=1:length(catNames), + try + data.(catNames{k}); + catch + error(sprintf('Please check if "%s" is a column in the dataset!',catNames{k})); + end +end + +for k=1:length(compareNames), + try + data.(compareNames{k}); + catch + error(sprintf('Please check if "%s" is a column in the dataset!',compareNames{k})); + end +end + +if length(catNames)>1 || length(compareNames)>1, + error('Only single entry in catNames and compareNames allowed.'); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Generate the plot +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Keep only selected columns +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +dataCompare = data(:,compareNames); +data = data(:,catNames); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% If is table then convert to double +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +if istable(data), + data = table2array(data); +end +if istable(dataCompare), + dataCompare = table2array(dataCompare); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Options / fontsizes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Subindex properties +Spacing = 0.00; +Padding = 0.0005; +Margin = .1; + +% Define fontsize +if length(catNames)<=6, + FONTSIZE = 10; +else + FONTSIZE = 8; +end + +% Define ncols and +ncols = length(catNames); +nrows = length(compareNames); + +% Cycle through compareNames +for kcompare=1:length(compareNames), + ir = kcompare; + ystr = compareNames{kcompare}; + y = dataCompare(:,kcompare); + + % Cycle through covNames + for kcov=1:length(catNames), + ic = kcov; + xstr = catNames{ic}; + x = data(:,ic); + ip = (ir-1)*ncols+ic; +% subaxis(nrows,ncols,ip,'Spacing',Spacing,'Padding',Padding,'Margin',Margin); + + % Exchange x categories for numbers 1:N + xu = unique(x); + % Find positions where the unique items are listed (to obtain + % number 1...N) ... stored in xn + iu = {}; + for k=1:length(xu), + iu{k} = find(x==xu(k)); + end + xn = NaN(size(x)); + for k=1:length(iu), + xn(iu{k}) = k; + end + + % Exchange y categories for numbers 1:N + yu = unique(y); + iu = {}; + for k=1:length(yu), + iu{k} = find(y==yu(k)); + end + yn = NaN(size(y)); + for k=1:length(iu), + yn(iu{k}) = k; + end + + % Find numbers of unique yn in unique xn + yn_all_percent = []; + for k=1:max(xn), + % Get number of xn values in bin k + NR_xnk = length(xn(xn==k)); + % Get number of different yn values in bin k + yn_all = []; + for k2=1:max(yn), + ynk = sum(xn==k & yn==k2); + yn_all(k2) = ynk; + end + % Determine percent + yn_all_percent = [yn_all_percent; 100*yn_all/NR_xnk]; + end + + % Plot + hp = bar(1:max(xn),yn_all_percent,'stacked'); + +% hatch_combinations = { +% 'fill' 0 +% 'single' 0 +% 'single' 45 +% 'single' 90 +% 'cross' 0 +% 'cross' 45 +% }; +% +% for k=1:min(length(hp),6), +% hatchfill2(hp(k),hatch_combinations{k,1},'HatchAngle',hatch_combinations{k,2}); +% end + + % Annotate + if ic==1 + ylabel('Percentage of x-bin','Interpreter','none','FontSize',12); + else + set(gca,'YTickLabel',[]); + end + + if ir==nrows + xlabel(xstr,'Interpreter','none','FontSize',FONTSIZE); + set(gca,'XTickLabel',xu); + else + set(gca,'XTickLabel',[]); + end + + set(gca,'YLim',[0 100]); + grid on; + + end +end diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotfacetgrid.m b/IQM Tools/IQMlite/tools/plots/IQMplotfacetgrid.m index 4604d19843cea18e9db351e605a45d0a0ab1443f..0c5a8837b1b0ce705a2df0777559093d82075143 100644 --- a/IQM Tools/IQMlite/tools/plots/IQMplotfacetgrid.m +++ b/IQM Tools/IQMlite/tools/plots/IQMplotfacetgrid.m @@ -230,7 +230,7 @@ for kX=1:length(allGroupX), if dataXYC.colorgroup(1) == -1, datatemp.color = -1*ones(height(datatemp),1); else - datatemp.color = find(allGROUPcolor==dataXYC.colorgroup(1))*ones(length(datatemp),1); + datatemp.color = find(allGROUPcolor==dataXYC.colorgroup(1))*ones(height(datatemp),1); end else datatemp.color = find(strcmp(allGROUPcolor,dataXYC.colorgroup{1}))*ones(height(datatemp),1); diff --git a/IQM Tools/IQMlite/tools/plots/IQMplotpairwiseCorr.m b/IQM Tools/IQMlite/tools/plots/IQMplotpairwiseCorr.m index 17eb0198510763e47dafe87861f5dbcf34da367f..ac2f85b795f90949c21a00a1ac4ec60665d18f3b 100644 --- a/IQM Tools/IQMlite/tools/plots/IQMplotpairwiseCorr.m +++ b/IQM Tools/IQMlite/tools/plots/IQMplotpairwiseCorr.m @@ -49,7 +49,7 @@ end LogFlag = 0; % Coloring CorrThres = 0.3; % Pearson correlation coefficient threshold for different background color -AxisColor = [1 .2 .2]; %color of axis when +AxisColor = [1 .4 .4]; %color of axis when % Get optional values if defined try names = OPTIONS.names; catch, end; @@ -111,7 +111,7 @@ for ir=1:n if abs(rho)>CorrThres optcorr.Color = AxisColor; else - optcorr.Color = 0.4*[1 1 1]; + optcorr.Color = 0.6*[1 1 1]; end optcorr.LineColor = [0 0 0]; optcorr.TitleType = 'none'; @@ -131,7 +131,7 @@ for ir=1:n else str = '|corr|<0.01'; end - text(xt,yt,str,'Color',[0 0 0],'Hor','Center','Ver','Middle','FontWeight','Bold','Interpreter','none'); + text(xt,yt,str,'Color',[0 0 0],'Hor','Right','Ver','Middle','FontWeight','Bold','Interpreter','none'); if min(xuse)~=max(xuse), set(gca,'XLim',[min(xuse) max(xuse)]); diff --git a/IQM Tools/IQMlite/tools/plots/auxiliary/plotgeneralIQM.m b/IQM Tools/IQMlite/tools/plots/auxiliary/plotgeneralIQM.m index 191c97d48ba527e1c6d2d7e35dee8084256ac837..1d65309da87393609674fee1f15bee2d84fdedbe 100644 --- a/IQM Tools/IQMlite/tools/plots/auxiliary/plotgeneralIQM.m +++ b/IQM Tools/IQMlite/tools/plots/auxiliary/plotgeneralIQM.m @@ -363,13 +363,13 @@ if showloessline, % Generate loess smoothed line if logX==0 && logY==0, - Ysmooth = smoothIQM(Xdata,Ydata,0.1,'loess'); + Ysmooth = smoothIQM(Xdata,Ydata,0.5,'loess'); elseif logX==1 && logY==0, - Ysmooth = smoothIQM(log(Xdata),Ydata,0.1,'loess'); + Ysmooth = smoothIQM(log(Xdata),Ydata,0.5,'loess'); elseif logX==0 && logY==1, - Ysmooth = exp(smoothIQM(Xdata,log(Ydata),0.1,'loess')); + Ysmooth = exp(smoothIQM(Xdata,log(Ydata),0.5,'loess')); else - Ysmooth = exp(smoothIQM(log(Xdata),log(Ydata),0.1,'loess')); + Ysmooth = exp(smoothIQM(log(Xdata),log(Ydata),0.5,'loess')); end % Handle color, etc. if datakkk.color(1) == -1, diff --git a/IQM Tools/IQMlite/tools/survival/IQMcoxExt.m b/IQM Tools/IQMlite/tools/survival/IQMcoxExt.m new file mode 100644 index 0000000000000000000000000000000000000000..b7b77c491cd9302c405362f0b813cfa2e2fa53a0 --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMcoxExt.m @@ -0,0 +1,202 @@ +function [stats,st] = IQMcoxExt(sID_ext,X_ext,d_ext,t_ext,e) +% [stats,st] = IQMcoxExt(sID_ext,X_ext,d_ext,t_ext,e) +% +% Parameter estimation for the extended Cox model. This function uses as +% input the output of IQMxext but with X_ext having new time-dependent +% columns added by the user (eg. the product of a column of X_ext with +% t_ext. Survival time ties are handled using Efron's method. +% +% Input +% sID_ext: Extended subjects' IDs (nx1). +% X_ext: Extended design matrix (nxp). +% d_ext: Extended censorship status vector (nx1). +% t_ext: Extended survival time vector (nx1). +% e: Convergence epsilon (gradient's norm). Default 10^-3; +% +% Output +% stats.Bhat: Estimated vector of the population regression parameters. +% stats.CovBhat: Estimated covariance matrix of the population regression +% parameters. +% stats.llh: Values of the maximum log-likelihood across the optimization +% process. +% st: Termination state (1 for convergence and 0 otherwise). +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 4 + error('Too few inputs'); +elseif nargin < 5 + e = 0.001; +end; +tic; +[n,p] = size(X_ext); +if (length(sID_ext)~=n) || (length(d_ext)~=n) || (length(t_ext)~=n) + error(['The design matrix X_ext, censorship status vector d_ext, time'... + ' vector t_ext and subject ID vector sID_ext must all have the same'... + ' number of rows.']); +end; +%indices of unique failure times in ft_ix (last index when ties happen) +st_ix = find(d_ext==1); +[~,ft_ix] = unique(t_ext(st_ix),'last'); +ft_ix = st_ix(ft_ix); +%Starting values +Bhat = zeros(p,1); + +%% Iterations +nit = 50; +gnorm = e+1; +it = 1; +display('Starting Newton-Raphson iterations'); +while (gnorm>e) && (it<=nit) + gr = SStat_Gradient(X_ext,t_ext,Bhat,ft_ix); + He = SStat_Hessian(X_ext,t_ext,Bhat,ft_ix); + if (cond(He) < 1e+10) + invHe = He\eye(p); + else + [Vtemp,Dtemp] = eig(He); + invHe = Vtemp*diag(1./max(diag(Dtemp),1e-5))*Vtemp'; + end + Bhat = Bhat - invHe*gr; + %log-likelihood + llh = SStat_Likelihood(X_ext,t_ext,Bhat,ft_ix); + display(['Likelihood at iteration ' num2str(it) ' : ' num2str(llh)]); + gnorm = norm(gr); + display(['Gradient norm: ' num2str(gnorm)]); + it = it+1; +end; +%% Termination +z_sc = Bhat./sqrt(diag(-invHe)); +pval = 2*(1-normcdf(abs(z_sc),0,1)); +stats = struct('Bhat',Bhat,'zscore',z_sc,'pval',pval,'CovBhat',-invHe,'llh',llh); +if (gnorm<=e) + st = 1; +else + st = 0; + display(['Algorithm does not converge after ' num2str(nit)... + ' iterations!!!']); +end; +et = toc; +display(['Total elapsed time is ' num2str(et) ' seconds']); +end + + + + + + +%% Likelihood, Gradient and Hessian + +function llk = SStat_Likelihood(X_ext,t_ext,Bhat,ft_ix) +% +% Log-likelihood value. +% +% Input +% X_ext: Extended design matrix. +% t_ext: Extended survival time vector. +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X_ext (last index if any tie). +% +% Output +% llk: Log-likelihood value. +% +llk = 0; +nft = length(ft_ix); +for j=1:nft + term = 0; + ties = ft_ix(t_ext(ft_ix(j))==t_ext(ft_ix)); + nties = length(ties)-1; + lpr = X_ext(ties,:)*Bhat; + aux1 = sum(exp(lpr)); + aux2 = sum(exp(X_ext(t_ext(ft_ix(j))==t_ext,:)*Bhat)); + for l=0:nties + term = term + log(aux2-l*aux1/(nties+1)); + end; + llk = llk + sum(lpr) - term; +end; +end + + +function gr = SStat_Gradient(X_ext,t_ext,Bhat,ft_ix) +% +% Gradient vector for the log-likelihood. +% +% Input +% X_ext: Extended design matrix. +% t_ext: Extended survival time vector. +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X_ext (last index if any tie). +% +% Output +% gr: Gradient vector. +% +p = size(X_ext,2); +gr = zeros(p,1); +nft = length(ft_ix); +for j=1:nft + ties = ft_ix(t_ext(ft_ix(j))==t_ext(ft_ix)); + nties = length(ties)-1; + riskset = t_ext(ft_ix(j))==t_ext; + term1 = sum(X_ext(ties,:),1); + term2 = exp(X_ext(riskset,:)*Bhat); + term3 = exp(X_ext(ties,:)*Bhat); + term4 = term2'*X_ext(riskset,:); + term5 = term3'*X_ext(ties,:); + term = 0; + for l=0:nties + term = term + (term4-l*term5/(nties+1))/(sum(term2)-l*sum(term3)/(nties+1)); + end; + gr = gr + (term1 - term)'; +end; +end + + +function He = SStat_Hessian(X_ext,t_ext,Bhat,ft_ix) +% +% Hessian matrix for the log-likelihood. +% +% Input +% X_ext: Extended design matrix. +% t_ext: Extended survival time vector. +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X_ext (last index if any tie). +% +% Output +% He: Hessian matrix. +% +p = size(X_ext,2); +He = zeros(p,p); +nft = length(ft_ix); +for j=1:nft + ties = ft_ix(t_ext(ft_ix(j))==t_ext(ft_ix)); + nties = length(ties)-1; + rsk_ix = find(t_ext(ft_ix(j))==t_ext); + m = length(rsk_ix); + term1 = 0; + for i=1:m + term1 = term1 + exp(X_ext(rsk_ix(i),:)*Bhat)*X_ext(rsk_ix(i),:)'*X_ext(rsk_ix(i),:); + end; + term2 = 0; + for i=1:nties + term2 = term2 + exp(X_ext(ties(i),:)*Bhat)*X_ext(ties(i),:)'*X_ext(ties(i),:); + end; + term3 = sum(exp(X_ext(rsk_ix,:)*Bhat)); + term4 = sum(exp(X_ext(ties,:)*Bhat)); + term5 = exp(X_ext(rsk_ix,:)*Bhat)'*X_ext(rsk_ix,:); + term6 = exp(X_ext(ties,:)*Bhat)'*X_ext(ties,:); + term = 0; + for l=0:nties + Z = term5 - l*term6/(nties+1); + phi = term3 - l*term4/(nties+1); + term = term + (term1-l*term2/(nties+1))/phi - Z'*Z/(phi*phi); + end; + He = He - term; +end; +end + diff --git a/IQM Tools/IQMlite/tools/survival/IQMcoxPH.m b/IQM Tools/IQMlite/tools/survival/IQMcoxPH.m new file mode 100644 index 0000000000000000000000000000000000000000..84cd86923744232a0917ceedd5b43cc1bc3f5913 --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMcoxPH.m @@ -0,0 +1,241 @@ +function [stats,st] = IQMcoxPH(X,d,t,e) +% [stats,st] = IQMcoxPH(X,d,t,e) +% +% Parameter estimation for the Cox proportional hazards model. Survival +% time ties are handled using Efron's method. +% +% Input +% X: Design Matrix with the time-independent covariates. (mxp, m # of +% subjects, p # of covariates). +% d: Logical vector (mx1) indicating censorship status (1 if the subject got +% the failure event or 0 otherwise). +% t: Vector (mx1) whose entries are the survival and censored times (ordered +% according to X). +% e: Convergence epsilon (gradient's norm). Default 10^-3; +% +% Output +% stats.Bhat: Estimated vector of the population regression parameters. +% stats.CovBhat: Estimated covariance matrix of the population regression +% parameters. +% stats.llh: Values of the maximum log-likelihood across the optimization +% process. +% st: Termination state (1 for convergence and 0 otherwise). +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 3 + error('Too few inputs'); +elseif nargin < 4 + e = 0.001; +end; +tic; +[m,p] = size(X); +if (length(d)~=m) || (length(t)~=m) + error(['All, the design matrix X, censorship status vector d and'... + ' time vector t must have the same number of rows.']); +end; +%Sort the data by time. If there is a tie between a failure time and a +%censored time then the failure time goes first. +st_ix = find(d==1); +t1 = t(st_ix); +[t1,t1_ix] = sort(t1); +X1 = X(st_ix(t1_ix),:); +cs_ix = find(d==0); +if ~isempty(cs_ix) + t2 = t(cs_ix); + [t2,t2_ix] = sort(t2); + X2 = X(cs_ix(t2_ix),:); + count1 = 1; count2 = 1; i = 0; + while (count1 <= length(t1)) && (count2 <= length(t2)) + i = i + 1; + if t1(count1) <= t2(count2) + X(i,:) = X1(count1,:); + d(i) = 1; + t(i) = t1(count1); + count1 = count1 + 1; + else + X(i,:) = X2(count2,:); + d(i) = 0; + t(i) = t2(count2); + count2 = count2 + 1; + end; + end; + if (count1 > length(t1)) + X(i+1:end,:) = X2(count2:end,:); + d(i+1:end) = 0; + t(i+1:end) = t2(count2:end); + else + X(i+1:end,:) = X1(count1:end,:); + d(i+1:end) = 1; + t(i+1:end) = t1(count1:end); + end; +else + X = X1; + t = t1; +end; +%indices of unique failure times in ft_ix (last index when ties happen) +st_ix = find(d==1); +[ft,ft_ix] = unique(t(st_ix),'last'); +ft_ix = st_ix(ft_ix); +nft = length(ft); +%handling ties in failure times +nties = zeros(1,nft); +for j=1:nft + i = 1; + while (ft_ix(j)-i>0) && (ft(j)==t(ft_ix(j)-i)) + i = i + 1; + end; + nties(j) = i-1; +end; +%Starting values +Bhat = zeros(p,1); + +%% Iterations +nit = 50; +gnorm = e+1; +it = 1; +display('Starting Newton-Raphson iterations'); +while (gnorm>e) && (it<=nit) + gr = SStat_Gradient(X,Bhat,ft_ix,nties); + He = SStat_Hessian(X,Bhat,ft_ix,nties); + if (cond(He) < 1e+10) + invHe = He\eye(p); + else + [Vtemp,Dtemp] = eig(He); + invHe = Vtemp*diag(1./max(diag(Dtemp),1e-5))*Vtemp'; + end + Bhat = Bhat - invHe*gr; + %log-likelihood + llh = SStat_Likelihood(X,Bhat,ft_ix,nties); + display(['Likelihood at iteration ' num2str(it) ' : ' num2str(llh)]); + gnorm = norm(gr); + display(['Gradient norm: ' num2str(gnorm)]); + it = it+1; +end; +%% Termination +z_sc = Bhat./sqrt(diag(-invHe)); +pval = 2*(1-normcdf(abs(z_sc),0,1)); +stats = struct('Bhat',Bhat,'zscore',z_sc,'pval',pval,'CovBhat',-invHe,'llh',llh); +if (gnorm<=e) + st = 1; +else + st = 0; + display(['Algorithm does not converge after ' num2str(nit)... + ' iterations!!!']); +end; +et = toc; +display(['Total elapsed time is ' num2str(et) ' seconds']); +end + + + + + + +%% Likelihood, Gradient and Hessian + +function llk = SStat_Likelihood(X,Bhat,ft_ix,nties) +% +% Log-likelihood value. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% llk: Log-likelihood value. +% +llk = 0; +nft = length(ft_ix); +for j=1:nft + term = 0; + lpr = X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat; + aux1 = sum(exp(lpr)); + aux2 = sum(exp(X(ft_ix(j)-nties(j):end,:)*Bhat)); + for l=0:nties(j) + term = term + log(aux2-l*aux1/(nties(j)+1)); + end; + llk = llk + sum(lpr) - term; +end; +end + + +function gr = SStat_Gradient(X,Bhat,ft_ix,nties) +% +% Gradient vector for the log-likelihood. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% gr: Gradient vector. +% +p = size(X,2); +gr = zeros(p,1); +nft = length(ft_ix); +for j=1:nft + term1 = sum(X(ft_ix(j)-nties(j):ft_ix(j),:),1); + term2 = exp(X(ft_ix(j)-nties(j):end,:)*Bhat); + term3 = exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat); + term4 = term2'*X(ft_ix(j)-nties(j):end,:); + term5 = term3'*X(ft_ix(j)-nties(j):ft_ix(j),:); + term = 0; + for l=0:nties(j) + term = term + (term4-l*term5/(nties(j)+1))/(sum(term2)-l*sum(term3)/(nties(j)+1)); + end; + gr = gr + (term1 - term)'; +end; +end + + +function He = SStat_Hessian(X,Bhat,ft_ix,nties) +% +% Hessian matrix for the log-likelihood. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% He: Hessian matrix. +% +[m,p] = size(X); +He = zeros(p,p); +nft = length(ft_ix); +for j=1:nft + term1 = 0; + for i=ft_ix(j)-nties(j):m + term1 = term1 + exp(X(i,:)*Bhat)*X(i,:)'*X(i,:); + end; + term2 = 0; + for i=ft_ix(j)-nties(j):ft_ix(j) + term2 = term2 + exp(X(i,:)*Bhat)*X(i,:)'*X(i,:); + end; + term3 = sum(exp(X(ft_ix(j)-nties(j):end,:)*Bhat)); + term4 = sum(exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat)); + term5 = exp(X(ft_ix(j)-nties(j):m,:)*Bhat)'*X(ft_ix(j)-nties(j):m,:); + term6 = exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat)'*X(ft_ix(j)-nties(j):ft_ix(j),:); + term = 0; + for l=0:nties(j) + Z = term5 - l*term6/(nties(j)+1); + phi = term3 - l*term4/(nties(j)+1); + term = term + (term1-l*term2/(nties(j)+1))/phi - Z'*Z/(phi*phi); + end; + He = He - term; +end; +end + diff --git a/IQM Tools/IQMlite/tools/survival/IQMcoxStratPH.m b/IQM Tools/IQMlite/tools/survival/IQMcoxStratPH.m new file mode 100644 index 0000000000000000000000000000000000000000..c63902d42e0f9ff3d612441e65ff075e8bbe8dd3 --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMcoxStratPH.m @@ -0,0 +1,265 @@ +function [stats,st] = IQMcoxStratPH(X,z,d,t,e) +% [stats,st] = IQMcoxPH(X,z,d,t,e) +% +% Parameter estimation for the Stratified Cox proportional hazards model. +% Survival time ties are handled using Efron's method. +% +% Input +% X: Design Matrix with the time-independent covariates. (mxp, m # of +% subjects, p # of covariates). +% z: Vector (mx1) with the strata for the rows of X; +% d: Logical vector (mx1) indicating censorship status (1 if the subject got +% the failure event or 0 otherwise). +% t: Vector (mx1) whose entries are the survival and censored times (ordered +% according to X). +% e: Convergence epsilon (gradient's norm). Default 10^-3; +% +% Output +% stats.Bhat: Estimated vector of the population regression parameters. +% stats.CovBhat: Estimated covariance matrix of the population regression +% parameters. +% stats.llh: Values of the maximum log-likelihood across the optimization +% process. +% st: Termination state (1 for convergence and 0 otherwise). +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 4 + error('Too few inputs'); +elseif nargin < 5 + e = 0.001; +end; +tic; +[m,p] = size(X); +if (length(z)~=m) || (length(d)~=m) || (length(t)~=m) + error(['The design matrix X, strata vector z, censorship status vector d'... + ' and time vector t must all have the same number of rows.']); +end; +%Sort the data by time. If there is a tie between a failure time and a +%censored time then the failure time goes first. +st_ix = find(d==1); +t1 = t(st_ix); +[t1,t1_ix] = sort(t1); +X1 = X(st_ix(t1_ix),:); +z1 = z(st_ix(t1_ix)); +cs_ix = find(d==0); +if ~isempty(cs_ix) + t2 = t(cs_ix); + [t2,t2_ix] = sort(t2); + X2 = X(cs_ix(t2_ix),:); + z2 = z(cs_ix(t2_ix)); + count1 = 1; count2 = 1; i = 0; + while (count1 <= length(t1)) && (count2 <= length(t2)) + i = i + 1; + if t1(count1) <= t2(count2) + X(i,:) = X1(count1,:); + z(i) = z1(count1); + d(i) = 1; + t(i) = t1(count1); + count1 = count1 + 1; + else + X(i,:) = X2(count2,:); + z(i) = z2(count2); + d(i) = 0; + t(i) = t2(count2); + count2 = count2 + 1; + end; + end; + if (count1 > length(t1)) + X(i+1:end,:) = X2(count2:end,:); + z(i+1:end) = z2(count2:end); + d(i+1:end) = 0; + t(i+1:end) = t2(count2:end); + else + X(i+1:end,:) = X1(count1:end,:); + z(i+1:end) = z1(count1:end); + d(i+1:end) = 1; + t(i+1:end) = t1(count1:end); + end; +else + X = X1; + t = t1; + z = z1; +end; +uz = unique(z); +nst = length(uz); +%indices of unique failure times for each stratum in ft_ix (last index when ties happen) +ft_ix = zeros(nst,sum(d)); +ft = ft_ix; +nft = zeros(nst,1); +nties = ft; +for k=1:nst + t_strat = t(z==uz(k)); + st_ix = find(d(z==uz(k))==1); + [ft_aux,ft_ix_aux] = unique(t_strat(st_ix),'last'); + nft(k) = length(ft_aux); + ft_ix(k,1:nft(k)) = st_ix(ft_ix_aux); + ft(k,1:nft(k)) = ft_aux; + %handling ties in failure times + for j=1:nft(k) + i = 1; + while (ft_ix(k,j)-i>0) && (ft(k,j)==t(ft_ix(k,j)-i)) + i = i + 1; + end; + nties(k,j) = i-1; + end; +end; +%Starting values +Bhat = zeros(p,1); + +%% Iterations +nit = 50; +gnorm = e+1; +it = 1; +display('Starting Newton-Raphson iterations'); +while (gnorm>e) && (it<=nit) + gr = zeros(p,1); + He = zeros(p,p); + for k=1:length(uz) + gr = gr + SStat_Gradient(X(z==uz(k),:),Bhat,ft_ix(k,1:nft(k)),nties(k,1:nft(k))); + He = He + SStat_Hessian(X(z==uz(k),:),Bhat,ft_ix(k,1:nft(k)),nties(k,1:nft(k))); + end; + if (cond(He) < 1e+10) + invHe = He\eye(p); + else + [Vtemp,Dtemp] = eig(He); + invHe = Vtemp*diag(1./max(diag(Dtemp),1e-5))*Vtemp'; + end + Bhat = Bhat - invHe*gr; + %log-likelihood + llh = 0; + for k=1:length(uz) + llh = llh + SStat_Likelihood(X(z==uz(k),:),Bhat,ft_ix(k,1:nft(k)),nties(k,1:nft(k))); + end; + display(['Likelihood at iteration ' num2str(it) ' : ' num2str(llh)]); + gnorm = norm(gr); + display(['Gradient norm: ' num2str(gnorm)]); + it = it+1; +end; +%% Termination +z_sc = Bhat./sqrt(diag(-invHe)); +pval = 2*(1-normcdf(abs(z_sc),0,1)); +stats = struct('Bhat',Bhat,'zscore',z_sc,'pval',pval,'CovBhat',-invHe,'llh',llh); +if (gnorm<=e) + st = 1; +else + st = 0; + display(['Algorithm does not converge after ' num2str(nit)... + ' iterations!!!']); +end; +et = toc; +display(['Total elapsed time is ' num2str(et) ' seconds']); +end + + + + + + +%% Likelihood, Gradient and Hessian + +function llk = SStat_Likelihood(X,Bhat,ft_ix,nties) +% +% Log-likelihood value. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% llk: Log-likelihood value. +% +llk = 0; +nft = length(ft_ix); +for j=1:nft + term = 0; + lpr = X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat; + aux1 = sum(exp(lpr)); + aux2 = sum(exp(X(ft_ix(j)-nties(j):end,:)*Bhat)); + for l=0:nties(j) + term = term + log(aux2-l*aux1/(nties(j)+1)); + end; + llk = llk + sum(lpr) - term; +end; +end + + +function gr = SStat_Gradient(X,Bhat,ft_ix,nties) +% +% Gradient vector for the log-likelihood. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% gr: Gradient vector. +% +p = size(X,2); +gr = zeros(p,1); +nft = length(ft_ix); +for j=1:nft + term1 = sum(X(ft_ix(j)-nties(j):ft_ix(j),:),1); + term2 = exp(X(ft_ix(j)-nties(j):end,:)*Bhat); + term3 = exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat); + term4 = term2'*X(ft_ix(j)-nties(j):end,:); + term5 = term3'*X(ft_ix(j)-nties(j):ft_ix(j),:); + term = 0; + for l=0:nties(j) + term = term + (term4-l*term5/(nties(j)+1))/(sum(term2)-l*sum(term3)/(nties(j)+1)); + end; + gr = gr + (term1 - term)'; +end; +end + + +function He = SStat_Hessian(X,Bhat,ft_ix,nties) +% +% Hessian matrix for the log-likelihood. +% +% Input +% X: Ordered design Matrix (according to survival time). +% Bhat: Estimated vector of the population regression parameters. +% ft_ix: Failure time indices in X (last index if any tie). +% nties: Number of ties for each survival time. +% +% Output +% He: Hessian matrix. +% +[m,p] = size(X); +He = zeros(p,p); +nft = length(ft_ix); +for j=1:nft + term1 = 0; + for i=ft_ix(j)-nties(j):m + term1 = term1 + exp(X(i,:)*Bhat)*X(i,:)'*X(i,:); + end; + term2 = 0; + for i=ft_ix(j)-nties(j):ft_ix(j) + term2 = term2 + exp(X(i,:)*Bhat)*X(i,:)'*X(i,:); + end; + term3 = sum(exp(X(ft_ix(j)-nties(j):end,:)*Bhat)); + term4 = sum(exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat)); + term5 = exp(X(ft_ix(j)-nties(j):m,:)*Bhat)'*X(ft_ix(j)-nties(j):m,:); + term6 = exp(X(ft_ix(j)-nties(j):ft_ix(j),:)*Bhat)'*X(ft_ix(j)-nties(j):ft_ix(j),:); + term = 0; + for l=0:nties(j) + Z = term5 - l*term6/(nties(j)+1); + phi = term3 - l*term4/(nties(j)+1); + term = term + (term1-l*term2/(nties(j)+1))/phi - Z'*Z/(phi*phi); + end; + He = He - term; +end; +end + diff --git a/IQM Tools/IQMlite/tools/survival/IQMhr.m b/IQM Tools/IQMlite/tools/survival/IQMhr.m new file mode 100644 index 0000000000000000000000000000000000000000..018d5d30adf7fd2496fba198dceb471566e8dadc --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMhr.m @@ -0,0 +1,41 @@ +function [hr,pval,CI] = IQMhr(x1,x2,stats) +% [hr,pval,CI] = IQMhr(x1,x2,stats) +% +% Hazard ratio estimate for Cox models. +% +% Input +% +% x1: Row vector with the covariate values for the first group. +% x2: Row vector with the covariate values for the second group. +% stats: Structure containing statistiscs obtained with any of IQMcoxPH, +% IQMcoxStratPH and IQMcoxExt. +% +% Output +% hr: Hazard ratio value. +% pval: P-value for the hr value. +% CI: 95% confidence interval for the hazard ratio estimate. +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 3 + error('Too few inputs'); +end; +p = length(stats.Bhat); +if (length(x1)~=p) || (length(x2)~=p) + error(['Vectors x1, x2 and stats.Bhat must have the same length.']); +end; +d = x1-x2; +lrp = d*stats.Bhat; +hr = exp(lrp); +lrp_std = (d'*d).*stats.CovBhat; +lrp_std = sqrt(sum(lrp_std(:))); +CI.low = exp(lrp-1.96*lrp_std); +CI.high = exp(lrp+1.96*lrp_std); +pval = 2*(1-normcdf(abs(lrp/lrp_std),0,1)); \ No newline at end of file diff --git a/IQM Tools/IQMlite/tools/survival/IQMkm.m b/IQM Tools/IQMlite/tools/survival/IQMkm.m new file mode 100644 index 0000000000000000000000000000000000000000..9a0e5b80121ab56a02e316a6b6adc4144f103c9c --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMkm.m @@ -0,0 +1,59 @@ +function [km] = IQMkm(z,d,t,ptype) +% [km] = IQMkm(z,d,t,ptype) +% +% Kaplan-Meier curves estimation and plot. +% +% Input +% z: Discrete covariate (mx1, m # of subjects) with the strata categories. +% d: Logical vector (mx1) indicating censorship status (1 if the subject got +% the failure event or 0 otherwise). +% t: Vector (mx1) whose entries are the survival and censored times (ordered +% according to X). +% ptype: Plot type string. It can only be either 'km' to plot the KM curves +% or 'll' to plot the -log(-log(KM)) curves. +% +% Output +% km: Structure array with the Kaplan-Meier estimates for each category of +% z. +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer. +% +if nargin < 3 + error('Too few inputs'); +elseif nargin < 4 + ptype = 'km'; +end; +uz = unique(z); +nuz = length(uz); +km(1:nuz) = struct('f',[],'t',[]); +if strcmpi(ptype,'km') + figure('Name','KM plot'); +elseif strcmpi(ptype,'ll') + figure('Name','-log(-log(KM)) plot'); +else + error('The only posible values for ptype are ''km'' or ''ll''.'); +end; +hold on +colors = {'b','k','g','c','m','r','y'}; +for i=1:length(uz) + t_strat = t(z==uz(i)); + d_strat = d(z==uz(i)); + [km(i).f,km(i).t] = ecdf(t_strat,'censoring',~d_strat); + km(i).f = 1 - km(i).f; + if strcmpi(ptype,'km') + stairs(km(i).t,km(i).f,colors{mod(i,7)+1},'LineWidth',2); + else + stairs(km(i).t,-log(-log(km(i).f)),colors{mod(i,7)+1},'LineWidth',2); + end; +end; +legend(num2str(uz)); +hold off + + diff --git a/IQM Tools/IQMlite/tools/survival/IQMphTest.m b/IQM Tools/IQMlite/tools/survival/IQMphTest.m new file mode 100644 index 0000000000000000000000000000000000000000..2c427c49f975347b3368e4266274a5e3df2fb3e3 --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMphTest.m @@ -0,0 +1,131 @@ +function [pval,rho] = IQMphTest(X,d,t,stats_PH) +% [pval,rho] = IQMphTest(X,d,t,stats_PH) +% +% Schoenfeld residuals test for the proportional hazards assumption. +% +% Input +% X: Design Matrix with the time-independent covariates. (mxp, m # of +% subjects, p # of covariates). +% d: Logical vector (mx1) indicating censorship status (1 if the subject got +% the failure event or 0 otherwise). +% t: Vector (mx1) whose entries are the survival and censored times (ordered +% according to X). +% stats_PH: Structure obtained from IQMcoxPH +% +% Output +% pval: P-values indicating the probability of the PH assumption for each +% covariate. +% rho: Correlations between Schoenfeld residuals and ranked failure times. +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 4 + error('Too few inputs'); +end; +[m,p] = size(X); +if (length(d)~=m) || (length(t)~=m) + error(['All, the design matrix X, censorship status vector d and'... + ' time vector t must have the same number of rows.']); +end; +if length(stats_PH.Bhat)~=p + error(['The vector stats.Bhat must have the same length as the number'... + ' of colums of X.']); +end; +%Sort the data by time. If there is a tie between a failure time and a +%censored time then the failure time goes first. +st_ix = find(d==1); +t1 = t(st_ix); +[t1,t1_ix] = sort(t1); +X1 = X(st_ix(t1_ix),:); +cs_ix = find(d==0); +if ~isempty(cs_ix) + t2 = t(cs_ix); + [t2,t2_ix] = sort(t2); + X2 = X(cs_ix(t2_ix),:); + count1 = 1; count2 = 1; i = 0; + while (count1 <= length(t1)) && (count2 <= length(t2)) + i = i + 1; + if t1(count1) <= t2(count2) + X(i,:) = X1(count1,:); + d(i) = 1; + t(i) = t1(count1); + count1 = count1 + 1; + else + X(i,:) = X2(count2,:); + d(i) = 0; + t(i) = t2(count2); + count2 = count2 + 1; + end; + end; + if (count1 > length(t1)) + X(i+1:end,:) = X2(count2:end,:); + d(i+1:end) = 0; + t(i+1:end) = t2(count2:end); + else + X(i+1:end,:) = X1(count1:end,:); + d(i+1:end) = 1; + t(i+1:end) = t1(count1:end); + end; +else + X = X1; + t = t1; +end; +%indices of unique failure times in ft_ix (last index when ties happen) +st_ix = find(d==1); +[ft,ft_ix] = unique(t(st_ix),'last'); +ft_ix = st_ix(ft_ix); +nft = length(ft); +%handling ties in failure times by substracting a very small random number +rand('state',sum(100*clock)); +for j=1:nft + i = 1; + while (ft_ix(j)-i>0) && (ft(j)==t(ft_ix(j)-i)) + i = i + 1; + end; + nties = i-1; + tt = t(ft_ix(j)-nties:ft_ix(j)) - 1e-5*(1 + rand(nties+1,1)); + tX = X(ft_ix(j)-nties:ft_ix(j),:); + [stt,stt_ix] = sort(tt); + t(ft_ix(j)-nties:ft_ix(j)) = stt; + X(ft_ix(j)-nties:ft_ix(j),:) = tX(stt_ix,:); +end; +nft = length(st_ix); +r = zeros(nft,p); +for i=1:nft + lprv = exp(X(st_ix(i):end,:)*stats_PH.Bhat); + r(i,:) = X(st_ix(i),:) - (lprv'*X(st_ix(i):end,:))./sum(lprv); +end; +[rho,pval] = corr(r,[1:nft]'); + +% Plot Schoenfeld residuals over Ranked failure times for eye inspection. +figure; +nall = size(r,2); +ncol = ceil(sqrt(nall)); +nrow = ceil(nall/ncol); +for k=1:nall, + subplot(nrow,ncol,k); + plot([1:nft],r(:,k),'o'); + ylabel('Schoenfeld residuals'); + xlabel('Ranked failure times'); + title(sprintf('beta_%d',k),'Interpreter','none'); + grid on + set(gca,'XLim',[1 nft]) + % Add loess line + hold on + rsmooth = smoothIQM([1:nft],r(:,k),0.5,'loess'); + plot([1:nft],rsmooth,'k-','LineWidth',2); + % Plot zero line + plot(get(gca,'XLim'),[0 0],'k--'); + % Add legend + h = legend(['Residuals over time ' sprintf('\n(corr=%1.2g, p=%1.2g)',rho(k),pval(k))],'Loess smoother','Zero line','Location','best'); + set(h,'Interpreter','none') + axis square +end + diff --git a/IQM Tools/IQMlite/tools/survival/IQMxext.m b/IQM Tools/IQMlite/tools/survival/IQMxext.m new file mode 100644 index 0000000000000000000000000000000000000000..ae3fa25311d2160cd87b5b5a02d5ba26461e2e9a --- /dev/null +++ b/IQM Tools/IQMlite/tools/survival/IQMxext.m @@ -0,0 +1,114 @@ +function [sID_ext,X_ext,d_ext,t_ext] = IQMxext(sID,X,d,t) +% [sID_ext,X_ext,d_ext,t_ext] = IQMxext(sID,X,d,t) +% +% Extension (by adding new rows) of a design matrix comprising only time- +% independent covariates to a design matrix for which time-dependent +% covariates can be easily added. +% +% Input +% sID: Subjects' IDs (for each row of X). +% X: Design Matrix with the time-independent covariates. (mxp, m # of +% subjects, p # of covariates). +% d: Logical vector (mx1) indicating censorship status (1 if the subject got +% the failure event or 0 otherwise). +% t: Vector (mx1) whose entries are the survival and censored times (ordered +% according to X). +% +% Output +% sID_ext: Extended subjects' IDs. +% X_ext: Extended design matrix. +% d_ext: Extended censorship status vector. +% t_ext: Extended survival time vector. +% +% $Revision: 1.1.1.1 $ $Date: 2013/27/03 11:25:52 $ +% Original Author: Jorge Luis Bernal Rusiel +% CVS Revision Info: +% $Author: jbernal$ +% $Date: 2013/27/03 11:25:52 $ +% $Revision: 1.1 $ +% References: Kleinbaum, D.G., Klein, M., 2005. Survival analysis. A self- +% learning approach, second edition. New York: Springer.. +% +if nargin < 4 + error('Too few inputs'); +end; +[m,p] = size(X); +if (length(sID)~=m) || (length(d)~=m) || (length(t)~=m) + error(['All, the design matrix X, censorship status vector d, '... + 'time vector t and subject ID vector must have the same number of rows.']); +end; +%Sort the data by time. If there is a tie between a failure time and a +%censored time then the failure time goes first. +st_ix = find(d==1); +t1 = t(st_ix); +[t1,t1_ix] = sort(t1); +X1 = X(st_ix(t1_ix),:); +sID1 = sID(st_ix(t1_ix)); +cs_ix = find(d==0); +if ~isempty(cs_ix) + t2 = t(cs_ix); + [t2,t2_ix] = sort(t2); + X2 = X(cs_ix(t2_ix),:); + sID2 = sID(cs_ix(t2_ix)); + count1 = 1; count2 = 1; i = 0; + while (count1 <= length(t1)) && (count2 <= length(t2)) + i = i + 1; + if t1(count1) <= t2(count2) + sID(i) = sID1(count1); + X(i,:) = X1(count1,:); + d(i) = 1; + t(i) = t1(count1); + count1 = count1 + 1; + else + sID(i) = sID2(count2); + X(i,:) = X2(count2,:); + d(i) = 0; + t(i) = t2(count2); + count2 = count2 + 1; + end; + end; + if (count1 > length(t1)) + sID(i+1:end) = sID2(count2:end); + X(i+1:end,:) = X2(count2:end,:); + d(i+1:end) = 0; + t(i+1:end) = t2(count2:end); + else + sID(i+1:end) = sID1(count1:end); + X(i+1:end,:) = X1(count1:end,:); + d(i+1:end) = 1; + t(i+1:end) = t1(count1:end); + end; +else + sID = sID1; + X = X1; + t = t1; +end; +%indices of unique failure times in ft_ix (last index when ties happen) +st_ix = find(d==1); +[ft,ft_ix] = unique(t(st_ix),'last'); +ft_ix = st_ix(ft_ix); +nft = length(ft); +n = 0; +for j=1:nft + n = n + sum(t>t(ft_ix(j))); +end; +n = n + m; +X_ext = zeros(n,p); +d_ext = zeros(n,1); +t_ext = zeros(n,1); +count = 1; +for i=1:m + ni = sum(t(ft_ix) < t(i)); + X_ext(count:count+ni,:) = kron(ones(ni+1,1),X(i,:)); + sID_ext(count:count+ni) = sID(i); + d_ext(count:count+ni-1) = 0; + d_ext(count+ni) = d(i); + t_ext(count:count+ni-1) = t(ft_ix(t(ft_ix)<t(i))); + t_ext(count+ni) = t(i); + count = count + ni + 1; +end; + + + + + diff --git a/IQM Tools/IQMlite/tools/templates/scriptTemplateIQM.m b/IQM Tools/IQMlite/tools/templates/scriptTemplateIQM.m index 4aec74ecd49ff198381bf1a05081d9669638186a..eedba36c5bce258c5779a6f805f7744f3935ec7d 100644 --- a/IQM Tools/IQMlite/tools/templates/scriptTemplateIQM.m +++ b/IQM Tools/IQMlite/tools/templates/scriptTemplateIQM.m @@ -1,43 +1,45 @@ -%% In this section the purpose of the script should be described. -% Purpose of activity: +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% SCRIPT_NAME % -% Required previous scripts to be run: +% [PURPOSE] +% +% +% [AUTHOR] % -% Author: % -% Date: +% [DATE] +% % +% [DEPENDENCIES] +% +% +% [INPUT] +% +% +% +% [OUTPUT] +% +% [OTHER] +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Installation of IQM Tools -% This block should always be on the top of every analysis script with IQM -% Tools. It might only be ommitted in the case that a central installation -% of IQM Tools is present on the computer system and the IQM Tools are -% automatically loaded during the startup of MATLAB. -clc; % Clear command window -clear all; % Clear workspace from all defined variables -close all; % Close all figures -fclose all; % Close all open files -restoredefaultpath(); % Clear all user installed toolboxes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Clear all - install needed scripts +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +clc; +clear all; +close all; +fclose all; +restoredefaultpath; % In the next line, please enter the path to your IQM Tools folder. It can % be a relative or an absolute path. PATH_IQM = 'D:\IQM Tools Suite'; -oldpath = pwd(); cd(PATH_IQM); installIQMtoolsInitial; cd(oldpath); - -%% "Initialize the compliance mode". -% IQM Tools allows to automatically generate logfiles for all output that -% is generated using the functions "IQMprintFigure" and -% "IQMwriteText2File". These logfiles contain the username, the name of the -% generated file (including the path), and the scripts and functions that -% have been called to generate the output file. In order to ensure this is -% working correctly, the only thing that needs to be done is to execute the -% following command at the start of each analysis script. - -% The input argument to the "IQMinitializeCompliance" function needs to be -% the name of the script file. -IQMinitializeCompliance('TEMPLATE_SCRIPT_NAME'); - -%% Here your code starts - +oldpath = pwd(); cd(PATH_IQM); installIQMtools; cd(oldpath); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Intialize compliance mode +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +IQMinitializeCompliance('SCRIPT_NAME.m'); diff --git a/IQM Tools/IQMpro/Contents.m b/IQM Tools/IQMpro/Contents.m index 25a52d1094305764153b1bb5d2d3127a781fdd8c..c013ffb8abc60584abdb25a88e12e1e7e64699c4 100644 --- a/IQM Tools/IQMpro/Contents.m +++ b/IQM Tools/IQMpro/Contents.m @@ -1,5 +1,5 @@ % IQM Tools Pro -% Version 1.2.1 (R2015B) 30.04.2016 +% Version 1.2.2 (R2015B) 02.01.2017 % % IQMpro % ======= diff --git a/IQM Tools/IQMpro/SETUP_PATHS_TOOLS_IQMPRO.m b/IQM Tools/IQMpro/SETUP_PATHS_TOOLS_IQMPRO.m index 8cb89316d93e2e4a460b1ccd299a21015f101624..4fed82542cdade9bb73a9ac08fe1fbbb4c919d7b 100644 --- a/IQM Tools/IQMpro/SETUP_PATHS_TOOLS_IQMPRO.m +++ b/IQM Tools/IQMpro/SETUP_PATHS_TOOLS_IQMPRO.m @@ -13,16 +13,17 @@ % assumed that the command that runs a NONMEM or MONOLIX job via the queue % returns the jobID as a number. It is also assumed that this number % appears when the queue status command is called: -PATH_SYSTEM_QUEUE_STATUS_UNIX = 'squeue'; +PATH_SYSTEM_QUEUE_STATUS_UNIX = ''; % If no queuing system used then keep this variable empty ('') % NONMEM (currently tested version: 7.2 and 7.3) PATH_SYSTEM_NONMEM_WINDOWS = 'nmfe73'; -PATH_SYSTEM_NONMEM_UNIX = 'qnmfe -n -t 1440 -x'; % Define UNIX path for Mac +PATH_SYSTEM_NONMEM_UNIX = 'nmfe73'; % NONMEM PARALLEL PATH_SYSTEM_NONMEM_PARALLEL_WINDOWS = 'nmfe73par'; -PATH_SYSTEM_NONMEM_PARALLEL_UNIX = 'qnmfe -n -t 1440 -x -c'; % Define UNIX path for Mac +PATH_SYSTEM_NONMEM_PARALLEL_UNIX = 'nmfe73par'; + % nmfe73par is assumed to be a shell script with the following calling % syntax: "nmfe73par NRNODES controlfile outputfile". If you do not have % one available, please ask your sysadmin to generate on for you diff --git a/IQM Tools/IQMpro/auxiliary/data/checkDataColumnsIQM.m b/IQM Tools/IQMpro/auxiliary/data/checkDataColumnsIQM.m index c973bef47126e1dc7833daee16a27f5185a53718..71b739ac696bb4c98a095b32749ba7f62981eef0 100644 --- a/IQM Tools/IQMpro/auxiliary/data/checkDataColumnsIQM.m +++ b/IQM Tools/IQMpro/auxiliary/data/checkDataColumnsIQM.m @@ -33,8 +33,10 @@ end colnamestable = data.Properties.VariableNames; errorText = ''; for k=1:length(COLNAMES), - if isempty(strmatchIQM(COLNAMES{k},colnamestable,'exact')), - errorText = sprintf('%sThe dataset does not contain the column "%s".\n',errorText,COLNAMES{k}); + if ~isempty(COLNAMES{k}), + if isempty(strmatchIQM(COLNAMES{k},colnamestable,'exact')), + errorText = sprintf('%sThe dataset does not contain the column "%s".\n',errorText,COLNAMES{k}); + end end end diff --git a/IQM Tools/IQMpro/release.txt b/IQM Tools/IQMpro/release.txt index 42c36a424641ae60bfd590acee81242f71a16029..7ab71b93e7796aa74aba9450f0a7ae6221b65a30 100644 --- a/IQM Tools/IQMpro/release.txt +++ b/IQM Tools/IQMpro/release.txt @@ -1,6 +1,11 @@ Release Notes IQM Tools Pro =========================== +Version 1.2.2 (02.01.2017) +-------------------------- +- Minor big fixes +- Improved graphical output + Version 1.2.1 (30.04.2016) -------------------------- - Minor bug fixes diff --git a/IQM Tools/IQMpro/tools/01-MEXmodels/CVODEMEX/lib/CVODEmex25.a b/IQM Tools/IQMpro/tools/01-MEXmodels/CVODEMEX/lib/CVODEmex25.a new file mode 100644 index 0000000000000000000000000000000000000000..fd4d2f3c6a2cac80df2cee1ca4437312bc150e7a Binary files /dev/null and b/IQM Tools/IQMpro/tools/01-MEXmodels/CVODEMEX/lib/CVODEmex25.a differ diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMaddNLMEinfo2data.m b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMaddNLMEinfo2data.m index 5a9fc43af8d073ae801079f24066f4f3e1f0d70a..c82ed919110cc7578f30579a7f19e2abc1bff5a7 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMaddNLMEinfo2data.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMaddNLMEinfo2data.m @@ -6,7 +6,10 @@ function [dataOut] = IQMaddNLMEinfo2data(data,DOSENAMES,OBSNAMES,FLAG_EXPAND_REP % % Repeated doses, defined by entries in columns INTERVAL and NRDOSES are % NOT expanded by default. Expansion can be done by setting input argument -% FLAG_EXPAND_REPEATED_DOSES = 1 (default: 0). +% FLAG_EXPAND_REPEATED_DOSES = 1 (default: 0). Note that NRDOSES codes for +% ADDITIONAL doses, in the same way (identical) as the ADDL column in +% NONMEM and MONOLIX. This means that NRDOSES=1 codes for 2 doses, spaced +% by INTERVAL. % % The following columns are added (if already present they are overwritten): % @@ -93,6 +96,9 @@ function [dataOut] = IQMaddNLMEinfo2data(data,DOSENAMES,OBSNAMES,FLAG_EXPAND_REP % INTERVAL and NRDOSES are NOT expanded by default. % Expansion can be done by setting input argument % FLAG_EXPAND_REPEATED_DOSES = 1 (default: 0). +% NOTE THAT NRDOSES has the same meaning as ADDL in +% NONMEM and MONOLIX ... so it is the number of +% ADDITIONAL doses. % % [OUTPUT] % dataOut: Dataset with added NLME information. diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormat.m b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormat.m index 9ed29d754e6047ce3ac235ffaada74ff470aca0e..bf040aee8685286fa4caba4f8a2e4d49f37d55da 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormat.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormat.m @@ -61,8 +61,11 @@ function [data] = IQMcheckGeneralDataFormat(data,silent) % or the value of other readouts. The values need to be in the units, defined in the UNIT column. % Specific cases: % - For concomitant medications the dose will be given -% - For BLOQ values, 0 will be used % - Severity levels for adverse events +% - For BLOQ records: any value can be entered that is lower than the actual LLOQ. +% It is not acceptable to set this value to �NaN� or �NA� since then no discrimination +% can be made between �missing� and �<LLOQ�. For PK records on untransformed data �0� +% is suggested. On log transformed data 0 should not be used but log(LLOQ/2) would be acceptable. % Should not be populated if VALUETXT is populated % VALUETXT string Text version of value (if available and useful) % Character value as given in the CRF. @@ -72,7 +75,9 @@ function [data] = IQMcheckGeneralDataFormat(data,silent) % LLOQ numeric Lower limit of quantification of event defined by NAME % ROUTE string Route of administration (iv, subcut, intramuscular, intraarticular, oral, inhaled, topical, rectal) % INTERVAL numeric Interval of dosing, if single row should define multiple dosings -% NRDOSES numeric Number of doses given with the specified interval, if single row should define multiple dosings +% NRDOSES numeric Number of ADDITIONAL doses given within the specified interval, +% NRDOSES=N codes for a total of N+1 doses with an interval as defined in the +% column "INTERVAL" starting at the time defined in the dose record. % COMMENT string Additional information for the observation/event % For example: % - For PK: concatenation of DMPK flag to exclude or not the PK from the @@ -443,6 +448,21 @@ for k=1:length(check_1), end end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Checking NRDOSES and warning if used so that user can check if it was +% done correctly. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Check if NRDOSES somewhere different from NaN +NRDOSES = data.NRDOSES(~isnan(data.NRDOSES)); +if ~isempty(NRDOSES), + fprintf('\nThe NRDOSES column has been defined in the dataset. Please consider expansion of these dose records to individual dose records when\n'); + fprintf('generating the task dataset. The function IQMconvertGeneral2TaskDataset allows the definition of a flag to do this expansion automatically.\n'); + fprintf('The expansion is beneficial since the VPC functions in IQM tools require definition of single dose records. Parameter estimation in NONMEM\n'); + fprintf('and Monolix will not be affected by your choice. Also note that NRDOSES is equivalent to the ADDL column in NONMEM and MONOLIX and should\n'); + fprintf('be defined accordingly (NRDOSES=N codes for 1 dose and N additional doses => N+1 total doses).\n'); +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Final message %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormatHeader.m b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormatHeader.m index bef0f50cd58e3561fd809da7ad0e868a614d93ae..52cc7a572639cc3e9657a1bd9b4067ae4dafa6fb 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormatHeader.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcheckGeneralDataFormatHeader.m @@ -53,8 +53,11 @@ function [data] = IQMcheckGeneralDataFormatHeader(data) % or the value of other readouts. The values need to be in the units, defined in the UNIT column. % Specific cases: % - For concomitant medications the dose will be given -% - For BLOQ values, 0 will be used % - Severity levels for adverse events +% - For BLOQ records: any value can be entered that is lower than the actual LLOQ. +% It is not acceptable to set this value to �NaN� or �NA� since then no discrimination +% can be made between �missing� and �<LLOQ�. For PK records on untransformed data �0� +% is suggested. On log transformed data 0 should not be used but log(LLOQ/2) would be acceptable. % Should not be populated if VALUETXT is populated % VALUETXT string Text version of value (if available and useful) % Character value as given in the CRF. @@ -64,7 +67,9 @@ function [data] = IQMcheckGeneralDataFormatHeader(data) % LLOQ numeric Lower limit of quantification of event defined by NAME % ROUTE string Route of administration (iv, subcut, intramuscular, intraarticular, oral, inhaled, topical, rectal) % INTERVAL numeric Interval of dosing, if single row should define multiple dosings -% NRDOSES numeric Number of doses given with the specified interval, if single row should define multiple dosings +% NRDOSES numeric Number of ADDITIONAL doses given within the specified interval, +% NRDOSES=N codes for a total of N+1 doses with an interval as defined in the +% column "INTERVAL" starting at the time defined in the dose record. % COMMENT string Additional information for the observation/event % For example: % - For PK: concatenation of DMPK flag to exclude or not the PK from the diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcreateGeneralDataset.m b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcreateGeneralDataset.m index fc26fd915e050cbdd34c6db5c316cc74f97c2756..3becfc9c879ccd46364d2cfd5ab35facedba57c5 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcreateGeneralDataset.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/IQMcreateGeneralDataset.m @@ -1 +1 @@ -function [dataGeneral] = IQMcreateGeneralDataset(varargin) % The IQM Tools' workflow functions assume a general dataset format that is % independent of modeling activities and tools. This function here allows % to generate an empty dataset with this structure. Additionally, in this % help text here the format of this dataset is defined. % % [SYNTAX] % [data] = IQMcreateGeneralDataset() % [data] = IQMcreateGeneralDataset(NROWS) % % [INPUT] % NRROWS: Number of rows in the generated (empty) dataset (default: 1) % % [OUTPUT] % Empty dataset in the general dataset format % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Specification of general dataset format: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % COLUMN NAME TYPE DESCRIPTION % ------------------------------------------------------------------------- % IXGDF numeric Column containing numeric indices for each record (1,2,3,4,5, ...) % to allow for matching records in case of postprocessing the general dataset format % IGNORE string Reason/comment related to exclusion of the sample/observation from the analysis % USUBJID string Unique subject identifier % COMPOUND string Name of the investigational compound % STUDY string Study number % STUDYDES string Study description % PART string Part of study as defined per protocol (1 if only one part) % EXTENS numeric Extension of the core study (0 if not extension, 1 if extension) % CENTER numeric Center number % SUBJECT string Subject identifier (within a center - typically not unique across whole dataset) % INDNAME string Indication name % TRTNAME string Name of actual treatment given % TRTNAMER string Name of treatment to which individual was randomized % VISIT numeric Visit number % VISNAME string Visit name % BASE numeric Flag indicating assessments at baseline % (=0 for non-baseline, =1 for first baseline, =2 for second baseline, etc.) % SCREEN numeric Flag indicating assessments at screening % (=0 for non-screening, =1 for first screening, =2 for second screening, etc.) % DATEDAY string Start date of event ('01-JUL-2015') % DATETIME string Start time of event ('09:34') % DURATION numeric Duration of event in same time units as TIMEUNIT % NT numeric Planned time of event. Based on protocol, in the time unit defined in TIMEUNIT column % TIME numeric Actual time of event relative to first administration, in the time unit defined in TIMEUNIT column % TIMEUNIT string Unit of all numerical time definitions in the dataset ('hours','days','weeks','minutes') % TYPENAME string Unique type of event % NAME string Unique name for the event % VALUE numeric Value of the event, defined by NAME. E.g., the given dose, the observed PK concentration, % or the value of other readouts. The values need to be in the units, defined in the UNIT column. % Specific cases: % - For concomitant medications the dose will be given % - For BLOQ values, 0 will be used % - Severity levels for adverse events % Should not be populated if VALUETXT is populated % VALUETXT string Text version of value (if available and useful) % Character value as given in the CRF. % Should not be populated if VALUE is populated % UNIT string Unit of the value reported in the VALUE column. For same event the same unit has to be used across the dataset. % ULOQ numeric Upper limit of quantification of event defined by NAME % LLOQ numeric Lower limit of quantification of event defined by NAME % ROUTE string Route of administration (iv, subcut, intramuscular, intraarticular, oral, inhaled, topical, rectal) % INTERVAL numeric Interval of dosing, if single row should define multiple dosings % NRDOSES numeric Number of doses given with the specified interval, if single row should define multiple dosings % COMMENT string Additional information for the observation/event % For example: % - For PK: concatenation of DMPK flag to exclude or not the PK from the % DMPK analysis and comment for each sample (e.g., if the sample is % flagged as 'Excluded' than this word should be a prefix to the comment) % - For adverse events concatenation of the seriousness and if the AE is drug related or not % - For an imputation, it should mention 'Imputed'. % % The general data format might also contain the following columns, which % are numeric equivalents to some string columns. If not provided, then % these can be generated automatically by using the command % IQMconvertGeneral2TaskDataset. % % IND: Numeric indication flag (unique for each entry in INDNAME) % STUDYN: Numeric study flag (unique for each entry in STUDY) % TRT: Numeric actual treatment flag (unique for each entry in TRTNAME) % TRTR: Numeric randomized treatment flag (unique for each entry in TRTNAMER) % <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> NROWS = 1; if nargin==1, NROWS = varargin{1}; end % Create standard dataset structure dataGeneral = table(); % Indices dataGeneral.IXGDF = [1:NROWS]'; % General dataGeneral.IGNORE = cell(NROWS,1); dataGeneral.IGNORE(1:end) = {''}; % Unique subject identifier dataGeneral.USUBJID = cell(NROWS,1); dataGeneral.USUBJID(1:end) = {''}; % Study information dataGeneral.COMPOUND = cell(NROWS,1); dataGeneral.COMPOUND(1:end) = {''}; dataGeneral.STUDY = cell(NROWS,1); dataGeneral.STUDY(1:end) = {''}; dataGeneral.STUDYDES = cell(NROWS,1); dataGeneral.STUDYDES(1:end) = {''}; dataGeneral.PART = cell(NROWS,1); dataGeneral.PART(1:end) = {''}; dataGeneral.EXTENS = zeros(NROWS,1); dataGeneral.CENTER = NaN(NROWS,1); dataGeneral.SUBJECT = cell(NROWS,1); dataGeneral.SUBJECT(1:end) = {''}; dataGeneral.INDNAME = cell(NROWS,1); dataGeneral.INDNAME(1:end) = {''}; % Treatment group information dataGeneral.TRTNAME = cell(NROWS,1); dataGeneral.TRTNAME(1:end) = {''}; dataGeneral.TRTNAMER = cell(NROWS,1); dataGeneral.TRTNAMER(1:end) = {''}; % Visit information dataGeneral.VISIT = NaN(NROWS,1); dataGeneral.VISNAME = cell(NROWS,1); dataGeneral.VISNAME(1:end) = {''}; dataGeneral.BASE = zeros(NROWS,1); dataGeneral.SCREEN = zeros(NROWS,1); % Event time information dataGeneral.DATEDAY = cell(NROWS,1); dataGeneral.DATEDAY(1:end) = {''}; dataGeneral.DATETIME = cell(NROWS,1); dataGeneral.DATETIME(1:end) = {''}; dataGeneral.DURATION = zeros(NROWS,1); dataGeneral.NT = NaN(NROWS,1); dataGeneral.TIME = NaN(NROWS,1); dataGeneral.TIMEUNIT = cell(NROWS,1); dataGeneral.TIMEUNIT(1:end) = {''}; % Event value information dataGeneral.TYPENAME = cell(NROWS,1); dataGeneral.TYPENAME(1:end) = {''}; dataGeneral.NAME = cell(NROWS,1); dataGeneral.NAME(1:end) = {''}; dataGeneral.VALUE = NaN(NROWS,1); dataGeneral.VALUETXT = cell(NROWS,1); dataGeneral.VALUETXT(1:end) = {''}; dataGeneral.UNIT = cell(NROWS,1); dataGeneral.UNIT(1:end) = {''}; dataGeneral.ULOQ = NaN(NROWS,1); dataGeneral.LLOQ = NaN(NROWS,1); dataGeneral.ROUTE = cell(NROWS,1); dataGeneral.ROUTE(1:end) = {''}; dataGeneral.INTERVAL = NaN(NROWS,1); dataGeneral.NRDOSES = NaN(NROWS,1); % Comment dataGeneral.COMMENT = cell(NROWS,1); dataGeneral.COMMENT(1:end) = {''}; \ No newline at end of file +function [dataGeneral] = IQMcreateGeneralDataset(varargin) % The IQM Tools' workflow functions assume a general dataset format that is % independent of modeling activities and tools. This function here allows % to generate an empty dataset with this structure. Additionally, in this % help text here the format of this dataset is defined. % % [SYNTAX] % [data] = IQMcreateGeneralDataset() % [data] = IQMcreateGeneralDataset(NROWS) % % [INPUT] % NRROWS: Number of rows in the generated (empty) dataset (default: 1) % % [OUTPUT] % Empty dataset in the general dataset format % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Specification of general dataset format: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % COLUMN NAME TYPE DESCRIPTION % ------------------------------------------------------------------------- % IXGDF numeric Column containing numeric indices for each record (1,2,3,4,5, ...) % to allow for matching records in case of postprocessing the general dataset format % IGNORE string Reason/comment related to exclusion of the sample/observation from the analysis % USUBJID string Unique subject identifier % COMPOUND string Name of the investigational compound % STUDY string Study number % STUDYDES string Study description % PART string Part of study as defined per protocol (1 if only one part) % EXTENS numeric Extension of the core study (0 if not extension, 1 if extension) % CENTER numeric Center number % SUBJECT string Subject identifier (within a center - typically not unique across whole dataset) % INDNAME string Indication name % TRTNAME string Name of actual treatment given % TRTNAMER string Name of treatment to which individual was randomized % VISIT numeric Visit number % VISNAME string Visit name % BASE numeric Flag indicating assessments at baseline % (=0 for non-baseline, =1 for first baseline, =2 for second baseline, etc.) % SCREEN numeric Flag indicating assessments at screening % (=0 for non-screening, =1 for first screening, =2 for second screening, etc.) % DATEDAY string Start date of event ('01-JUL-2015') % DATETIME string Start time of event ('09:34') % DURATION numeric Duration of event in same time units as TIMEUNIT % NT numeric Planned time of event. Based on protocol, in the time unit defined in TIMEUNIT column % TIME numeric Actual time of event relative to first administration, in the time unit defined in TIMEUNIT column % TIMEUNIT string Unit of all numerical time definitions in the dataset ('hours','days','weeks','minutes') % TYPENAME string Unique type of event % NAME string Unique name for the event % VALUE numeric Value of the event, defined by NAME. E.g., the given dose, the observed PK concentration, % or the value of other readouts. The values need to be in the units, defined in the UNIT column. % Specific cases: % - For concomitant medications the dose will be given % - Severity levels for adverse events % - For BLOQ records: any value can be entered that is lower than the actual LLOQ. % It is not acceptable to set this value to �NaN� or �NA� since then no discrimination % can be made between �missing� and �<LLOQ�. For PK records on untransformed data �0� % is suggested. On log transformed data 0 should not be used but log(LLOQ/2) would be acceptable. % Should not be populated if VALUETXT is populated % VALUETXT string Text version of value (if available and useful) % Character value as given in the CRF. % Should not be populated if VALUE is populated % UNIT string Unit of the value reported in the VALUE column. For same event the same unit has to be used across the dataset. % ULOQ numeric Upper limit of quantification of event defined by NAME % LLOQ numeric Lower limit of quantification of event defined by NAME % ROUTE string Route of administration (iv, subcut, intramuscular, intraarticular, oral, inhaled, topical, rectal) % INTERVAL numeric Interval of dosing, if single row should define multiple dosings % NRDOSES numeric Number of ADDITIONAL doses given within the specified interval, % NRDOSES=N codes for a total of N+1 doses with an interval as defined in the % column "INTERVAL" starting at the time defined in the dose record. % COMMENT string Additional information for the observation/event % For example: % - For PK: concatenation of DMPK flag to exclude or not the PK from the % DMPK analysis and comment for each sample (e.g., if the sample is % flagged as 'Excluded' than this word should be a prefix to the comment) % - For adverse events concatenation of the seriousness and if the AE is drug related or not % - For an imputation, it should mention 'Imputed'. % % The general data format might also contain the following columns, which % are numeric equivalents to some string columns. If not provided, then % these can be generated automatically by using the command % IQMconvertGeneral2TaskDataset. % % IND: Numeric indication flag (unique for each entry in INDNAME) % STUDYN: Numeric study flag (unique for each entry in STUDY) % TRT: Numeric actual treatment flag (unique for each entry in TRTNAME) % TRTR: Numeric randomized treatment flag (unique for each entry in TRTNAMER) % <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> NROWS = 1; if nargin==1, NROWS = varargin{1}; end % Create standard dataset structure dataGeneral = table(); % Indices dataGeneral.IXGDF = [1:NROWS]'; % General dataGeneral.IGNORE = cell(NROWS,1); dataGeneral.IGNORE(1:end) = {''}; % Unique subject identifier dataGeneral.USUBJID = cell(NROWS,1); dataGeneral.USUBJID(1:end) = {''}; % Study information dataGeneral.COMPOUND = cell(NROWS,1); dataGeneral.COMPOUND(1:end) = {''}; dataGeneral.STUDY = cell(NROWS,1); dataGeneral.STUDY(1:end) = {''}; dataGeneral.STUDYDES = cell(NROWS,1); dataGeneral.STUDYDES(1:end) = {''}; dataGeneral.PART = cell(NROWS,1); dataGeneral.PART(1:end) = {''}; dataGeneral.EXTENS = zeros(NROWS,1); dataGeneral.CENTER = NaN(NROWS,1); dataGeneral.SUBJECT = cell(NROWS,1); dataGeneral.SUBJECT(1:end) = {''}; dataGeneral.INDNAME = cell(NROWS,1); dataGeneral.INDNAME(1:end) = {''}; % Treatment group information dataGeneral.TRTNAME = cell(NROWS,1); dataGeneral.TRTNAME(1:end) = {''}; dataGeneral.TRTNAMER = cell(NROWS,1); dataGeneral.TRTNAMER(1:end) = {''}; % Visit information dataGeneral.VISIT = NaN(NROWS,1); dataGeneral.VISNAME = cell(NROWS,1); dataGeneral.VISNAME(1:end) = {''}; dataGeneral.BASE = zeros(NROWS,1); dataGeneral.SCREEN = zeros(NROWS,1); % Event time information dataGeneral.DATEDAY = cell(NROWS,1); dataGeneral.DATEDAY(1:end) = {''}; dataGeneral.DATETIME = cell(NROWS,1); dataGeneral.DATETIME(1:end) = {''}; dataGeneral.DURATION = zeros(NROWS,1); dataGeneral.NT = NaN(NROWS,1); dataGeneral.TIME = NaN(NROWS,1); dataGeneral.TIMEUNIT = cell(NROWS,1); dataGeneral.TIMEUNIT(1:end) = {''}; % Event value information dataGeneral.TYPENAME = cell(NROWS,1); dataGeneral.TYPENAME(1:end) = {''}; dataGeneral.NAME = cell(NROWS,1); dataGeneral.NAME(1:end) = {''}; dataGeneral.VALUE = NaN(NROWS,1); dataGeneral.VALUETXT = cell(NROWS,1); dataGeneral.VALUETXT(1:end) = {''}; dataGeneral.UNIT = cell(NROWS,1); dataGeneral.UNIT(1:end) = {''}; dataGeneral.ULOQ = NaN(NROWS,1); dataGeneral.LLOQ = NaN(NROWS,1); dataGeneral.ROUTE = cell(NROWS,1); dataGeneral.ROUTE(1:end) = {''}; dataGeneral.INTERVAL = NaN(NROWS,1); dataGeneral.NRDOSES = NaN(NROWS,1); % Comment dataGeneral.COMMENT = cell(NROWS,1); dataGeneral.COMMENT(1:end) = {''}; \ No newline at end of file diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/auxiliary/expandGeneralNR_DOSES_intervalIQM.m b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/auxiliary/expandGeneralNR_DOSES_intervalIQM.m index 636a758cce4800094c03b455e6519d952cb92f22..fbabc017de44674185aebf30cb6cdc4ff85fac7f 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/auxiliary/expandGeneralNR_DOSES_intervalIQM.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/01-GeneralDataFormat/auxiliary/expandGeneralNR_DOSES_intervalIQM.m @@ -5,6 +5,9 @@ function [dataOut] = expandGeneralNR_DOSES_intervalIQM(data,DOSENAME) % VIST, VISNAME, DATEDAY/TIME and DURATION entries are kept on the % original dose definitions, as well as IXGDF. TIME and NT will be adjusted. % +% NOTE THAT NRDOSES codes for ADDITIONAL doses ... so NRDOSES=1 codes for 2 +% doses, etc. Spacing of doses is defined by the column INTERVAL. +% % [SYNTAX] % [dataOut] = expandGeneralNR_DOSES_intervalIQM(data,DOSENAME) % @@ -27,16 +30,19 @@ end dataDOSES = data(strcmp(data.NAME,DOSENAME),:); dataOTHER = data(~strcmp(data.NAME,DOSENAME),:); % Expand doses if both INTERVAL and NRDOSES defined +% NRDOSES+1 DOSES need to be created if expansion is done... dataDOSES_expanded = table(); for k=1:height(dataDOSES), datak = dataDOSES(k,:); if ~isnan(datak.INTERVAL) && ~isnan(datak.NRDOSES), - dataexp = datak(ones(1,datak.NRDOSES),:); - dataexp.TIME = dataexp.TIME+[0:datak.INTERVAL:datak.INTERVAL*(datak.NRDOSES-1)]'; - dataexp.NT = dataexp.NT+[0:datak.INTERVAL:datak.INTERVAL*(datak.NRDOSES-1)]'; + NREXPANDED_DOSES = datak.NRDOSES + 1; + + dataexp = datak(ones(1,NREXPANDED_DOSES),:); + dataexp.TIME = dataexp.TIME+[0:datak.INTERVAL:datak.INTERVAL*(NREXPANDED_DOSES-1)]'; + dataexp.NT = dataexp.NT+[0:datak.INTERVAL:datak.INTERVAL*(NREXPANDED_DOSES-1)]'; dataexp.INTERVAL(1:end) = NaN; - dataexp.NRDOSES(1:end) = NaN; - dataDOSES_expanded = [dataDOSES_expanded; dataexp]; + dataexp.NRDOSES(1:end) = NaN; + dataDOSES_expanded = [dataDOSES_expanded; dataexp]; elseif isnan(datak.INTERVAL) && isnan(datak.NRDOSES), % Nothing to expand (single dose definition) dataDOSES_expanded = [dataDOSES_expanded; datak]; diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/03-TaskDataset/IQMconvertGeneral2TaskDataset.m b/IQM Tools/IQMpro/tools/03-ClinicalData/03-TaskDataset/IQMconvertGeneral2TaskDataset.m index 1dc63600882404ffe70d3d8823736fe51075432e..37e04cd8c360cc9b17fa5fa57c687a19b4f1a177 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/03-TaskDataset/IQMconvertGeneral2TaskDataset.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/03-TaskDataset/IQMconvertGeneral2TaskDataset.m @@ -75,11 +75,11 @@ function [dataOut] = IQMconvertGeneral2TaskDataset(data,DOSENAMES,OBSNAMES,covar % DOSE: Time dependent DOSE amount (carry forward imputation) % This column is only present in the case that a single % element in DOSENAMES is defined. -% DOSE_"name" where "name" is generated from the DOSENAMES entries. +% DOSE"name" where "name" is generated from the DOSENAMES entries. % These columns are only present if DOSENAMES contains % multiple entries. % -% For DOSE and DOSE_"name" columns. Prior to the first dose the entries are +% For DOSE and DOSE"name" columns. Prior to the first dose the entries are % NaN, since dose undefined. % % [SYNTAX] @@ -234,7 +234,7 @@ if length(DOSENAMES)==1, dataOut.DOSE(isnan(dataOut.DOSE)) = 0; else for k=1:length(DOSENAMES), - colname_dose = sprintf('DOSE_%s',makeVariableNameIQM(DOSENAMES{k})); + colname_dose = sprintf('DOSE%s',makeVariableNameIQM(DOSENAMES{k})); dataOut = IQMdataAddTimeDependentCovariate(dataOut,{DOSENAMES{k} colname_dose},FLAG_CARRY_FIRST_NON_NAN_BACKWARD); % Handle cases where a subject has not received any dose in the dataset (DOSE values are NaN) ... set these to 0, since diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreBLLOQdata.m b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreBLLOQdata.m index 391b94b93cb4a40ada891f0a8ee6b1a084813868..a14041c19a942dcf8cb1899f65d10a8b73218912 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreBLLOQdata.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreBLLOQdata.m @@ -32,7 +32,7 @@ if nargin<2, filename = ''; end -% Find all NAMEs which have LLOQ information +% Find all NAMEs which have LLOQ information (only observations considered) NAMES_BLLOQ_present = unique(data.NAME(~isnan(data.LLOQ) & data.EVID==0)); % Find all records that are BLOQ for all names that have LLOQ information diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivData.m b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivData.m index 3f78104f7265bee143f1574078a1b88875528324..7d95310bbca16b2a7c148294dd13886849799daf 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivData.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivData.m @@ -193,6 +193,26 @@ if ~isempty(titlefontsize), options.titlefontsize = titlefontsize; end +%% Handle case of BLOQ data present +BLLOQdataPresent = 0; +% Check if dataPlot contains LLOQ information and if yes then check if BLLOQ data are present +if sum(~isnan(dataPlot.LLOQ)) > 0, + % LLOQ information present + if sum(dataPlot.DV<dataPlot.LLOQ) > 0, + % BLLOQ data present + BLLOQdataPresent = 1; + end +end + +if BLLOQdataPresent, + dataPlot.isBLLOQ = double(dataPlot.DV<dataPlot.LLOQ); + options.nameColorGroup = 'isBLLOQ'; + options.linecolorsCustom = [0.2 0.2 0.2; 0.8500 0.3250 0.0980]; + options.linetypesCustom = {'.','x'}; + options.showmarkers = 1; + options.markersize = 12; +end + %% Do the plotting IQMplottrellis(dataPlot,nameGroup,nameX,nameY,options) diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivDataRelation.m b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivDataRelation.m index 7b21f45009e98ac808d5b227a3cbc1644e32cc73..1757e08e36fce502a4b704b553a75c71acd65d46 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivDataRelation.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreIndivDataRelation.m @@ -3,8 +3,9 @@ function [] = IQMexploreIndivDataRelation(data,OBSNAMES,GROUP,options) % dataset used in IQM Tools. % % [SYNTAX] -% [] = IQMexploreIndivData(data,OBSNAMES,GROUP) -% [] = IQMexploreIndivData(data,OBSNAMES,GROUP,options) +% [] = IQMexploreIndivDataRelation(data,OBSNAMES) +% [] = IQMexploreIndivDataRelation(data,OBSNAMES,GROUP) +% [] = IQMexploreIndivDataRelation(data,OBSNAMES,GROUP,options) % % [INPUT] % data: MATLAB PKPD dataset in general standard data format. diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexplorePKdataWrapper.m b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexplorePKdataWrapper.m index c1c9cf4ca6ce50e1f1874887d5166b0c6f69c430..201ea4ffeb2d7b00415b30a213b5d5a8fbb683fc 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexplorePKdataWrapper.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexplorePKdataWrapper.m @@ -183,6 +183,7 @@ IQMexploreDataContents(data,DOSENAME,OBSNAME,[outputPath '/00_Data_Contents']); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Summary plot individual data on linear Y axis +% Show BLLOQ data by different marker and color %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% options = []; options.logY = 0; @@ -197,6 +198,7 @@ IQMexploreIndivData(data,OBSNAME,'',options) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Summary plot individual data on logarithmic Y axis +% Show BLLOQ data by different marker and color %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% options = []; options.logY = 1; @@ -211,6 +213,7 @@ IQMexploreIndivData(data,OBSNAME,'',options) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Plot individual data - 1 page per ID - linear Y axis +% Show BLLOQ data by different marker and color %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% options = []; options.logY = 0; @@ -224,6 +227,7 @@ IQMexploreIndivData(data,OBSNAME,DOSENAME,options) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Plot individual data - 1 page per ID - log Y axis +% Show BLLOQ data by different marker and color %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% options = []; options.logY = 1; @@ -236,7 +240,8 @@ options.filename = [outputPath '/04_Individual_Single_Log']; IQMexploreIndivData(data,OBSNAME,DOSENAME,options) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Assessment of data availability TRT/STUDY +%% Assessment of data availability TRT/STUDY +% Show BLLOQ data by different marker and color %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% filename = [outputPath '/05_Summary_Study_Treatment']; IQMstartNewPrintFigure(filename); @@ -267,13 +272,35 @@ options.maxlegendentries = 20; options.titlefontsize = 8; options.labeltextsize = 8; options.ticklabeltextsize = 8; + +% Handle case of BLOQ data present +BLLOQdataPresent = 0; +% Check if dataPlot contains LLOQ information and if yes then check if BLLOQ data are present +if sum(~isnan(dataPlot.LLOQ)) > 0, + % LLOQ information present + if sum(dataPlot.DV<dataPlot.LLOQ) > 0, + % BLLOQ data present + BLLOQdataPresent = 1; + end +end +if BLLOQdataPresent, + dataPlot.isBLLOQ = double(dataPlot.DV<dataPlot.LLOQ); + options.nameColorGroup = 'isBLLOQ'; + options.linecolorsCustom = [0.2 0.2 0.2; 0.8500 0.3250 0.0980]; + options.linetypesCustom = {'.-','x-'}; + options.showmarkers = 1; + options.markersize = 12; +end + +% Plot IQMplotfacetgrid(dataPlot,nameX,nameY,nameGroupX,nameGroupY,options) IQMprintFigure(gcf,filename); close(gcf); IQMconvert2pdf(filename); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Dose normalized plots - over TIME +%% Dose normalized plots - over TIME +% Not considering BLOQ data (remove it prior to dose-normalization) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% filename = [outputPath '/06_Dose_Normalized_TIME']; @@ -283,6 +310,9 @@ IQMstartNewPrintFigure(filename); % Linear %%%%%%%%% dataPlot = subsetIQM(data,'NAME',OBSNAME); +% Remove BLLOQ data +dataPlot(dataPlot.DV<dataPlot.LLOQ,:) = []; + % Dose normalize the PK data DOSE = dataPlot.DOSE; DOSE(DOSE==0) = 1; dataPlot.DVnorm = dataPlot.DV./DOSE; @@ -299,7 +329,7 @@ options.linewidth = 1; options.nameSubGroup = 'USUBJID'; options.nameColorGroup = 'TRTNAME'; options.xlabelText = sprintf('Time [%s]',dataPlot.TIMEUNIT{1}); -options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlot.NAME{1}); +options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlot.NAME{1}); options.ylabelfirstonly = 1; options.logY = 0; options.showmedian = 1; @@ -323,6 +353,9 @@ close(gcf); % Log %%%%%%%%% dataPlot = subsetIQM(data,'NAME',OBSNAME); +% Remove BLLOQ data +dataPlot(dataPlot.DV<dataPlot.LLOQ,:) = []; + % Dose normalize the PK data DOSE = dataPlot.DOSE; DOSE(DOSE==0) = 1; dataPlot.DVnorm = dataPlot.DV./DOSE; @@ -339,7 +372,7 @@ options.linewidth = 1; options.nameSubGroup = 'USUBJID'; options.nameColorGroup = 'TRTNAME'; options.xlabelText = sprintf('Time [%s]',dataPlot.TIMEUNIT{1}); -options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlot.NAME{1}); +options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlot.NAME{1}); options.ylabelfirstonly = 1; options.logY = 1; options.showmedian = 1; @@ -363,7 +396,8 @@ IQMconvert2pdf(filename); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Dose normalized plots - over TAD +%% Dose normalized plots - over TAD +% Not considering BLOQ data (remove it prior to dose-normalization) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% filename = [outputPath '/07_Dose_Normalized_TAD']; @@ -373,6 +407,9 @@ IQMstartNewPrintFigure(filename); % Linear %%%%%%%%% dataPlot = subsetIQM(data,'NAME',OBSNAME); +% Remove BLLOQ data +dataPlot(dataPlot.DV<dataPlot.LLOQ,:) = []; + % Dose normalize the PK data DOSE = dataPlot.DOSE; DOSE(DOSE==0) = 1; dataPlot.DVnorm = dataPlot.DV./DOSE; @@ -389,7 +426,7 @@ options.linewidth = 1; options.nameSubGroup = 'USUBJID'; options.nameColorGroup = 'TRTNAME'; options.xlabelText = sprintf('TAD [%s]',dataPlot.TIMEUNIT{1}); -options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlot.NAME{1}); +options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlot.NAME{1}); options.ylabelfirstonly = 1; options.logY = 0; options.showmedian = 1; @@ -413,6 +450,9 @@ close(gcf); % Log %%%%%%%%% dataPlot = subsetIQM(data,'NAME',OBSNAME); +% Remove BLLOQ data +dataPlot(dataPlot.DV<dataPlot.LLOQ,:) = []; + % Dose normalize the PK data DOSE = dataPlot.DOSE; DOSE(DOSE==0) = 1; dataPlot.DVnorm = dataPlot.DV./DOSE; @@ -429,7 +469,7 @@ options.linewidth = 1; options.nameSubGroup = 'USUBJID'; options.nameColorGroup = 'TRTNAME'; options.xlabelText = sprintf('TAD [%s]',dataPlot.TIMEUNIT{1}); -options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlot.NAME{1}); +options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlot.NAME{1}); options.ylabelfirstonly = 1; options.logY = 1; options.showmedian = 1; @@ -452,7 +492,7 @@ close(gcf); IQMconvert2pdf(filename); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Summary statistics covariates +%% Summary statistics covariates %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if ~isempty(covNames) || ~isempty(catNames), filename = [outputPath '/08_Summary_Statistics']; @@ -470,9 +510,13 @@ end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Graphical exploration of potential covariate effect on PK % We only look at dose normalized information +% Not considering BLOQ data (remove it prior to dose-normalization) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dataPlot = subsetIQM(data,'NAME',OBSNAME); +% Remove BLLOQ data +dataPlot(dataPlot.DV<dataPlot.LLOQ,:) = []; + % Dose normalize the PK data DOSE = dataPlot.DOSE; DOSE(DOSE==0) = 1; dataPlot.DVnorm = dataPlot.DV./DOSE; @@ -520,7 +564,7 @@ if ~isempty(covNames), options.nameSubGroup = 'USUBJID'; options.nameColorGroup = newCatCovNames{k}; options.xlabelText = sprintf('TAD [%s]',dataPlotCov.TIMEUNIT{1}); - options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlotCov.NAME{1}); + options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlotCov.NAME{1}); options.ylabelfirstonly = 1; options.logY = 0; options.showmedian = 1; @@ -549,7 +593,7 @@ if ~isempty(covNames), options.nameSubGroup = 'ID'; options.nameColorGroup = newCatCovNames{k}; options.xlabelText = sprintf('TAD [%s]',dataPlotCov.TIMEUNIT{1}); - options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlotCov.NAME{1}); + options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlotCov.NAME{1}); options.ylabelfirstonly = 1; options.logY = 1; options.showmedian = 1; @@ -598,7 +642,7 @@ if ~isempty(catNames), options.nameSubGroup = 'ID'; options.nameColorGroup = catNames{k}; options.xlabelText = sprintf('TAD [%s]',dataPlotCov.TIMEUNIT{1}); - options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlotCov.NAME{1}); + options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlotCov.NAME{1}); options.ylabelfirstonly = 1; options.logY = 0; options.showmedian = 1; @@ -627,7 +671,7 @@ if ~isempty(catNames), options.nameSubGroup = 'ID'; options.nameColorGroup = catNames{k}; options.xlabelText = sprintf('TAD [%s]',dataPlotCov.TIMEUNIT{1}); - options.ylabelText = sprintf('(DOSE NORMALIZED) %s',dataPlotCov.NAME{1}); + options.ylabelText = sprintf('(DOSE NORMALIZED) %s - BLLOQ data not considered',dataPlotCov.NAME{1}); options.ylabelfirstonly = 1; options.logY = 1; options.showmedian = 1; diff --git a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreSummaryStats.m b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreSummaryStats.m index 26fec432509acf731c6692d8ffae812e0323e4a3..521a0ff40584b61459888804424529c53808a22f 100644 --- a/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreSummaryStats.m +++ b/IQM Tools/IQMpro/tools/03-ClinicalData/04-DataExploration/IQMexploreSummaryStats.m @@ -1,8 +1,10 @@ function [continuousTable,categoricalTable] = IQMexploreSummaryStats(data,covNames,catNames,filename) % This function produces summary statistics for the provided dataset % and displays the results in a table in the MATLAB window. If a filename -% is provdided, the results are also exported to this file. The data need -% to be provided at least in the general dataset format or in the task +% is provdided, the results are also exported to separate files for +% continuous and categorical covariates. (_continuous and _categorical are +% postfixed to the filename). +% The data need to be provided at least in the general dataset format or in the task % specific dataset format. The covariates considered need to be available % as columns. % @@ -141,11 +143,13 @@ disp(textDisplay); textDisplay = IQMconvertCellTable2ReportTable(categoricalTable,'text'); disp(textDisplay); -% Convert to report text and export to file if filename defined +% Convert to report text text1 = IQMconvertCellTable2ReportTable(continuousTable,'report'); text2 = IQMconvertCellTable2ReportTable(categoricalTable,'report'); -text = sprintf('%s\r\n\r\n%s',text1,text2); -IQMwriteText2File(text,[strrep(filename,'.txt','') '.txt']); + +% Export to file if filename defined +IQMwriteText2File(text1,[strrep(filename,'.txt','') '_continuous' '.txt']); +IQMwriteText2File(text2,[strrep(filename,'.txt','') '_categorical' '.txt']); diff --git a/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/02-BLOQhandling/IQMhandleBLOQdata.m b/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/02-BLOQhandling/IQMhandleBLOQdata.m index 6d28042618d211d536805377967b380cdbbb45bd..7e4b29e418eacd28ff4a8b0dea822c5342d6c699 100644 --- a/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/02-BLOQhandling/IQMhandleBLOQdata.m +++ b/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/02-BLOQhandling/IQMhandleBLOQdata.m @@ -63,7 +63,7 @@ end % Copy dataset datanew = data; -% Find all NAMEs which have LLOQ information +% Find all NAMEs which have LLOQ information and are observations NAMES_BLLOQ_present = unique(datanew.NAME(~isnan(datanew.LLOQ) & datanew.EVID==0)); % Find all records that are BLOQ for all names that have LLOQ information diff --git a/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/03-DataNLMEConversion/IQMconvertTask2NLMEdataset.m b/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/03-DataNLMEConversion/IQMconvertTask2NLMEdataset.m index 875aef45f720be05a3a1220d636223947cc8c08f..c1c79039d0126fc05c9ba6072e28670c47addec7 100644 --- a/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/03-DataNLMEConversion/IQMconvertTask2NLMEdataset.m +++ b/IQM Tools/IQMpro/tools/04-NLMEdataPreparation/03-DataNLMEConversion/IQMconvertTask2NLMEdataset.m @@ -29,7 +29,8 @@ function [dataNLME] = IQMconvertTask2NLMEdataset(data,DOSENAMES,OBSNAMES,covName % with a more informed dataset % - Displaying mapping between ADM and ROUTE, and YTYPE and OBSNAMES % - If NRDOSES and INTERVAL not all NaN then an ADDL and II column are -% added to the dataNLME dataset +% added to the dataNLME dataset. NRDOSES is same as ADDL and INTERVAL +% same as II. % % [SYNTAX] % [dataNLME] = IQMconvertTask2NLMEdataset(data,DOSENAMES,OBSNAMES,covNames,catNames,regressionNames) @@ -130,7 +131,7 @@ if sum(abs(dataNLME.CENS)) ~= 0, end % Define initial structure of NLME dataset -varNames = {'IXGDF' 'IGNORE' 'ID' 'USUBJID' 'STUDY' 'STUDYN' 'TRT' 'TRTNAME' 'TIME' 'TIMEPOS' 'NT' 'TIMEUNIT' 'YTYPE' 'NAME' 'DV' 'UNIT' 'CENS' 'MDV' 'EVID' 'AMT' 'ADM' 'INTERVAL' 'NRDOSES' 'ROUTE' 'RATE' 'TINF'}; +varNames = {'IXGDF' 'IGNORE' 'ID' 'USUBJID' 'STUDY' 'STUDYN' 'TRT' 'TRTNAME' 'TIME' 'TIMEPOS' 'NT' 'TAD' 'TIMEUNIT' 'YTYPE' 'NAME' 'DV' 'UNIT' 'CENS' 'MDV' 'EVID' 'AMT' 'ADM' 'INTERVAL' 'NRDOSES' 'ROUTE' 'RATE' 'TINF'}; % Check if DOSE is present in the dataset then add it if ~isempty(strmatchIQM('DOSE',dataNLME.Properties.VariableNames,'exact')), @@ -139,7 +140,7 @@ if ~isempty(strmatchIQM('DOSE',dataNLME.Properties.VariableNames,'exact')), else % DOSE not present ... might be due to multiple DOSENAMES ... check for k=1:length(DOSENAMES), - doseNameTest = ['DOSE_' makeVariableNameIQM(DOSENAMES{k})]; + doseNameTest = ['DOSE' makeVariableNameIQM(DOSENAMES{k})]; if ~isempty(strmatchIQM(doseNameTest,dataNLME.Properties.VariableNames,'exact')), % if only one DOSENAME(S) then rename to DOSE ... otherwise % keep changed name diff --git a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMLXTRANfile.m b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMLXTRANfile.m index 8c7ba80d95750ba99fde522863616d764b905705..2f4ce57625823c8d6c2c7933eef2cc82e08e5b5e 100644 --- a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMLXTRANfile.m +++ b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMLXTRANfile.m @@ -9,7 +9,7 @@ function [filename, moddosinfo] = IQMcreateMLXTRANfile(model,dosing,varargin) % [SYNTAX] % [filename] = IQMcreateMLXTRANfile(model,dosing) % [filename] = IQMcreateMLXTRANfile(model,dosing,filename) -% [filename] = IQMcreateMLXTRANfile(model,dosing,filename,SILENT,regressionParameters) +% [filename] = IQMcreateMLXTRANfile(model,dosing,filename,SILENT,regressionParameters,startTime) % % [INPUT] % model: IQMmodel (annotated with additional information, see above) @@ -29,6 +29,8 @@ function [filename, moddosinfo] = IQMcreateMLXTRANfile(model,dosing,varargin) % in the dataset. Default: {}. If this input argument % is given, then the regressor information in the % IQMmodel and IQMdosing scheme is ignored!!! +% startTime: Allows to set the start of the integration to a desired time. +% Default: []: do not set. % % [OUTPUT] % filename: constructed from the model name "modelname_MLXTRAN". @@ -81,17 +83,22 @@ if nargin >= 3, end SILENT = 0; -if nargin == 4, +if nargin >= 4, SILENT = varargin{2}; end regressionParameters = {}; IGNORE_MODELDEFINED_REGRESSORS = 0; -if nargin == 5, +if nargin >= 5, regressionParameters = varargin{3}; IGNORE_MODELDEFINED_REGRESSORS = 1; end +startTime = []; +if nargin>=6, + startTime = varargin{4}; +end + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Start conversion protocol with important information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -392,6 +399,12 @@ fprintf(fid,'; =============================================\r\n'); fprintf(fid,'EQUATION:\r\n'); fprintf(fid,'; =============================================\r\n'); +if ~isempty(startTime), + fprintf(fid,'\r\n\t; Start time of integration'); + fprintf(fid,'\r\n\t; -------------------------\r\n'); + fprintf(fid,'\tt0 = %g\r\n\r\n',startTime); +end + fprintf(fid,'\r\n\t; Always use stiff solver'); fprintf(fid,'\r\n\t; -----------------------\r\n'); fprintf(fid,'\todeType = stiff\r\n\r\n'); diff --git a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMONOLIXproject.m b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMONOLIXproject.m index 81270ff061a4069495bb84956fe2e049b059ddbd..ce128ba061d4e4e8826192eba11bbbbd48f14bc1 100644 --- a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMONOLIXproject.m +++ b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/IQMcreateMONOLIXproject.m @@ -81,6 +81,7 @@ function IQMcreateMONOLIXproject(model,dosing,data,projectPath,varargin) % options.algorithm.LLsetting: 'linearization' (default) or 'importantsampling' % options.algorithm.FIMsetting: 'linearization' (default) or 'stochasticApproximation' % options.algorithm.INDIVparametersetting: 'conditionalMode' (default) ... others not considered for now. +% options.algorithm.startTime: start time of integration. default: [] (not set). % options.SILENT: =0: do some output in the command window, =1: do no output in command window (default: 0) % options.keepProjectFolder: =0: remover already existing folder, =1: keep it % @@ -159,6 +160,7 @@ try SILENT = options.SILENT; try INDIVparametersetting = options.algorithm.INDIVparametersetting; catch, INDIVparametersetting = 'conditionalMode'; end try LLsetting = options.algorithm.LLsetting; catch, LLsetting = 'linearization'; end try FIMsetting = options.algorithm.FIMsetting; catch, FIMsetting = 'linearization'; end +try startTime = options.algorithm.startTime; catch, startTime = []; end try keepProjectFolder = options.keepProjectFolder; catch, keepProjectFolder = 0; end try Ntests = options.Ntests; catch, Ntests = 1; end try std_noise_setting = options.std_noise_setting; catch, std_noise_setting = 0.5; options.std_noise_setting = 0.5; end @@ -372,7 +374,7 @@ ms = struct(model); % Provide the correct regression parameter names as ordered in the dataset %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% regressionParametersData = dataCSV.Properties.VariableNames(strmatchIQM('X',explodePCIQM(data.dataHeaderIdent),'exact')); -[modelFileName, modelInfo] = IQMcreateMLXTRANfile(model,dosing,'model',SILENT,regressionParametersData); +[modelFileName, modelInfo] = IQMcreateMLXTRANfile(model,dosing,'model',SILENT,regressionParametersData,startTime); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % If needed, reorder parameters diff --git a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/auxiliary/sampleMONOLIXpopulationParametersIQM.m b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/auxiliary/sampleMONOLIXpopulationParametersIQM.m index 576628fdb6faf9357f5a1e4bf17d1293e11d87b8..8cdc03c5a9e637362687ba534ce4d8077aa05025 100644 --- a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/auxiliary/sampleMONOLIXpopulationParametersIQM.m +++ b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/01-MONOLIX/auxiliary/sampleMONOLIXpopulationParametersIQM.m @@ -343,13 +343,20 @@ COV2informationCategories = {}; COV2informationValues = {}; for k=1:length(COVparameters), - if k==1 || isempty(strmatchIQM(COVparameters{k},COV2parameters,'exact')), + if k==1, COV2parameters{k} = COVparameters{k}; COV2covNames{k} = COVcovNames{k}; % Determine reference value reference = input.covariates.catReference(strmatchIQM(COVcovNames{k},input.covariates.catNames,'exact')); COV2informationCategories{k} = [reference COVcategory(k)]; COV2informationValues{k} = [0 COVvalues(k)]; + elseif isempty(strmatchIQM(COVparameters{k},COV2parameters,'exact')), + COV2parameters{end+1} = COVparameters{k}; + COV2covNames{end+1} = COVcovNames{k}; + % Determine reference value + reference = input.covariates.catReference(strmatchIQM(COVcovNames{k},input.covariates.catNames,'exact')); + COV2informationCategories{end+1} = [reference COVcategory(k)]; + COV2informationValues{end+1} = [0 COVvalues(k)]; else % No all parameter names are present ... need to check if covariate % name already present in this parameter diff --git a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/modelconversion/checkAndChangeModelSyntax4NONMEMconversionIQM.m b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/modelconversion/checkAndChangeModelSyntax4NONMEMconversionIQM.m index 067acea1e397ed454608240cbef00665c15185a5..6b8008f47386b5d983a11801aa8adee255f799d7 100644 --- a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/modelconversion/checkAndChangeModelSyntax4NONMEMconversionIQM.m +++ b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/modelconversion/checkAndChangeModelSyntax4NONMEMconversionIQM.m @@ -103,9 +103,12 @@ end available_state_numbers = setdiff(1:length(ms.states),input_numbers); % Distribute the available_state_numbers to the NaN input values input_states(isnan(input_states(:,1)),1) = available_state_numbers; + + % Column one in input_states defines the new state numbers and second column the old ones % Now switch the ODEs -ms.states(input_states(:,2)) = ms.states(input_states(:,1)); +sort_ix = sortrows(input_states,1); +ms.states = ms.states(sort_ix(:,2)); % Need to reassign stateindex in ms.states.input for k=1:length(ms.inputs), ms.inputs(k).stateindex = input_numbers(k); diff --git a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/parseNONMEMresultsIQM.m b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/parseNONMEMresultsIQM.m index c5b2e143b6735acac30370d02436da0c7e567e5f..f5a38fcef165d371b3c9505f2d5c768a84156806 100644 --- a/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/parseNONMEMresultsIQM.m +++ b/IQM Tools/IQMpro/tools/05-NLMEtoolsInterfaces/02-NONMEM/auxiliary/parseNONMEMresultsIQM.m @@ -28,6 +28,7 @@ function [ output ] = parseNONMEMresultsIQM( path_to_nonmem_project_folder, tran % <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> +% Set seed to lead to consistent sampling results if nargin == 1, transformFlag = 0; end diff --git a/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMcreateNLMEproject.m b/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMcreateNLMEproject.m index f289856335b4f7bb998d79dcee5db418d2683719..ccfaeabf5ce90db98ae5b957dc0fe5d84f6ad1b5 100644 --- a/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMcreateNLMEproject.m +++ b/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMcreateNLMEproject.m @@ -101,6 +101,7 @@ function [] = IQMcreateNLMEproject(TOOL,model,dosing,data,projectPath,options) % options.algorithm.LLsetting: 'linearization' (default) or 'importantsampling' % options.algorithm.FIMsetting: 'linearization' (default) or 'stochasticApproximation' % options.algorithm.INDIVparametersetting: 'conditionalMode' (default) ... others not considered for now. +% options.algorithm.startTime: start time of integration. default: [] (not set). % <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> diff --git a/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMgetNLMEfitIndivPopMeanParam.m b/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMgetNLMEfitIndivPopMeanParam.m index 52f15a2d875bfc5671e5acea4de3f03c87101381..5d6396d7a6acee5710c4bd02cc5e0031d560657a 100644 --- a/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMgetNLMEfitIndivPopMeanParam.m +++ b/IQM Tools/IQMpro/tools/06-NMLEprojectHandling/IQMgetNLMEfitIndivPopMeanParam.m @@ -47,31 +47,61 @@ elseif isempty(data), cd(oldpath); end -% Get data with only covariates covNames and catNames and USUBJID -dataCOV = unique(data(:,{'USUBJID',header.COVARIATENAMES{:}})); +% process some things +x = strmatchIQM('',header.CATNAMES); +header.CATNAMES(x) = []; +x = strmatchIQM('',header.COVNAMES); +header.COVNAMES(x) = []; -% Cycle through dataCOV and keep only first entry for a single USUBJID +% Cycle through data and keep only first entry for a single USUBJID % (should not happen anyway) -allID = unique(dataCOV.USUBJID); +dataFIRST = data; +allID = unique(dataFIRST.USUBJID); for k=1:length(allID), - ix = ixdataIQM(dataCOV,'USUBJID',allID(k)); + ix = ixdataIQM(dataFIRST,'USUBJID',allID(k)); if length(ix)>1, - dataCOV(ix(2:end),:) = []; + dataFIRST(ix(2:end),:) = []; end end -% Separate dataCOV into continuous and categorical covariates -dataCAT = dataCOV(:,{'USUBJID' header.CATNAMES{:}}); -dataCOV = dataCOV(:,{'USUBJID' header.COVNAMES{:}}); +% Separate dataFIRST into continuous and categorical covariates +if ~isempty(header.CATNAMES), + if ~isempty(header.CATNAMES{1}), + dataCAT = dataFIRST(:,{'USUBJID' header.CATNAMES{:}}); + end +else + dataCAT = []; +end +if ~isempty(header.COVNAMES), + if ~isempty(header.COVNAMES{1}), + dataCOV = dataFIRST(:,{'USUBJID' header.COVNAMES{:}}); + end +else + dataCOV = []; +end % Sample population parameters from fit FLAG_SAMPLE = 3; % Use point estimates of population parameters (do not consider uncertainty) % Return Nsamples sets of population parameters with covariates taken into account. -NSAMPLES = height(dataCOV); -covNames = dataCOV.Properties.VariableNames(2:end); -covValues = table2array(dataCOV(:,2:end)); -catNames = dataCAT.Properties.VariableNames(2:end); -catValues = table2array(dataCAT(:,2:end)); +NSAMPLES = height(dataFIRST); + +if isempty(dataCOV), + covNames = {}; + covValues = []; +else + covNames = dataCOV.Properties.VariableNames(2:end); + covValues = table2array(dataCOV(:,2:end)); +end + +if isempty(dataCAT), + catNames = {}; + catValues = []; +else + catNames = dataCAT.Properties.VariableNames(2:end); + catValues = table2array(dataCAT(:,2:end)); +end + +% Sample parameters param = IQMsampleNLMEfitParam(projectPath,FLAG_SAMPLE,NSAMPLES,covNames,covValues,catNames,catValues); % Create table out of parameters and names @@ -79,7 +109,7 @@ popmean_param = array2table(param.parameterValuesPopulation); popmean_param.Properties.VariableNames = param.parameterNames; % Add USUBJID to table (same order as for covariates) -popmean_param.USUBJID = dataCOV.USUBJID; +popmean_param.USUBJID = dataFIRST.USUBJID; % Reorder to have USUBJID in the front popmean_param = popmean_param(:,[end 1:end-1]); diff --git a/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisConditionNumber.m b/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisConditionNumber.m new file mode 100644 index 0000000000000000000000000000000000000000..7e726fca419220b1b0c5b8a66b680a7ac1ccb524 --- /dev/null +++ b/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisConditionNumber.m @@ -0,0 +1,78 @@ +function [cNfull,cNfebeta,cNreerror,cNcorr] = IQMfitanalysisConditionNumber(projectPath) +% Determine the condition number for an NLME fit. +% Several condition numbers will be generated: +% - condition number for the full covariance matrix (cNfull) +% - condition number for fixed effects + covariate coefficients (cNfebeta) +% - condition number for random effects + error model parameters (cNreerror) +% - condition number for correlation parameters (cNcorr) +% +% [SYNTAX] +% [cNfull,cNfebeta,cNreerror,cNcorr] = IQMfitanalysisConditionNumber(projectPath) +% +% [INPUT] +% projectPath: Path to the project.nmctl NONMEM project file +% +% [OUTPUT] +% Different condition numbers + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> + +% parse the results +if isNONMEMprojectIQM(projectPath), + x = parseNONMEMresultsIQM(projectPath); +elseif isMONOLIXprojectIQM(projectPath), + x = parseMONOLIXresultsIQM(projectPath); +else + error('Unknown project type.'); +end + +cNfull = NaN; +cNfebeta = NaN; +cNreerror = NaN; +cNcorr = NaN; + +if ~isempty(x.parameters.correlationmatrix), + + cNfull = cond(x.parameters.correlationmatrix); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % fE and beta + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + names = [x.rawParameterInfo.fixedEffects.names x.rawParameterInfo.covariate.names]; + ix_all = []; + for k=1:length(names), + ix_all(k) = strmatchIQM(names{k},x.parameters.names,'exact'); + end + cNfebeta = cond(x.parameters.correlationmatrix(ix_all,ix_all)); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % omegas and error parameters + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if isNONMEMprojectIQM(projectPath), + names = [strrep(x.rawParameterInfo.randomEffects.names,'omega','omega2') x.rawParameterInfo.errorParameter.names]; + else + names = [x.rawParameterInfo.randomEffects.names x.rawParameterInfo.errorParameter.names]; + end + ix_all = []; + for k=1:length(names), + ix_all(k) = strmatchIQM(names{k},x.parameters.names,'exact'); + end + cNreerror = cond(x.parameters.correlationmatrix(ix_all,ix_all)); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Correlation of correlations + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if isNONMEMprojectIQM(projectPath), + names = [strrep(x.rawParameterInfo.correlation.names,'corr(','omega2(')]; + else + names = x.rawParameterInfo.correlation.names; + end + if length(names) >= 1, + ix_all = []; + for k=1:length(names), + ix_all(k) = strmatchIQM(names{k},x.parameters.names); + end + cNcorr = cond(x.parameters.correlationmatrix(ix_all,ix_all)); + end +end + diff --git a/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisShrinkage.m b/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisShrinkage.m new file mode 100644 index 0000000000000000000000000000000000000000..0833667f450409d2295e326efe11508e51c83259 --- /dev/null +++ b/IQM Tools/IQMpro/tools/07-NLMEfitAnalysis/IQMfitanalysisShrinkage.m @@ -0,0 +1,31 @@ +function [OMEGAnames,eta_shrinkage_percent] = IQMfitanalysisShrinkage(projectPath) +% This function determines the ETA shrinkage for the random effects +% ETA Shrinkage is defined as: +% +% eta_shrinkage_percent = 100*(1-std(eta))/OMEGA; +% +% [SYNTAX] +% [OMEGAnames,eta_shrinkage_percent] = IQMfitanalysisShrinkage(projectPath) +% +% [INPUT] +% projectPath: Path to a NONMEM or MONOLIX project folder. +% +% [OUTPUT] +% OMEGAnames: Cell-array with names of all fixed effect parameters +% eta_shrinkage_percent: Vector with shrinkage values. NaN if parameter +% was not having a random effect. + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> + +% Handle NONMEM/MONOLIX +if isMONOLIXprojectIQM(projectPath), + [ dataeta, OMEGA, OMEGAnames ] = parseMONOLIXetasIQM( projectPath ); +elseif isNONMEMprojectIQM(projectPath), + [ dataeta, OMEGA, OMEGAnames ] = parseNONMEMetasIQM( projectPath ); +else + error('Unknown project type.'); +end + +% Determine shrinkage in percent +eta_shrinkage_percent = 100*(1-std(table2array(dataeta))./OMEGA); + diff --git a/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTable.m b/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTable.m index 1dbb12a119f37d8780ad63f126c70d032081a532..175c7d0bb01011d3b571b1fbbae42a8f06fa38e4 100644 --- a/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTable.m +++ b/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTable.m @@ -1,11 +1,11 @@ -function [] = IQMfitsummaryTable(projectPaths,filename) +function [tableCell] = IQMfitsummaryTable(projectPaths,filename) % This function creates a typical report-type NLME model parameter table. % "projectPaths" defines the NLME projects that are to be reported in this % table. % % [SYNTAX] -% [] = IQMfitsummaryTable(projectPaths) -% [] = IQMfitsummaryTable(projectPaths,filename) +% [tableCell] = IQMfitsummaryTable(projectPaths) +% [tableCell] = IQMfitsummaryTable(projectPaths,filename) % % [INPUT] % projectPaths: Cell-array with paths to NLME projects to create the @@ -110,7 +110,7 @@ end tableCell = {'<TT>' 'Population parameter estimates for considered models'}; tableCell(end+1,1:2+length(RESULTS)) = {'<TH>' '' modelsNames_Short{:}}; -tableCell(end+1,1:2) = {'<TH>' 'Parameter'}; +tableCell(end+1,1:2) = {'<TR>' 'Parameter'}; for k=1:length(RESULTS), tableCell(end,2+k) = {'Value (RSE%) (trans)'}; end diff --git a/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTableSingle.m b/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTableSingle.m new file mode 100644 index 0000000000000000000000000000000000000000..9bd8575bceecfac3c423f21376d8ecd36f1c633a --- /dev/null +++ b/IQM Tools/IQMpro/tools/08-NLMEfitSummary/IQMfitsummaryTableSingle.m @@ -0,0 +1,420 @@ +function [tableCell] = IQMfitsummaryTableSingle(projectPath,filename) +% This function creates a typical report-type NLME model parameter table. +% "projectPaths" defines the NLME projects that are to be reported in this +% table. In contrast to IQMfitsummaryTable this function creates a single +% table with dedicated columns for value, RSE and trans. +% +% [SYNTAX] +% [tableCell] = IQMfitsummaryTableSingle(projectPath) +% [tableCell] = IQMfitsummaryTableSingle(projectPath,filename) +% +% [INPUT] +% projectPath: String with path to NLME project to create the +% fit results table for. +% filename: Path and filename where to generate the output file. +% default: '' (no file generated). +% +% [OUTPUT] +% Table shown in command window and if desired also saved to file for +% reporting purpose. + +% <<<COPYRIGHTSTATEMENT - IQM TOOLS PRO>>> + +% Handle variable input arguments +if nargin<2, + filename = ''; +end + +% Handle cell-array thing +if ischar(projectPath), + projectPath = {projectPath}; +end + +% Check projects if NLME +for k=1:length(projectPath), + if ~isNLMEprojectIQM(projectPath{k}), + error('Path "%s" does not contain an NLME project.',projectPath{k}); + end +end + +% Load project results - do not reorder +order = ''; +RESULTS = parseSelectedProjectFolderResultsIQM(projectPath,order); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Process the RESULT information somewhat +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Determine all available fixed effect parameters in the models +ALLfixEffectNames = {}; +for k=1:length(RESULTS), + if ~isempty(RESULTS(k).rawParameterInfo), + fek = RESULTS(k).rawParameterInfo.fixedEffects.names; + ALLfixEffectNames = [ALLfixEffectNames fek]; + end +end +ALLfixEffectNames = unique(ALLfixEffectNames); + +% Determine all available random effect parameters in the models +ALLrandomEffectNames = {}; +for k=1:length(RESULTS), + if ~isempty(RESULTS(k).rawParameterInfo), + fek = RESULTS(k).rawParameterInfo.randomEffects.names; + ALLrandomEffectNames = [ALLrandomEffectNames fek]; + end +end +ALLrandomEffectNames = unique(ALLrandomEffectNames); + +% Determine all available correlation parameters in the models +ALLcorrelationNames = {}; +for k=1:length(RESULTS), + if ~isempty(RESULTS(k).rawParameterInfo), + fek = RESULTS(k).rawParameterInfo.correlation.names; + ALLcorrelationNames = [ALLcorrelationNames fek]; + end +end +ALLcorrelationNames = unique(ALLcorrelationNames); + +% Determine all available covariate parameters in the models +ALLcovariateNames = {}; +for k=1:length(RESULTS), + if ~isempty(RESULTS(k).rawParameterInfo), + fek = RESULTS(k).rawParameterInfo.covariate.names; + ALLcovariateNames = [ALLcovariateNames fek]; + end +end +ALLcovariateNames = unique(ALLcovariateNames); + +% Determine all available covariate parameters in the models +ALLerrorNames = {}; +for k=1:length(RESULTS), + if ~isempty(RESULTS(k).rawParameterInfo), + fek = RESULTS(k).rawParameterInfo.errorParameter.names; + ALLerrorNames = [ALLerrorNames fek]; + end +end +ALLerrorNames = unique(ALLerrorNames); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Calculate shrinkage +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +[shOMEGAnames,sheta_shrinkage_percent] = IQMfitanalysisShrinkage(projectPath{1}); + +% Need to order sheta_shrinkage_percent according to ALLrandomEffectNames +eta_shrinkage_ordered = []; +for k=1:length(ALLrandomEffectNames), + xxx = strrep(strrep(ALLrandomEffectNames{k},')',''),'omega(',''); + % find index in ALLrandomEffectNames + ix = strmatchIQM(xxx,shOMEGAnames,'exact'); + if isempty(ix), + error('Check!'); + end + eta_shrinkage_ordered(k) = sheta_shrinkage_percent(ix); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Create the table +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Retain last name of model only +modelsNames_Short = {RESULTS.model}; +for k=1:length(modelsNames_Short), + [~,f,e] = fileparts(modelsNames_Short{k}); + modelsNames_Short{k} = [f e]; +end + + + +tableCell = {'<TT>' 'Population parameter estimates for considered model' '' '' '' ''}; +tableCell(end+1,:) = {'<TH>' 'Parameter' 'Value' 'RSE (%)' 'Transformation' 'Shrinkage (%)'}; + + +% FIXED EFFECTs - non-estimated get postfix (FIX). +FLAG_FOOTER = 0; +for k0=1:length(ALLfixEffectNames), + tableCell(end+1,1:2) = {'<TR>' ALLfixEffectNames{k0}}; + for k=1:length(RESULTS), + % Check if parameter in model + ix = strmatchIQM(ALLfixEffectNames{k0},RESULTS(k).rawParameterInfo.fixedEffects.names,'exact'); + if ~isempty(ix), + modelVALUES = RESULTS(k).rawParameterInfo.fixedEffects.values(ix); + modelRSES = RESULTS(k).rawParameterInfo.fixedEffects.rse(ix); + modelESTIMATED = RESULTS(k).rawParameterInfo.fixedEffects.estimated(ix); + if modelESTIMATED, + value = sprintf('%1.3g',modelVALUES); + if RESULTS(k).NONMEM, + rse = sprintf('%1.3g%%*',modelRSES); + FLAG_FOOTER = 1; + else + rse = sprintf('%1.3g%%',modelRSES); + end + else + if ~isnan(modelVALUES), + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(FIX)'); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),postFillCharIQM(rse,14,' ')); + else + value = NaN; + rse = NaN; + end + end + % Get MU referencing transformation of fixed effect + modelTRANS = RESULTS(k).rawParameterInfo.fixedEffects.distribution_info{ix}; + if strcmp(modelTRANS,'(psi)'), + modelTRANS = 'normal'; + elseif strcmp(modelTRANS,'log(psi./(1-psi))'), + modelTRANS = 'logitnormal'; + elseif strcmp(modelTRANS,'log(psi)'), + modelTRANS = 'lognormal'; + else + error('Unknown transformation of fixed effect.'); + end + else + valuerse = '-'; + value = '-'; + rse = '-'; + modelTRANS = '-'; + end + % Add to table + tableCell(end,2+k) = {value}; + tableCell(end,2+k+1) = {rse}; + tableCell(end,2+k+2) = {modelTRANS}; + end +end + +% % Add footer if needed +% if FLAG_FOOTER, +% tableCell(end+1,1:2) = {'<TF>' '* Standard errors for NONMEM models approximated by sampling due to MU-Referencing'}; +% end + +% Separator +tableCell(end+1,1) = {'<TR>'}; + +% RANDOM EFFECTs - non-estimated get postfix (FIX). +for k0=1:length(ALLrandomEffectNames), + tableCell(end+1,1:2) = {'<TR>' ALLrandomEffectNames{k0}}; + for k=1:length(RESULTS), + % Check if parameter in model + ix = strmatchIQM(ALLrandomEffectNames{k0},RESULTS(k).rawParameterInfo.randomEffects.names,'exact'); + if ~isempty(ix), + modelVALUES = RESULTS(k).rawParameterInfo.randomEffects.values(ix); + modelRSES = RESULTS(k).rawParameterInfo.randomEffects.rse(ix); + modelESTIMATED = RESULTS(k).rawParameterInfo.randomEffects.estimated(ix); + if modelESTIMATED==1, + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('%1.3g%%',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + if ~isnan(modelVALUES), + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(FIX)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + value = NaN; + rse = NaN; + end + end + else + value = '-'; + rse = '-'; + end + % Add to table + tableCell(end,2+k) = {value}; + tableCell(end,2+k+1) = {rse}; + % Add shrinkage + if isnan(eta_shrinkage_ordered(k0)), + tableCell(end,2+k+3) = {'-'}; + else + tableCell{end,2+k+3} = round(eta_shrinkage_ordered(k0),1); + end + end +end + +% Separator +tableCell(end+1,1) = {'<TR>'}; + +% CORRELATIONS - non-estimated get postfix (FIX). +for k0=1:length(ALLcorrelationNames), + tableCell(end+1,1:2) = {'<TR>' ALLcorrelationNames{k0}}; + for k=1:length(RESULTS), + % Check if parameter in model + ix = strmatchIQM(ALLcorrelationNames{k0},RESULTS(k).rawParameterInfo.correlation.names,'exact'); + if ~isempty(ix), + modelVALUES = RESULTS(k).rawParameterInfo.correlation.values(ix); + modelRSES = RESULTS(k).rawParameterInfo.correlation.rse(ix); + modelESTIMATED = RESULTS(k).rawParameterInfo.correlation.estimated(ix); + if modelESTIMATED, + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('%1.3g%%',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + if ~isnan(modelVALUES), + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(FIX)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + value = NaN; + rse = NaN; + end + end + else + value = '-'; + rse = '-'; + end + % Add to table + tableCell(end,2+k) = {value}; + tableCell(end,2+k+1) = {rse}; + end +end + +% Separator +tableCell(end+1,1) = {'<TR>'}; + +% COVARIATES - non-estimated get postfix (FIX). +for k0=1:length(ALLcovariateNames), + tableCell(end+1,1:2) = {'<TR>' ALLcovariateNames{k0}}; + for k=1:length(RESULTS), + % Check if parameter in model + ix = strmatchIQM(ALLcovariateNames{k0},RESULTS(k).rawParameterInfo.covariate.names,'exact'); + if ~isempty(ix), + modelVALUES = RESULTS(k).rawParameterInfo.covariate.values(ix); + modelRSES = RESULTS(k).rawParameterInfo.covariate.rse(ix); + modelESTIMATED = RESULTS(k).rawParameterInfo.covariate.estimated(ix); + if modelESTIMATED, + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(%1.3g%%)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),postFillCharIQM(rse,14,' ')); + else + if ~isnan(modelVALUES), + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(FIX)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),postFillCharIQM(rse,14,' ')); + else + value = NaN; + rse = NaN; + end + end + transinfo = getCovCatTransInfo(ALLcovariateNames{k0},RESULTS(k)); + valuerse = sprintf('%s%s',valuerse,transinfo); + else + value = '-'; + rse = '-'; + transinfo = '-'; + end + % Add to table + tableCell(end,2+k) = {value}; + tableCell(end,2+k+1) = {rse}; + tableCell(end,2+k+2) = {transinfo}; + end +end + +% Separator +tableCell(end+1,1) = {'<TR>'}; + +% ERROR PARAMETERS - non-estimated get postfix (FIX). +for k0=1:length(ALLerrorNames), + tableCell(end+1,1:2) = {'<TR>' ALLerrorNames{k0}}; + for k=1:length(RESULTS), + % Check if parameter in model + ix = strmatchIQM(ALLerrorNames{k0},RESULTS(k).rawParameterInfo.errorParameter.names,'exact'); + if ~isempty(ix), + modelVALUES = RESULTS(k).rawParameterInfo.errorParameter.values(ix); + modelRSES = RESULTS(k).rawParameterInfo.errorParameter.rse(ix); + modelESTIMATED = RESULTS(k).rawParameterInfo.errorParameter.estimated(ix); + if modelESTIMATED, + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(%1.3g%%)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + if ~isnan(modelVALUES), + value = sprintf('%1.3g',modelVALUES); + rse = sprintf('(FIX)',modelRSES); + valuerse = sprintf('%s %s',postFillCharIQM(value,10,' '),rse); + else + value = NaN; + rse = NaN; + end + end + else + value = '-'; + rse = '-'; + end + % Add to table + tableCell(end,2+k) = {value}; + tableCell(end,2+k+1) = {rse}; + end +end + +% Separator +tableCell(end+1,1) = {'<TR>'}; + +% OBJ, AIC, BIC +tableCell(end+1,1:3) = {'<TR>' 'OBJ' round(RESULTS.OBJ,2)}; +tableCell(end+1,1:3) = {'<TR>' 'AIC' round(RESULTS.AIC,2)}; +tableCell(end+1,1:3) = {'<TR>' 'BIC' round(RESULTS.BIC,2)}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Display results and save to file +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Convert to text and display text +textDisplay = IQMconvertCellTable2ReportTable(tableCell,'text'); +disp(textDisplay); + +% Convert to report text and export to file if filename defined +IQMconvertCellTable2ReportTable(tableCell,'report',filename); + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Function to generate transformation and reference value information for +% covariates ... +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [transinfo] = getCovCatTransInfo(ALLcovariateNamesK0,RESULTS) +COVNAMES = RESULTS.projectHeader.COVNAMES; +CATNAMES = RESULTS.projectHeader.CATNAMES; +BETACOVNAMES = RESULTS.projectHeader.BETACOVNAMES; +BETACOVTRANS = RESULTS.projectHeader.BETACOVTRANS; +BETACATNAMES = RESULTS.projectHeader.BETACATNAMES; +BETACATREFERENCE = RESULTS.projectHeader.BETACATREFERENCE; + +% Process covariate name to get parameter and covariate name +name = strrep(ALLcovariateNamesK0,'beta_',''); +name = strrep(name,')',''); +name = strrep(name,'(',','); +terms = explodePCIQM(name); +pn = terms{1}; +cn = terms{2}; +% Check if categorical or continuous +ctypecont = NaN; +ix = strmatchIQM(cn,COVNAMES,'exact'); +if ~isempty(ix), + ctypecont = 1; + cc = ''; +else + terms = explodePCIQM(cn,'_'); + cn = terms{1}; + cc = terms{2}; + ix = strmatchIQM(cn,CATNAMES,'exact'); + if ~isempty(ix), + ctypecont = 0; + end +end +if isnan(ctypecont), + error('Difficulty to decide covariate type.'); +end +% Handle continuous covariate +if ctypecont, + % Only need to get the covariate transformation + ix = strmatchIQM(ALLcovariateNamesK0,BETACOVNAMES,'exact'); + trans = BETACOVTRANS{ix}; + trans = strrep(trans,'cov',cn); +% transinfo = strrep(sprintf('(%s)',trans),'.',''); + transinfo = sprintf('%s',trans); +else + % Need to get reference category + test = sprintf('beta_%s(%s)',pn,cn); + ix = strmatchIQM(test,BETACATNAMES,'exact'); + trans = BETACATREFERENCE{ix}; + transinfo = sprintf('Reference group: %s',trans); +end +return diff --git a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcompareModels.m b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcompareModels.m index 9e719ecad2592006ad0867b73a41418492d796aa..46b17f6b9457b57878c12b9556f71b10dad72f1b 100644 --- a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcompareModels.m +++ b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcompareModels.m @@ -217,16 +217,16 @@ if ~isempty(data), for k=1:length(catNames), dataCAT.(catNames{k}) = firstRowData.(catNames{k}); end - NdataSubjects = length(dataCOV); + NdataSubjects = height(dataCOV); sampleCovariateDosingIX = ceil(NdataSubjects*rand(1,Nsim)); if ~isempty(dataCOV), - COVvaluesSampled = double(dataCOV(sampleCovariateDosingIX,:)); + COVvaluesSampled = table2array(dataCOV(sampleCovariateDosingIX,:)); else COVvaluesSampled = []; covNames = {}; end if ~isempty(dataCAT), - CATvaluesSampled = double(dataCAT(sampleCovariateDosingIX,:)); + CATvaluesSampled = table2array(dataCAT(sampleCovariateDosingIX,:)); else CATvaluesSampled = []; catNames = {}; @@ -376,4 +376,4 @@ else axis([min(obsTimes) max(obsTimes) get(gca,'YLim')]); end -legend(legendText,'Location','NorthWest','Interpreter','none'); \ No newline at end of file +legend(legendText,'Location','Best','Interpreter','none'); \ No newline at end of file diff --git a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcovariateAssessmentUncertainty.m b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcovariateAssessmentUncertainty.m index 9c6e1488c3a4345680efc97002694b5219561721..686278a8e3b79626e1cfce4f5a902922af6f1d6d 100644 --- a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcovariateAssessmentUncertainty.m +++ b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcovariateAssessmentUncertainty.m @@ -542,7 +542,7 @@ for k=1:length(covariateInfo.ParameterName), tableText{ixRow,ixCol} = sprintf('%1.4g / %1.4g / %1.4g**',quantileIQM(x,0.025),quantileIQM(x,0.5),quantileIQM(x,0.975)); end end -tableText(end+1,1:2) = {'<TF>' sprintf('*Parameter value and 95%% uncertainty range for reference subject.\n**Values have been normalized by the median of the non-normalized value (reference subject).')}; +tableText(end+1,1:2) = {'<TF>' sprintf('*Parameter value and 95 percent uncertainty range for reference subject.\n**Values have been normalized by the median of the non-normalized value (reference subject).')}; % Convert to text and display text textDisplay = IQMconvertCellTable2ReportTable(tableText,'text'); diff --git a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPC.m b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPC.m index af69eab857cb20de4d3a57ff95f8b082711e996f..487dc8c67fa97cf8e25e8ca058eed291af5fb667 100644 --- a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPC.m +++ b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPC.m @@ -164,6 +164,21 @@ if ischar(data), data = IQMloadCSVdataset(data); end +% Check if ADDL present and used - in this case: error +showErrorADDL = 0; +try + ADDL = data.ADDL; + % ADDL exists + if sum(ADDL>0) ~= 0, + showErrorADDL = 1; + end +catch + % ADDL does not exist - fine :) - so it does not need to be checked +end +if showErrorADDL, + error('ADDL column in dataset used to define additional doses. This is not handled in the VPC function.'); +end + if ischar(userDosing), userDosing = IQMdosing(userDosing); end diff --git a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPCstratified.m b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPCstratified.m index 554f8167b96e56cf554cde43c5a8a92b87168c4e..b5bb3737338d67f65ab4d87bcd09a7e6f33ba776 100644 --- a/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPCstratified.m +++ b/IQM Tools/IQMpro/tools/09-NLMEtools/IQMcreateVPCstratified.m @@ -129,6 +129,21 @@ if ischar(model), model = IQMmodel(model); end +% Check if ADDL present and used - in this case: error +showErrorADDL = 0; +try + ADDL = data.ADDL; + % ADDL exists + if sum(ADDL>0) ~= 0, + showErrorADDL = 1; + end +catch + % ADDL does not exist - fine :) - so it does not need to be checked +end +if showErrorADDL, + error('ADDL column in dataset used to define additional doses. This is not handled in the VPC function.'); +end + % Check GROUP checkDataColumnsIQM(data,GROUP); diff --git a/IQM Tools/IQMpro/tools/14-GeneralLinearNLMEmodel/auxiliary/NONMEM/createGeneralLinear_NONMEMprojectIQM.m b/IQM Tools/IQMpro/tools/14-GeneralLinearNLMEmodel/auxiliary/NONMEM/createGeneralLinear_NONMEMprojectIQM.m index c511f8003c12305f211f8ed89d9894f4d08885b5..ddbc6ee21be2db7c55a764dd32ae7a53abb1e19f 100644 --- a/IQM Tools/IQMpro/tools/14-GeneralLinearNLMEmodel/auxiliary/NONMEM/createGeneralLinear_NONMEMprojectIQM.m +++ b/IQM Tools/IQMpro/tools/14-GeneralLinearNLMEmodel/auxiliary/NONMEM/createGeneralLinear_NONMEMprojectIQM.m @@ -246,7 +246,7 @@ try IIVvalues0 = options.IIVvalues0; try errorModels = options.errorModels; catch, errorModels = ''; end try errorParam0 = options.errorParam0; catch, errorParam0 = []; end try covarianceModel = options.covarianceModel; catch, covarianceModel = 'diagonal'; end -try covariateModel = options.covariateModel; catch, covariateModel = ''; end +try covariateModel = options.covariateModel; catch, covariateModel = ''; options.covariateModel = ''; end try covariateModelValues = options.covariateModelValues; catch, covariateModelValues = {}; end try COVestimate = options.COVestimate; catch, COVestimate = {}; end @@ -1850,7 +1850,7 @@ COVARIATENAMES_info = sprintf('; COVARIATENAMES = ''%s''\r\n',x(1:end-1)); PROJECT_INFO_TEXT = sprintf('%s%s',PROJECT_INFO_TEXT,COVARIATENAMES_info); % COVARIATESUSED -COVARIATESUSED = setdiff(explodePCIQM(strrep(strrep(covariateModel,'{',''),'}','')),parameterNames); +COVARIATESUSED = setdiff(explodePCIQM(strrep(strrep(options.covariateModel,'{',''),'}','')),parameterNames); x = sprintf('%s,',COVARIATESUSED{:}); COVARIATESUSED_info = sprintf('; COVARIATESUSED = ''%s''\r\n',x(1:end-1)); PROJECT_INFO_TEXT = sprintf('%s%s',PROJECT_INFO_TEXT,COVARIATESUSED_info);