How to manipulate text in uitable?

Hello,
I have a uitable which is based on a cell.
Please see attached an example column.
I would like to do the following:
  1. Align ALL the fields to be in the CENTER.
  2. To make all the numbers in the following format: XXX.XXXEXX (I mean, no more than 3 digits after the dot).
Can someone post a simple code the the 2 tasks above? THANKS !!!

 Accepted Answer

Adam Danz
Adam Danz on 31 Aug 2020
Edited: Adam Danz on 31 Aug 2020
Three methods of centering data containing strings and numbers within a uitable column and setting the format of the numbers for Matlab releases
  1. >= r2019b
  2. >= r2016b
  3. >= r2013a
For Matlab r2019b or later
This will only work if the uitable is within a uifigure (not a regular figure) and requires Matlab r2019b or later.
It converts numeric values to string which often causes more work because if you need to access those values, you must convert them back to numeric.
% Create demo data
C = {'Normal';rand;rand;'Pass';rand;rand};
% Convert numbers to string in %3.2E format
isNum = cellfun(@isnumeric,C);
C(isNum) = compose('%3.2E ',[C{isNum}])
% Load data into UITable
fig = uifigure();
uit = uitable(fig, "Data", C);
% Center all values (requires r2019b or later & use of uifigure)
uis = uistyle('HorizontalAlignment', 'center');
addStyle(uit, uis, 'Column', 1)
For Matlab 2016b or later
This method uses regular figures and use pad() which wasn't available until 2016b.
It also converts numeric values to string which often causes more work because if you need to access those values, you must convert them back to numeric.
% Create demo data
C = {'Normal';rand;rand;'Pass';rand;rand};
% Convert numbers to string in %3.2E format
isNum = cellfun(@isnumeric,C(:,1));
C(isNum,1) = strsplit(strtrim(sprintf('%3.2E ',[C{isNum,1}])));
% Pad left and right with spaces to equate lengths
C(:,1) = pad(C,'both');
% Load data into UITable
fig = figure();
uit = uitable(fig, "Data", C);
For Matlab r2013a or later
Time to upgrade!
This version uses sprintf instead of compose and it uses a low-level method of padding instead of pad. It also uses strsplit which became available in r2013a.
It also converts numeric values to string which often causes more work because if you need to access those values, you must convert them back to numeric.
% Create demo data
C = {'Normal';rand;rand;'Pass';rand;rand};
% Convert numbers to string in %3.2E format
isNum = cellfun(@isnumeric,C(:,1));
C(isNum,1) = strsplit(strtrim(sprintf('%3.2E ',[C{isNum,1}])));
% produce pre and post pads
nchar = cellfun(@numel, C(:,1));
nSpaces = bsxfun(@minus, max(nchar), nchar)/2; % *
prePad = arrayfun(@(n){repmat(' ',1,n)},floor(nSpaces));
postPad = arrayfun(@(n){repmat(' ',1,n)},ceil(nSpaces));
% Pad the cell array
C(:,1) = strcat(prePad, C(:,1),postPad)
% Load data into UITable
fig = figure();
uit = uitable(fig, "Data", C);
*Thanks Walter Roberson for the bsxfun reminder!

13 Comments

Mark Golberg
Mark Golberg on 31 Aug 2020
Edited: Mark Golberg on 31 Aug 2020
That looks great. Unfortunatelly I have Matlab 2015a.
Any work around I can use to achieve similiar results?
Adam Danz
Adam Danz on 31 Aug 2020
Edited: Adam Danz on 31 Aug 2020
I've updated my answer with an alternative but I just realized pad() didn't come out until r2016b. I'll have to think some more.
Ok, you should be good to go with the 3rd method. Let us know if it's ok (written in r2019b).
WOW! Thank you so much. I'm pretty sure that's the solution I'm looking for.
BUT, my original cell is 5x19, and contains all sorts of fields (nums , string , etc..).
I've attached it in myCell.mat file here.
Can you please check? Your 2013b solution doesn't seem to work...
Let me just recap - I'm trying to do the following:
  1. Center align all my fields (some fields are of the form: [num1 num2]
  2. Make all the numbers (whether it is string or a number) to be of the format - XX.XXeXX
meaning:
- two digits to the left of the decimal point (if applicable of course).
- two digits to the right of the decimal point + exponential view with 2 digits.
THANK YOU SO MUCH !!!
(I wish I could upgrade my MATLAB version...)
I can't test it in r15a but I've checked the releases to the functions involved in the 3rd method and there shouldn't be any version-related problems but I can't be certain without testing it.
Notice in the code, the solution is designed to act on column 1 and only column 1. If you want to adapt that to working will all of your table you'll need to remove the (:,1) indexing (see below) or loop through the columns you want to reformat.
load('C:\Users\adamd\OneDrive\Documents\Matlab\temp\myCell.mat')
C = Y;
isNum = cellfun(@isnumeric,C);
C(isNum) = strsplit(strtrim(sprintf('%3.2E ',[C{isNum}])));
nchar = cellfun(@numel, C);
nSpaces = bsxfun(@minus, max(nchar), nchar)/2;
prePad = arrayfun(@(n){repmat(' ',1,n)},floor(nSpaces));
postPad = arrayfun(@(n){repmat(' ',1,n)},ceil(nSpaces));
C = strcat(prePad, C,postPad)
fig = figure();
uit = uitable(fig, "Data", C);
More importantly, you have html code in several of your cells which you hadn't mentioned before. "Pass" is a lot different than "'<html><table border=0 width=400 bgcolor=#00FF00><TR><TD>Pass</TD></TR> </table></html>'" .
This solultion will not work for cells that have html. Did you programmatically insert the html? If so, the fix is easy -- just execute the solution prior to converting to html. If your raw data contain the html then the solution will be more difficul. You'll need to make a copy of the array, use regular expressions to remove the html, apply the solution to the simplified array without html, and then add the html back in.
Lastly, if you update to r2019b or later, all of this is much easier with the uistyle function that can be used to control cell color and alignment without the need to use html.
prePad = arrayfun(@(n){repmat(' ',1,n)},floor((max(nchar)-nchar)/2));
that line of code gives error:
Error using -
Matrix dimensions must agree.
Should it be - " ... max(max(nchar..." ? (I've tried, but it creates very strange effect).
What's the logic of that line of code?
floor((bsxfun(@minus, max(nchar), nchar)/2)
Adam Danz
Adam Danz on 31 Aug 2020
Edited: Adam Danz on 31 Aug 2020
ahhh right. Implicit expansion became available in r2016b. Thanks, Walter.
@Mark Golberg, I've updated the 3rd method with Walter's correction.
Thank you. Still some gaps are left. Please see image below:
I'm running this code:
C = Y;
isStringWhichIsNum = cellfun(@(x) str2double(x) , C);
isStringWhichIsNum2 = arrayfun(@(x) ~isnan(x) , isStringWhichIsNum);
C(isStringWhichIsNum2) = cellfun(@(x) {str2double(x)} , C(isStringWhichIsNum2));
isNum = cellfun(@isnumeric,C);
C(isNum) = strsplit(strtrim(sprintf('%3.3e ',[C{isNum}])));
nchar = cellfun(@numel, C);
nSpaces = bsxfun(@minus, max(nchar), nchar)/2;
prePad = arrayfun(@(n){repmat(' ',1,n)},floor(nSpaces));
postPad = arrayfun(@(n){repmat(' ',1,n)},ceil(nSpaces));
C = strcat(prePad, C ,postPad);
Some questions:
  1. Column 1 - why there is such difference? several fields (row 3 for example) are not centered at all.
  2. Column 2 - some fields are PERFECT (like row 14-15), while some are not centered at all (like 2-4).
  3. Can I somehow apply a rule, if the exponent is less than 3 then display it normally ---> 0.5 leave 0.5, no need to convert to 5.000e-01
  4. Why field (15,2) is 0.1786 instead of 1.786e-01?
  5. Also, how can I make the 'PASS' fields to be centered (I can't execute the HTML before... not even sure what it means...) ?
THANKS again for all the HELP !!!
Adam Danz
Adam Danz on 31 Aug 2020
Edited: Adam Danz on 31 Aug 2020
Column 1 - why there is such difference? several fields (row 3 for example) are not centered at all.
These aren't the data you shared so I cannot look into it too deeply. One explanation is that the unexpected cells contain html strings. You should know that this method does not center the string within the column. It centers all of the strings about the longest string. If you want the strings to appear centered within the column, all you need to do is set the column width to the width of the longest string. That can be done by setting ColumnWidth to Auto, I believe. Here's the 2015a documentation for this (FYI, you're going to lose the online documentation relatively soon since MathWorks only keeps it for ~5 years).
Column 2 - some fields are PERFECT (like row 14-15), while some are not centered at all (like 2-4).
Same comment as #1.
Can I somehow apply a rule, if the exponent is less than 3 then display it normally ---> 0.5 leave 0.5, no need to convert to 5.000e-01
Try using sprintf('%.3g ',__) instead of %e. Explore other options.
Why field (15,2) is 0.1786 instead of 1.786e-01?
I don't know. I don't have the data. But I have a feeling some of your numeric data are strings and other numeric data are actual numbers (e.g. 3.14 vs '3.14'). Since you're only formatting the actual numbers, any numbers in string format will not be formatted. Look at your data at various stages in the process.
Also, how can I make the 'PASS' fields to be centered (I can't execute the HTML before... not even sure what it means...) ?
How did the html code get there in the first place? If you don't have a version of your data without the html code, you'll have to use regular expressions to isolate the key words, remove the html code, add the spaces around the words using the existing algorithm, and then add back in the html. That's some heavy lifting if you haven't worked with regexp before and it's well beyond the scope of this question.
Just so you know, this is the results I get with the data you've shared, using default column widths (r2019b)
THANK YOU SO Much.
I've actually applied your solution on all the fields, excluded the HTML ones, and it works just fine (I can live with the 'PASS' not being centered).
Last question:
is there a way to automatically set ColumnWidth to a state where even the longest character is seen? the 'auto' property doesn't quite make it...
Sounds like you got it working. Figuring out what works 10-releases back can be challenging and time comsuming.
I briefly looked into the column width fitting, specifically for threads in the Answers forum that dated back to 2015 or prior. It's unclear whether the auto-column-with is expected to work or not but some people were setting the column widths based on the max number of characters in each column. Again, the html code poses a problem for your data since that adds a lot of extra characters that don't actually appear in the table.
These 4 lines below find the max number of characters per column while ignoring any cells with html
nonHTML = cellfun(@isempty, regexp(C, '\<html\>'));
strlen = cellfun(@numel, C);
strlen(~nonHTML) = nan;
maxLen = max(strlen,[],1);
% maxLen =
% 9 9 9 9 86 86 10 10 9 9 11 86 86 32 27 86 31 26 86
As you can see, there are some columns with large numbers of characters even though we're ignoring html cells. I looked into this and apparently you've got some cells in the example data that contain a large number of empty spaces. Maybe that doesn't exist in your data.
If you use this method, note that column width is specified in units of pixesl whereas the values above are in units of characters. Unless you use a fixed-width font, the pixel width of characters vary. So you can either estimate the avg pixel-width of all character and then add a bit of space (I'm sure there's data out there already) or you can used a fixed width font and figure out how many pixels you need per character.
Thank you. Indeed that's what I'm using, estimating column width based on maxLen in each column (multiplying by some apriory found factor).
I agree, it's quite frustrating working with such an old version, knowing that alot of this could be easly solved in the newer ones. But, chanllenges are what we're here for, right?! :-)
THANK you very much for all your assitance, Adam.
---
I've posted another question, in a different thread. regarding freezing row/column in a uitable? Perhaps you could have a look.

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2015a

Asked:

on 31 Aug 2020

Edited:

on 1 Sep 2020

Community Treasure Hunt

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

Start Hunting!