(problem solved, question revised) How to select table rows with varargin and not using logical condition as input argument.
11 次查看(过去 30 天)
显示 更早的评论
(original question)"I have tables. Selecting rows where values meet logical conditions from them is a frequent task. It becomes tiresome when it's done over many tables and logical conditions, and error-prone. So I try to make it easier that I don't need to type repetitive logical conditions. My current solution is using inputParser to re-syntax it as name-value argument.
Because this is the first time I use inputParser, my solution is done in an ad-hoc manner. Any suggestons for improvement is greatly appereciated. For example, in addParameters( ) how do I handle the default values, which I don't need to specify at all."
(revised question) My original question did not really ask what I want to ask. The more accurately way to state my question is: Is there any way to set default input argument to be of all possible values. So when an input argument is not specified, all possible values will be included in a selection logic?
Demo data
T = cell2table({'income', 2020, 1; 'income', 2020, 2; 'income', 2021, 1; 'income', 2021, 2; ...
'expense', 2020, 1; 'expense', 2020, 2}, "VariableNames", {'account', 'year', 'month'});
The output I want for my codes to achieve but fail.
ix = T.account=="income";
% when year and month are not specified in the selection,
% all years and months will be included.
T(ix, :)
% when account and year are assigned values but month is not,
% all months will be included in the selection logic.
ix = T.account=="income";
iy = T.year==2020;
T(ix&iy,:)
However, my codes fail to get similar outputs.
foo(T, 'account', 'expense')
% I want all years and months included in the output.
My codes
function out = foo(T, varargin)
% ???? How to set default values to be all possible values ????%
defaultAccount='income';
defaultYear = 2020;
defaultMonth = 1;
p = inputParser;
addRequired(p, 'T');
addParameter(p, 'account', defaultAccount);
addParameter(p, 'year', defaultYear);
addParameter(p, 'month', defaultMonth);
parse(p, T, varargin{:});
if ismember('account', p.UsingDefaults)
ixAccount = true;
else
ixAccount = T.account==string(p.Results.account);
end
if ismember('year', p.UsingDefaults)
ixYear = true;
else
ixYear = T.year==p.Results.year;
end
if ismember('month', p.UsingDefaults)
ixMonth = true;
else
ixMonth = T.month==p.Results.month;
end
% ???? When year or month is not specified, how to
% make ixYear or ixMonth not appear in the final
% selection logic or they would be set 'true'????
ixRows = ixAccount&ixYear&ixMonth;
out = T(ixRows, :);
end
0 个评论
采纳的回答
Steven Lord
2023-7-4
For this particular application, I wouldn't use the dot notation to access the variables in your table. I'd use one of the other ways to access data in a table, specifically curly brace indexing, to extract the contents of the variable.
load patients
T = table(LastName, Age, Weight);
head(T) % Show just the first few rows
If I just wanted to get all the patients whose Age is 38, I could do so in a couple different ways. I could hard-code that I want to use the Age variable:
A1 = T.Age == 38;
or I could use a more general indexing approach.
variableName = 'Age';
A2 = T{:, variableName} == 38;
isequal(A1, A2)
So if I wanted to select those patients who are 38 and weigh 131 pounds, I could hard code those constraints.
selectedPatients = T.Age == 38 & T.Weight == 131;
Or I could use that curly brace indexing. Let's start off with our criteria and a vector indicating all patients are still eligible for selection (as an initial condition.)
criteria = {'Age', 38, 'Weight', 131};
eligible = true(height(T), 1);
Now iterate.
for whichCriterion = 1:2:length(criteria)
name = criteria{whichCriterion};
value = criteria{whichCriterion+1};
eligible = eligible & T{:, name} == value;
end
Compare the two approaches to check:
isequal(selectedPatients, eligible)
This approach doesn't care how long your criteria cell array is (so long as it has an even length.) It gets a little more complicated if you wanted to use different comparisons (less than, greater than, etc.) instead of or in addition to equality testing, but not significantly more so.
selectedPatients = T.Age > 35 & T.Weight < 150;
Here the criteria is a cell array with three elements: name, comparison method, and value to compare the variable value against.
criteria = {'Age', @(x, y) x > y, 35, 'Weight', @(x, y) x < y, 150};
eligible = true(height(T), 1);
Now iterate.
for whichCriterion = 1:3:length(criteria)
name = criteria{whichCriterion};
comparisonMethod = criteria{whichCriterion+1};
value = criteria{whichCriterion+2};
eligible = eligible & comparisonMethod(T{:, name}, value);
end
Compare the two approaches to check:
isequal(selectedPatients, eligible)
Instead of using the anonymous functions I could have just used the names of the functions associated with the relational operators. gt, > is greater-than and lt, < is less-than.
criteria = {'Age', @gt, 35, 'Weight', @lt, 150}
eligible = true(height(T), 1);
for whichCriterion = 1:3:length(criteria)
name = criteria{whichCriterion};
comparisonMethod = criteria{whichCriterion+1};
value = criteria{whichCriterion+2};
eligible = eligible & comparisonMethod(T{:, name}, value);
end
isequal(selectedPatients, eligible)
Note that while I created criteria manually, you could do it as inputs to a function. Compare what the following function shows it receives as input with the criteria variable above.
showInputsAsCell('Age', @gt, 35, 'Weight', @lt, 150)
function showInputsAsCell(varargin)
disp(varargin)
end
更多回答(2 个)
Satwik
2023-6-26
Hi Simon,
I understand that you are using inputParser in your code but don't want to specify the default values as there is no logic associated to the default values.
The inputParser function requires that a default value be provided for each parameter in case the user does not pass in a value for that parameter. This is to ensure that the function can be parsed correctly even if the user does not pass in all of the required parameters.
Hope this helps!
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Argument Definitions 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!