2017-05-26 122 views
1

我想檢索兩個大型矩陣之間精確匹配(行特定)的索引。我有一個n×61的矩陣A,其中包含從0到9的值,另一個是n×61的矩陣B,而這裏的每行都包含0到9的值,但大多數是NaN(矩陣B的每一行中只有2到8列包含實際的數字)。矩陣A預計有150萬到300萬行,而矩陣B有大約0.2到50萬行。下面是設置的例子:兩個大型矩陣的Matlab比較

% create matrix a with random data 

dataSample = [0 9]; 
numRows = 1000000; 
numCols = 61; 
A = randi(dataSample,numRows,numCols); 

% create matrix B with random data 
numRows = 100000; 
numCols = 61; 
numColsUse = 2:8; 
dataRange = 0:9; 
B = NaN(numRows,numCols); 
for i = 1:size(B,1) 

    % randomly selet number of columns to fill 
    numColsFill = datasample(numColsUse,1); 

    % randomly select column index from available columns 
    colIdx = datasample([1:numCols],numColsFill); 

    % randomly select values from 0 to 9 
    numFill = datasample([0:9],numColsFill); 

    % insert numbers at respective column in matrix B 
    B(i,colIdx) = numFill; 

end 

我想矩陣A的每一行比較整個矩陣B,找到精確匹配,其中矩陣B匹配的數字矩陣A的數量在各自的位置(列) - 因此矩陣B中的NaN將被忽略。

我可以使用cellfun,其中I切片矩陣A在幾個子集,然後使用自定義函數來與每行中的矩陣B的子集的行比較,像這樣實現所需的結果:

% put all rows of matrix B in single cell 
cellB = {B}; 

% take subset of matrix A and convert to cell array 
subA = A(1000:5000,:); 
subA = num2cell(subA,2); 

% prepare cellB to meet cellfun conditions 
cellB = repmat(cellB, [size(subA,1) 1]); 

% apply cellfun to retrieve index of each exact match 
idxContainer = cellfun(@findMatch, cellB, subA, 'UniformOutput', false); 

功能findMatch如下所示:

function [ idx ] = LTableEval(cellB, subA) 

    idxCheckLT = lt(cellB, repmat(subA, [size(cellB,1) 1])); 
    idxCheckGT = gt(cellB, repmat(subA, [size(cellB,1) 1])); 
    idxCheck = idxCheckLT + idxCheckGT; 
    idxSum = sum(idxCheck,2); 
    idx = find(idxSum == 0); 

end 

這種方法的工作原理,但它似乎是非常低效的,尤其是RAM明智的,因爲cellfun要求所有輸入具有相同的大小,因此一個相同的數據的乘法集。關於如何以更高效的方式解決這個問題的任何想法?非常感謝!

+1

矩陣減法? – ldgorman

+0

'A-B == 0'?減法似乎是要走的方法是的 –

+0

我同意,減法對實際比較有很大的意義,並且比我現在的方法好得多。然而,這並不能解決我的更基本的問題,我想找到一種有效的方法來減少B的每一行中的每一行。任何關於這方面的想法?謝謝! – Benvaulter

回答

0

下列溶液在MathWorks的論壇提供給我:

matches = cell(size(B, 1), 1); 
for Brow = 1:size(B, 1) 
    Bcols = find(~isnan(B(Brow, :))); 
    fdsmatchedrows = find(all(A(:, Bcols) == B(Brow, Bcols), 2)); 
    matches{Brow} = [matchedrows, repmat(Brow, size(matchedrows))]; 
end 
matches = cell2mat(matches); 

這僅適用於R2016a及更高版本。可替代地,用這種線替換第二行中的for循環:

matches{Brow} = find(all(bsxfun(@eq, A(:, Bcols), B(Brow, Bcols)), 2)); 

結果是相同的傑德提供的解決方案,但相信通過使用bsxfun的更快一點。希望有所幫助!

0

這個怎麼樣:

for br = 1:size(B,1) 
    abs_diff = abs(repmat(B(br,:),[size(A,1) 1]) - A); 
    abs_diff(isnan(abs_diff)) = 0; 
    match = abs_diff == 0; 
    ind = find(sum(match,2)==size(match,2)); 
    matches{br} = [repmat(br,[length(ind) 1]) ind]; 
end 
matches = cell2mat(matches'); 
+0

嗨傑德,謝謝你的建議。這已經工作得更好了,我喜歡循環遍歷B的每一行而不是A,並且不需要將A「切片」爲子部分。然而,對於我來說,循環的signle迭代仍然在1s以上,所以即使在使用parfor時,我也在考慮非常長的計算時間...還有什麼想法?謝謝! – Benvaulter

+0

是啊...使用mex函數。 – Jed