用parfor填写地图

时间:2014-10-02 19:25:52

标签: matlab map

我在Matlab中解析一个数据文件来绘制它。我正在解析的数据有3列:

category | x | y

该文件可能包含任何类别的多个点。为了将它们绘制在一起并使图例正确,我需要对数据进行排序,以便每个类别生成n x 2矩阵,表示给定类别中的n点。

在下面的摘录中,不要担心解析,而是重要的部分是如何附加地图。 category是关键,newVal是单个x-y对值,需要连接到category点的矩阵。

disp('Sorting categories...')
map = containers.Map();
for i = 2:size(data,2)
    str = strsplit(data{i}, '\t');
    category = strsplit(str{1}, '.');
    category = category{1};
    newVal=sscanf([str{2} ',' str{3}],'%f,%f')';

    %Interesting stuff starts here
    if(isKey(map, category))
        mapVal = [map(category); newVal];
    else
        mapVal = newVal;
    end

    map = [map; containers.Map(category, mapVal)];
end

这需要59k点,但我真的想将for更改为parfor。我的进程需要对map变量进行读取 - 修改 - 写入,这不起作用。我希望代码可以做这样的事情(假设它会更快,无论如何都可能不是这样):

disp('Sorting categories...')
map = containers.Map();
for i = 2:size(data,2)
    str = strsplit(data{i}, '\t');
    category = strsplit(str{1}, '.');
    category = category{1};
    newVal=sscanf([str{2} ',' str{3}],'%f,%f')';

    maps{i} = containers.Map(category, mapVal);

end
map = [maps{:}];

但是,在Matlab中连接映射会导致值被覆盖而不是追加。这将导致每个类别仅保留为该类别解析的最后一个点。有没有办法避免这种行为?

3 个答案:

答案 0 :(得分:1)

我不确定时间太久了。这个脚本花了我52s来完成使用6个工人。 parfor为22秒,合并循环为29秒。它不优雅,但它确实应用了parfor循环。您可以使用parfevalfetchnext函数进一步加快速度。 fetchnext将允许您在容器变为可用时合并它们,从而在工作人员仍然构建地图容器时执行合并。我猜这将比52秒转化为大约30秒。

注意,parfevalfetchnext仅适用于2013b及以上版本。

% Create some test data
N = 59000;
aa(:,1) = 10*rand(N,1);
% aa(:,1) = [1 2 2 4 5 6 6 8 9 10];
aa(:,2) = rand(N,1);
aa(:,3) = rand(N,1);
data = strtrim(cellstr(num2str(aa,'%g,%g,%g'))');


ndata = size(data,2);
p = gcp;
if isempty(p)
    NumWorkers = 1;
else
    NumWorkers = p.NumWorkers;
end

tic;
% work out the indices for each worker
numchunks = ceil(ndata/NumWorkers);
for kk = 1:numchunks
    strt = (kk-1)*NumWorkers+1;
    endl = kk*NumWorkers;
    if endl > ndata
        endl = ndata;
    end
    ind{kk} = strt:endl;
end

maps = cell(0);
parfor jj = 1:numel(ind)
%     disp('Sorting categories...')
    maps{jj,1} = containers.Map();
    for i = 1:numel(ind{jj})
        str = strsplit(data{ind{jj}(i)}, ',');
        category = strsplit(str{1}, '.');
        category = category{1};
        newVal=sscanf([str{2} ',' str{3}],'%f,%f')';

        %Interesting stuff starts here
        if(isKey(maps{jj,1}, category))
            mapVal = [maps{jj,1}(category); newVal];
        else
            mapVal = newVal;
        end

        maps{jj,1} = [maps{jj,1}; containers.Map(category, mapVal)];

    end
end

% merge your map containers one-by-one
for kk = 2:numel(maps)
    auxkeys = maps{1}.keys;
    currkeys = maps{2}.keys;
    [ia,ib] = ismember(currkeys,auxkeys);
    if any(ia)
        ia = find(ia);
        for mm = 1:numel(ia)
            maps{1}(auxkeys{ib(ia(mm))}) = [maps{1}(auxkeys{ib(ia(mm))}); maps{2}(currkeys{ia(mm)})];
            remove(maps{2},currkeys{ia(mm)});
        end
    end
    maps{1} = [maps{1}; maps{2}];
    maps(2) = [];
end
toc

答案 1 :(得分:1)

虽然我喜欢推广PARFOR和PARFEVAL,但我认为这是ACCUMARRAY的工作。这是一个非常接近你的问题的东西。在第一个块中,我合成了一些数据 - 在您的实际情况中,我建议您的目标是从一个文件中读取所有数据(可能使用TEXTSCAN)。无论如何,一旦你完成了这个,我们的想法是将类别转换为数字形式,然后调用accumarray来收集同一类别的结果。 accumarray需要掌握一些棘手的野兽,所以也许本领域技术人员可以找到比我正在制作的两个电话更好的方法 - 但在我的机器上,accumarray片断在0.02秒内运行......

% Generate some data rather than reading from a file
N = 59000;
allCats = {'foo', 'bar', 'baz'};
categoryData = allCats(randi([1,numel(allCats)], N, 1));
xyData = rand(N, 2);

% work out which category each row is in (in your case, you could
% first generate allCats using "unique(categoryData)"
[allCats, ~, whichCategory] = unique(categoryData);

% Use accumarray to build up one cell array for each category, once
% each for the x and y data
tic
xByCategory = accumarray(whichCategory, xyData(:,1), [], @(x){x});
yByCategory = accumarray(whichCategory, xyData(:,2), [], @(x){x});
toc

答案 2 :(得分:0)

不幸的是,我不能对接受的答案发表评论,因为我宁愿将其插入那里。我发现这个问题是一次很棒的学习经历,因为我从未使用maps.containersaccumarray函数。我对accumarray很感兴趣并尝试将其应用于我自己的一个问题,但我发现它要慢得多。在那种情况下,我有一个带有5e6元素的向量,我正在对8e5类别进行分组并采用值的L2范数。然后我决定在这里对问题采用类似的方法。我发现没有使用accumarray更快地成为头发。这是我做的,

tic;
xyByCategory = cell(numel(allCats),1);
for kk = 1:numel(allCats)
    xyByCategory{kk} = xyData(strcmp(categoryData,allCats{kk}),:);
end
toc

使用N = 6e6

accumarray接近答案,
经过的时间是0.611510秒。

上述方法,
经过的时间是0.429321秒。