In MATLAB how can I write out a multidimensional array as a string that looks like a raw numpy array?

27 views (last 30 days)
gwoo
gwoo on 10 Aug 2019
Commented: gwoo on 11 Aug 2019
The Goal
(Forgive me for length of this, it's mostly background and detail.)
I'm contributing to a TOML encoder/decoder for MATLAB and I'm working with numerical arrays right now. I want to input (and then be able to write out) the numerical array in the same format. This format is the nested square-bracket format that is used by *numpy.array*. For example, to make multi-dimensional arrays in numpy:
The following is in python, just to be clear. It is a useful example though my work is in MATLAB.
2D arrays
```python
>> x = np.array([1,2])
>> x
array([1, 2])
>> x = np.array([[1],[2]])
>> x
array([[1],
[2]])
```
3D array
```python
>> x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>> x
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
```
4D array
```python
>> x = np.array([[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]])
>> x
array([[[[ 1, 2],
[ 3, 4]],
[[ 5, 6],
[ 7, 8]]],
[[[ 9, 10],
[11, 12]],
[[13, 14],
[15, 16]]]])
```
The input is a logical construction of the dimensions by nested brackets. Turns out this works pretty well with the TOML array structure. I can already successfully parse and decode any size/any dimension numeric array with this format from TOML to MATLAB numerical array data type.
Now, I want to encode that MATLAB numerical array back into this char/string structure to write back out to TOML (or whatever string).
So I have the following 4D array in MATLAB (same 4D array as with numpy):
```matlab
>> x = permute(reshape([1:16],2,2,2,2),[2,1,3,4])
x(:,:,1,1) =
1 2
3 4
x(:,:,2,1) =
5 6
7 8
x(:,:,1,2) =
9 10
11 12
x(:,:,2,2) =
13 14
15 16
```
And I want to turn that into a string that has the same format as the 4D numpy input (with some function named *bracketarray* or something):
```matlab
>> str = bracketarray(x)
str =
'[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'
```
I can then write out the string to a file.
EDIT: I should add, that the function numpy.array2string() basically does exactly what I want, though it adds some other whitespace characters. But I can't use that as part of the solution, though it is basically the functionality I'm looking for.
The Problem
Here's my problem. I have successfully solved this problem for up to 3 dimensions using the following function, but I cannot for the life of me figure out how to extend it to N-dimensions. I feel like it's an issue of the right kind of counting for each dimension, making sure to not skip any and to nest the brackets correctly.
Current bracketarray.m that works up to 3D
```matlab
function out = bracketarray(in, internal)
in_size = size(in);
in_dims = ndims(in);
% if array has only 2 dimensions, create the string
if in_dims == 2
storage = cell(in_size(1), 1);
for jj = 1:in_size(1)
storage{jj} = strcat('[', strjoin(split(num2str(in(jj, :)))', ','), ']');
end
if exist('internal', 'var') || in_size(1) > 1 || (in_size(1) == 1 && in_dims >= 3)
out = {strcat('[', strjoin(storage, ','), ']')};
else
out = storage;
end
return
% if array has more than 2 dimensions, recursively send planes of 2 dimensions for encoding
else
out = cell(in_size(end), 1);
for ii = 1:in_size(end) %<--- this doesn't track dimensions or counts of them
out(ii) = bracketarray(in(:,:,ii), 'internal'); %<--- this is limited to 3 dimensions atm. and out(indexing) need help
end
end
% bracket the final bit together
if in_size(1) > 1 || (in_size(1) == 1 && in_dims >= 3)
out = {strcat('[', strjoin(out, ','), ']')};
end
end
```
Help me Obi-wan Kenobis, y'all are my only hope!
EDIT 2: Added test suite below and modified current code a bit.
Test Suite
Here is a test suite to use to see if the output is what it should be. Basically just copy and paste it into the MATLAB command window. For my current posted code, they all return true except the ones more than 3D. My current code outputs as a cell. If your solution output differently (like a string), then you'll have to remove the curly brackets from the test suite.
disp({1, isequal(bracketarray(ones(1,1)), {'[1]'})})
disp({2, isequal(bracketarray(ones(2,1)), {'[[1],[1]]'})})
disp({3, isequal(bracketarray(ones(1,2)), {'[1,1]'})})
disp({4, isequal(bracketarray(ones(2,2)), {'[[1,1],[1,1]]'})})
disp({5, isequal(bracketarray(ones(3,2)), {'[[1,1],[1,1],[1,1]]'})})
disp({6, isequal(bracketarray(ones(2,3)), {'[[1,1,1],[1,1,1]]'})})
disp({7, isequal(bracketarray(ones(1,1,2)), {'[[[1]],[[1]]]'})})
disp({8, isequal(bracketarray(ones(2,1,2)), {'[[[1],[1]],[[1],[1]]]'})})
disp({9, isequal(bracketarray(ones(1,2,2)), {'[[[1,1]],[[1,1]]]'})})
disp({10,isequal(bracketarray(ones(2,2,2)), {'[[[1,1],[1,1]],[[1,1],[1,1]]]'})})
disp({11,isequal(bracketarray(ones(1,1,1,2)), {'[[[[1]]],[[[1]]]]'})})
disp({12,isequal(bracketarray(ones(2,1,1,2)), {'[[[[1],[1]]],[[[1],[1]]]]'})})
disp({13,isequal(bracketarray(ones(1,2,1,2)), {'[[[[1,1]]],[[[1,1]]]]'})})
disp({14,isequal(bracketarray(ones(1,1,2,2)), {'[[[[1]],[[1]]],[[[1]],[[1]]]]'})})
disp({15,isequal(bracketarray(ones(2,1,2,2)), {'[[[[1],[1]],[[1],[1]]],[[[1],[1]],[[1],[1]]]]'})})
disp({16,isequal(bracketarray(ones(1,2,2,2)), {'[[[[1,1]],[[1,1]]],[[[1,1]],[[1,1]]]]'})})
disp({17,isequal(bracketarray(ones(2,2,2,2)), {'[[[[1,1],[1,1]],[[1,1],[1,1]]],[[[1,1],[1,1]],[[1,1],[1,1]]]]'})})
disp({18,isequal(bracketarray(permute(reshape([1:16],2,2,2,2),[2,1,3,4])), {'[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'})})
disp({19,isequal(bracketarray(ones(1,1,1,1,2)), {'[[[[[1]]]],[[[[1]]]]]'})})
  4 Comments

Sign in to comment.

Answers (1)

Steven Lord
Steven Lord on 10 Aug 2019
To iterate over the nth dimension of an array where n is not fixed, there is an indexing trick you can use.
ndim = randi([4 7]);
dims = randi([2 5], 1, ndim);
A = randi(10, dims);
A is an array with either 4, 5, 6, or 7 dimensions. Its size in each of its dimensions is between 2 and 5. Its values are all integer values between 1 and 10. I constrained the size of A, but I did not fix it.
inds = repmat({':'}, 1, ndims(A));
inds{end-1} = 2;
Q = A(inds{:})
Q is the same size as A except in its next to last dimension, where it has size 1. It is the second slice in that dimension. [This is safe to do, since I know the size of A is at least two in that dimension due to the way I constructed dims.] When I tried this, my results were as follows. I compared Q to the result of manually indexing into A to extract that slice and they are the same.
>> size(A)
ans =
2 2 2 5 5 3 2
>> size(Q)
ans =
2 2 2 5 5 1 2
>> isequal(A(:, :, :, :, :, 2, :), Q)
ans =
logical
1
  3 Comments

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!