Is rowfun Showing Strange Behavior when the Target Function Returns an Empty Output?

Define a function that echos a scalar input, unless the input is 3, which returns an empty row vector 1x0.
function y = fun1(x)
if x == 3
y = zeros(1,0);
else
y = x;
end
end
Create a table
T = table((1:5).','VariableNames',"x");
As expected, the size of T is 5 x 1 (though I don't understand the return of ans here; I don't see ans when running locally with R2024a)
size(T)
ans = 1×2
5 1
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Now call @doc:rowfun to execute fun1() on each row of T.x. What might be the impact of fun1 returning an empty matrix for row 3?
foo = rowfun(@fun1,T,"InputVariables","x","OutputVariableNames","y")
Not sure what happened there. Why isn't there any output displayed? What actually is foo?
foo
Still no output displayed.
Apparently foo is 5x1 table, even though rowfun presumably only returned four rows (because of the empty)
whos foo
Name Size Bytes Class Attributes foo 5x1 953 table
We can access the variable, but foo.y only has four rows
foo.y
ans = 4×1
1 2 4 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
while the size of foo is 5 x 1 (and why is ans being shown)?
size(foo)
ans = 1×2
5 1
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Common use of rowfun is to append to the original table.
T = [T,rowfun(@fun1,T,"InputVariables","x","OutputVariableNames","y")]
T
Why wasn't there any output? And why isn't there an error somewhere?
T is now being shown as 5 x 2
size(T)
ans = 1×2
5 2
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
x doesn't change
T.x
ans = 5×1
1 2 3 4 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
But T.y only has four rows. How can two variables in the same table have different number of rows?
T.y
ans = 4×1
1 2 4 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Just out of curiosity, the third row of T is
T(3,:)
ans = 1×2 table
x y _ _ 3 4
We can't access the "end" of the table (and I believe we'd get the same error if tyring T(5,:) )
try
T(end,:)
catch ME
ME.message
end
ans = 'Index in position 1 exceeds array bounds. Index must not exceed 4.'
The behavior of the call to rowfun is different on R2024a, where it throws an error (f there is an anonymous function that is functionally equivalent to fun1 here)
But that output showing foo as a 5x1 table before the error message seems strange. Shouldn't the error prevent that output? And if that output is there, shouldn't that output show what foo actually is? And the error message doesn't really make sense anyway. Prior to that line foo wasn't even defined, so if rowfun is returning a 4x1, it should not be a problem to assign that output to the previously-nonexistent LHS variable.
Anyway, continuing in 2026a ...
Change the function so that an input of 3 returns a 0x0 empty
function y = fun2(x)
if x == 3
y = [];
else
y = x;
end
end
T = table((1:5).','VariableNames',"x");
T = [T,rowfun(@fun2,T,"InputVariables","x","OutputVariableNames","y")]
Error using tabular/rowfun (line 191)
The function 'fun2' returned an output with more than one row when applied to the 3rd row in A.
Now rowfun fails, as probably should be the expected behavior, but the error message makes no sense. How could fun2 return an output with more than one row when applied to the third row of T when the output in that case should be 0x0?
Should rowfun always throw an error if the output doesn't have the same number of rows as the input, as seems to be the case in 2024a? Maybe not if rowfun is being used in isolation, but then there should definitely be an error when that output of rowfun is horizontally concatenated with the original table.
And why is there no output from lines that don't end with a semicolon?

3 Comments

Also, the doc page for rowfun states that for the default OutputFormat, as above,
"func must return an array with the same number of rows each time it is called."
Seems to me that rowfun should have immediately returned an error in the examples above.
Now I'm confused about that statement in the doc. It seems to me that if the output of rowfun is a table, then the output of func should always have one row. But the doc says only that it have "the same number of rows each time it is called."
Of course, if func returns, for example, two rows on each call, then rowfun immediately errors, as it should (unless the OutputFormat is cell, see below).
T = table((1:3)');
try
S = rowfun(@fun,T,"InputVariables","Var1")
catch ME
ME.message
end
ans = 'The function 'fun' returned an output with more than one row when applied to the 1st row in A.'
Why doesn't the doc say "func must return an array with one row each time it is called." with the understanding that a scalar has one row in case the OutputFormat is uniform.
The complete statement in the doc is:
"If func returns an array with a different number of rows each time it is called, then specify the OutputFormat name-value argument as "cell". Otherwise, func must return an array with the same number of rows each time it is called."
That first sentence doesn't seem correct, or at least not all-encompassing. Cell output is also needed if func always returns the same number of rows each time it's called, but not one row.
S = rowfun(@fun,T,"InputVariables","Var1",OutputFormat="cell")
S = 3×1 cell array
{2×1 double} {2×1 double} {2×1 double}
function y = fun(x)
y = [x;x];
end

Sign in to comment.

Answers (2)

Stephen23
Stephen23 on 22 Sep 2026 at 6:51
Edited: Stephen23 on 22 Sep 2026 at 7:51
Interesting.
Apparently you have discovered a way to generate a fundamentally malformed table :)
My guess about the behaviors that you demonstrated is that they are caused by the lack of a single point of truth declaring the table to be invalid, so each operation or function decides for itself how it can handle the malformed table:
  • Operations that only consult the table header (size, height, horzcat) succeed, because they never touch the columns/variables.
  • Operations that use the actual column data (display, T.y, T(X,:)) will either fail outright or return the true length, because they're bound by the real data, not the table header.
This explains the observed behavior:
  • The lack of display may be caused by the overloaded DISPLAY (or similar) routine attempting/checking that all columns/variables have the same number of rows (a prerequisite for displaying), which fails... and because this was not a forseen use-case it simply has no alternative display or even any error message. It simply fails by doing nothing.
  • "How can two variables in the same table have different number of rows?" by tricking it, as you just did.
  • "the third row of T is..." this works because the overloaded indexing actively goes through each column/variable and selects that data. When you try to access the 5th row it fails due to indexing into a non-existent element of x.
  • The size is 5x2 because the size is stored in the table header as its own data. Operations that refer to the size but do not need to access the actual columns/variables still work, e.g. SIZE, HEIGHT, HORZCAT as you demonstrate, etc.
This behavior matches earlier explanations of the table implementation:
So the answer to your title is: yes, rowfun should not let that occur.
I do not understand your comments regarding ans.

3 Comments

Consider the table bytes listed here:
V = 1:5;
T = array2table(V(:),'VariableNames',"x");
U = rowfun(@fun1,T,"InputVariables","x","OutputVariableNames","y")
whos
Name Size Bytes Class Attributes T 5x1 961 table U 5x1 953 table V 1x5 40 double
The difference is 8 bytes, i.e. one element of a double array. So apparently WHOS checks the actual table data.
function y = fun1(x)
if x==3
y = nan(1,0);
else
y = x;
end
end
"Apparently you have discovered ..."
And it was an unfortunate discovery at that. Had to spend some time debugging to figure out that my target function was the culprit. Fortunately for me, I immediately observed the problem because I did something like T(end,:). But if I only needed T(3,:) then the code would have merrily executed while generating incorrect results. If only rowfun had returned an error as it probably should in accordance with its own documentation.
"The lack of display .... " This was interesting. Here on Answers I'd occasionally get a wall of red text at one of those lines without the semicolon. But then I'd edit, not changing anything related to the line that generated the message, and rerun and the same wall of text did not show up. I don't recall whether or not I saw that same behavior running locally. I distinctly recall the red text starting as "Warning: Error ....." , which I thought was an interesting choice of words for Matlab.
As for ans .... Compare the output here on 2026a
V = 1:5;
T = array2table(V(:),'VariableNames',"x");
size(T)
ans = 1×2
5 1
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
with the output from 2024a
That extra "1x2" here in 2026a was throwing me for a loop. It does seem to be the current style for displaying 2D arrays but the more I look at it the less I like it, at least for 2D arrays.
Just to show that the observed behavior isn't limited to rowfun output with a single variable ....
[V1,V2,V3] = deal((1:5).');
T = table(V1,V2,V3)
T = 5×3 table
V1 V2 V3 __ __ __ 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
foo = rowfun(@fun,T,"InputVariables","V"+(1:3),"OutputVariableNames","y"+(1:3)) % No display?
foo.y1
ans = 5×1
1 2 3 4 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
foo.y2
ans = 4×1
1 2 4 5
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
●
foo.y3
ans = 3×1
2 3 4
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
function [y1,y2,y3] = fun(V1,V2,V3)
y1 = V1;
if V2 == 3
y2 = zeros(1,0);
else
y2 = V2;
end
if V3 == 1 || V3 == 5
y3 = zeros(1,0);
else
y3 = V3;
end
end

Sign in to comment.

How could fun2 return an output with more than one row when applied to the third row of T when the output in that case should be 0x0?
The relevant section of rowfun is,
n = size(b_data{igrp,jout},1);
if grouped
...
elseif any(n ~= 1) % ~grouped
error(message('MATLAB:table:rowfun:UngroupedRowSize',funName,ordinalString(igrp)));
end
Here, n=0 but the code assumes that n~=1 is the same as n>1.

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Products

Release

R2026a

Asked:

on 22 Sep 2026 at 2:23

Commented:

about 23 hours ago

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!