3

私はmatlabが初めてで、これを行う方法に興味があります:

私はかなり大きな(27000x11)マトリックスを持っており、8番目の列には時々変化するが2000行のように一定である数値が含まれています(必ずしも連続しているわけではありません)。

8列目に同じ値がある行の3列目のエントリの平均を計算したいと思います。これは、8 番目の列の各値です。また、3 列目の平均を 8 列目の値の関数としてプロットしたいと思いますが、[mean_of_3rd,8th] を含む新しい行列 (2x2) を取得できれば実行できます。

例: (便宜上小さい行列)

1 2 3 4 5
3 7 5 3 2
1 3 2 5 3
4 5 7 5 8
2 4 7 4 4

4 列目は行 1 と 5 で同じ値を持っているので、2 と 4 (列 2 の対応する要素、斜体の太字) の平均を計算し、4 列目の値と一緒に別の行列に入れたいと思います。3 と 5 (太字) も同じです。これは、4 番目の列がこれら 2 つの値と同じであるためです。

3 4
4 5

など...これは簡単な方法で可能ですか?

4

3 に答える 3

4

万能で十分に活用されていない を使用しますaccumarray

この行は、2 番目の列によって累積された 4 番目の列の平均値を示します。

means = accumarray( A(:,4) ,A(:,2),[],@mean)

この行は、各セットの要素数を示します。

count = accumarray( A(:,4) ,ones(size(A(:,4))))

少なくとも 1 回出現するものだけをフィルタリングする場合は、次のようにします。

>> filtered = means(count>1)

filtered =

     3
     4

これは、4 列目の正の整数に対してのみ機能します。


各セット内の要素の数をカウントする別の可能性:

 count = accumarray( A(:,4) ,A(:,4),[],@numel)
于 2012-10-08T20:23:41.790 に答える
2

Andrey と Rody のアイデアに基づいた、少し洗練されたアプローチ。データは整数ではなく実数であるため、accumarray を直接使用することはできません。しかし、unique を使用して繰り返しエントリのインデックスを見つけることができます。次に、整数を操作します。

% get unique entries in 4th column
[R, I, J] = unique(A(:,4));

% count the repeating entries: now we have integer indices!
counts = accumarray(J, 1, size(R));

% sum the 2nd column for all entries
sums   = accumarray(J, A(:,2), size(R));

% compute means
means  = sums./counts;

% choose only the entries that show more than once in 4th column
inds   = counts>1;
result = [means(inds) R(inds)];

次の合成データの時間比較:

A=randi(100, 1000000, 5);

% Rody's solution
Elapsed time is 0.448222 seconds.

% The above code
Elapsed time is 0.148304 seconds.
于 2012-10-09T08:32:40.017 に答える
1

私の公式の答え:

A4 = A(:,4);
R = unique(A4);   

means = zeros(size(R));
inds  = false(size(R));

for jj = 1:numel(R)        
    I = A4==R(jj);
    sumI = sum(I);        
    inds(jj)  = sumI>1;
    means(jj) = sum(A(I,2))/sumI;        
end

result = [means(inds) R(inds)];

これは以下の理由による。プロファイリング形式で、私たちが思いついたすべての代替手段を次に示します。

%# sample data
A = [
    1 2 3 4 5
    3 7 5 3 2
    1 3 2 5 3
    4 5 7 5 8
    2 4 7 4 4];

%# accumarray
%# works only on positive integers in A(:,4)
tic
for ii = 1:1e4
    means = accumarray( A(:,4) ,A(:,2),[],@mean);
    count = accumarray( A(:,4) ,ones(size(A(:,4))));
    filtered = means(count>1);
end
toc

%# arrayfun
%# works only on integers in A(:,4)
tic
for ii = 1:1e4
    B = arrayfun(@(x) A(A(:,4)==x, 2), min(A(:,4)):max(A(:,4)), 'uniformoutput', false);
    filtered = cellfun(@mean, B(cellfun(@(x) numel(x)>1, B)) );    
end
toc


%# ordinary loop
%# works only on integers in A(:,4)    
tic
for ii = 1:1e4

    A4 = A(:,4);
    R = min(A4):max(A4);

    means = zeros(size(R));
    inds  = false(size(R));
    for jj = 1:numel(R)
        I = A4==R(jj);
        sumI = sum(I);        
        inds(jj) = sumI>1;
        means(jj) = sum(A(I,2))/sumI; 
    end

    filtered = means(inds);   
end
toc

結果:

Elapsed time is 1.238352 seconds.  %# (accumarray)
Elapsed time is 7.208585 seconds.  %# (arrayfun + cellfun)
Elapsed time is 0.225792 seconds.  %# (for loop)

通常のループは明らかにここに行く方法です。

mean内側のループにがないことに注意してください。これは、mean(少なくとも R2010 では) Matlab の組み込み関数ではないためです。ループ内で使用すると、ループが JIT コンパイルの対象外になり、10 倍以上遅くなります。上記の形式を使用すると、ループが高速化されます。ソリューションのほぼ 5.5 倍の速度になりaccumarrayます。

あなたのコメントから判断すると、A(:,4)(整数だけでなく)すべてのエントリで機能するようにループを変更するのはほとんど簡単です。

A4 = A(:,4);
R = unique(A4);   

means = zeros(size(R));
inds  = false(size(R));
for jj = 1:numel(A4)
    I = A4==R(jj);
    sumI = sum(I);        
    inds(jj) = sumI>1;
    means(jj) = sum(A(I,2))/sumI;
end

filtered = means(inds); 

公式の回答としてコピーしてトップに貼り付けます:)

于 2012-10-09T04:54:09.807 に答える