Поиск всех индексов по 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?

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});

Обратите внимание, что выводом является массив ячеек. Для вашего примера вы должны получить:

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

это означает, что в B согласование A(1) а также A(2), A(3) соответствует элементам B(2), B(3) а также B(4), а также A(4) равняется B(1),

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

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,

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

Тем не менее, для тех, кто заинтересован в решении с недокументированной функциональностью и, по общему признанию, хакерским подходом, есть другой способ сделать это (то есть для каждого элемента Aнайти индексы всех соответствующих элементов в B). Мышление идет следующим образом: в отсортированном B, что если у вас есть первый и последний индексы каждого соответствующего элемента? Оказывается, есть две вспомогательные функции, используемые ismember (если у вас есть R2012b+, я думаю), который даст вам оба этих индекса: _ismemberfirstbuiltin) а также 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) сортируется и не тратит время на проверки. Попробуйте и посмотрите, что работает для вас.

Простой подход заключается в использовании 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

Решения 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
Другие вопросы по тегам