Matlab:在单元格数组中提取具有特定属性值

时间:2016-07-05 10:38:03

标签: performance matlab oop indexing cell-array

我正在开发一个用面向对象的Matlab(2016a)编写的项目,而我目前正在尝试优化一些代码以改善运行时。使用分析器我已经确定了一个可能的性能改进 - 我们使用很多的功能看起来特别低效,而不是特别的" matlab"在其实施中。

基本上,我想知道的是,是否有一种有效的方法来索引类对象的CellArray,只提取具有我们感兴趣的特定属性值的那些。目前我们这样做for循环,我想知道我是否可以做些什么来矢量化它或者可能使用某种逻辑索引。不幸的是,我的搜索似乎没有找到答案,使用对象对CellArrays进行逻辑索引似乎并不是很多人想做的事情......

这是我尝试改进的功能的最小工作示例。 MWE的其他代码位如下。

我意识到通过更改实现的其余部分以避免必须执行此操作来解决此问题可能更明智。但是,如果可能的话,我宁愿避免这样做。

编辑:在我的典型用例中,单元格对象的数量很小(大约10或20)但是效率低下的方法被大量调用(数千次)。如此小的阵列,很多都找到了操作。

%% is there a "more matlab" / faster way to do this?
function outputCellArray = ThereMustBeABetterWayToDoThis(cellArrayOfClassObjects,arrayOfTypesToFind)
outputCellArray = {};

for iType = 1:numel(arrayOfTypesToFind)
    thisType = arrayOfTypesToFind(iType); % this line is the real bottleneck according to the profiler
    for iObject = 1:numel(cellArrayOfClassObjects)


        thisClassObj = cellArrayOfClassObjects{iObject};

        if (thisClassObj.specificEnumType == thisType) % this line is also quite slow
            outputCellArray{end+1} = thisClassObj;
        end
    end
end

班级定义:

classdef MyClass < handle %% dummy example class

    properties
        specificEnumType;
        x;
        y;
    end

    methods
        function this = MyClass(x,y,specificEnumType)
           this.specificEnumType = specificEnumType;
           this.x = x;
           this.y = y;
        end
    end
end

和另一个:

classdef EnumType < uint32 %%dummy example class
    enumeration
       Type0 (0),
       Type1 (1),
       Type2 (2),
       Type3 (3)
    end

end

调用整个事情的脚本:

% use this script to call the whole thing

%% we have a cell array of class objects: they each have different enumTypes as a property
cellArrayOfClassObjects{1} = MyClass(rand,rand,EnumType.Type0);
cellArrayOfClassObjects{2} = MyClass(rand,rand,EnumType.Type1);
cellArrayOfClassObjects{3} = MyClass(rand,rand,EnumType.Type2);
cellArrayOfClassObjects{4} = MyClass(rand,rand,EnumType.Type3);
cellArrayOfClassObjects{5} = MyClass(rand,rand,EnumType.Type3);
cellArrayOfClassObjects{6} = MyClass(rand,rand,EnumType.Type2);

%% we want to find the ones that have these specific enumTypes
arrayOfTypesToFind = [EnumType.Type0,EnumType.Type2];

%% there must be a better way than this inefficient method
outputArray = ThereMustBeABetterWayToDoThis(cellArrayOfClassObjects,arrayOfTypesToFind);

2 个答案:

答案 0 :(得分:1)

好吧,那是一个棘手的问题。为了加快速度,我会尽量避免使用cellarray。在函数中,单元格被更改为对象数组,然后作为向量扫描以加速循环,然后更改回单元格

试试这个

cellArrayOfClassObjects=cell(10000,1);
for i=1:10000
    switch randi(4,1,1)
        case 1
            cellArrayOfClassObjects{i} = MyClass(rand,rand,EnumType.Type0);
        case 2
            cellArrayOfClassObjects{i} = MyClass(rand,rand,EnumType.Type1);
        case 3
            cellArrayOfClassObjects{i} = MyClass(rand,rand,EnumType.Type2);
        case 4
            cellArrayOfClassObjects{i} = MyClass(rand,rand,EnumType.Type3);
    end
end

%% we want to find the ones that have these specific enumTypes
arrayOfTypesToFind = [EnumType.Type0,EnumType.Type2];

%% there must be a better way than this inefficient method
tic
outputArray = ThereMustBeABetterWayToDoThis(cellArrayOfClassObjects,arrayOfTypesToFind);
toc
tic
outputArray2 = ThereIs(cellArrayOfClassObjects,arrayOfTypesToFind);
toc


function outputCellArray = ThereIs(cellArrayOfClassObjects,arrayOfTypesToFind)
outputCellArray = {};
X=[cellArrayOfClassObjects{:}];%matrix of the cell 

for iType = 1:numel(arrayOfTypesToFind)
    %for each type check [X.specificEnumType]==arrayOfTypesToFind(iType)
    % then get the objects by X([X.specificEnumType]==arrayOfTypesToFind(iType))
    % then put them in cells mat2cell( X(...) , 1 , sum of those X(...)
    % and add the to the existing outputCellArray=outputCellArray+matcell
    % in one line
    outputCellArray=[outputCellArray mat2cell(X([X.specificEnumType]==arrayOfTypesToFind(iType)),1,sum([X.specificEnumType]==arrayOfTypesToFind(iType)))];
end

end

通过预先分配outputarray可能有一种方法可以让它更快。我现在正在尝试。 的修改 那不起作用......但第一部分应该这样做

答案 1 :(得分:0)

我设法找到一种更好的方法(对于我的特定用例)利用了我匹配的属性是枚举类型的事实。也就是说,我仍然想知道是否有更多通用解决方案。当对象数量很大时,@ Finn的答案同样好。

这是我的更新版本。有可能进一步改进。

function outputCellArray = ExploitEnums(cellArrayOfClassObjects,arrayOfTypesToFind)


typesCastToInts = uint32(arrayOfTypesToFind);

objectTaskTypesTakenFromArray = cellfun(@(x) uint32(x.specificEnumType), cellArrayOfClassObjects);

typesExistInBothArrays = ismember(objectTaskTypesTakenFromArray, typesCastToInts);
matchingIndices = find(typesExistInBothArrays);

if (~isempty(matchingIndices))
    outputCellArray = cellArrayOfClassObjects(matchingIndices);
end

end