The fastest way to get class types of cell array elements

I have an array of (large) cells with various data types. For example,

 myCell = { 1, 2, 3, 'test',  1 , 'abc';
            4, 5, 6, 'foob', 'a', 'def' };

This may include more obscure types, such as objects java.awt.Color.

I want to make sure that the data in each column is of the same type , since I want to perform table operations on it. However, this process seems very slow!

My current method is to use cellfunto get classes, and strcmpto check them

% Get class of every cell element
types = cellfun( @class, myCell, 'uni', false );
% Check that they are consistent for each column
typesOK = all( strcmp(repmat(types(1,:), size(types,1), 1), types), 1 );
% Output the types (mixed type columns can be handled using typesOK)
types = types(1, :);

% Output for the above example: 
% >> typesOK = [1 1 1 1 0 1]
% >> types = {'double', 'double', 'double', 'char', 'double', 'char'}

I thought to use cell2table, since it checks the type for the same reason. However, it does not give me the desired result (which columns are rows).

Is there a faster way to check the consistency of types in columns of an array of cells?


: ...

, types = cellfun( @class, ...) 90% . , , , strcmp .


: , .

+6
5

, , :

function [b] = IsTypeConsistentColumns(myCell)
%[
    b = true;
    try
        for ci = 1:size(myCell, 2)
           cell2mat(myCell(:, ci));
        end
    catch err
        if (strcmpi(err.identifier, 'MATLAB:cell2mat:MixedDataTypes'))
            b = false;
        else
            rethrow(err);
        end
    end
%]
end

, cell2mat ( cell2mat .

, cell2mat , (identifier: 'MATLAB:cell2mat:MixedDataTypes', message = 'All contents of the input cell array must be of the same data type.')

EDIT: cellfun ('isclass', c, cellclass)

, cell2mat:

function [consistences, types] = IsTypeConsistentColumns(myCell)
%[
    ncols = size(myCell, 2);
    consistences = false(1, ncols);
    types = cell(1, ncols);
    for ci = 1:ncols
        cellclass = class(myCell{1, ci});
        ciscellclass = cellfun('isclass', myCell(:, ci), cellclass);

        consistences(ci) = all(ciscellclass);
        types{ci} = cellclass; 
    end    
%]
end

myCell = repmat( { 1, 2, 3, 'test', 1 , 'abc'; 4, 5, 6, 'foob', 'a', 'def' }, 10000, 5 );,

0,0123 R2015b... , ( )

+3

script ...

function benchie    
    % Create a large, mixed type cell array
    myCell = repmat( { 1, 2, 3, 'test',  1 , 'abc';
                       4, 5, 6, 'foob', 'a', 'def' }, 10000, 5 );

    % Create anonymous functions for TIMEIT               
    f1 = @() usingStrcmp(myCell);
    f2 = @() usingUnique(myCell);
    f3 = @() usingLoops(myCell);
    f4 = @() usingISA(myCell);
    f5 = @() usingIsClass(myCell);
    % Timing of different methods
    timeit(f1)
    timeit(f2)
    timeit(f3)    
    timeit(f4)
    timeit(f5)
end

function usingStrcmp(myCell)
    % The original method
    types = cellfun( @class, myCell, 'uni', false );
    typesOK = all( strcmp(repmat(types(1,:), size(types,1), 1), types), 1 );
    types = types(1, :);
end

function usingUnique(myCell)
    % Using UNIQUE instead of STRCMP, as suggested by rahnema1 
    types = cellfun( @class, myCell, 'uni', false );
    [type,~,idx]=unique(types);
    u = unique(reshape(idx,size(types)),'rows');
    if size(u,1) == 1
        % consistent
    else
        % not-consistent
    end
end

function usingLoops(myCell)
    % Using loops instead of CELLFUN. Move onto the next column if a type
    % difference is found, otherwise continue looping down the rows
    types = cellfun( @class, myCell(1,:), 'uni', false );
    typesOK = true(size(types));
    for c = 1:size(myCell,2)
        for r = 1:size(myCell,1)
            if ~strcmp( class(myCell{r,c}), types{c} )
                typesOK(c) = false;
                continue
            end
        end
    end
end

function usingISA(myCell)
    % Using ISA instead of converting all types to strings. Suggested by Sam
    types = cellfun( @class, myCell(1,:), 'uni', false );
    for ii = 1:numel(types)
       typesOK(ii) = all(cellfun(@(x)isa(x,types{ii}), myCell(:,ii)));
    end
end

function usingIsClass(myCell)
    % using the same method as found in CELL2MAT. Suggested by CitizenInsane 
    ncols = size(myCell, 2);
    typesOK = false(1, ncols);
    types = cell(1, ncols);
    for ci = 1:ncols
        cellclass = class(myCell{1, ci});
        ciscellclass = cellfun('isclass', myCell(:, ci), cellclass);
        typesOK(ci) = all(ciscellclass);
        types{ci} = cellclass; 
    end  
end

:

R2015b

usingStrcmp:  0.8523 secs
usingUnique:  1.2976 secs
usingLoops:   1.4796 secs
usingISA:    10.2670 secs 
usingIsClass: 0.0131 secs % RAPID!

R2017b

usingStrcmp:  0.8282 secs
usingUnique:  1.2128 secs
usingLoops:   0.4763 secs % ZOOOOM! (Relative to R2015b)
usingISA:     9.6516 secs
usingIsClass: 0.0093 secs % RAPID!

, , .

, ( ), MATLAB (2017b), > 65% 50% , !


:

  • ( ) .
  • MATLAB .

  • : CitizenInsane, , , , , Matlab cell2mat.

    : usingIsClass.

+3

unique:

myCell = { 1, 2, 3, 'test',  1 , 'abc';
            4, 5, 6, 'foob', 'a', 'def' };

types = cellfun( @class, myCell, 'uni', false );
[type,~,idx]=unique(types);
u = unique(reshape(idx,size(types)),'rows');
if size(u,1) == 1
    disp('consistent')
else
     disp('non-consistent')
end
+1

:

>>  myCell = { 1, 2, 3, 'test',  1 , 'abc';
               4, 5, 6, 'foob', 'a', 'def' }
myCell =
  2×6 cell array
    [1]    [2]    [3]    'test'    [1]    'abc'
    [4]    [5]    [6]    'foob'    'a'    'def'

>> firstRowTypes = cellfun(@class, myCell(1,:), 'uni', false)
firstRowTypes =
  1×6 cell array
    'double'    'double'    'double'    'char'    'double'    'char'

>> for i = 1:numel(firstRowTypes)
       typesOK(i) = all(cellfun(@(x)isa(x,firstRowTypes{i}), myCell(:,i)));
   end

>> typesOK
typesOK =
  1×6 logical array
   1   1   1   1   0   1

, , ( , ),

  • isa, , .
+1

validateattributes, , . , .

foo = { 'abc' 5 'def'; ...
        'foo' 6 'bar' };

cellfun(@(x) validateattributes(x, {'char'}, {}, foo(:, [1 3]))
0

All Articles