【问题标题】:Change default NaN representation of fprintf() in Matlab在 Matlab 中更改 fprintf() 的默认 NaN 表示
【发布时间】:2014-09-23 13:05:48
【问题描述】:

我正在尝试以其他应用程序可以理解的格式从 Matlab 导出数据...为此,我需要更改 NaNInf-Inf 字符串(Matlab 默认为此类打印值)到//m//inf+//Inf-

总的来说,我知道如何做到这一点。我在问如何(以及是否有可能)在 Matlab 中利用一个特定的东西。实际问题位于最后一段。

我尝试了两种方法(代码如下)。

  1. 数据strrep()输出上使用sprintf()。这是以逐行方式完成的,以节省内存。这个解决方案比简单的fprintf() 花费的时间几乎多出 10 倍。优点是内存开销低。
  2. 与选项 1. 相同,但对整个数据 进行一次转换。此解决方案速度更快,但容易受到内存不足异常的影响。这种方法的问题是我不想不必要地复制 数据

代码:

rows = 50000  
cols = 40  
data = rand(rows, cols); % generate random matrix  
data([1 3 8]) = NaN; % insert some NaN values  
data([5 6 14]) = Inf; % insert some Inf values  
data([4 2 12]) = -Inf; % insert some -Inf values  

fid = fopen('data.txt', 'w'); %output file  

%% 0) Write data using default fprintf  
format = repmat('%g ', 1, cols);  

tic  
fprintf(fid, [format '\n'], data');  
toc  

%% 1) Using strrep, writing line by line  
fprintf(fid, '\n');  
tic  
for i = 1:rows  
    fprintf(fid, '%s\n', strrep(strrep(strrep(sprintf(format, data(i, :)), 'NaN', '//m'), '-Inf', '//inf-'), 'Inf', '//inf+'));  
end  
toc  

%% 2) Using strrep, writing all at once  
fprintf(fid, '\n');  
format = [format '\n'];  
tic  
    fprintf(fid, '%s\n', strrep(strrep(strrep(sprintf(format, data'), 'NaN', '//m'), '-Inf', '//inf-'), 'Inf', '//inf+'));  
toc  

输出:

经过的时间是 1.651089 秒。 % 正则 fprintf()
经过的时间是 11.529552 秒。 % 选项 1
经过的时间是 2.305582 秒。 % 选项 2

现在问题...

与简单的 fprintf() 相比,我对使用我的解决方案的内存开销和时间损失感到不满意。
我的理由是'NaN''Inf''-Inf' 字符串是保存在*printf()*2str() 实现中的某个变量中的简单数据。有没有办法在运行时改变它们的值?
例如,在 C# 中,我将更改 System.Globalization.CultureInfo.NumberFormat.NaNSymbol 等,如 here 所述。

【问题讨论】:

  • 为什么不在 MATLAB 工作区中的数据替换这些值,然后导出结果?这应该比导出时替换要快得多。
  • @EliDuenisch 因为工作区中的数据保存为 double。它必须是 string 才能让我先验地应用转换。
  • 找到 NaN 等的索引,然后使用 num2cell 将数据转换为元胞数组,将索引替换为字符串,然后导出
  • @EliDuenisch 我也尝试过(以及其他几种方法),也许你可以检查我的问题的编辑历史,我没有写下我为了篇幅而尝试的所有内容。 .. 将 data 转换为元胞数组是 1) 慢(示例中为 70 秒)2) 内存很重...我不想复制所有数据,因为我们可能会说话千兆字节。
  • 内部字符串转换似乎回到了一个未记录的内置辅助函数sprintfc,据我所知,它是不可更改的。关于我建议尝试的唯一事情是使用~isfinite 预先检查包含这些值之一的行数,然后在可能的情况下一次循环fprintf 行块而不是遍历每一行(但是tbh 我可能会在这一点上放弃,将数据转储到文件并在事后使用非 MATLAB 方法来操作文本文件)。

标签: matlab printf


【解决方案1】:

在 cmets 中提到的有限情况下,许多(未知,每个数据集都在变化)列可能完全是 NaN(或 Inf 等),但没有不需要的 NaN 值,否则,另一种可能性是检查第一行数据,组装一个直接写入\\m 字符串的格式字符串,并在告诉fprintf 忽略包含NaN 或其他不需要的值的列时使用它。

y = ~isnan(data(1,:)); % find all non-NaN
format = sprintf('%d ',y); % print a 1/0 string
format = strrep(format,'1','%g'); 
format = strrep(format,'0','//m'); 

fid = fopen('data.txt', 'w'); 
fprintf(fid, [format '\n'], data(:,y)'); %pass only the non-NaN data
fclose(fid);

通过我对NaN 的两列的检查,此fprintf 与您的“常规”fprintf 几乎相同,并且比循环更快-不考虑生成format 的初始化步骤。如果您还必须考虑 +/- Inf ,将其设置为自动生成格式字符串会更麻烦,但肯定是可能的。可能还有一种更清洁的方式来生成format

它是如何工作的:

您可以传入数据的子集,也可以将您喜欢的任何文本插入到格式字符串中,因此,如果每一行在同一位置具有相同的所需“文本”(在本例中为 NaN 列以及我们想要的“NaN”替换),我们可以将我们想要的文本放在那个位置,然后首先不将这些数据部分传递给fprintf。一个更简单的命令行尝试示例:

x = magic(5);
x(:,3)=NaN
sprintf('%d %d ihatethrees %d %d \n',x(:,[1,2,4,5])');

【讨论】:

  • +1 用于提高我的代码的性能,这是我尝试过的一件事的一个很好的旋转,我认为它现在实际上会更顺畅......但是它并没有解决问题是否可以更改 NaN 的默认字符串(所以我不能绿色勾选它)......但是经过许多其他搜索后,我担心 OP 的正确答案很简单:“NO”=(非常感谢无论如何 nkjt。
猜你喜欢
  • 1970-01-01
  • 2010-12-16
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 2021-11-02
  • 1970-01-01
相关资源
最近更新 更多