I have a two-dimensional matrix A, where each column represents a time-series.
For each time-series, I need to calculate the cumulative maximum of each window of length k:
RESULT = nan(size(A));
% loop over each time-series.
for col = 1:size(A, 2)
% loop over the elements of the time-series (excluding the first 'k-1' elements).
for row = k:size(A, 1)
% extract the sliding window.
window = A((row-k+1):row, col);
% calculate the cumulative maximum of that sliding window.
cumax_vector = cummax(window);
% do something with it.
RESULT(row,col) = ...;
end
end
I realize that extracting a sliding window for each element of A and subsequently calculating the vector containing its cumulative maximum is very inefficient.
Is there a way to do that more efficiently?
you could rearrange matrix A and calculate cummax on new matrix,
to do that you should first create an index matrix (C) then calculate cummax(A(C))
i demonstrated the method with following example, for simplicity i assumed that A is 5x1 matrix and k=3
for 2d A matrix you could loop on A's column or you could simply create a 3d index matrix ad so on.
>> A=rand(5,1)
A =
0.6557
0.0357
0.8491
0.9340
0.6787
>> C=ones(3,3).*(1:3)'+(0:1:2)
C =
1 2 3
2 3 4
3 4 5
>> A(C)
ans =
0.6557 0.0357 0.8491
0.0357 0.8491 0.9340
0.8491 0.9340 0.6787
>> cummax(A(C))
ans =
0.6557 0.0357 0.8491
0.6557 0.8491 0.9340
0.8491 0.9340 0.9340
Edit 1:
if you haven't enough memory to calculate whole A at once you could calculate index matrix and slice it ,then calulate cummax on each slice. in a loop. after each iteration you should write results on a file and clear variables to free memory.
Related
I have an image that I'm converting to a binary matrix with size (n,m)
I need a MATLAB function to reshape the size of this matrix to be (n,n).
Otherwise, would it be possible to make the size of image be (n,n) versus the initial (n,m)?
It's actually quite easy. Supposing that your matrix is A and is n x m. I'm assuming you'll want to zero-pad the matrix, meaning that the extra elements would be set to 0. You would simply do this:
[n,m] = size(A);
A(:,m+1:n) = 0;
The first line of code finds the rows n and columns m of the matrix A. Next, we will make all of the rows from the (m+1)th column to the nth column all 0 which effectively makes this a n x n matrix.
Example Run
Here's an example with a 4 x 2 matrix A, and the process requires that we change the size so that A is 4 x 4.
>> A = rand(4,2)
A =
0.9575 0.9572
0.9649 0.4854
0.1576 0.8003
0.9706 0.1419
>> [n,m] = size(A);
>> A(:,m+1:n) = 0
A =
0.9575 0.9572 0 0
0.9649 0.4854 0 0
0.1576 0.8003 0 0
0.9706 0.1419 0 0
Minor Note
This assumes that the number of rows is greater than the number of columns... I'm assuming that this is a requirement on your end. If this is not the case, then the above code won't work. You can make the algorithm agnostic whereas you would zero-pad the matrix in the dimension that has the least amount of entries, but I'll leave that to you as an exercise.
for an input matrix
in = [1 1;
1 2;
1 3;
1 4;
2 5;
2 6;
2 7;
3 8;
3 9;
3 10;
3 11];
i want to get the output matrix
out = [1 5 8;
2 6 9;
3 7 10;
4 0 11];
meaning i want to reshape the second input column into an output matrix, where all values corresponding to one value in the first input column are written into one column of the output matrix.
As there can be different numbers of entries for each value in the first input column (here 4 values for "1" and "3", but only 3 for "2"), the normal reshape function is not applicable. I need to pad all columns to the maximum number of rows.
Do you have an idea how to do this matlab-ish?
The second input column can only contain positive numbers, so the padding values can be 0, -x, NaN, ...
The best i could come up with is this (loop-based):
maxNumElem = 0;
for i=in(1,1):in(end,1)
maxNumElem = max(maxNumElem,numel(find(in(:,1)==i)));
end
out = zeros(maxNumElem,in(end,1)-in(1,1));
for i=in(1,1):in(end,1)
tmp = in(in(:,1)==i,2);
out(1:length(tmp),i) = tmp;
end
Either of the following approaches assumes that column 1 of in is sorted, as in the example. If that's not the case, apply this initially to sort in according to that criterion:
in = sortrows(in,1);
Approach 1 (using accumarray)
Compute the required number of rows, using mode;
Use accumarray to gather the values corresponding to each column, filled with zeros at the end. The result is a cell;
Concatenate horizontally the contents of all cells.
Code:
[~, n] = mode(in(:,1)); %//step 1
out = accumarray(in(:,1), in(:,2), [], #(x){[x; zeros(n-numel(x),1)]}); %//step 2
out = [out{:}]; %//step 3
Alternatively, step 1 could be done with histc
n = max(histc(in(:,1), unique(in(:,1)))); %//step 1
or with accumarray:
n = max(accumarray(in(:,1), in(:,2), [], #(x) numel(x))); %//step 1
Approach 2 (using sparse)
Generate a row-index vector using this answer by #Dan, and then build your matrix with sparse:
a = arrayfun(#(x)(1:x), diff(find([1,diff(in(:,1).'),1])), 'uni', 0); %//'
out = full(sparse([a{:}], in(:,1), in(:,2)));
Introduction to proposed solution and Code
Proposed here is a bsxfun based masking approach that uses the binary operators available as builtins for use with bsxfun and as such I would consider this very appropriate for problems like this. Of course, you must also be aware that bsxfun is a memory hungry tool. So, it could pose a threat if you are dealing with maybe billions of elements depending also on the memory available for MATLAB's usage.
Getting into the details of the proposed approach, we get the counts of each ID from column-1 of the input with histc. Then, the magic happens with bsxfun + #le to create a mask of positions in the output array (initialized by zeros) that are to be filled by the column-2 elements from input. That's all you need to tackle the problem with this approach.
Solution Code
counts = histc(in(:,1),1:max(in(:,1)))'; %//' counts of each ID from column1
max_counts = max(counts); %// Maximum counts for each ID
mask = bsxfun(#le,[1:max_counts]',counts); %//'# mask of locations where
%// column2 elements are to be placed
out = zeros(max_counts,numel(counts)); %// Initialize the output array
out(mask) = in(:,2); %// place the column2 elements in the output array
Benchmarking (for performance)
The benchmarking presented here compares the proposed solution in this post against the various methods presented in Luis's solution. This skips the original loopy approach presented in the problem as it appeared to be very slow for the input generated in the benchmarking code.
Benchmarking Code
num_ids = 5000;
counts_each_id = randi([10 100],num_ids,1);
num_runs = 20; %// number of iterations each approach is run for
%// Generate random input array
in = [];
for k = 1:num_ids
in = [in ; [repmat(k,counts_each_id(k),1) rand(counts_each_id(k),1)]];
end
%// Warm up tic/toc.
for k = 1:50000
tic(); elapsed = toc();
end
disp('------------- With HISTC + BSXFUN Masking approach')
tic
for iter = 1:num_runs
counts = histc(in(:,1),1:max(in(:,1)))';
max_counts = max(counts);
out = zeros(max_counts,numel(counts));
out(bsxfun(#le,[1:max_counts]',counts)) = in(:,2);
end
toc
clear counts max_counts out
disp('------------- With MODE + ACCUMARRAY approach')
tic
for iter = 1:num_runs
[~, n] = mode(in(:,1)); %//step 1
out = accumarray(in(:,1), in(:,2), [], #(x){[x; zeros(n-numel(x),1)]}); %//step 2
out = [out{:}];
end
toc
clear n out
disp('------------- With HISTC + ACCUMARRAY approach')
tic
for iter = 1:num_runs
n = max(histc(in(:,1), unique(in(:,1))));
out = accumarray(in(:,1), in(:,2), [], #(x){[x; zeros(n-numel(x),1)]}); %//step 2
out = [out{:}];
end
toc
clear n out
disp('------------- With ARRAYFUN + Sparse approach')
tic
for iter = 1:num_runs
a = arrayfun(#(x)(1:x), diff(find([1,diff(in(:,1).'),1])), 'uni', 0); %//'
out = full(sparse([a{:}], in(:,1), in(:,2)));
end
toc
clear a out
Results
------------- With HISTC + BSXFUN Masking approach
Elapsed time is 0.598359 seconds.
------------- With MODE + ACCUMARRAY approach
Elapsed time is 2.452778 seconds.
------------- With HISTC + ACCUMARRAY approach
Elapsed time is 2.579482 seconds.
------------- With ARRAYFUN + Sparse approach
Elapsed time is 1.455362 seconds.
slightly better, but still uses a loop :(
out=zeros(4,3);%set to zero matrix
for i = 1:max(in(:,1)); %find max in column 1, and loop for that number
ind = find(in(:,1)==i); %
out(1: size(in(ind,2),1),i)= in(ind,2);
end
don't know if you can avoid the loop...
Hell all, I have some problem when compute the rank of binary matrix that only 1 or 0. The rank of binary matrix will based on the row reduction using boolean operations XOR. Let see the XOR operation:
1 xor 1 =0
1 xor 0= 1
0 xor 0= 0
0 xor 1= 1
Given a binary matrix as
A =
1 1 0 0 0 0
1 0 0 0 0 1
0 1 0 0 0 1
We can see the third row equals first row xor with second row. Hence, the rank of matrix A only 2, instead of 3 by rank matlab function.
I have one way to compute the extractly rank of binary matrix using this code
B=gf(A)
rank(B)
It will return 2. However, when I compute with large size of matrix, for example 400 by 400. It does not return the rank (never stop). Could you suggest to me the good way to find rank of binary matrix for large size? Thank all
UPDATE: this is computation time using tic toc
N=50; Elapsed time is=0.646823 seconds
N=100;Elapsed time is 3.123573 seconds.
N=150;Elapsed time is 7.438541 seconds.
N=200;Elapsed time is 11.349964 seconds.
N=400;Elapsed time is 66.815286 seconds.
Note that check rank is only the condition in my algorithm. However, it take very long long time, then it will affect to my method
Base on the suggestion of R. I will use Gaussian Elimination to find the rank. This is my code. However, it call the rank function (spend some computation times). Could you modify help me without using rank function?
function rankA=GaussEliRank(A)
mat = A;
[m n] = size(A); % read the size of the original matrix A
for i = 1 : n
j = find(mat(i:m, i), 1); % finds the FIRST 1 in i-th column starting at i
if isempty(j)
mat = mat( sum(mat,2)>0 ,:);
rankA=rank(mat); %%Here
return;
else
j = j + i - 1; % we need to add i-1 since j starts at i
temp = mat(j, :); % swap rows
mat(j, :) = mat(i, :);
mat(i, :) = temp;
% add i-th row to all rows that contain 1 in i-th column
% starting at j+1 - remember up to j are zeros
for k = find(mat( (j+1):m, i ))'
mat(j + k, :) = bitxor(mat(j + k, :), mat(i, :));
end
end
end
%remove all-zero rows if there are some
mat = mat( sum(mat,2)>0 ,:);
if any(sum( mat(:,1:n) ,2)==0) % no solution because matrix A contains
error('No solution.'); % all-zero row, but with nonzero RHS
end
rankA=rank(mat); %%Here
end
Let check the matrix A at here. Correct ans is 393 for rank of A.
Once you get the matrix into row echelon form with Gaussian elimination, the rank is the number of nonzero rows. You should be able to replace the code after the loop with something like rankA=sum(sum(mat,2)>0);.
I have a algorithm to randomly select element t in a array with out repeated. This is more detail of algorithm
It can explain as folowing:
Initial a array index u that stores the index of numbers from 1 to k (line 1 to 3)
Set initial of gamma from k and reduce by one for each iteration. The purpose of gamma is for without repeated (line 4,9,10)
Random choose a number t from 1 to N(at the j=1, choose 1 to k, N are nonrepated number), and then put the number to the end of array.
Repate the step 2 to 3
If gamma =0,reset gamma=k
This function will return the t.
For example, I have a array A=[1,2,3,4,5,6,7,8,9], k=9 =size(A), N=12 (From 1 to 9, number select only one time). Now I want to use this algorithm to randomly select number t from array A. This is my code. However, it does not similar the line 6 in the algorithm. Is it right? Let see my code help me
function nonRepeat
k=9;
u=1:k; % initial value of index
N=12
gamma=k;
for j=1:N
index=randi(gamma,1); % use other choosing
t=u(index)
%%swapping
temp=u(t);
u(t)=u(gamma);
u(gamma)=temp;
gamma=gamma-1;
if gamma==0
gamma=k;
end
end
end
I think index=randi(gamma,1); is not right because it says select number t randomly but you select index randomly and assign t=u(index).
See if it works,
k = 9;
u = 1 : k;
N = 12;
gamma = k;
for j = 1 : N
t = randi(gamma,1);
temp = u(t);
u(t) = u(gamma);
u(gamma) = temp;
gamma = gamma - 1;
if gamma == 0
gamma = k;
end
end
im not not a programmer, i just need to solve something numerically in matlab.
i need a function to make the following transformation for any square matrix:
from
row 1: 1 2 3
row 2: 4 5 6
row 3: 7 8 9
to
1 4 2 7 5 3 8 6 9
ie write the matrix in a vector along its diagonals from left to top right.
any ideas please?
i really need a little more help though:
say the matrix that we have transformed into the vector, has entries denoted by M(i,j), where i are rows and j columns. now i need to be able to find out from a position in the vector, the original position in the matrix, i.e say if its 3rd entry in the vector, i need a function that would give me i=1 j=2. any ideas please? im really stuck on this:( thanks
This is quite similar to a previous question on traversing the matrix in a zigzag order. With slight modification we get:
A = rand(3); %# input matrix
ind = reshape(1:numel(A), size(A)); %# indices of elements
ind = spdiags(fliplr(ind)); %# get the anti-diagonals
ind = ind(end:-1:1); %# reverse order
ind = ind(ind~=0); %# keep non-zero indices
B = A(ind); %# get elements in desired order
using the SPDIAGS function. The advantage of this is that it works for any arbitrary matrix size (not just square matrices). Example:
A =
0.75127 0.69908 0.54722 0.25751
0.2551 0.8909 0.13862 0.84072
0.50596 0.95929 0.14929 0.25428
B =
Columns 1 through 6
0.75127 0.2551 0.69908 0.50596 0.8909 0.54722
Columns 7 through 12
0.95929 0.13862 0.25751 0.14929 0.84072 0.25428
Here's one way to do this.
%# n is the number of rows (or cols) of the square array
n = 3;
array = [1 2 3;4 5 6;7 8 9]; %# this is the array we'll reorder
%# create list of indices that allow us
%# to read the array in the proper order
hh = hankel(1:n,n:(2*n-1)); %# creates a matrix with numbered antidiagonals
[dummy,sortIdx] = sort(hh(:)); %# sortIdx contains the new order
%# reorder the array
array(sortIdx)
ans =
1 4 2 7 5 3 8 6 9
You can convert your matrix to a vector using the function HANKEL to generate indices into the matrix. Here's a shortened version of Jonas' answer, using M as your sample matrix given above:
N = size(M,1);
A = hankel(1:N,N:(2*N-1));
[junk,sortIndex] = sort(A(:));
Now, you can use sortIndex to change your matrix M to a vector vec like so:
vec = M(sortIndex);
And if you want to get the row and column indices (rIndex and cIndex) into your original matrix that correspond to the values in vec, you can use the function IND2SUB:
[rIndex,cIndex] = ind2sub(N,sortIndex);
A=[1,2,3;4,5,6;7,8,9];
d = size(A,1);
X=[];
for n = 1:2*size(A,1) - 1
j = min(n,d); i = (n+1)-(j);
X = cat(2,X,diag(flipud(A(i:j,i:j)))');
end
X
X =
1 4 2 7 5 3 8 6 9
You can generate the diagonals in this way:
for i = -2:2
diag(flipud(a), i)
end
I don't know whether this is the optimal way to concatenate the diagonals:
d = []
for i = -2:2
d = vertcat(d, diag(flipud(a), i))
end
(I tested it in octave, not in matlab)