【问题标题】:Improving MATLAB code speed提高 MATLAB 代码速度
【发布时间】:2013-09-09 08:39:58
【问题描述】:

不胜感激。我正在阅读大约 1M 行数据,使用以下代码需要将近 24 小时。如何提高执行时间?

数组Day 包含从开始到第nth 天的值,并且某一天有多个记录。该程序会检查一个特定的 id(存储在unique_id)是否在 180 天内重复。

%// calculating the number of repeats within 180 days
fid2 = 'data_050913/Unique_id_repeat_count1.xlsx';
fid1 = 'data_050913/data_050913_2000.csv';

fid_data = fopen(fid1);
data     = fgetl(fid_data); %// the first line, title line
ep       = 0; %// position point number

while 1
    data = fgetl(fid_data);
    if(length(data)<10)
      break;
    end
    ep = ep+1;
    id = find(data == ',');
    unique_id(ep) = str2num(data(1:id(1)-1)); 
    day(ep) = str2num(data(id(8)+1:id(9)-1)); 
end

repeat = zeros(ep,1);

tic
i = 1; 
count = 0;
while i <= ep
    j = i+1;
    while ( (j<=ep) && (day(j)<= day(i)+179) )
        if unique_id(i) == unique_id(j)
           count = 1;
           break;
        end
        j = j+1;
    end

    repeat(i,1) = count;

    count = 0;
    i = i+1;
end
toc

i = 1;
k = 1;
while i<=ep
    count = repeat(i,1);
    j=i;
    while (day(j) == day(i))
        count = repeat(j,1)+count;
        j = j+1;
        if j > ep
            break;
        end
    end

    day_final(k,1)= day(i);
    repeat_final(k,1) = count;

    k = k+1;
    i = j;
end

xlswrite(fid2,day_final,'Repeat_Count','A2');
xlswrite(fid2,repeat_final,'Repeat_Count','B2');

谢谢

【问题讨论】:

  • @AkiSuihkonen:几秒钟……你的意思是?
  • 只是为了澄清:你的代码的哪一部分占用了这么多时间?如果是第一个循环,请查看@Dr.ABT 的回答和/或阅读有关textscanimportdatatextread 等的信息。您使用过探查器吗?
  • 您能否在较小的文件(可能 10000 行)上运行它并对其进行分析以查看瓶颈在哪里?
  • 关于 break - 是的,这可能是因为它抑制了矢量化(不是 matlab 的那个 - 而是来自编译器的那个 - 参见 en.wikipedia.org/wiki/Vectorization_%28parallel_computing%29
  • sort 的重点是估计复杂度的下限:通过对所有 id 进行排序,可以更轻松地找到重复项——OTOH,已经有一个内置函数unique()在matlab中。也可以利用 unique(days) 来定位第一个列表中的所有唯一日期;然后迭代它并使用unique(unique_id(days &gt;= dayX .* days &lt;= (dayX + 179)));

标签: arrays matlab loops execution-time


【解决方案1】:

如果尚未这样做,请确保在可能的情况下预先分配所有内存。我已经看到 Matlab 脚本通过这样做从 24 小时缩短到 8 分钟。

使用zeros 函数为所有不断增长的数组(dayunique_idrepeatday_finalrepeat_final)预分配内存。

x = zeros(1000); %// Creates a 1000 element array of all zeros

【讨论】:

  • 我的 Matlab 有点生锈了。那么 unique_id、day_final、repeat_final、day 呢?
  • 对不起,我错过了那些。尽管如此,您的回答是一个很好的观点,但可能不是全部。在 R2011a 中,循环中的增长数组变得更好了,所以 1)它取决于 OP 的 MATLAB 版本,以及 2)我们不知道代码的哪些部分占用了这么多时间。
  • 时间是从tic到toc..剩下的代码不需要太多时间
【解决方案2】:

以下代码的运行速度比原始代码快约 200 倍,并且结果相同。

当然,加速取决于输入数据的分布,我的假设可能不正确(我有 1000 个唯一 ID,平均每天有 19 条记录)。

我还编写了一些代码来生成类似于我认为您的输入数据的数据。

% Generate Input data 
ep = 100000;

isRepeatedDay = rand(1,ep) < 0.95;
day = cumsum(~isRepeatedDay);
unique_ids = 1:1000;
unique_id_indices = round(rand(ep,1)*length(unique_ids));
unique_id_indices(unique_id_indices < 1) = 1;
unique_id_indices(unique_id_indices > length(unique_id_indices) ) = length(unique_id_indices);

unique_id = unique_ids(unique_id_indices);

%Process the input data to find repeats
tic
repeat = zeros(ep,1);
[unique_values,~,indices] = unique(unique_id);
for uv_index = 1:length(unique_values)
    uv = unique_values(uv_index);
    uv_indices = find(indices == uv_index);
    for i=1:length(uv_indices)-1
        daysDifference = day(uv_indices(i+1)) - day(uv_indices(i));
        if daysDifference <= 179
            repeat(uv_indices(i),1) = 1;
        end
    end
end
toc

【讨论】:

    【解决方案3】:

    如果unique_id 可以有许多不同的值(甚至可能没有),我会这样做。

    在我的系统上操作不到 5 秒:

    x = round(rand(1000000,1)*10);
    result = zeros(size(x));
    windowsize = 180;
    for t = 1:(numel(x)-windowsize)
        result(t) = sum(x(t+1:t+windowsize)==x(t));
    end
    

    我认为这是您需要的,请务必检查您是要“向前”还是“向后”。

    【讨论】:

      【解决方案4】:
      • 您可以使用 MATLAB 的 profiler 检查哪一行需要的时间最多。
      • 您应该知道的一件事:MATLAB 处理向量和矩阵的速度很快,但循环速度通常很慢。如果可能,您应该始终尝试使用整个向量(或矩阵)。有多种方法可以做到这一点。
      • 一种方法是逻辑索引。我认为这应该适用于您的问题的一部分。在

      首先我将向您展示一个逻辑索引如何工作的小例子:

      vector=[0 4 5 2 4]
      
      logicalIndex=(vector==4) %the type of logicalIndex is bool!
      
      excerpOfVector=vector(logicalIndex) %some other ways to use logial Indexing 
      excerpOfVectorSecondVariation=zeros(1,length(vector)) 
      excerpOfVectorSecondVariation(logicalIndex)=vector(logicalIndex)
      vector(vector < 5) = 11;            %implicit use of logical indexing
      
      • 您应该将凌乱的 while 循环更改为 for 循环! ;-)

      【讨论】:

      • 我认为您错过了要扫描的元素数由匹配日向量中记录的日期数设置的要求。您需要扫描 180 天而不是 180 个元素。
      猜你喜欢
      • 1970-01-01
      • 2013-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多