在MATLAB中如何将多维数组写为看起来像原始numpy数组的字符串?

时间:2019-08-10 00:52:13

标签: arrays matlab numpy encode toml

目标

(请原谅我的时长,主要是背景和细节。)

我正在为MATLAB的TOML编码器/解码器做贡献,并且现在正在使用数值数组。我想以相同的格式输入(然后能够写出)数字数组。此格式是 numpy.array 使用的嵌套方括号格式。例如,要在numpy中创建多维数组:

下面是在python中,请清楚。尽管我的工作是在MATLAB中进行的,但这是一个有用的示例。

2D数组

>> x = np.array([1,2])
>> x
array([1, 2])

>> x = np.array([[1],[2]])
>> x
array([[1],
       [2]])

3D阵列

>> x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>> x
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

4D阵列

>> 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]]]])

输入是使用嵌套方括号对维度进行的逻辑构造。事实证明,这对于TOML数组结构非常有效。我已经可以成功解析和解码从TOML到MATLAB数值数组数据类型的任何大小/任何尺寸的数值数组。

现在,我想将MATLAB数值数组编码回此char / string结构,以写回TOML(或任何字符串)。

所以我在MATLAB中有以下4D数组(与numpy相同的4D数组):

>> 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

我想将其转换为与4D numpy输入具有相同格式的字符串(带有一些名为 bracketarray 的函数):

>> str = bracketarray(x)
str =
    '[[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]]'

然后我可以将字符串写到文件中。

编辑:我应该补充一点,尽管功能numpy.array2string()确实可以实现我想要的功能,但是它添加了其他一些空格字符。但我不能将其用作解决方案的一部分,尽管它基本上是我正在寻找的功能。

问题

这是我的问题。我已经使用以下函数成功地解决了3个维度的问题,但是 我一生都无法弄清楚如何将其扩展到N维 。我觉得这是对每个尺寸进行正确计数的问题,请确保不要略过任何尺寸并正确嵌套括号。

适用于3D的当前方括号array.m

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

帮我欧比旺基诺比斯,你们都是我唯一的希望!

编辑2: 在下面添加了测试套件,并修改了一些当前代码。

测试套件

这里是一个测试套件,可用于查看输出是否应该是正确的。基本上,只需将其复制并粘贴到MATLAB命令窗口中即可。对于我当前发布的代码,除3D以上的代码外,它们都返回true。我当前的代码输出为单元格。如果您的解决方案输出的结果不同(例如字符串),则必须从测试套件中删除大括号。

isequal(bracketarray(ones(1,1)), {'[1]'})
isequal(bracketarray(ones(2,1)), {'[[1],[1]]'})
isequal(bracketarray(ones(1,2)), {'[1,1]'})
isequal(bracketarray(ones(2,2)), {'[[1,1],[1,1]]'})
isequal(bracketarray(ones(3,2)), {'[[1,1],[1,1],[1,1]]'})
isequal(bracketarray(ones(2,3)), {'[[1,1,1],[1,1,1]]'})
isequal(bracketarray(ones(1,1,2)), {'[[[1]],[[1]]]'})
isequal(bracketarray(ones(2,1,2)), {'[[[1],[1]],[[1],[1]]]'})
isequal(bracketarray(ones(1,2,2)), {'[[[1,1]],[[1,1]]]'})
isequal(bracketarray(ones(2,2,2)), {'[[[1,1],[1,1]],[[1,1],[1,1]]]'})
isequal(bracketarray(ones(1,1,1,2)), {'[[[[1]]],[[[1]]]]'})
isequal(bracketarray(ones(2,1,1,2)), {'[[[[1],[1]]],[[[1],[1]]]]'})
isequal(bracketarray(ones(1,2,1,2)), {'[[[[1,1]]],[[[1,1]]]]'})
isequal(bracketarray(ones(1,1,2,2)), {'[[[[1]],[[1]]],[[[1]],[[1]]]]'})
isequal(bracketarray(ones(2,1,2,2)), {'[[[[1],[1]],[[1],[1]]],[[[1],[1]],[[1],[1]]]]'})
isequal(bracketarray(ones(1,2,2,2)), {'[[[[1,1]],[[1,1]]],[[[1,1]],[[1,1]]]]'})
isequal(bracketarray(ones(2,2,2,2)), {'[[[[1,1],[1,1]],[[1,1],[1,1]]],[[[1,1],[1,1]],[[1,1],[1,1]]]]'})
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]]]]'})
isequal(bracketarray(ones(1,1,1,1,2)), {'[[[[[1]]]],[[[[1]]]]]'})

2 个答案:

答案 0 :(得分:1)

递归函数几乎完成。缺少的是索引最后一个维度的方法。有几种方法可以做到,我发现最整洁的方法如下:

n = ndims(x);
index = cell(n-1, 1);
index(:) = {':'};
y = x(index{:}, ii);

起初有点棘手,但是会发生这种情况:index是一组n-1字符串':'index{:}是这些字符串的逗号分隔列表。当我们为x(index{:},ii)编制索引时,我们实际上会进行x(:,:,:,ii)(如果n为4)。

完成的递归函数是:

function out = bracketarray(in)
n = ndims(in);
if n == 2
   % Fill in your n==2 code here
else
   % if array has more than 2 dimensions, recursively send planes of 2 dimensions for encoding
   index = cell(n-1, 1);
   index(:) = {':'};
   storage = cell(size(in, n), 1);
   for ii = 1:size(in, n)
      storage(ii) = bracketarray(in(index{:}, ii)); % last dimension automatically removed
   end
end
out = { strcat('[', strjoin(storage, ','), ']') };

请注意,我已经预先分配了storage单元格数组,以防止在每次循环迭代中调整其大小。您应该在2D案例代码中执行相同的操作。由于性能原因,预分配在MATLAB中很重要,因此MATLAB编辑器也应该对此有所帮助。

答案 1 :(得分:1)

我认为循环和使用join会更容易。您的测试用例通过了。

function out = bracketarray_matlabbit(in)

    out = permute(in, [2 1 3:ndims(in)]);
    out = string(out);

    dimsToCat = ndims(out);
    if iscolumn(out)
       dimsToCat = dimsToCat-1; 
    end

    for i = 1:dimsToCat
       out = "[" + join(out, ",", i) + "]"; 
    end
end

这似乎比您选择的路线还快:

>> x = permute(reshape([1:16],2,2,2,2),[2,1,3,4]);

>> tic; for i = 1:1e4; bracketarray_matlabbit(x); end; toc
Elapsed time is 0.187955 seconds.

>> tic; for i = 1:1e4; bracketarray_cris_luengo(x); end; toc
Elapsed time is 5.859952 seconds.