Быстро подсчитайте количество уникальных значений для каждого столбца подматрицы.

У меня есть матрица X с десятками строк и тысячами столбцов, все элементы являются категориальными и реорганизованы в индексную матрицу. Например, столбец ith X(:,i) = [-1,-1,0,2,1,2]' преобразуется в X2(:,i) = ic из [x,ia,ic] = unique(X(:,i)) для удобства использования функции accumarray. Я случайным образом выбрал подматрицу из матрицы и подсчитал количество уникальных значений каждого столбца подматрицы. Я проделал эту процедуру 10000 раз. Я знаю несколько методов подсчета количества уникальных значений в столбце, быстрый способ, который я нашел до сих пор, показан ниже:

mx = max(X);
for iter = 1:numperm
    for j = 1:ny
        ky = yrand(:,iter)==uy(j);
        % select submatrix from X where all rows correspond to rows in y that y equals to uy(j)
        Xk = X(ky,:);
        % specify the sites where to put the number of each unique value
        mxj = mx*(j-1);
        mxi = mxj+1;
        mxk = max(Xk)+mxj;
        % iteration to count number of unique values in each column of the submatrix
        for i = 1:c
            pxs(mxi(i):mxk(i),i) = accumarray(Xk(:,i),1);
        end
    end
end

Это способ выполнить тест случайной перестановки для расчета прироста информации между матрицей данных X размера n by c и категориальной переменной y, при которой y переставляется случайным образом. В приведенных выше кодах все случайно переставленные y хранятся в матрице yrand, а количество перестановок равно numperm. Уникальные значения y хранятся в uy, а уникальный номер — ny. В каждой итерации 1:numperm подматрица Xk выбирается в соответствии с уникальным элементом y, и количество уникальных элементов в каждом столбце этой подматрицы подсчитывается и сохраняется в матрице pxs.

Наиболее затратный по времени раздел в приведенном выше коде — это итерации i = 1:c для больших c.

Можно ли выполнить функцию accumarray матричным способом, чтобы избежать цикла for? Как еще я могу улучшить приведенный выше код?

-------

В соответствии с запросом предоставляется упрощенная функция тестирования, включающая приведенные выше коды.

%% test
function test(x,y)

[r,c] = size(x);
x2 = x;
numperm = 1000;

% convert the original matrix to index matrix for suitable and fast use of accumarray function
for i = 1:c
    [~,~,ic] = unique(x(:,i));
    x2(:,i) = ic;
end

% get 'numperm' rand permutations of y
yrand(r, numperm) = 0;
for i = 1:numperm
    yrand(:,i) = y(randperm(r));
end

% get statistic of y
uy = unique(y);
nuy = numel(uy);

% main iterations
mx = max(x2);
pxs(max(mx),c) = 0;
for iter = 1:numperm
    for j = 1:nuy
        ky = yrand(:,iter)==uy(j);
        xk = x2(ky,:);
        mxj = mx*(j-1);
        mxk = max(xk)+mxj;
        mxi = mxj+1;
        for i = 1:c
            pxs(mxi(i):mxk(i),i) = accumarray(xk(:,i),1);
        end
    end
end

И тестовые данные

x = round(randn(60,3000));
y = [ones(30,1);ones(30,1)*-1];

Проверьте функцию

tic; test(x,y); toc

вернуть Elapsed time is 15.391628 seconds. на мой компьютер. В тестовой функции установлено 1000 перестановок. Поэтому, если я выполняю 10 000 перестановок и выполняю некоторые дополнительные вычисления (незначительные по сравнению с приведенным выше кодом), ожидается время более 150 s. Я думаю, можно ли улучшить код. Интуитивно понятно, что выполнение accumarray в виде матрицы может сэкономить много времени. Могу я?


person Elkan    schedule 01.03.2017    source источник
comment
Ваше описание немного сложно понять. Не могли бы вы добавить в вопрос код, который генерирует ввод, аналогичный тому, который у вас есть (например, с использованием rng(42528955) и randi), и показывает ожидаемый вывод (< href="https://stackoverflow.com/help/minimal-reproducible-example">минимальный воспроизводимый пример)? Вопросы, связанные с улучшением существующего кода, на мой взгляд, должны иметь какой-то функционирующий базовый код.   -  person Dev-iL    schedule 01.03.2017
comment
@Dev-iL Спасибо. Добавлена ​​простая функция с тестовыми данными.   -  person Elkan    schedule 01.03.2017
comment
Вы можете использовать hist или histcount. Предположим, у меня есть массив 5 * 5, и я хочу найти счетчик каждого столбца. a=randi(10,5,5);h=hist(a,1:10)   -  person rahnema1    schedule 01.03.2017
comment
@rahnema1 Это превосходно! Он работает в пять раз быстрее, чем мой, если цикл for i = 1:c заменить одной строкой pxs(mmx*(j-1)+1:mmx*j,:)=hist(x2(ky,:), xbin), где xbin=(0.5:1:max(mx)-0.5)'. Спасибо.   -  person Elkan    schedule 01.03.2017
comment
Но мне все еще интересно, существует ли встроенная функция, которая делает то же самое. Это было бы еще лучше.   -  person Elkan    schedule 01.03.2017
comment
@ rahnema1, не могли бы вы опубликовать свое решение в качестве ответа? Элкан - ты тоже можешь - просто ответь на свой вопрос (не обязательно принимать ответ).   -  person Dev-iL    schedule 02.03.2017
comment
@Dev-iL, чтобы ответить на вопрос, я должен прочитать вопрос целиком. Я ответил на заголовок вопроса в комментариях, но кажется, что в вопросе рассматривается больше дополнительных вещей.   -  person rahnema1    schedule 02.03.2017
comment
@Dev-iL В мой код внесено всего несколько изменений, которые были упомянуты в моем комментарии, т. Е. Просто измените цикл for i=1:c;...; end на одну строку pxs(mmx*(j-1)+1:mmx*j,:)=hist(x2(ky,:), xbin). Новое обновление использует функцию histc. Я не знаю, достаточно ли этого, чтобы показать ответ.   -  person Elkan    schedule 03.03.2017
comment
@Элкан Почему бы и нет? Вы всегда можете улучшить его позже...   -  person Dev-iL    schedule 03.03.2017
comment
@Dev-iL Добавлен как новый ответ, спасибо.   -  person Elkan    schedule 05.03.2017


Ответы (1)


Способ, предложенный @rahnema1, значительно улучшил вычисления, поэтому я разместил свой ответ здесь, как и по просьбе @Dev-iL.

%% test
function test(x,y)

[r,c] = size(x);
x2 = x;
numperm = 1000;

% convert the original matrix to index matrix for suitable and fast use of accumarray function
for i = 1:c
    [~,~,ic] = unique(x(:,i));
    x2(:,i) = ic;
end

% get 'numperm' rand permutations of y
yrand(r, numperm) = 0;
for i = 1:numperm
    yrand(:,i) = y(randperm(r));
end

% get statistic of y
uy = unique(y);
nuy = numel(uy);

% main iterations
mx = max(max(x2));
% preallocation
pxs(mx*nuy,c) = 0;
% set the edges of the bin for function histc
binrg = (1:mx)';
% preallocation of the range of matrix into which the results will be stored
mxr = mx*(0:nuy);
for iter = 1:numperm
    yt = yrand(:,iter);
    for j = 1:nuy
        pxs(mxr(j)+1:mxr(j),:) = histc(x2(yt==uy(j)),binrg);
    end
end

Результаты теста:

>> x = round(randn(60,3000));
>> y = [ones(30,1);ones(30,1)*-1];
>> tic; test(x,y); toc
Elapsed time is 15.632962 seconds.
>> tic; test(x,y); toc % using the way suggested by rahnema1, i.e., revised function posted above
Elapsed time is 2.900463 seconds.
person Elkan    schedule 05.03.2017