How can I use table() with a variable number of columns?

10 views (last 30 days)
I'm making a set of tables from a 4x4x4 array, like so:
T{jj} = table(data(jj,:,1)',data(jj,:,2)',data(jj,:,3)',data(jj,:,4)','VariableNames',colName,'RowNames',rowName);
The annoying thing is, I have to change this line every time there's a different number of columns. For example, for 3 columns, the command is:
T{jj} = table(data(jj,:,1)',data(jj,:,2)',data(jj,:,3)','VariableNames',colName,'RowNames',rowName);
I know I could do a switch on the number of columns and have a case for every possible number of columns I might ever have, but is some way around that? The obvious solution of giving it the data as an array doesn't work if you want named rows and columns:
T{jj} = table(data(jj,:,:),'VariableNames',colName,'RowNames',rowName);
Error using table (line 369)
The VariableNames property must contain one name for each variable in the table.
I get the exact same error back if I use slice():
T{jj} = table(slice(data(jj,:,:)),'VariableNames',colName,'RowNames',rowName);
But it works fine without row / column names
T{jj} = table(data(jj,:,:));
Am I missing something simple, or is this maybe a design oversight?
  4 Comments
Stephen23
Stephen23 on 4 Jun 2025
"I see what you're doing, the permute() sets B up so that we can say B(:,:,1) instead of squeeze(A(1,:,:))."
The code in my comment is completely tangential to the question of robustness, it just shows an alternative approach to solve this problem (sometimes posters on this forum appreciate being shown other approaches that they have not considered, or alternative ways to design their data, etc.).
Of course you can apply PERMUTE to each column separately, exactly as you did with SQUEEZE:
A = rand(2,5,4);
C{1} = array2table(permute(A(1,:,:),[2,3,1]), VariableNames="x"+(1:4));
C{2} = array2table(permute(A(2,:,:),[2,3,1]), VariableNames="x"+(1:4));
C{:}
ans = 5×4 table
x1 x2 x3 x4 _______ _______ _______ _______ 0.69712 0.24026 0.23652 0.31064 0.23611 0.66856 0.50021 0.14906 0.17603 0.15412 0.55058 0.11504 0.29684 0.98801 0.12747 0.60465 0.10538 0.20936 0.28676 0.1681
ans = 5×4 table
x1 x2 x3 x4 _______ _______ ________ ________ 0.20072 0.28959 0.79025 0.2991 0.14334 0.61453 0.3802 0.091008 0.48252 0.31495 0.62297 0.14665 0.93305 0.13457 0.9636 0.41809 0.49103 0.89416 0.086166 0.39347
A(2,:,1).' % for comparison: the first column of the second table
ans = 5×1
0.20072 0.14334 0.48252 0.93305 0.49103
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
"But I'm not clear on why that's more "robust"... What does it buy us?"
PERMUTE buys you the guarantee that input dimensions will end up being permuted into the dimensions that are specified, which SQUEEZE does not. Consider the code you showed in your comment here:
What happens one day the input data just happens to have only one column? Lets try it:
A = rand(2,1,4);
array2table(permute(A(1,:,:),[2,3,1]), VariableNames="x"+(1:4)) % PERMUTE -> robust
ans = 1×4 table
x1 x2 x3 x4 _______ ________ _______ _______ 0.20227 0.026228 0.71921 0.72629
array2table(squeeze(A(1,:,:)), VariableNames="x"+(1:4)) % SQUEEZE -> error
Error using array2table (line 57)
The VariableNames property must contain one name for each variable in the table.
Jerry Guern
Jerry Guern on 4 Jun 2025
Aah, okay, THAT's what you meant by robust. Thanks. I'd never actually even used squeeze() before this week, so while I knew it fixed today's problem, I didn't realize what kinds of problems my code was setting up for me. I appreciate the follow-up explanation.

Sign in to comment.

Accepted Answer

Star Strider
Star Strider on 3 Jun 2025
Something like this seems to work --
data = randn(2,5,4);
jj = 1;
T{jj} = array2table(squeeze(data(jj,:,1:3)), VariableNames=compose('Variable %2d',1:3));
jj = 2;
T{jj} = array2table(squeeze(data(jj,:,1:4)), VariableNames=compose('Variable %2d',1:4));
T{1}
ans = 5×3 table
Variable 1 Variable 2 Variable 3 ___________ ___________ ___________ -1.0069 -0.30554 -1.3129 -0.66706 0.97513 -0.12746 0.80276 0.37576 0.6067 0.64226 0.72238 0.10441 1.8353 -0.31753 -1.7157
T{2}
ans = 5×4 table
Variable 1 Variable 2 Variable 3 Variable 4 ___________ ___________ ___________ ___________ 0.3082 -0.7056 1.8303 0.44942 2.7611 1.4362 0.74181 0.76498 -2.2185 1.243 -0.15949 0.31181 -0.078775 0.56237 0.16815 -0.75866 0.30191 0.67109 0.17018 0.53208
.
  2 Comments
Jerry Guern
Jerry Guern on 3 Jun 2025
This works! Thank you.
Here was the syntax that worked for my case:
T{jj} = array2table(squeeze(data(jj,:,:)),VariableNames=colName,RowNames=rowName);

Sign in to comment.

More Answers (2)

the cyclist
the cyclist on 3 Jun 2025
I assume that data, colName, and rowName are consistently dimensioned.
slice = squeeze(data(jj,:,:));
TT{jj} = array2table( slice,'VariableNames',colName,'RowNames',rowName);
  4 Comments
the cyclist
the cyclist on 3 Jun 2025
Hm. This worked for me (and gave the same result as your syntax).
rng default
N = 4;
data = rand(N,N,N);
jj = 1;
colName = {'c1','c2','c3','c4'};
rowName = {'r1','r2','r3','r4'};
slice = squeeze(data(jj,:,:)); % this becomes an [nRows × nVars] matrix
TT{jj} = array2table( slice, ...
'VariableNames', colName, ...
'RowNames', rowName )
TT = 1×1 cell array
{4×4 table}
Image Analyst
Image Analyst on 4 Jun 2025
@Jerry Guern, perhaps this will clear things up:
% Get last dimension.
data = randn(4,4,4); % Create a 3-D array
slice1 = data(:, :, 1); % A 2-D array
whos slice1
Name Size Bytes Class Attributes slice1 4x4 128 double
% Get middle dimension.
array2 = data(:, 1, :); % A 3-D array
whos array2
Name Size Bytes Class Attributes array2 4x1x4 128 double
slice2 = squeeze(array2); % Collapse singleton dimension to make it a 2-D slice
whos slice2
Name Size Bytes Class Attributes slice2 4x4 128 double
% Get first dimension.
array3 = data(1, :, :); % A 3-D array
whos array3
Name Size Bytes Class Attributes array3 1x4x4 128 double
slice3 = squeeze(array3); % Collapse singleton dimension to make it a 2-D slice
whos slice3
Name Size Bytes Class Attributes slice3 4x4 128 double

Sign in to comment.


Image Analyst
Image Analyst on 3 Jun 2025
Yes, you're going to have to do something. If you have more variables that you want to add to a column or columns you want to remove, it won't telepathically know that you want to do it unless you tell it, and tell it how, so you're going to have to do something. Either make up the table brand new, or use functions to change the number of columns.
Try using addvars, removevars, and movevars - maybe they'll be easier for you. Maybe make up just one column and add the others in a loop with addvars until they're all added. Unless you have a very small amount like 2-4 columns it's easier to use a for loop than an if-else block to form the exact table you want.
  2 Comments
Jerry Guern
Jerry Guern on 3 Jun 2025
Well, I wasn't it expecting it to be telepathic, but I thought it might have been able to note the number of rows and columns in the first argument. In fact, I pointed out at the end that it does exactly that if you don't try to include row/col names.
Image Analyst
Image Analyst on 4 Jun 2025
OK, from your initial post and the other posts, I guess I misunderstood, thinking you wanted a table, but it appears you actually want a cell array with tables inside the cells.

Sign in to comment.

Tags

Products


Release

R2024a

Community Treasure Hunt

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

Start Hunting!