Поиск всех индексов по 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+, я думаю), который даст вам оба этих индекса: _ismemberfirst
(а 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
) сортируется и не тратит время на проверки. Попробуйте и посмотрите, что работает для вас.
Простой подход заключается в использовании 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