Extract diagonal element from each frontal slice of tensor

前端 未结 3 936
忘掉有多难
忘掉有多难 2021-01-19 10:55

I have a p-by-p-by-n tensor. I want to extract diagonal element for each p-by-p slice. Are there anyone know how to do this without looping?

Thank you.

相关标签:
3条回答
  • 2021-01-19 11:28

    Behold the ever mighty and ever powerful bsxfun for vectorizing MATLAB problems to do this task very efficiently using MATLAB's linear indexing -

    diags = A(bsxfun(@plus,[1:p+1:p*p]',[0:n-1]*p*p))
    

    Sample run with 4 x 4 x 3 sized input array -

    A(:,:,1) =
        0.7094    0.6551    0.9597    0.7513
        0.7547    0.1626    0.3404    0.2551
        0.2760    0.1190    0.5853    0.5060
        0.6797    0.4984    0.2238    0.6991
    A(:,:,2) =
        0.8909    0.1493    0.8143    0.1966
        0.9593    0.2575    0.2435    0.2511
        0.5472    0.8407    0.9293    0.6160
        0.1386    0.2543    0.3500    0.4733
    A(:,:,3) =
        0.3517    0.9172    0.3804    0.5308
        0.8308    0.2858    0.5678    0.7792
        0.5853    0.7572    0.0759    0.9340
        0.5497    0.7537    0.0540    0.1299
    diags =
        0.7094    0.8909    0.3517
        0.1626    0.2575    0.2858
        0.5853    0.9293    0.0759
        0.6991    0.4733    0.1299
    

    Benchmarking

    Here's few runtime tests comparing this bsxfun based approach against repmat + eye based approach for big datasizes -

    ***** Datasize: 500 x 500 x 500 *****
    ----------------------- With BSXFUN
    Elapsed time is 0.008383 seconds.
    ----------------------- With REPMAT + EYE
    Elapsed time is 0.163341 seconds.
    
    ***** Datasize: 800 x 800 x 500 *****
    ----------------------- With BSXFUN
    Elapsed time is 0.012977 seconds.
    ----------------------- With REPMAT + EYE
    Elapsed time is 0.402111 seconds.
    
    ***** Datasize: 1000 x 1000 x 500 *****
    ----------------------- With BSXFUN
    Elapsed time is 0.017058 seconds.
    ----------------------- With REPMAT + EYE
    Elapsed time is 0.690199 seconds.
    
    0 讨论(0)
  • 2021-01-19 11:28

    Just reading Divakar's answer and trying to understand why he again is roughly 10 times faster than my idea I put together code mixing both, and ended up with code which is faster:

    A=reshape(A,[],n);
    diags2 = A(1:p+1:p*p,:);
    

    For a 500x500x500 tensor I get 0.008s for Divakar's solution and 0.005s for my solution using Matlab 2013a. Probably plain indexing is the only way to beat bsxfun.

    0 讨论(0)
  • 2021-01-19 11:50

    One suggestion I have is to create a p x p logical identity matrix, replicate this n times in the third dimension, and then use this matrix to access your tensor. Something like this, supposing that your tensor was stored in A:

    ind = repmat(logical(eye(p)), [1 1 n]);
    out = A(ind);
    

    Example use:

    >> p = 5; n = 3;
    >> A = reshape(1:75, p, p, n)
    
    A(:,:,1) =
    
         1     6    11    16    21
         2     7    12    17    22
         3     8    13    18    23
         4     9    14    19    24
         5    10    15    20    25
    
    
    A(:,:,2) =
    
        26    31    36    41    46
        27    32    37    42    47
        28    33    38    43    48
        29    34    39    44    49
        30    35    40    45    50
    
    
    A(:,:,3) =
    
        51    56    61    66    71
        52    57    62    67    72
        53    58    63    68    73
        54    59    64    69    74
        55    60    65    70    75
    
    >> ind = repmat(logical(eye(p)), [1 1 n]);
    >> out = A(ind)
    
    out =
    
         1
         7
        13
        19
        25
        26
        32
        38
        44
        50
        51
        57
        63
        69
        75
    

    You'll notice that we grab the diagonals of the first slice, followed by the diagonals of the second slice, etc. up until the last slice. These values are all concatenated into a single vector.

    0 讨论(0)
提交回复
热议问题