MATLAB Answers

Using one variable containing all name-value pairs for built-in Matlab functions

10 views (last 30 days)
I want to give users of my code the option to specify name-value pairs when/if they care about them, but if they don't specify them, let Matlab use default values.
For example, a user might enter an options structure for an SVM classifier such as:
Options.kernel = 'rbf';
Options.kernelScale = 0.15;
Then my program would train an SVM classifier using these parameters:
svmModel = fitcsvm(observations, labels, 'KernelFunction', Options.kernel, 'KernelScale', Options.kernelScale)
But if the user only enters the options structure with the 'kernel' field, then my program would call
svmModel = fitcsvm(observations, labels, 'KernelFunction', Options.kernel)
leaving the kernel scale as the default. The issue comes down to how to do this elegantly. I can check all the field names of the Options structure, looking for name value pairs to assign, e.g.
if any(strcmp('kernel', fieldnames(Options)))
if strcmp('rbf', Options.kernel) && any(strcmp('kernelScale', fieldnames(Options)))
svmModel = fitcsvm(observations, labels, 'KernelFunction', Options.kernel, 'KernelScale', Options.kernelScale);
else
svmModel = fitcsvm(observations. labels, 'KernelFunction', Options.kernel);
end
end
But this can quickly get unwieldy for built-in functions with lots of options. Is there a better way? What I'd like to do is send in a single variable with all the name-value pairs, but when I do this, Matlab interprets varargin as being one value not a set of name-value pairs, e.g.
% Set up parameters for model
nameValuePairs = {'KernelFunction', Options.kernel};
switch Options.kernel
case {'rbf', 'gaussian'}
if any(strcmp('kernelScale', fieldnames(Options)))
nameValuePairs = cat(2, nameValuePairs, ...
{'KernelScale', Options.kernelScale});
end
case {'polynomial'}
if any(strcmp('polynomialOrder', fieldnames(Options)))
nameValuePairs = cat(2, nameValuePairs, ...
{'PolynomialOrder', Options.polynomialOrder});
end
end
% Train
svmModel = fitcsvm(observations, labels, nameValuePairs);
Is there a way to make Matlab interpret a single variable as a set of name-value pairs? Many thanks for any thoughts on this!

  0 Comments

Sign in to comment.

Accepted Answer

Matt J
Matt J on 2 Dec 2019
Edited: Matt J on 2 Dec 2019
See inputParser
which by default will expand structures into Name-Value pairs for you automatically. Note, however, that you have the option of turning this behavior off,

  3 Comments

Sandy Throckmorton
Sandy Throckmorton on 2 Dec 2019
I thought about inputParser, but I couldn't figure out how to make Matlab accept it as a list of name-value pairs. For example, if I have the following code:
p = inputParser;
addOptional(p, 'Kernel', 'linear');
addOptional(p, 'KernelScale', 1);
Options.Kernel = 'rbf';
Options.KernelScale = 0.15;
parse(p, Options)
svmModel = fitcsvm(observations, labels, p.Results);
Then I get the following error (which I think is due to Matlab interpretting inputParser as a single variable):
Error using internal.stats.parseArgs (line 42)
Wrong number of arguments.
Error in classreg.learning.paramoptim.parseOptimizationArgs (line 6)
[OptimizeHyperparameters,~,~,RemainingArgs] = internal.stats.parseArgs(...
Error in fitcsvm (line 330)
[IsOptimizing, RemainingArgs] = classreg.learning.paramoptim.parseOptimizationArgs(varargin);
Error in pnTrainSvmClassifier (line 20)
svmModel = fitcsvm(observations, labels, p.Results);
Am I using inputParser wrong? The same error occurs if I put in just 'p' rather than the results.
Matt J
Matt J on 2 Dec 2019
addOptional isn't what you want. You need to use addParameter.
addParameter(p, 'Kernel', 'linear');
addParameter(p, 'KernelScale', 1);
To pass the parsed name-value pairs to another function, you can use my attached struct2pairs mfunction,
parse(p, Options);
nvPairs=struct2pairs(p.Results);
svmModel = fitcsvm(observations, labels, nvPairs{:});
Sandy Throckmorton
Sandy Throckmorton on 3 Dec 2019
Thanks, Matt J! From working through your code, I can see the only thing I was doing wrong before was not transposing my cell array. But the solution you give is also much much cleaner than what I was doing. Many thanks!

Sign in to comment.

More Answers (1)

Image Analyst
Image Analyst on 2 Dec 2019
If you have version 2019b or later version of MATLAB, you might want to use the arguments block.
See Loren's latest blog to learn how it replaces the old input parser.
For example:
arguments % R2019b or newer
Tpoints(1, :) double {mustBeNumeric, mustBeNonnegative} = 0:0.1:10
beta(1, 1) double {mustBeReal} = 3.4e-5
c(1, 1) double {mustBeReal} = 3.3
delta(1, 1) double {mustBeReal} = 3.4
p(1, 1) double {mustBeReal} = 7.9e-3
end
The variable names at the beginning of those lines above are the variables passed into your function via the arguments list. Use whatever names you have for your particular function.

  1 Comment

Matt J
Matt J on 2 Dec 2019
Sadly, the new arguments block functionality does not seem to have the StructExpand functionality that inputParser does (no idea why - it seems like it should be easy). Example,
s.a=3;
s.b=5;
ab0=local
ab1=local('a',3,'b',5)
ab2=local(s)
function ab=local(options)
arguments
options.a=1;
options.b=2;
end
ab=[options.a,options.b];
end
results in
ab0 =
1 2
ab1 =
3 5
Error using test>local
Invalid input argument list. Too many input arguments.
If specifying name-value arguments, ensure that all required inputs are provided, and check for
invalid name.
Error in test (line 6)
ab2=local(s)

Sign in to comment.

Sign in to answer this question.