Поиск всех индексов
Это то, что описано в одном из примеров 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
Вы можете поменять входные аргументы на 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)
.
Ответ 2
Эта строка вернет все индексы:
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
.
Ответ 3
Простым подходом является использование 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
Ответ 4
Самые элегантные решения (т.е. те, которые не используют итерации find
), включают в себя обмен входов на ismember
и группировку, как индексы с accumarray
, как в ответе Eitan, или векторизация find с помощью 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
без необходимости выполнять цикл. В B
не встречается A(1)
или A(2)
(5 или 3), поэтому эти индексы 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
) сортируется и не тратит время на проверки. Попробуйте и посмотрите, что сработает для вас.
Ответ 5
Решения 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