मतलाब वेक्टराइजेशन - सेल में कोई भी शून्य मैट्रिक्स रो इंडेक्स नहीं है


10

मैं मतलूब के साथ काम कर रहा हूं।

मेरे पास एक बाइनरी स्क्वायर मैट्रिक्स है। प्रत्येक पंक्ति के लिए, 1. की एक या एक से अधिक प्रविष्टियां हैं। मैं इस मैट्रिक्स की प्रत्येक पंक्ति से गुजरना चाहता हूं और उन 1s के सूचकांक को वापस करता हूं और उन्हें सेल की प्रविष्टि में संग्रहीत करता हूं।

मैं सोच रहा था कि क्या इस मैट्रिक्स की सभी पंक्तियों पर लूपिंग के बिना ऐसा करने का एक तरीका है, जैसा कि Matlab में लूप वास्तव में धीमा है।

उदाहरण के लिए, मेरा मैट्रिक्स

M = 0 1 0
    1 0 1
    1 1 1 

फिर आखिरकार, मुझे कुछ ऐसा चाहिए

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

तो Aएक सेल है।

क्या लूप के लिए उपयोग किए बिना इस लक्ष्य को प्राप्त करने का एक तरीका है, परिणाम को अधिक तेज़ी से गणना करने के उद्देश्य से?


क्या आप चाहते हैं कि परिणाम तेज हो या आप चाहते हैं कि परिणाम forलूप से बचें ? इस समस्या के लिए, MATLAB के आधुनिक संस्करणों के साथ, मुझे forसबसे तेज़ समाधान होने का संदेह है । यदि आपको कोई प्रदर्शन समस्या है, तो मुझे संदेह है कि आप पुरानी सलाह के आधार पर समाधान के लिए गलत जगह देख रहे हैं।
विल

@ मैं चाहता हूं कि परिणाम तेजी से आएं। मेरा मैट्रिक्स बहुत बड़ा है। मेरे कंप्यूटर में लूप के लिए रन टाइम लगभग 30s है। मैं जानना चाहता हूं कि क्या कुछ चालाक वेक्टराइजेशन ऑपरेशंस या, मैपरेड आदि ऐसे हैं जो गति बढ़ा सकते हैं।
ftxx

1
मुझे संदेह है, आप नहीं कर सकते। वेक्टराइजेशन सटीक रूप से वर्णित वैक्टर और मैट्रिस पर काम करता है, लेकिन आपका परिणाम विभिन्न लंबाई के वैक्टर के लिए अनुमति देता है। इस प्रकार, मेरी धारणा यह है कि आपके पास हमेशा कुछ स्पष्ट लूप या कुछ लूप-इन-भेस होंगे cellfun
हंसहिरस

@ftxx कितना बड़ा? और 1एक विशिष्ट पंक्ति में कितने एस? मैं एक findलूप की अपेक्षा नहीं कर सकता कि वह किसी भी चीज को 30 के करीब ले जाए, जो कि शारीरिक मेमोरी पर फिट होने के लिए काफी कम हो।
विल

@ftxx कृपया मेरे अपडेट किए गए उत्तर को देखें, मैंने संपादित किया है क्योंकि इसे मामूली प्रदर्शन सुधार के साथ स्वीकार कर लिया गया था
वोल्फी

जवाबों:


11

इस उत्तर के निचले भाग में कुछ बेंचमार्किंग कोड है, क्योंकि आपने स्पष्ट किया है कि आप forलूप से बचने के बजाय प्रदर्शन में रुचि रखते हैं ।

वास्तव में, मुझे लगता है कि forलूप शायद यहां का सबसे अच्छा विकल्प है। चूंकि "नया" (2015 बी) जेआईटी इंजन पेश किया गया था ( स्रोत ) forलूप स्वाभाविक रूप से धीमा नहीं हैं - वास्तव में वे आंतरिक रूप से अनुकूलित हैं।

आप बेंचमार्क से देख सकते हैं कि mat2cellथॉमसआईसकोडिंग द्वारा यहां दिया गया विकल्प बहुत धीमा है ...

तुलना १

यदि हम स्केल को स्पष्ट करने के लिए उस रेखा से छुटकारा पा लेते हैं, तो मेरा splitapplyतरीका काफी धीमा है, ऑबकार्डन का एक्सीमरेयर विकल्प थोड़ा बेहतर है, लेकिन सबसे तेज़ (और तुलनीय) विकल्प या तो उपयोग कर रहे हैं arrayfun(जैसा कि थॉमस द्वारा भी सुझाव दिया गया है) या एक forलूप। ध्यान दें कि arrayfunमूल forरूप से अधिकांश उपयोग-मामलों के लिए भेस में एक लूप है, इसलिए यह एक आश्चर्यजनक टाई नहीं है!

तुलना २

मैं आपको 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

मैंने इसे नीचे दिए गए मानदंड में जोड़ा है, यहाँ लूप-शैली के दृष्टिकोण की तुलना है:

तुलना ३

बेंचमार्किंग कोड:

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
पहले से ही देखा और उखाड़ा। :-) अभी भी लुइस की प्रतीक्षा कर रहा है; वह यकीन है कि उसके लिए कुछ काले MATLAB जादू है।
हंसहिरस

@ हांस हां हां, हालांकि उनका सामान्य बैग चालें (अंतर्निहित विस्तार, चतुर अनुक्रमण, ...) आमतौर पर मैट्रिस के रूप में चीजों को रखता है, यहां अड़चन कोशिकाओं में संक्षेप है
वोल्फी

1
ध्यान दें कि ये समय की दृढ़ता पर निर्भर हैं M। यदि, उदाहरण के लिए, केवल 5% तत्व आबाद हैं, M = randi([0,20],N) == 20;तो forलूप अब तक सबसे धीमा है और आपकी arrayfunविधि जीत जाती है।
विल

@ हंसीसे :-) मेरा दृष्टिकोण accumarrayबिना किसी के रहा होगा ind2sub, लेकिन यह forलूप की तुलना में धीमा है
लुइस मेंडो

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) तेजी से किया जा रहा है, जैसा कि ओपी द्वारा आशा व्यक्त की गई है
वोल्फी

2

संपादित करें : मैंने एक बेंचमार्क जोड़ा, परिणाम बताते हैं कि एक लूप की तुलना में अधिक कुशल हैaccumarray


आप उपयोग कर सकते हैं findऔर accumarray:

[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.

पाश के लिए एक से अधिक कुशल है accumarray...


यह बहुत पहले से ही obchardon द्वारा प्रस्तावित विधि है , नहीं?
वोल्फि

हां, मैं थोड़ा धीमा था, मैंने मेरा पोस्ट करने के बाद उसका जवाब देखा।
एलियाहु आरोन

2

अभिवृद्धि का उपयोग करना :

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

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

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

1
ऑक्टेव और MATLAB ऑनलाइन में निष्पादन का समय लूप के लिए एक साधारण से 2x है MM{I} = find(M(I, :))
11

2
@Hans आप को देखने के लिए चाहते हो सकता है मेरा उत्तर
Wolfie

हाँ, चूंकि प्रत्येक कोशिका का आकार समान नहीं है, इसलिए यह समस्या पूरी तरह से सदिश नहीं की जा सकती है (या एक चाल है जिसे मैंने नहीं देखा है)। यह केवल एक समाधान है जो लूप के लिए छिपा है।
obchardon

इसके लिए कोई आवश्यकता नहीं है ind2sub:[ii, jj] = find(M); accumarray(ii, jj, [], @(x){x})
लुइस मेंडो

@LuisMendo धन्यवाद, मैंने अपना उत्तर संपादित कर दिया है।
रात्रि

2

आप strfind का उपयोग कर सकते हैं :

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

मैंने (आलसी) भी डॉक्स में नहीं देखा है, लेकिन क्या यह वास्तविक stringप्रकारों का उपयोग करने के बजाय जल्दी होगा , न कि चार्ट के बजाय? स्ट्रिंग्स के लिए बहुत सारे अनुकूलन हैं, इसलिए वे क्यों मौजूद हैं ...
वुल्फ

@ मुझे लगता है कि संख्यात्मक सारणी तार सरणियों की तुलना में चार सरणियों के समान हैं, इसलिए वर्ण सरणी के लिए संख्यात्मक सरणी का रूपांतरण स्ट्रिंग में रूपांतरण की तुलना में अधिक सीधा होना चाहिए।
rahnema1
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.