在 MATLAB 中,如何将多维数组写成看起来像原始 numpy 数组的字符串?
In MATLAB how can I write out a multidimensional array as a string that looks like a raw numpy array?
目标
(请原谅我的篇幅,主要是背景和细节。)
我正在为 MATLAB 的 TOML encoder/decoder 做贡献,我现在正在处理数值数组。我想以相同的格式输入(然后能够写出)数值数组。此格式是 numpy.array 使用的嵌套方括号格式。比如在numpy中制作多维数组:
下面在python里,为了清楚。尽管我的工作是在 MATLAB 中进行的,但这是一个有用的示例。
二维数组
>> x = np.array([1,2])
>> x
array([1, 2])
>> x = np.array([[1],[2]])
>> x
array([[1],
[2]])
三维数组
>> x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>> x
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
4维数组
>> 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 数值数组数据类型解析和解码具有这种格式的任何 size/any 维数值数组。
现在,我想将该 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()
基本上完全符合我的要求,尽管它添加了一些其他空白字符。但我不能将它用作解决方案的一部分,尽管它基本上是我正在寻找的功能。
问题
这是我的问题。我已经使用 following 函数成功地解决了最多 3 个维度的这个问题,但是 我一辈子都想不出如何扩展它到 N 维。我觉得这是每个维度正确计数的问题,确保不跳过任何维度并正确嵌套括号。
当前 bracketarray.m 最高可达 3D
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 命令 window 中即可。对于我目前发布的代码,它们都是 return true
除了超过 3D 的。我当前的代码输出为一个单元格。如果您的解决方案输出不同(如字符串),则您必须从测试套件中删除大括号。
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]]]]]'})
递归函数几乎完成。缺少的是索引最后一个维度的方法。有几种方法可以做到这一点,我发现最简洁的方法如下:
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 编辑器也应该让您了解这一点。
我认为只循环并使用连接会更容易。您的测试用例通过了。
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.
目标
(请原谅我的篇幅,主要是背景和细节。)
我正在为 MATLAB 的 TOML encoder/decoder 做贡献,我现在正在处理数值数组。我想以相同的格式输入(然后能够写出)数值数组。此格式是 numpy.array 使用的嵌套方括号格式。比如在numpy中制作多维数组:
下面在python里,为了清楚。尽管我的工作是在 MATLAB 中进行的,但这是一个有用的示例。
二维数组
>> x = np.array([1,2])
>> x
array([1, 2])
>> x = np.array([[1],[2]])
>> x
array([[1],
[2]])
三维数组
>> x = np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
>> x
array([[[1, 2],
[3, 4]],
[[5, 6],
[7, 8]]])
4维数组
>> 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 数值数组数据类型解析和解码具有这种格式的任何 size/any 维数值数组。
现在,我想将该 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()
基本上完全符合我的要求,尽管它添加了一些其他空白字符。但我不能将它用作解决方案的一部分,尽管它基本上是我正在寻找的功能。
问题
这是我的问题。我已经使用 following 函数成功地解决了最多 3 个维度的这个问题,但是 我一辈子都想不出如何扩展它到 N 维。我觉得这是每个维度正确计数的问题,确保不跳过任何维度并正确嵌套括号。
当前 bracketarray.m 最高可达 3D
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 命令 window 中即可。对于我目前发布的代码,它们都是 return true
除了超过 3D 的。我当前的代码输出为一个单元格。如果您的解决方案输出不同(如字符串),则您必须从测试套件中删除大括号。
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]]]]]'})
递归函数几乎完成。缺少的是索引最后一个维度的方法。有几种方法可以做到这一点,我发现最简洁的方法如下:
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 编辑器也应该让您了解这一点。
我认为只循环并使用连接会更容易。您的测试用例通过了。
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.