342 lines
9.6 KiB
Matlab
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
|