MATLAB Vectorization-셀에 대한 0이 아닌 행렬 행 인덱스


10

Matlab과 함께 일하고 있습니다.

이진 정사각 행렬이 있습니다. 각 행마다 1 이상의 항목이 있습니다.이 행렬의 각 행을 통과하여 해당 1의 인덱스를 반환하여 셀 항목에 저장하려고합니다.

Matlab에서는 for 루프가 실제로 느리기 때문에이 행렬의 모든 행을 반복하지 않고이 작업을 수행 할 수있는 방법이 있는지 궁금합니다.

예를 들어, 내 매트릭스

M = 0 1 0
    1 0 1
    1 1 1 

결국에는

A = [2]
    [1,3]
    [1,2,3]

A세포도 마찬가지 입니다.

for 루프를 사용하지 않고 결과를 더 빨리 계산할 목적으로이 목표를 달성 할 수있는 방법이 있습니까?


빠른 결과를 원하십니까 for? 아니면 루프 를 피하기 위해 결과를 원 하십니까? 이 문제의 경우 최신 버전의 MATLAB을 사용하면 for루프가 가장 빠른 솔루션이라고 생각합니다 . 성능 문제가있는 경우 오래된 조언을 기반으로 솔루션에 대한 잘못된 위치를 찾고 있다고 생각합니다.

@ 빠른 결과를 원합니다. 내 매트릭스는 매우 큽니다. 컴퓨터에서 for 루프를 사용하여 실행 시간은 약 30 초입니다. 속도를 높일 수있는 영리한 벡터화 작업이나 mapReduce 등이 있는지 알고 싶습니다.
ftxx

1
나는 당신이 할 수없는 것 같아요. 벡터화는 정확하게 설명 된 벡터 및 행렬에서 작동하지만 결과는 길이가 다른 벡터를 허용합니다. 따라서 내 가정은 항상 명시 적 루프 또는과 같은 루프 인 변장을해야한다고 가정 cellfun합니다.
HansHirse

@ftxx 얼마나 큽니까? 그리고 1전형적인 행에 몇 개가 있습니까? 나는 find실제 메모리에 맞을 정도로 작은 것을 위해 루프가 30에 가까운 것을 취할 것으로 기대하지 않습니다 .

@ftxx 업데이트 된 답변을 참조하십시오. 성능이 약간 향상되었으므로 편집 한 것입니다
Wolfie February

답변:


11

이 답변의 맨 아래에는 임의의 for루프를 피하는 것이 아니라 성능에 관심이 있다는 것을 분명히 했으므로 벤치마킹 코드가 있습니다.

실제로 for루프는 아마도 가장 성능이 좋은 옵션 이라고 생각 합니다. "새로운"(2015b) JIT 엔진이 도입 된 이후 ( 소스 ) for루프는 본질적으로 느리지 않습니다. 실제로 내부적으로 최적화되어 있습니다.

것을 당신은 벤치 마크에서 볼 수 mat2cellThomasIsCoding에 의해 제공되는 옵션은 여기에 매우 느립니다 ...

비교 1

우리가 스케일을 더 명확하게하기 위해 그 라인을 제거하면 내 splitapply방법이 상당히 느리고 obchardon의 accumarray 옵션 이 조금 더 좋지만 가장 빠른 (그리고 비교 가능한) 옵션은 arrayfun(Thomas가 제안한대로) 또는 for루프를 사용하고 있습니다. 참고 arrayfun기본적으로 인 for이 놀라운 넥타이되지 않도록, 대부분의 사용 사례에 대한 변장 루프!

비교 2

for코드 가독성을 높이고 최상의 성능을 얻으려면 루프를 사용하는 것이 좋습니다 .

편집 :

루핑이 가장 빠른 방법이라고 가정하면 find명령을 최적화 할 수 있습니다 .

구체적으로 특별히

  • M논리적으로 만듭니다 . 아래 그림에서 알 수 있듯이 비교적 작은 M경우에는 빠를 수 있지만 큰 경우에는 유형 변환의 절충으로 인해 느려질 수 있습니다 M.

  • 논리 M를 사용하여 1:size(M,2)대신 배열을 색인화 하십시오 find. 이렇게하면 루프의 가장 느린 부분 ( find명령)을 피하고 형식 변환 오버 헤드를 능가하여 가장 빠른 옵션이됩니다.

최상의 성능을위한 권장 사항은 다음과 같습니다.

function A = f_forlooplogicalindexing( M )
    M = logical(M);
    k = 1:size(M,2);
    N = size(M,1);
    A = cell(N,1);
    for r = 1:N
        A{r} = k(M(r,:));
    end
end

이것을 아래의 벤치 마크에 추가했습니다. 다음은 루프 스타일 방식의 비교입니다.

비교 3

벤치마킹 코드 :

rng(904); % Gives OP example for randi([0,1],3)
p = 2:12; 
T = NaN( numel(p), 7 );
for ii = p
    N = 2^ii;
    M = randi([0,1],N);

    fprintf( 'N = 2^%.0f = %.0f\n', log2(N), N );

    f1 = @()f_arrayfun( M );
    f2 = @()f_mat2cell( M );
    f3 = @()f_accumarray( M );
    f4 = @()f_splitapply( M );
    f5 = @()f_forloop( M );
    f6 = @()f_forlooplogical( M );
    f7 = @()f_forlooplogicalindexing( M );

    T(ii, 1) = timeit( f1 ); 
    T(ii, 2) = timeit( f2 ); 
    T(ii, 3) = timeit( f3 ); 
    T(ii, 4) = timeit( f4 );  
    T(ii, 5) = timeit( f5 );
    T(ii, 6) = timeit( f6 );
    T(ii, 7) = timeit( f7 );
end

plot( (2.^p).', T(2:end,:) );
legend( {'arrayfun','mat2cell','accumarray','splitapply','for loop',...
         'for loop logical', 'for loop logical + indexing'} );
grid on;
xlabel( 'N, where M = random N*N matrix of 1 or 0' );
ylabel( 'Execution time (s)' );

disp( 'Done' );

function A = f_arrayfun( M )
    A = arrayfun(@(r) find(M(r,:)),1:size(M,1),'UniformOutput',false);
end
function A = f_mat2cell( M )
    [i,j] = find(M.');
    A = mat2cell(i,arrayfun(@(r) sum(j==r),min(j):max(j)));
end
function A = f_accumarray( M )
    [val,ind] = ind2sub(size(M),find(M.'));
    A = accumarray(ind,val,[],@(x) {x});
end
function A = f_splitapply( M )
    [r,c] = find(M);
    A = splitapply( @(x) {x}, c, r );
end
function A = f_forloop( M )
    N = size(M,1);
    A = cell(N,1);
    for r = 1:N
        A{r} = find(M(r,:));
    end
end
function A = f_forlooplogical( M )
    M = logical(M);
    N = size(M,1);
    A = cell(N,1);
    for r = 1:N
        A{r} = find(M(r,:));
    end
end
function A = f_forlooplogicalindexing( M )
    M = logical(M);
    k = 1:size(M,2);
    N = size(M,1);
    A = cell(N,1);
    for r = 1:N
        A{r} = k(M(r,:));
    end
end

1
이미보고 공표했습니다. :-) 여전히 Luis를 기다리고 있습니다. 그는 확실히 그것을 위해 검은 MATLAB 마술을 가지고 있습니다.
HansHirse

@Hans Haha 그래 그의 평범한 트릭 (암시 적 확장, 영리한 인덱싱 등)은 일반적으로 행렬을 유지하지만 병목 현상은 셀에 요약되어 있습니다
Wolfie

1
이 시간은의 희소성에 크게 의존합니다 M. 예를 들어 5 %의 요소 만 채워 M = randi([0,20],N) == 20;지면 for루프가 가장 느리고 arrayfun방법이 성공합니다.

@HansHirse :-) 내 접근 방식이었을 것 accumarray없이 ind2sub, 그러나보다 느린 for루프
루이스 Mendo

2

arrayfun아래처럼 시도해 볼 수 있습니다.M

A = arrayfun(@(r) find(M(r,:)),1:size(M,1),'UniformOutput',false)

A =
{
  [1,1] =  2
  [1,2] =

     1   3

  [1,3] =

     1   2   3

}

또는 (에 의한 느린 접근 mat2cell)

[i,j] = find(M.');
A = mat2cell(i,arrayfun(@(r) sum(j==r),min(j):max(j)))

A =
{
  [1,1] =  2
  [2,1] =

     1
     3

  [3,1] =

     1
     2
     3

}

1
비록이 arrayfun이 1 전선) 피 루프 고속 인 2) 모두에서 실패 할 수 있으므로 OP에 의해 희망으로, 기본적으로 루프 인 변이다
늑대

2

편집 : 벤치 마크를 추가하면 결과 가 for 루프가보다 효율적이라는 것을 알 수accumarray 있습니다.


당신은 사용할 수 있습니다 findaccumarray:

[c, r] = find(A');
C = accumarray(r, c, [], @(v) {v'});

열별로 그룹화 A'되므로 행렬이 바뀝니다 ( ) find.

예:

A = [1 0 0 1 0
     0 1 0 0 0
     0 0 1 1 0
     1 0 1 0 1];

%  Find nonzero rows and colums
[c, r] = find(A');

%  Group row indices for each columns
C = accumarray(r, c, [], @(v) {v'});

% Display cell array contents
celldisp(C)

산출:

C{1} = 
     1     4

C{2} = 
     2

C{3} =
     3     4

C{4} = 
     1     3     5

기준:

m = 10000;
n = 10000;

A = randi([0 1], m,n);

disp('accumarray:')
tic
[c, r] = find(A');
C = accumarray(r, c, [], @(v) {v'});
toc
disp(' ')

disp('For loop:')
tic
C = cell([size(A,1) 1]);
for i = 1:size(A,1)
    C{i} = find(A(i,:));
end
toc

결과:

accumarray:
Elapsed time is 2.407773 seconds.

For loop:
Elapsed time is 1.671387 seconds.

for 루프는 다음보다 효율적입니다 accumarray.


이것은 obchardon이 이미 제안한 방법 과 거의 같습니다.
Wolfie

예, 조금 느려서 게시 한 후 그의 답변을 보았습니다.
엘리아 후 아론

2

accumarray 사용 :

M = [0 1 0
     1 0 1
     1 1 1];

[val,ind] = find(M.');

A = accumarray(ind,val,[],@(x) {x});

1
Octave 및 MATLAB Online의 실행 시간은 다음과 같은 간단한 for 루프의 약 2 배입니다 MM{I} = find(M(I, :)).
HansHirse


예, 각 셀의 크기가 동일하지 않으므로이 문제를 완전히 벡터화 할 수 없습니다 (또는 내가 보지 못한 트릭이 있습니다). for 루프를 숨기는 솔루션 일뿐입니다.
obchardon

필요 없음 ind2sub:[ii, jj] = find(M); accumarray(ii, jj, [], @(x){x})
Luis Mendo

@LuisMendo 감사합니다. 내 답변을 편집했습니다.
obchardon

2

strfind 를 사용할 수 있습니다 :

A = strfind(cellstr(char(M)), char(1));

나 (게으른) 문서조차 보지 못했지만 string문자가 아닌 실제 유형을 사용하면 더 빠를 까요? 현에 대한 많은 최적화가 있기 때문에 왜 존재하는지 ...
Wolfie

@Wolfie 숫자 배열이 문자열보다 char 배열과 더 비슷하다고 생각하므로 숫자 배열을 문자 배열로 변환하는 것이 string 변환보다 더 간단해야합니다.
rahnema1
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.