2010-07-12 26 views
8

J'ai deux tableaux de chaînes de cellules, et je veux vérifier si elles contiennent les mêmes chaînes (ils ne doivent pas être dans le même ordre, nous ne savons pas si elles sont de les mêmes longueurs).MATLAB: comparaison des tableaux de cellules de la chaîne

Par exemple:

a = {'2' '4' '1' '3'}; 
b = {'1' '2' '4' '3'}; 

ou

a = {'2' '4' '1' '3' '5'}; 
b = {'1' '2' '4' '3'}; 

D'abord je pensais que des strcmp mais nécessiterait une boucle plus d'un contenu de la cellule et comparer contre l'autre. Je considérais aussi ismember en utilisant quelque chose comme:

ismember(a,b) & ismember(b,a) 

mais nous ne savons pas à l'avance qu'ils sont de la même longueur (cas évident de l'inégalité). Alors, comment effectueriez-vous cette comparaison de la manière la plus efficace sans écrire trop de cas d'if/else.

Répondre

17

Vous pouvez utiliser la fonction SETXOR, qui renvoie les valeurs qui ne se trouvent pas à l'intersection des deux matrices de cellules. Si elle retourne un tableau vide, les deux réseaux de cellules contiennent les mêmes valeurs:

arraysAreEqual = isempty(setxor(a,b)); 



EDIT: Certaines mesures de performance ...

Puisque vous étiez curieux à propos des mesures de performance, je pensais tester la vitesse de ma solution par rapport aux deux solutions listées par Amro (qui utilisent ISMEMBER et STRCMP/CELLFUN). J'ai d'abord créé deux grands réseaux de cellules:

a = cellstr(num2str((1:10000).')); %'# A cell array with 10,000 strings 
b = cellstr(num2str((1:10001).')); %'# A cell array with 10,001 strings 

Ensuite, j'ai couru chaque solution 100 fois pour obtenir un temps d'exécution moyenne. Ensuite, j'ai échangé a et b et l'a réexécuté. Voici les résultats:

Method  |  Time  | a and b swapped 
---------------+---------------+------------------ 
Using SETXOR | 0.0549 sec | 0.0578 sec 
Using ISMEMBER | 0.0856 sec | 0.0426 sec 
Using STRCMP |  too long to bother ;) 

Notez que la solution SETXOR a toujours rapide timing. La solution ISMEMBER s'exécutera légèrement plus rapidement si a comporte des éléments qui ne figurent pas dans b. Cela est dû au short-circuit && qui ignore la seconde moitié du calcul (car nous savons déjà a et b ne contiennent pas les mêmes valeurs). Cependant, si toutes les valeurs dans a sont également dans b, la solution ISMEMBER est significativement plus lente.

+1

Pour évaluer les performances, vous devez comparer une autre solution, comme la suggestion que vous avez faite en utilisant une boucle et [STRCMP] (http://www.mathworks.com/access/helpdesk/help/techdoc/ref/strcmp). html). J'imagine que la performance serait parfaitement bien, mais si vous découvrez que l'utilisation de [SETXOR] (http://www.mathworks.com/access/helpdesk/help/techdoc/ref/setxor.html) finit vraiment par être un goulot d'étranglement dans votre traitement, vous pouvez essayer de regarder son code source ('type setxor' ou' edit setxor') et le réécrire en coupant quelques erreurs-vérification, etc – gnovice

+1

merci, je pense que je vois ce que @Mikhail essayait de faire. Qu'en est-il des performances? il semble que XOR de deux ensembles est une opération coûteuse quand tout ce dont j'ai besoin est un vrai/faux type de réponse – Dave

+0

oups, j'ai édité mon commentaire et foiré la commande .. désolé – Dave

2

Jetez un oeil à la fonction intersect

Qu'est-ce Matlab Aide dit:

[c, ia, ib] = intersect(a, b) aussi rendements des vecteurs d'index de colonne ia et ib tels que c = a(ia) et b(ib) (ou c = a(ia,:) et b(ib,:)).

+0

Je ne suis pas sûr de savoir comment obtenir la solution à partir du résultat de «intersection» – Dave

+0

Cela dépend de ce que vous avez exactement à faire. Si vous avez besoin d'un booléen scalaire pour que les deux vecteurs contiennent les mêmes chaînes, alors la solution de gnovice est la bonne réponse pour vous. – Mikhail

5

Vous pouvez toujours utiliser la fonction IsMember comme vous avez fait avec une petite modification:

arraysAreEqual = all(ismember(a,b)) && all(ismember(b,a)) 

En outre, vous pouvez écrire la version en boucle avec STRCMP comme une seule ligne:

arraysAreEqual = all(cellfun(@(s)any(strcmp(s,b)), a)) 

EDIT: J'ajoute une troisième solution adaptée d'un autre SO question:

g = grp2idx([a;b]); 
v = all(unique(g(1:numel(a))) == unique(g(numel(a)+1:end))); 

Dans le même esprit, Im effectué la comparaison du temps (en utilisant la fonction TIMEIT):

function perfTests() 
    a = cellstr(num2str((1:10000)'));   %#' fix SO highlighting 
    b = a(randperm(length(a))); 

    timeit(@() func1(a,b)) 
    timeit(@() func2(a,b)) 
    timeit(@() func3(a,b)) 
    timeit(@() func4(a,b)) 
end 

function v = func1(a,b) 
    v = isempty(setxor(a,b));      %# @gnovice answer 
end 

function v = func2(a,b) 
    v = all(ismember(a,b)) && all(ismember(b,a)); 
end 

function v = func3(a,b) 
    v = all(cellfun(@(s)any(strcmp(s,b)), a)); 
end 

function v = func4(a,b) 
    g = grp2idx([a;b]); 
    v = all(unique(g(1:numel(a))) == unique(g(numel(a)+1:end))); 
end 

et les résultats dans le même ordre de fonctions (inférieure est mieux):

ans = 
    0.032527 
ans = 
    0.055853 
ans = 
     8.6431 
ans = 
    0.022362