Table element access problem
Show older comments
I am reading a table from an excel file
T_Log_Pf = readtable(Log_Pf_path_fn);
But the compiler generates mxArray instead of a table, and when I try to change the element
T_Log_Pf{1, {'sts_e'}} = 1;
or get value of element from the table
x = T_Log_Pf.sts_e(1);
the error "Cell contents reference from a non-cell array object" occurs.
What and how should be corrected in order to work with the table?
Ore the problem is in kompilator setup?
28 Comments
the cyclist
on 8 May 2023
It might be helpful for you to upload the data, or at least a small sample that reproduces the issue. You can use the paper clip icon in the INSERT section of the toolbar.
Alex K
on 9 May 2023
Alex K
on 9 May 2023
Matt J
on 9 May 2023
I see two .xlsx files but not .mat file.
Matt J
on 9 May 2023
I see no .mat file.
dpb
on 9 May 2023
Remove the coder.extrinsic statement from the s-file function...
Alex K
on 9 May 2023
dpb
on 9 May 2023
What are you trying to do here? I don't have Simulink but AFAIK you can write s-functions that call/use MATLAB features and return results to the model.
Where does the coder come into what you're trying to do?
Alex K
on 9 May 2023
I did a search and it appears that readtable isn't supported by the coder(*) -- I wasn't aware until I found <this link> that that's how Simulink function block was implemented; that person had the same problem of trying to use readtable.
The suggestion there was to use <FromSpreadsheet> block. It would seem that would be the way Simulink intends for users to go, having builtin that block.
I don't suppose the venerable xlsread would be supported by coder, either; it would return the numeric data from a spreadsheet as a double array if your spreadsheet were constructed as such without multiple data types. If it is mixed data, then you probaby have an issue in handling mixed data types in a composite object under Simulink.
(*)ADDENDUM:
I guess that's not quite right on reflection; it appears that the coder did actually return something from the spreadsheet after the call using the extrinsic; but the returned object isn't a table in that environment, but an mxArray. Thus, you can't expect to be able to address it as if it were a table; it isn't one. OTOMH, I'm not sure how one would go about dereferencing an mxArray object with m-code; that's the province of mex files that use the encapsulated array with its header info as well as just the array data to pass back and forth.
While got that far, unless you can find some reference or somebody else comes along that knows how to get at the data inside, ooks to me like the shorter route would still be to revert to using straight text files and the builtin tools/blocks in Simulink.
Sorry, without ever having seen a Simulink installation, that's the best I can offer; maybe somebody else will come along or submit an official support request to TMW.
Alex K
on 9 May 2023
dpb
on 9 May 2023
See https://www.mathworks.com/matlabcentral/answers/13297-subscripting-into-an-mxarray#answer_18242 that indicates you have to preallocate the array to be assigned the output by the coder in order to then index into it. The above answer would work for a known size; a comment there asked about dynamically-sized arrays but that one wasn't addressed there.
I don't know if something like (btw, it would be MUCH simpler if you would attach your code snippets as text inline and formatted with the {} Code button instead of images -- nothing can be done with an image but look at it).
function(data,flag)=read_table(fn)
coder.extrinsic('readtable');
tT=table[];
data=tT{:,:};
flag=~isempty(data);
end
or not -- it would create an empty table object; whether that's enough for the coder to stuff the returned table into correctly or not, I've no klew...
I'd still suggest trying the builtin block and/or using whatever toolset there is to read a double array into Simulink directly--that functionality surely must be supported natively without needing such gyrations.
Alex K
on 9 May 2023
"Yes, I know that it is recommended to predefine variables."
The link provided indicates for coder to create the variable from an extrinsic it isn't "recommended" but required in order to be able to address the array later; apparently the coder needs that memory location predefined in the workspace context in order to place the object in the MATLAB variable location to then subsequently be referenced by MATLAB m-file syntax.
You haven't done that above; as noted, the issue of other than a fixed-size array wasn't addressed in the specific forum Q/A; but it's pretty clear to me that SOMEHOW such a memory address has got to be created before the call the the extrinsic function in order that that location is available to the coder in which to place the returned object.
As noted, the point of allocating the null table is NOT that it is a useful entity in and of itself yet, but to have that memory location for the coder to return the table object into in the hope that the coder has enough power to place the actual table into that variable and that you can the reference it normally.
Clearly, what you've tried without doesn't work; this is a wild stab in the dark and probably won't work, either, given that it appeared from the TMW Staff response one needed to allocate the actual space. But, if it does happen to be enough, then you're in luck; if it doesn't, you're no worse off than already are.
I don't follow and you've not shown the code that created the table object -- if that was somehow done in Simulink, then can you make it GLOBAL or structure the code to simply pass it once it was created instead of trying to reread it from the file (which would seem to be quite inefficient way to go about it if this isn't just an initialization routine but one that is going to be called every timestep).
ADDENDUM: Why don't you back off from the table for testing and see if you can mange to read an ordinary double array following the protocol outlined. Until you can get at least the simple case working, there's not much chance of anything more complex...
Alex K
on 9 May 2023
Alex K
on 9 May 2023
dpb
on 9 May 2023
"T_test Local 1x1 mxArray"
shows that
T = readtable('test_table.xlsx');
[row col] = size(T);
will return [1,1] as I presumed it would; what is in the variable is the reference address of the object handle/container wrapping the underlying table data, not the derferenced content that the normal table variable in the MATLAB workspace would show. This obviously has to do with how Coder actually implements the call/return of the extrinsic function instead of acessing it directly from MATLAB code in an m-file.
It would take a lot of digging to figure out just how that all works behind the scenes; without Simulink I can't even think about trying to poke any deeper.
BUT, my poking has gotten to the help topic on <executing extrinsics> that outlines the rules given in the Answer before linked to -- namely the predefinition of the output variable, specifically.
Think you need to start here and read...it may lead you through the weeds to the promised alnd--then again, it wouldn't surprise me to learn tha the higher-abstraction objects like table simply aren't fully supported. The earlier link discussed the code generation process and the analysis going into it in defining the generated code. It is a static analysis which goes a long ways towards why changing sizes doesn't work. Makes me glad don't have to deal with Simulink; this would be a mess to have to deal with...
Alex K
on 9 May 2023
dpb
on 9 May 2023
See <Working with mxArrays>. It answers a lot once know from above that an extrinsic returns an mxArray and only an mxArray. Ergo, while you're calling readtable and expecting to get a MATLAB table; that simply ain't agonna' happen, no matter what. The content of the is contained as an mxArray, but getting at the content through MATLAB engine syntax isn't possible in this context, it seems. See <The MATLAB Array> for the description of how higher-level objects are stored.
In theory, you could write a mex file and decode the table, but if it were my problem, I'd look at far less complex storage mechanisms like straight double arrays if the need is to use the data in Simulink. You're just adding way too much overhead and complexity here.
It seems simple when you can call MATLAB code in a function block until one begins to fully understand how that functionality is implemented. It's nothing at all like just running the code in MATLAB, unfortunately,.
Alex K
on 9 May 2023
dpb
on 10 May 2023
Can you read a .mat file with Simulink? If so, the simpler way might be to save the table to a .mat file and read it instead; creating the .mat file when making the spreadsheet initially.
Alternatively, can you read/dereference a cell array or string array with the external keyword or are only doubles recognized by the preallocation step and everything else is still the raw mxArray? If could do that, then could save the Excel file as text/csv, read the first row only to get the names and then use readmatrix or another similar with the 'NumHeaderlLines',1 and then build the table internally instead of trying to read a table.
Thirdly, can you use low-level i/o functions like fgetl and/or fscanf? In that case, you can definitely read the file saved as text file and build the internal table.
Most of those if they would work would seem more direct than the above...
Alex K
on 10 May 2023
Alex K
on 11 May 2023
Answers (1)
Use this standard syntax to read the data from MS Excel into a table array:
% Way 1: Simple one
MY_file = 'DATA_Exp.xlsx'; % Note the file extension
D = readtable(MY_file); % Reads all data from sheet1 if sheet1 contains any data
% Way 2: A bit more specific
% Reads a data from sheet called "DATA" and all data in cells A1 to D232
D = readtable(MY_file, sheet='DATA', Range = 'A1:D232');
% Way 3: Even more specific
% Reads a data from sheet called "DATA" and all data in cells A1 to D232,
% and preserves variable names from data table headers
D = readtable(MY_file, sheet='DATA', Range = 'A1:D232', VariableNamingRule='preserve');
%% Access Table array elements:
CELL1 = D{1,1} % Cell 1
COL2 = D{:,2} % Column 2
COL34 = D{:,3:4} % Column 3 and 4
% Alt Ways:
Cell01 = D.Reg(1) % Cell 1
Col02 = D.Time_Day(:) % Column 2
Col034 = [D.Total, D.RRP] % Column3 and 4
1 Comment
Categories
Find more on Automated Fixed-Point Conversion in MATLAB in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!