ourMELONS/matlab/graph/plotmodel.m
2019-12-16 16:47:21 +01:00

342 lines
9.6 KiB
Matlab

function handle = plotmodel(B, k, varargin)
% PLOTMODEL Plot a causal model.
%
% Syntax: plotmodel(B, k, ...)
%
% Plots a causal model as a directed acyclic graph. The graph
% layout is done with Graphviz, see www.graphviz.org for details.
% You need either Graphviz installed in your system or a Java
% interpreter with internet connection.
%
% Intended usage is to first call LiNGAM to do the causal discovery
% and then to call 'plotmodel' to visualize the estimated model.
%
% Example:
% [B stde ci k] = lingam(data);
% plotmodel(B, k);
%
% Also latent models can be plotted with dashed nodes and incoming/
% outgoing arcs. Use 'latent' argument with logical column array
% indicating the latent variables (in causal order).
%
% Example:
% latent = logical([0 1 0 0]');
% plotmodel(B, k, 'latent', latent, 'target', 'psviewer');
%
% To plot on a specific Matlab figure, use the 'target' argument
% with value 'matlab:[handle]', e.g. 'matlab:3' to plot on figure
% no. 3.
%
% Required input arguments:
% B - the weight matrix.
% k - the causal order of variables.
%
% Optional input arguments in name, value pairs [default]:
% 'target' - target of output ['matlab' (Octave: 'psviewer')]:
% 'matlab' - plot graph on Matlab figure window
% 'java' - plot graph with Grappa. Uses a remote layout server
% if 'dot' can't be found. In that case, needs an
% internet connection.
% 'psviewer' - plot graph with a PS viewer. Default for Octave.
% Edit file 'settings.m' to set your PS viewer.
% '[filename]' - plot graph on eps-file '[filename]'
% 'varnames' - names of variables in cell array of strings [{'x1' 'x2' ...}]
% 'plotarcs' - plot labels for arcs (boolean) [true]
% 'layout' - layout of the graph ['td']:
% 'td' - plot nodes in hierarhial layers, top-down
% 'lr' - plot nodes in hierarhial layers, left to right
% 'circle' - plot nodes in cirle (not possible with
% 'target', 'java')
% 'nodeshapes' - cell array of node shapes ['ellipse']
% 'nodestyles' - cell array of node styles ['solid']
% 'arcstyles' - cell array of arc styles ['solid']
% 'arccolors' - cell array of arc colors ['black']
% 'arclabels' - cell array of arc labels ['B(j, i)']
% 'latent' - a logical column vector of latent variables [[]]
%
% Check if we are running Octave
isoctave = exist('OCTAVE_VERSION');
% Check number of input arguments
if nargin < 2 || rem(nargin, 2) ~= 0
error('number of input arguments must be >= 2 and even');
end
% Initialize variables
dims = size(B, 1);
adj = B(k, k)';
if isoctave
target = 'psviewer';
else
target = 'matlab';
end
handle = []; % Matlab figure handle, [] = create a new figure
varnames = cell([dims 1]);
for i = 1:dims
varnames{i} = strcat('x', int2str(k(i)));
end
plotarcs = true;
layout = 'td';
nodeshapes = cell(dims, 1);
nodeshapes(:) = {'ellipse'};
nodestyles = cell(dims, 1);
nodestyles(:) = {'solid'};
adjmat = adj ~= 0;
arcstyles = cell(dims);
arcstyles(adjmat) = {'solid'};
arccolors = cell(dims);
arccolors(adjmat) = {'black'};
latent = [];
% Handle optional input arguments
for i = 1:2:(nargin - 2)
switch varargin{i}
case 'target', [target handle] = parsetarget(varargin{i + 1});
case 'plotarcs', plotarcs = varargin{i + 1};
case 'layout', layout = varargin{i + 1};
case 'nodeshapes', nodeshapes = varargin{i + 1};
case 'nodestyles', nodestyles = varargin{i + 1};
case 'nodecolors', nodecolors = varargin{i + 1};
case 'arcstyles', arcstyles = varargin{i + 1};
case 'arccolors', arccolors = varargin{i + 1};
case 'arclabels', arclabels = varargin{i + 1};
case 'latent', latent = varargin{i + 1};
case 'varnames'
tempnames = varargin{i + 1};
for i = 1:dims
varnames{i} = tempnames{k(i)};
end
case 'graphvizpath', graphvizpath = varargin{i+1};
otherwise, warning('unknown input argument: %s\n', varargin{i});
end
end
% If the latent variables are given, force the drawing attributes
% of nodes and incoming/outgoing arcs to dashed
nodestyles(latent) = {'dashed'};
latentindices = find(latent)';
for i = latentindices
in = adjmat(:, i);
out = adjmat(i, :);
arcstyles(i, out) = {'dashed'};
arcstyles(in, i) = {'dashed'};
end
% Set filenames
dotfile = 'plotmodel_temp.dot';
switch target
case 'matlab'
if isoctave
format = 'ps';
imgfile = 'plotmodel_temp.ps';
else
format = 'png';
imgfile = 'plotmodel_temp.png';
end
case 'java'
% no need for image file
case 'psviewer'
format = 'ps';
imgfile = newtempfile('ps');
settings % find out the PS viewer
otherwise
format = 'ps';
imgfile = target;
end
% Generate labels for arcs
if ~exist('arclabels')
if plotarcs
arclabels = double2labels(adj);
else
arclabels = double2labels(zeros(dims));
end
end
% Call graph2dot to write the graph to a dot-file
graph2dot(adj, dotfile, ...
'nodelabels', varnames, ...
'nodeshapes', nodeshapes, ...
'nodestyles', nodestyles, ...
'nodecolors', nodecolors, ...
'arclabels', arclabels, ...
'arcstyles', arcstyles, ...
'arccolors', arccolors, ...
'arclabels', arclabels, ...
'leftright', strcmp(layout, 'lr'),...
'graphvizpath', graphvizpath);
if strcmp(target, 'java')
% Call Grappa to produce the picture
libstr = '-cp ../lib/grappa1_2.jar:../lib/lingam.jar';
classstr = ' lingam.GraphPlotter ';
if isoctave
progcall = strcat('java ', libstr, classstr, dotfile);
else
progcall = strcat(['java ', libstr, classstr, dotfile]);
end
else
% Call Graphviz to produce the picture
if strcmp(layout, 'circle')
prog = 'circo';
else
prog = 'dot';
end
if isoctave
progcall = strcat(prog, ' -T', format, ' ', dotfile, ' -o ', imgfile);
else
progcall = strcat([prog, ' -T', format, ' ', dotfile, ...
' -o ', imgfile]);
end
end
% Call Graphviz/Grappa
if ispc
shell = 'dos';
else
shell = 'unix';
end
cd(graphvizpath); % change the directory to use dot.exe
shellcall = strcat(shell, '(''', progcall, ''')');
[status msg] = eval(shellcall);
if status
if strcmp(target, 'java')
error('calling Java caused an error: %s\n', msg);
else
error('calling "%s" caused an error: %s\n', prog, msg);
end
end
switch target
case 'matlab'
% Read the image file
if isoctave
options = '-antialias';
[img cmap] = imread(imgfile, options);
else
[img cmap] = imread(imgfile, 'png');
end
% Create a new window / set the current
if isempty(handle)
handle = figure; % Create a new figure
figx = 100;
figy = 100;
else
figure(handle); % Draw on the given figure
if ~isoctave
position = get(handle, 'Position');
figx = position(1);
figy = position(2);
end
end
% Resize the figure window
if ~isoctave
set(handle, 'Position', [figx figy size(img, 2) size(img, 1)]);
end
% Show the image and set the color map
image(img);
if isoctave
toolow = find(cmap < 0.0);
toohigh = find(cmap > 1.0);
if any(toolow) || any(toohigh)
fprintf('Color map values are out of range [0 1]!\n');
else
colormap(cmap);
end
else
colormap(cmap);
set(gca, 'Position', [0 0 1 1]);
set(gca, 'XTick', []);
set(gca, 'YTick', []);
end
% delete(imgfile);
case 'java'
% nothing to do
case 'psviewer'
fprintf('graph plotted to file "%s"\n', imgfile);
if isoctave
progcall = strcat(PSVIEWER, ' ', imgfile, ' &');
else
progcall = strcat([PSVIEWER, ' ', imgfile, ' &']);
end
shellcall = strcat(shell, '(''', progcall, ''')');
[status msg] = eval(shellcall);
if status
error('calling "%s" caused an error: %s\n', prog, msg);
end
otherwise
fprintf('graph plotted to file "%s"\n', imgfile);
end
% added at 25.5.2009 for saving the raw dot and png from graphviz.
save_preproc = questdlg('Do you wish to save the graph?',...
'Save graph in png and dot format?',...
'Yes','No','Yes');
if isequal(save_preproc,'Yes')
waitALittle;
[filename, pathname] = uiputfile('*.png','Save the graph as');
if filename~=0
[path,name,suffix,version] = fileparts(filename);
copyfile(dotfile, [pathname '\' name '.dot']);
copyfile(imgfile, [pathname '\' filename]);
end
end
% h1 = findobj('Tag','filename1_text');
% [pathstr, name] = fileparts(get(h1,'string'));
delete(dotfile);
delete(imgfile);
figure(handle);
% -----------------------------------------------------------------------------
function name = newtempfile(suffix)
% NEWTEMPFILE Create a new temporary file.
prefix = 'plotmodel_temp_'; % common suffix for all temp files
maxnumber = 0;
files = dir('.');
for i = 1:length(files)
filename = files(i).name;
dotindex = find(filename == '.'); % works for Matlab and Octave
if isempty(dotindex)
continue;
end
suff = filename(dotindex + 1:length(filename));
if strcmp(suff, suffix) & findstr(filename, 'plotmodel_temp_') == 1
number = str2num(filename(16:dotindex - 1));
if number > maxnumber
maxnumber = number;
end
end
end
name = strcat('plotmodel_temp_', num2str(maxnumber + 1), '.', suffix);
% -----------------------------------------------------------------------------
function [target, handle] = parsetarget(s)
% PARSETARGET Parse target and a possible figure handle.
colonind = find(s == ':'); % works for Matlab and Octave
if isempty(colonind)
target = s;
handle = [];
else
colonind = colonind(1);
target = s(1:colonind - 1);
handle = str2num(s(colonind + 1:length(s)));
end