【问题标题】:MATLAB: Alternative to using 'eval' in order to evaluate stringMATLAB:替代使用“eval”来评估字符串
【发布时间】:2018-12-13 13:09:00
【问题描述】:

我在一个单元数组中有一堆 char 数组,它们实际上代表了 MATLAB 结构的声明。像这样的:

tmp{1} = 'testData.input = [1;2;3;4;5]'
tmp{2} = 'testData.output = [2;4;6;8;10]'

我需要执行这些“命令”并最终创建相应的结构。我在 for 循环中使用 eval 函数,它可以工作。

numEntries = numel(tmp);
for i = 1 : numEntries
    eval(tmp{i});
end

但是,这非常缓慢。我应该提一下,真正的 char 数组非常大,实际上包含 3,000 多个数字。此外,tmp 单元格数组本身包含大约 25,000 个单元格。 鉴于我无法更改输入数据,即 tmp 只是从外部来源提供,有没有办法提高性能?

【问题讨论】:

  • 你关心tmp中定义的结构的名字吗?如果不是,那么我将创建一个numel(tmp) 的单元格数组,读取每一行,将其拆分为字段名和数据,然后使用setField 将数据分配给该字段。否则我能想到的就是使用并行工具箱,如果你有它并且只是做一个 parfor 循环。
  • 我忽略了结构是在多个 tmp 元素上定义的事实。您可以检查结构名称与前一个相比是否发生了变化,并相应地移动到元胞数组中的下一个元素
  • 您需要评估所有字符串。评估后将创建的变量保存在 .mat 文件中以备将来使用。
  • @AlexanderVandenberghe 我实际上确实关心结构名称。据我了解,将eval 与 parfor 结合使用可能会导致错误。

标签: string matlab char eval


【解决方案1】:

我无法测试仅以您提供的 2 行为例,它是否明显更快,但我希望当 tmp 的元素数量增加时,此方法会更快。

想法是将包含在tmp 中的所有赋值指令写入一个文本文件(实际上是一个.m 文件),然后简单地执行.m 文件。对于大量行,我希望这比在循环中重复调用 eval 更快。

因此,这适用于您的示例 tmp,您最终会在工作区中使用结构 testData

%% Create an '.m' file containing all the assignment instructions from the cell array
tmpFile  = 'tmpFile2execute.m' ;
fidw = fopen( tmpFile , 'w' ) ;
fprintf(fidw,'%% Auto generated file\n'); % or any other header line you want, or none...
for i = 1 : numel(tmp) ;
    fprintf(fidw,'%s ;\n',tmp{i});
end
fclose(fidw) ;
% (optional) only to keep workspace tidy
clear i fidw tmpFile tmp 

%% Execute the file
tmpFile2execute ;

扩展您可以将其设为function 而不是script 的想法,您可以在其中添加一些后处理并将结果返回到变量中而不是直接在工作区中,但是您必须查看基础是否想法首先会带来任何速度的提升。

【讨论】:

  • 这可能是一个有趣的选择。但是,我无法衡量性能方面的任何显着优势。另外,我面临的问题是创建的 .m 文件非常大(~500MB)。由于 RAM 问题(16GB),我无法执行它。 (我的 tmp 变量包含约 25,000 个元素。每个元素定义一个 col 向量,其中包含约 3,000 行,每行包含一个带有 7 个小数的浮点数)。因此,我必须拆分文件并使用循环。
  • 哎哟......确实,一个 500MB 的文本文件肯定是一个大块,您希望分区以便于处理。但这让我想知道是谁设计了这些测试的输出。这个tmp 变量是存储大量数值结果的效率最低的方法。当然,您可以改进解析 tnp 结构的方式,但迄今为止最大的效率提升是修改它的创建方式......您对此有任何控制权吗?
  • 对不起,我在您的原始帖子中注意到您无法更改 tmp 的格式。当您将tmp 加载到matlab 中时,它需要什么样的大小? (在控制台中使用whos tmp 以获得完整尺寸)。一旦全部解析完毕,testData 的典型大小是多少?我可以修改我的解决方案以生成几个大小合理的 .m 文件,而不是一个巨大的文件,如果它对你有帮助的话。
  • 正如我在第一条评论中提到的,我已经遍历了一些较小的 .m 文件。我实际上将它们分成 26 个块。这基本上奏效了。但是性能没有我希望的那么好。比方说,在 20%-30% 的范围内。 eval 解决方案大约需要 45 分钟,.m-file 解决方案大约需要 35 分钟。如果我能把它缩短到不到 5 分钟,这将有很大帮助。
  • 正如我所说,让我们知道tmptestData 在内存中的完整大小。我怀疑是否有办法将时间缩短到 5 分钟,直到更改输入格式。目前,无论解决方案如何,我们仍在谈论解析大量字符串。无论使用何种方法,这都很慢。我建议您在这些文件到达后立即对其进行解析,然后将结果保存在 .mat 文件中(或任何方便您的二进制格式),因此 30-45 分钟的工作只花费一次。
【解决方案2】:

cellfun(@eval,tmp); 会在一定程度上提高循环的性能。否则,您可能必须编写自己的解析器(如果需要有限类型的输入,这可能会更快,例如仅像 structure 这样的赋值。some_field.some_other_subfield = [some_array]; )。

请注意,eval 使用起来是一个有风险的函数(如果 Mischief 先生在输入数据中写入类似 !rm -Rf /!del *.*rmdir(matlabroot,'s') 的内容,那么您最终可能会在评估这些字符串时一团糟)

【讨论】:

  • 结构其实相当复杂(最多七个子字段)。
  • 我知道eval 可能有点冒险。这也是我寻找替代方案的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-24
  • 2012-01-04
  • 2015-05-28
  • 1970-01-01
  • 2018-01-15
  • 1970-01-01
相关资源
最近更新 更多