When I answered this question, my answer started with: "For starters, you can get rid of all the if-statements by storing all the names in a cell."
The "For starters" bit was because I thought that I could add an edit with a vectorized solution. But when I tried to do so I ran into trouble vectorizing the use of mrdivide (b/a).
My question (marked below in the code) is if it is possible to solve b(z,:)/a(z,:) without using a loop. In other words, to solve b/a independently for each row of the matrices.
person = [98 206 35 114;
60 206 28 52;
100 210 31 116;
69 217 26 35;
88 213 42 100];
person1 = [93 208 34 107];
allNames = {'Cameron'; 'David'; 'Mike'; 'Bill'; 'Joe'};
n = 5;
a = max(person,repmat(person1,n,1));
b = min(person,repmat(person1,n,1));
for z = 1:5
percent_error = b(z,:)/a(z,:); %// Here is my question
if percent_error >= 0.85
disp(['Match, its ', allNames{z} ,'!'])
end
end
You can indeed eliminate the loop by vectorizing the operation. The trick is working with diagonal block matrices. Each block is matrix with only one row (each time a different row). After you create such a block matrix for a and for b, you can use mrdivide:
% # Without loop
tic
A = zeros(size(a) * size(a, 1));
B = zeros(size(b) * size(b, 1));
V = ones(size(a, 2), 1) * (1:size(a, 1));
idx = (0:size(A, 1):numel(A) - 1) + (V(:)' - 1) * size(a, 1) + 1;
A(idx) = a';
B(idx) = b';
X = diag(B / A);
percent_error1 = X(1:size(a, 1):end);
toc
% # With loop
tic
percent_error2 = zeros(5, 1);
for z = 1:5
percent_error2(z) = b(z,:) / a(z,:);
end
toc
The result is:
Elapsed time is 0.000160 seconds.
Elapsed time is 0.000048 seconds.
percent_error1 =
0.9741
0.8516
0.9670
0.8221
0.9611
percent_error2 =
0.9741
0.8516
0.9670
0.8221
0.9611
Note that this is one of those cases where matrix division of large arrays takes longer than a for loop.
I was thinking about this:
person/person1
But this would only give good result when every index in person is bigger than the corresponding index in person1.
Related
I want to write this matlab code in python but I do not know what LEV(1:n+1:n^2) = 0; or LEV(i,:) means. Can anyone explain me what are this notation? Thank you!
function A = ILU_p(A,p)
n = length(A);
LEV = inf(n);
LEV(find(A)) = 0;
LEV(1:n+1:n^2) = 0;
for i = 2:n
for k = 1:i-1
if LEV(i,k) > p
continue
end
A(i,k) = A(i,k) / A(k,k);
A(i,k+1:n) = A(i,k+1:n) - A(i,k) * A(k,k+1:n);
LEV(i,k+1:n) = min([LEV(i,k+1:n); LEV(i,k) + LEV(k,k+1:n) + 1]);
end
A(i,find(LEV(i,:)>p)) = 0;
end
The below sets up a vector of values to be used in an index. If n=10 then the below would yield a row vector of [1 12 23 34 45 56 67 78 89 100]
1:n+1:n^2
Since LEV is set up as an nxn matrix and the above row vector picks up the diagonal elements, i.e., LEV(1) = LEV(1,1), LEV(12) = LEV(2,2), etc.
LEV(i,:) is MATLAB's shorthand for referencing all columns in row i.
I have a simple 2D matrix like this in MATLAB:
A = [34 10;
23 10;
64 10];
What I need to do is to find max(A(:,1)), then while A(j,1) < max(A(:,1)) add rows like [A(j,1)+1 10] to the matrix, so I want to eventually get this:
A = [34 10;
35 10;
36 10;
37 10;
.
.
.
62 10;
63 10;
64 10;
.
23 10;
24 10;
25 10;
.
.
.
62 10
63 10
64 10
.
64 10];
I have written the following but it does not work:
for j = 1:size(A,1)
while A(j,1) < max(A(:,1))
A(end+1,:) = [A(j,1)+1 10];
end
end
Any ideas how I could do that?
You can try the following, for Matlab without loops:
my_max = max(A(:,1));
my_arrays = arrayfun(#(x) [x:my_max]', A(:,1), 'uni', 0);
my_expanded_column = cat(1,my_arrays{:});
my_output = [my_expanded_column, 10*ones(size(my_expanded_column))]
It first finds the maximum,
Then expands each entry to a range to the maximum,
Then combines all the ranges to one,
Then tacks on the second column of 10's.
Your while-loop gets stuck checking the same element over and over again (because you don't increment anything).
You could instead use a for-loop to count up from each element in A's first column. And make sure you add the rows to a new matrix, otherwise your loop will recursively expand A ad infinitum.
For example:
B = [];
index = 1; % keeps track of the last element of B
for j = 1:size(A,1)
for k = A(j,1):max(A(:,1))
B(index,:) = [k A(j,2)];
index = index + 1;
end
end
I have a main matrix, say
A=magic(5);
and also a vector
v=[1;3;5;2;2];
I want to add up row-wise the elements of A in this way: add first row from the v(1)st element to the end, second row from the v(2)rd element to the end, third row from the v(3)th element to the end, and so on.
I know that I can do this using for-loop. But I want to know if there is a vectorized way to do it.
edit:
Let me clarify my question with an example:
Assume A and v as above.
A =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
and
v =
1
3
5
2
2
Now I want a way to get the following results:
answer =
65 % 17+24+1+8+15
37 % 7+14+16
22 % 22
55 % 12+19+21+3
54 % 18+25+2+9
You can use cumsum along the rows. The solution is a bit complex, so I'll start with a simpler example:
Suppose you want to sum all elements of the i-th row of A till (including) the v(i)-th place: res_i = \sum_{k=1..v(i)} A_ik
m = size(A,1); % num of rows
csA = cumsum(A, 2); % cumsum along rows
res = csA( sub2ind( size(A), 1:m, v ) ); % pick the vi-th column for the i-th row
Now, for your question, since you want the sum of all elements from v(i) to the end, we need to flip A and change v accordingly
[m n] = size(A);
fA = fliplr(A);
fv = n + 1 - v; % flip the meaning of v
csA = cumsum( fA, 2 );
res = csA( sub2ind( [m n], 1:m, fv ) ); % should do the trick...
I know it's kinda cheating, but how about:
S = arrayfun(#(ii) sum(A(ii, v(ii):end)), 1:size(A,1)).';
Usually I'm a bit weary when using arrayfun, but when comparing:
% sample data
N = 5000;
A = magic(N);
v = randi(N, N,1);
% simple loop
tic
S = zeros(N,1);
for ii = 1:N
S(ii) = sum(A(ii, v(ii):end));
end
toc
% arrayfun solution
tic
S = arrayfun(#(ii) sum(A(ii, v(ii):end)), 1:N).';
toc
% Shai's solution
tic
[m n] = size(A);
fA = fliplr(A);
fv = n + 1 - v;
csA = cumsum( fA, 2 );
res = csA( sub2ind( [m n], 1:m, fv.' ) ).';
toc
Results:
Elapsed time is 0.386280 seconds. % simple loop
Elapsed time is 0.473916 seconds. % arrayfun
Elapsed time is 0.495794 seconds. % Shai's solution
So, arrayfun's not too bad after all.
But there is an important point here: Just look at the implementations from a distance. How easy to read/understand was the loop solution? How about the vectorized solutions?
With this in mind, also look at the performances. That's for a 5000x5000 matrix...that's 25 million elements there...
Now, would you really want to avoid that loop?
I have a very large matrix (216 rows, 31286 cols) of doubles. For reasons specific to the data, I want to average every 9 rows to produce one new row. So, the new matrix will have 216/9=24 rows.
I am a Matlab beginner so I was wondering if this solution I came up with can be improved upon. Basically, it loops over every group, sums up the rows, and then divides the new row by 9. Here's a simplified version of what I wrote:
matrix_avg = []
for group = 1:216/9
new_row = zeros(1, 31286);
idx_low = (group - 1) * 9 + 1;
idx_high = idx_low + 9 - 1;
% Add the 9 rows to new_row
for j = idx_low:idx_high
new_row = new_row + M(j,:);
end
% Compute the mean
new_row = new_row ./ 9
matrix_avg = [matrix_avg; new_row];
end
You can reshape your big matrix from 216 x 31286 to 9 x (216/9 * 31286).
Then you can use mean, which operates on each column. Since your matrix only has 9 rows per column, this takes the 9-row average.
Then you can just reshape your matrix back.
% generate big matrix
M = rand([216 31286]);
n = 9 % want 9-row average.
% reshape
tmp = reshape(M, [n prod(size(M))/n]);
% mean column-wise (and only 9 rows per col)
tmp = mean(tmp);
% reshape back
matrix_avg = reshape(tmp, [ size(M,1)/n size(M,2) ]);
In a one-liner (but why would you?):
matrix_avg = reshape(mean(reshape(M,[n prod(size(M))/n])), [size(M,1)/n size(M,2)]);
Note - this will have problems if the number of rows in M isn't exactly divisible by 9, but so will your original code.
I measured the 4 solutions and here are the results:
reshape: Elapsed time is 0.017242 seconds.
blockproc [9 31286]: Elapsed time is 0.242044 seconds.
blockproc [9 1]: Elapsed time is 44.477094 seconds.
accumarray: Elapsed time is 103.274071 seconds.
This is the code I used:
M = rand(216,31286);
fprintf('reshape: ');
tic;
n = 9;
matrix_avg1 = reshape(mean(reshape(M,[n prod(size(M))/n])), [size(M,1)/n size(M,2)]);
toc
fprintf('blockproc [9 31286]: ');
tic;
fun = #(block_struct) mean(block_struct.data);
matrix_avg2 = blockproc(M,[9 31286],fun);
toc
fprintf('blockproc [9 1]: ');
tic;
fun = #(block_struct) mean(block_struct.data);
matrix_avg3 = blockproc(M,[9 1],fun);
toc
fprintf('accumarray: ');
tic;
[nR,nC] = size(M);
n2average = 9;
[xx,yy] = ndgrid(1:nR,1:nC);
x = ceil(xx/n2average); %# makes xx 1 1 1 1 2 2 2 2 etc
matrix_avg4 = accumarray([xx(:),yy(:)],M(:),[],#mean);
toc
Here's an alternative based on accumarray. You create an array with row and column indices into matrix_avg that tells you which element in matrix_avg a given element in M contributes to, then you use accumarray to average the elements that contribute to the same element in matrix_avg. This solution works even if the number of rows in M is not divisible by 9.
M = rand(216,31286);
[nR,nC] = size(M);
n2average = 9;
[xx,yy] = ndgrid(1:nR,1:nC);
x = ceil(xx/n2average); %# makes xx 1 1 1 1 2 2 2 2 etc
matrix_avg = accumarray([xx(:),yy(:)],M(:),[],#mean);
Ax, Ay, Az: [N-by-N]
B=AA (a dyadic product)
It means :
B(i,j)= [Ax(i,j);Ay(i,j);Az(i,j)]*[Ax(i,j) Ay(i,j) Az(i,j)]
B(i,j) : a 3x3 matrix.
One way to construct B is:
N=2;
Ax=rand(N); Ay=rand(N); Az=rand(N); %# [N-by-N]
t=1;
F=zeros(3,3,N^2);
for i=1:N
for j=1:N
F(:,:,t)= [Ax(i,j);Ay(i,j);Az(i,j)]*[Ax(i,j) Ay(i,j) Az(i,j)];
t=t+1; %# t is just a counter
end
end
%# then we can write
B = mat2cell(F,3,3,ones(N^2,1));
B = reshape(B,N,N)';
B = cell2mat(B);
Is there a faster way for when N is large.
Edit:
Thanks for your answer. (It's faster)
Let's put:
N=2;
Ax=[1 2;3 4]; Ay=[5 6;7 8]; Az=[9 10;11 12];
B =
1 5 9 4 12 20
5 25 45 12 36 60
9 45 81 20 60 100
9 21 33 16 32 48
21 49 77 32 64 96
33 77 121 48 96 144
Run:
??? Error using ==> mtimes
Inner matrix dimensions must agree.
If I write :P = Ai*Aj; then
B =
7 19 31 15 43 71
23 67 111 31 91 151
39 115 191 47 139 231
10 22 34 22 50 78
34 78 122 46 106 166
58 134 210 70 162 254
That is defferent from above
A(:,:,1) deffer from [Ax(1,1) Ay(1,1) Az(1,1)]
Edit:
N=100;
Me :Elapsed time is 1.614244 seconds.
gnovice :Elapsed time is 0.056575 seconds.
N=200;
Me :Elapsed time is 6.044628 seconds.
gnovice :Elapsed time is 0.182455 seconds.
N=400;
Me :Elapsed time is 23.775540 seconds.
gnovice :Elapsed time is 0.756682 seconds.
Fast!
rwong: B was not the same.
Edit:
After some modification for my application :
by gnovice codes
1st code : 19.303310 seconds
2nd code: 23.128920 seconds
3rd code: 13.363585 seconds
It seems that any function calling like ceil,ind2sub ... make thw loops slow and shoud avoid if possible.
symIndex was interesting! Thank you.
Here's a fairly simple and general implementation that uses a single for loop to perform linear indexing and avoids dealing with 3-dimensional variables or reshaping:
%# General solution:
%# ----------------
B = cell(N);
for index = 1:N^2
A = [Ax(index) Ay(index) Az(index)];
B{index} = A(:)*A;
end
B = cell2mat(B);
EDIT #1:
In response to the additional question of how to reduce the number of calculations performed for a symmetric matrix B, you could use the following modified version of the above code:
%# Symmetric solution #1:
%# ---------------------
B = cell(N);
for index = find(tril(ones(N))).' %'# Loop over the lower triangular part of B
A = [Ax(index) Ay(index) Az(index)];
B{index} = A(:)*A;
symIndex = N*rem(index-1,N)+ceil(index/N); %# Find the linear index for the
%# symmetric element
if symIndex ~= index %# If we're not on the main diagonal
B{symIndex} = B{index}; %# then copy the symmetric element
end
end
B = cell2mat(B);
However, in such a case you may get better performance (or at least simpler looking code) by foregoing the linear indexing and using two for loops with subscripted indexing like so:
%# Symmetric solution #2:
%# ---------------------
B = cell(N);
for c = 1:N %# Loop over the columns
for r = c:N %# Loop over a subset of the rows
A = [Ax(r,c) Ay(r,c) Az(r,c)];
B{r,c} = A(:)*A;
if r ~= c %# If we're not on the main diagonal
B{c,r} = B{r,c}; %# then copy the symmetric element
end
end
end
B = cell2mat(B);
EDIT #2:
The second symmetric solution can be further sped up by moving the diagonal calculation outside of the inner loop (removing the need for a conditional statement) and overwriting A with the result A(:)*A so that we can update the symmetric cell entry B{c,r} using A instead of B{r,c} (i.e. updating one cell with the contents of another appears to carry some extra overhead):
%# Symmetric solution #3:
%# ---------------------
B = cell(N);
for c = 1:N %# Loop over the columns
A = [Ax(c,c) Ay(c,c) Az(c,c)];
B{c,c} = A(:)*A;
for r = c+1:N %# Loop over a subset of the rows
A = [Ax(r,c) Ay(r,c) Az(r,c)];
A = A(:)*A;
B{r,c} = A;
B{c,r} = A;
end
end
B = cell2mat(B);
And here are some timing results for the 3 symmetric solutions using the following sample symmetric matrices Ax, Ay, and Az:
N = 400;
Ax = tril(rand(N)); %# Lower triangular matrix
Ax = Ax+triu(Ax.',1); %'# Add transpose to fill upper triangle
Ay = tril(rand(N)); %# Lower triangular matrix
Ay = Ay+triu(Ay.',1); %'# Add transpose to fill upper triangle
Az = tril(rand(N)); %# Lower triangular matrix
Az = Az+triu(Az.',1); %'# Add transpose to fill upper triangle
%# Timing results:
%# --------------
%# Solution #1 = 0.779415 seconds
%# Solution #2 = 0.704830 seconds
%# Solution #3 = 0.325920 seconds
The big speed-up for solution 3 results primarily from updating the cell contents of B with the local variable A instead of updating one cell with the contents of another. In other words, copying cell contents with operations like B{c,r} = B{r,c}; carries more overhead than I expected.
A = cat(3, Ax, Ay, Az); % [N-by-N-by-3]
F = zeros(3, 3, N^2);
for i = 1:3,
for j = 1:3,
Ai = A(:,:,i);
Aj = A(:,:,j);
P = Ai(:) .* Aj(:);
F(i,j,:) = reshape(P, [1, 1, N^2]);
end
end
%# then we can write
B = mat2cell(F,3,3,ones(N^2,1));
B = reshape(B,N,N)';
B = cell2mat(B);