2013-12-08 3 views
10

Это то, что описано в одном из примеров для ismember:Нахождение всех индексов от IsMember

Определим два вектора со значениями в общем.

A = [5 3 4 2]; B = [2 4 4 4 6 8];

Определить, какие элементы A также находятся в B, а также их соответствующих местах в B.

[Lia,Locb] = ismember(A,B)

В результате:

Lia = 
    0  0  1  1 

Locb =  
    0  0  2  1 

Элемент в B с самым низким индексом, который соответствует A(3) является B(2). A(4) соответствует B(1). Есть ли способ, которым мы могли бы найти все индексы элементов B, соответствующих одному элементу в A?

+1

Вы просмотрели любой из предложенных ответов? Если вы обнаружили, что кто-либо из них полезен, пожалуйста, рассмотрите их и, возможно, пометьте один из них, как принято. –

+0

@ user3077261 У вас есть несколько хороших ответов здесь, как сказал Эйтан. Повысьте любые ответы, которые вы найдете полезными и/или предложите рабочее решение, а затем посмотрите, что вам больше всего нравится в вашей проблеме и принимайте (установите флажок). – chappjc

ответ

5

Вы можете поменять входные аргументы в ismember:

[tf, ia] = ismember(B, A) 

Для примера, вы должны получить:

tf = 
    1  1  1  1  0  0 

ia = 
    4  3  3  3  0  0 

Это позволяет найти, скажем, , индексы всех элементов B, равные A(3), просто:

find(ia == 3) 

Вот отличное решение для общего случая:

[tf, ia] = ismember(B, A); 
idx = 1:numel(B); 
ib = accumarray(nonzeros(ia), idx(tf), [], @(x){x}); 

Обратите внимание, что на выходе будет cell array. Для примера, вы должны получить:

ib = 
    [] 
    [] 
    [2  3  4] 
    [  1] 

, что означает, что нет никаких элементов в B соответствие A(1) и A(2), A(3) Удачных элементов B(2), B(3) и B(4) и A(4) равна B(1).

+1

Превосходное использование' accumarray' для инкапсуляции индексов для уникальных элементов в ячейках. +1 Я использовал вывод ячейки точно [один раз] (http://stackoverflow.com/a/20255070/2778484) раньше, и он был полностью академичен, но у меня есть чувство, что я буду делать это больше в будущем. – chappjc

+0

И что, если оба A и B содержат неповторимые элементы, скажем A = [5 3 4 4 2] и B = [2 4 4 4 6 8] (т. Е. Как A, так и B содержат несколько записей 4)? – Confounded

+0

@Confounded Хороший вопрос. Короче говоря, текущий solutoin не будет работать, если 'A' содержит неповторимые элементы. Его можно, однако, расширить, чтобы поддержать это ... –

3

Эта строка возвращает все индексы:

F=arrayfun(@(x)(find(A(x)==B)),1:numel(A),'UniformOutput',false) 

Или более длинная версия, которая может быть легче читать:

F=cell(numel(A),1); 
for x=1:numel(A) 
    F{x}=find(A(x)==B); 
end 

find(A(x)==B) проверяет все вхождения A(x) в B. Это делается для каждого элемента массива, используя либо цикл for, либо arrayfun.

+0

Не могли бы вы объяснить, что будет делать эта линия. Я новичок в этом.BTW спасибо. – user3077261

+0

Я обновил свой ответ. – Daniel

+0

Что делать, если записи находятся в тексте и импортированы как векторы столбцов? – user3077261

0

Решения Eitan T. и Daniel R отвечают на ваш вопрос в целом. Мое решение является удобной и простой альтернативой, если вас интересуют только элементы, которые являются общими для обоих векторов, но НЕ как они связаны между собой с помощью ismember, например.вы просто хотите, чтобы фильтровать данные для общих элементов:

Я хотел бы использовать инверсию противоположности: setxor

A = [5 3 4 2]; 
B = [2 4 4 4 6 8]; 

[~,iai,ibi] = setxor(A,B); % elements which are not in common 
ia = 1:numel(A); 
ib = 1:numel(B); 
ia(iai) = [];    %indices of elements of B in A 
ib(ibi) = [];    %indices of elements of A in B 

или просто то же самое дважды:

[~,iai,ibi] = setxor(A,B); 
ia = setxor(1:numel(A),iai); 
ib = setxor(1:numel(B),ibi); 

возвращает в обоих случаях индексы элементов, которые также существуют в соответствующем другом векторе, так сказать, на из ~isnotmember

ia = 

    3  4 

ib = 

    1  2  3  4 
+0

Как этот результат говорит, например, что 'A (3)' соответствует элементам # 2, # 3 и # 4 в 'B'? –

+0

@EitanT: Это не так. Вопрос в том, важно ли это. Но я передумал. – thewaywewalk

+1

Вопрос об обнаружении индексов всех элементов в 'B', соответствующих элементам в' A' ... –

2

Простой подход заключается в использовании bsxfun для проверки равенства между каждым элементом A и B:

ind = bsxfun(@eq, A(:), B(:).'); 
list = cellfun(@find, mat2cell(ind, ones(1,numel(A)), numel(B)), 'uni', 0); 

Матрица ind дает результат в логической форме (то есть 0 или 1 значения), и list является массив ячеек, содержащие индексы:

>> ind 

ind = 

    0  0  0  0  0  0 
    0  0  0  0  0  0 
    0  1  1  1  0  0 
    1  0  0  0  0  0 

>> celldisp(list) 

list{1} = 

    [] 


list{2} = 

    []  

list{3} = 

    2  3  4 

list{4} = 

    1 
+1

Умное использование 'mat2cell' для разбивки вывода. +1 Я обнаружил, что 'mat2cell' и' cellfun' делают отличную команду. Это ответ между этим и ответом Эйтана, но вы знаете, как я себя чувствую в отношении «аккумулятора». :) – chappjc

+1

@chappjc Когда я увидел этот вопрос, ответ на «накопитель» уже был дан. Но по крайней мере я мог бы использовать 'bsxfun', что является еще одним из моих любимых :-) –

2

самые элегантные решения (то есть те, без использования итераций find) включают замены входов до ismember и группировки, как индексы с accumarray, как в ответе Эйтана, или векторизации поиска с bsxfun, как и в ответе Луиса Мендо, ИМХО.

Однако для тех, кто заинтересован в растворе с незарегистрированной функциональностью, и по общему признанию хака подхода, здесь это еще один способ сделать это (то есть для каждого элемента A, найти индексы всех соответствующих элементов в B). Мысль выглядит следующим образом: в отсортированном B, что, если у вас были первые и последние индексы каждого соответствующего элемента?Оказывается, есть две вспомогательные функции, используемые ismember (если у вас есть R2012b +, я думаю), что даст вам оба этих показателя: _ismemberfirst (a builtin) и ismembc2.

Для примера данных A = [5 3 4 2]; B = [2 4 4 4 6 8]; в вопросе, здесь реализация:

[Bs,sortInds] = sort(B); % nop for this B, but required in general 
firstInds = builtin('_ismemberfirst',A,Bs) % newish version required 
firstInds = 
    0  0  2  1 
lastInds = ismembc2(A,Bs) 
lastInds = 
    0  0  4  1 

Тяжелая атлетика это делается сейчас - у нас есть первые и последние индексы в B для каждого элемента в A без необходимости делать петли. Не существует A(1) или A(2) (5 или 3) в B, поэтому эти индексы 0. Значение 4 (A(3)) происходит в местах 2: 4 (то есть all(B(2:4)==A(3))). Аналогично, A(4) находится по адресу B(1:1).

Мы можем игнорировать sortInds в приведенном выше примере, так как B уже отсортированы, но несортированный B обрабатываются просто глядя на места в несортированном массиве. Мы можем быстро сделать этот поиск и упаковать каждый диапазон индексов с arrayfun, имея в виду, что объем вычислений задача на самом деле нахождения индексов уже сделано:

allInds = arrayfun(@(x,y)sortInds(x:y-(x==0)),firstInds,lastInds,'uni',0) 
allInds = 
    [1x0 double] [1x0 double] [1x3 double] [1] 

Каждая ячейка имеет индексы в B (если таковые имеются) каждого элемента A. Первые две ячейки представляют собой пустые массивы, как и ожидалось. Приглядевшись на третьем элементе:

>> allInds{3} 
ans = 
    2  3  4 
>> A(3) 
ans = 
    4 
>> B(allInds{3}) 
ans = 
    4  4  4 

операция Тестирование с несортированным B:

B(4:5) = B([5 4]) 
B = 
    2  4  4  6  4  8 

[Bs,sortInds] = sort(B); 
firstInds = builtin('_ismemberfirst',A,Bs); 
lastInds = ismembc2(A,Bs); 
allInds = arrayfun(@(x,y)sortInds(x:y-(x==0)),firstInds,lastInds,'uni',0); 

allInds{3} % of A(3) in B 
ans = 
    2  3  5 

B(allInds{3}) 
ans = 
    4  4  4 

Стоит ли делать это таким образом, со штрафом за sort и два эффективных ismember звонков? Возможно, нет, но я думаю, что это интересное решение. Если у вас есть отсортированный B, он еще быстрее, так как две встроенные функции предполагают, что второй аргумент (Bs) сортируется и не тратит время на проверки. Попытайтесь посмотреть, что сработает для вас.

+0

Казалось, что все возможные подходы уже были изучены, но затем появился @chappjc ... +1 –

Смежные вопросы