find longest sequence of non nan values but allow for threshold - matlab
Is it possible to find the non nan values of a vector but also allowing n number of nans? For example, if I have the following data:
X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24]; %// input array
thres = 1; % this is the number of nans to allow
and I would like to only keep the longest sequence of values with non nans but allow 'n' number of nans to be kept in the data. So, say that I am willing to keep 1 nan I would have an output of
X_out = [8 10 11 nan 9 14 6 1 4 23 24]; %// output array
Thats is, the two nans at the beginning have been removed becuase they exceed the values in 'thres' above, but the third nan is on its own thus can be kept in the data. I would like to develop a method where thres can be defined as any value.
I can find the non nan values with
Y = ~isnan(X); %// convert to zeros and ones
Any ideas?
In order to find the longest sequence containing at most threshold times NaN we must find the start and the end of said sequence(s).
To generate all possible start points, we can use hankel:
H = hankel(X)
H =
18 3 NaN NaN 8 10 11 NaN 9 14 6 1 4 23 24
3 NaN NaN 8 10 11 NaN 9 14 6 1 4 23 24 0
NaN NaN 8 10 11 NaN 9 14 6 1 4 23 24 0 0
NaN 8 10 11 NaN 9 14 6 1 4 23 24 0 0 0
8 10 11 NaN 9 14 6 1 4 23 24 0 0 0 0
10 11 NaN 9 14 6 1 4 23 24 0 0 0 0 0
11 NaN 9 14 6 1 4 23 24 0 0 0 0 0 0
NaN 9 14 6 1 4 23 24 0 0 0 0 0 0 0
9 14 6 1 4 23 24 0 0 0 0 0 0 0 0
14 6 1 4 23 24 0 0 0 0 0 0 0 0 0
6 1 4 23 24 0 0 0 0 0 0 0 0 0 0
1 4 23 24 0 0 0 0 0 0 0 0 0 0 0
4 23 24 0 0 0 0 0 0 0 0 0 0 0 0
23 24 0 0 0 0 0 0 0 0 0 0 0 0 0
24 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Now we need to find the last valid element in each row.
To do so, we can use cumsum:
C = cumsum(isnan(H),2)
C =
0 0 1 2 2 2 2 3 3 3 3 3 3 3 3
0 1 2 2 2 2 3 3 3 3 3 3 3 3 3
1 2 2 2 2 3 3 3 3 3 3 3 3 3 3
1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
0 0 1 1 1 1 1 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
The end point for each row is the one, where the corresponding element in C is at most threshold:
threshold = 1;
T = C<=threshold
T =
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
The last valid element is found using:
[~,idx]=sort(T,2);
lastone=idx(:,end)
lastone =
3 2 1 4 15 15 15 15 15 15 15 15 15 15 15
We must make sure that the actual length of each row is respected:
lengths = length(X):-1:1;
real_length = min(lastone,lengths);
[max_length,max_idx] = max(real_length)
max_length =
11
max_idx =
5
In case there are more sequences of equal maximum length, we just take the first and display it:
selected_max_idx = max_idx(1);
H(selected_max_idx, 1:max_length)
ans =
8 10 11 NaN 9 14 6 1 4 23 24
full script
X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24];
H = hankel(X);
C = cumsum(isnan(H),2);
threshold = 1;
T = C<=threshold;
[~,idx]=sort(T,2);
lastone=idx(:,end)';
lengths = length(X):-1:1;
real_length = min(lastone,lengths);
[max_length,max_idx] = max(real_length);
selected_max_idx = max_idx(1);
H(selected_max_idx, 1:max_length)
Approach 1: convolution
One possible approach is to convolve Y = double(~isnan(X)); with a window of n ones, where n is decreased by until an acceptable subsequence is found. "Acceptable" means that the subsequence contains at least n-thres ones, that is, the convolution gives at least n-thres.
Y = double(~isnan(X));
for n = numel(Y):-1:1 %// try all possible sequence lengths
w = find(conv(Y,ones(1,n),'valid')>=n-thres); %// is there any acceptable subsequence?
if ~isempty(w)
break
end
end
result = X(w:w+n-1);
Aproach 2: cumulative sum
Convolving Y with a window of n ones (as in approach 1) is equivalent to computing a cumulative sum of Y and then taking differences with n spacing. This is more efficient in terms of number of operations.
Y = double(~isnan(X));
Z = cumsum(Y);
for n = numel(Y):-1:1
w = find([Z(n) Z(n+1:end)-Z(1:end-n)]>=n-thres);
if ~isempty(w)
break
end
end
result = X(w:w+n-1);
Approach 3: 2D convolution
This essentially computes all iterations of the loop in approach 1 at once.
Y = double(~isnan(X));
z = conv2(Y, tril(ones(numel(Y))));
[nn, ww] = find(bsxfun(#ge, z, (1:numel(Y)).'-thres)); %'
[n, ind] = max(nn);
w = ww(ind)-n+1;
result = X(w:w+n-1);
Let's try my favorite tool: RLE. Matlab doesn't have a direct function, so use my "seqle" posted to exchange central. Seqle's default is to return run length encoding. So:
>> foo = [ nan 1 2 3 nan nan 4 5 6 nan 5 5 5 ];
>> seqle(isnan(foo))
ans =
run: [1 3 2 3 1 3]
val: [1 0 1 0 1 0]
The "run" indicates the length of the current run; "val" indicates the value. In this case, val==1 indicates the value is nan and val==0 indicates numeric values. You can see it'll be relatively easy to extract the longest sequence of "run" values meeting the condition val==0 | run < 2 to get no more than one nan in a row. Then just grab the cumulative indices of that run and that's the subset of foo you want.
EDIT:
sadly, what's trivial to find by eye may not be so easy to extract via code. I suspect there's a much faster way to use the indices identified by longrun to get the desired subsequence.
>> foo = [ nan 1 2 3 nan nan 4 5 6 nan nan 5 5 nan 5 nan 4 7 4 nan ];
>> sfoo= seqle(isnan(foo))
sfoo =
run: [1 3 2 3 2 2 1 1 1 3 1]
val: [1 0 1 0 1 0 1 0 1 0 1]
>> longrun = sfoo.run<2 |sfoo.val==0
longlong =
run: [2 1 1 1 6]
val: [1 0 1 0 1]
% longrun identifies which indices might be part of a run
% longlong identifies the longest sequence of valid run
>> longlong = seqle(longrun)
>> lfoo = find(sfoo.run<2 |sfoo.val==0);
>> sbar = seqle(lfoo,1);
>> maxind=find(sbar.run==max(sbar.run),1,'first');
>> getlfoo = lfoo( sum(sbar.run(1:(maxind-1)))+1 );
% first value in longrun , which is part of max run
% getbar finds end of run indices
>> getbar = getlfoo:(getlfoo+sbar.run(maxind)-1);
>> getsbar = sfoo.run(getbar);
% retrieve indices of input vector
>> startit = sum(sfoo.run(1:(getbar(1)-1))) +1;
>> endit = startit+ ((sum(sfoo.run(getbar(1):getbar(end ) ) ) ) )-1;
>> therun = foo( startit:endit )
therun =
5 5 NaN 5 NaN 4 7 4 NaN
Hmmm, who doesn't like challenges, my solution is not as good as m.s.'s, but it is an alternative.
X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24]; %// input array
thresh =1;
X(isnan(X))= 0 ;
for i = 1:thresh
Y(i,:) = circshift(X',-i); %//circular shift
end
For some reason, the Matlab invert " ' " makes the formatting looks weird.
D = X + sum(Y,1);
Discard = find(D==0)+thresh; %//give you the index of the part that needs to be discarded
chunk = find(X==0); %//Segment the Vector into segments delimited by NaNs
seriesOfZero = circshift(chunk',-1)' - chunk;
bigchunk =[1 chunk( find(seriesOfZero ~= 1)) size(X,2)]; %//Convert series of NaNs into 1 chunk
[values,DiscardChunk] = intersect(bigchunk,Discard);
DiscardChunk = sort(DiscardChunk,'descend')
for t = 1:size(DiscardChunk,2)
X(bigchunk(DiscardChunk(t)-1):bigchunk(DiscardChunk(t))) = []; %//Discard the data
end
X(X == 0) = NaN
%//End of Code
8 10 11 NaN 9 14 6 1 4 23 24
When:
X = [18 3 nan nan nan 8 10 11 nan nan 9 14 6 1 nan nan nan 4 23 24]; %// input array
thresh =2;
8 10 11 NaN 4 23 24
Related
How to normalize matrix setting 0 for minimum values and 1 for maximum values?
I need to transform a neural network output matrix with size 2 X N in zeros and ones, where 0 will represent the minimum value of the column and 1 contrariwise. This will be necessary in order to calculate the confusion matrix. For example, consider this matrix 2 X 8: 2 33 4 5 6 7 8 9 1 44 5 4 7 5 2 1 I need to get this result: 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0 How can I do this in MATLAB without for loops? Thanks in advance.
>> d = [ 2 33 4 5 6 7 8 9; 1 44 5 4 7 5 2 1]; >> bsxfun(#rdivide, bsxfun(#minus, d, min(d)), max(d) - min(d)) ans = 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0 The bsxfun function is necessary to broadcast the minus and division operations to matrices of different dimensions (min and max have only 1 row each). Other solution is the following (works only for 2 rows): >> [d(1,:) > d(2,:); d(1,:) < d(2,:)] ans = 1 0 0 1 0 1 1 1 0 1 1 0 1 0 0 0
If it's just 2xN, then this will work: floor(A./[max(A); max(A)]) In general: floor(A./repmat(max(A),size(A,1),1))
MATLAB - Inserting zero rows and columns into matrix
I have written some code that compresses a matrix to remove zero columns and rows, but I can't work out how to reconstruct the original matrix. Say I have a matrix: A = [ 0 3 0 2 1 0 6 3 0 0 4 8 0 5 0 0 0 0 0 0 0 2 4 0 0 2 0 1 1 8 0 2 0 0 7 0 0 0 0 0 0 0 6 5 0 1 7 0 0 ] Here rows/columns 3 and 6 are empty, so my compression function will give the output: A_dash = [ 0 3 2 1 6 3 0 4 8 5 2 4 0 2 1 1 8 2 0 7 6 5 1 7 0 ] A_map = [ 1 2 4 5 7] Where A_map is a vector mapping the indicies of the rows/columns of A_dash to A. This means that if A_map(3) = 4, then row/column 4 of A is the same as row/column 3 of A_dash - ie. a row/column of zeroes must be inserted between columns/rows 2 and 3 in A_dash What is the easiest way people can suggest for me to recreate matrix A from A_dash, using the information in A_map? Here is what I have got so far: % orig_size is original number of columns/rows c_count = size(A_dash,1); A = zeros(c_count, orig_size); % c_count rows to avoid dimension mismatch for ii = 1:c_count A(:,A_map(ii)) == A_dash(:,ii); end This gives me the right result column-wise: A = [ 0 3 0 2 1 0 6 3 0 0 4 8 0 5 2 4 0 0 2 0 1 1 8 0 2 0 0 7 6 5 0 1 7 0 0 ] However, I'm not sure how i should go about inserting the rows, i suppose i could copy the first 1:i rows into one matrix, i:end rows to a second matrix and concatenate those with a zero row in between, but that feels like a bit of a clunky solution, and probably not very efficient for large sized matrices.. Otherwise, is there a better way that people can suggest I store the map information? I was thinking instead of storing the mapping between column/row indices, that I just store the indices of the zero columns/rows and then insert columns/rows of zeros where appropriate. Would this be a better way?
You've got the indices of the valid rows/columns. Now all you've got to do is put them in a new matrix of zeros the same size as A: B=zeros(size(A)); B(A_map,A_map)=A_dash B = 0 3 0 2 1 0 6 3 0 0 4 8 0 5 0 0 0 0 0 0 0 2 4 0 0 2 0 1 1 8 0 2 0 0 7 0 0 0 0 0 0 0 6 5 0 1 7 0 0 Just to check... >> A==B ans = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 A and B are equal everywhere, so we've reconstructed A.
Finding the non-intersecting rows in a matrix
I want to find the non-intersecting rows in a large matrix. As an example: A=[1 5 3; 3 4 5; 7 9 10;4 5 6;11 2 8; 3 5 10] In this matrix, the non-intersecting rows are: [1 5 3], [11 2 8] and [7 9 10]. How can I program this in Matlab in a fast way?
If I may bsxfun - M = squeeze(any(bsxfun(#eq,A,permute(unique(A),[3 2 1])),2)) [~,row_idx] = max(M,[],1) out = A(sum(M,2).' == histc(row_idx,1:size(A,1)),:) Sample step-by-step run - A = 1 5 3 3 4 5 7 9 10 4 5 6 11 2 8 3 5 10 M = 1 0 1 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 row_idx = 1 5 1 2 1 4 3 5 3 3 5 out = 1 5 3 7 9 10 11 2 8
You can look for rows that adding them to union of previous rows increases the number of elements in the union by the number of columns (i.e. all elements in that row are new): B = []; C = zeros(1,size(A,1)); for k=1:size(A,1), B1 = union(B, A(k,:)); C(k) = numel(B1)-numel(B); B=B1; end result = A(C==size(A,2),:);
Concatenating binary bits into a number
I want to concatenate last four bits of binary into a number i have tried the following code x8=magic(4) x8_n=dec2bin(x8) m=x8_n-'0' which gives me the following output m = 1 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 1 0 0 0 0 1 1 0 1 0 1 0 0 0 1 1 0 0 1 1 1 1 0 1 1 0 1 0 1 0 0 0 0 1 1 0 0 0 0 0 0 1 now i want to take every last 4 bits it each row and convert it into an integer
n = 4; %// number of bits you want result = m(:,end-n+1:end) * pow2(n-1:-1:0).'; %'// matrix multiplication Anyway, it would be easier to use mod on x8 directly, without the intermediate step of m: result = mod(x8(:), 2^n); In your example: result = 0 5 9 4 2 11 7 14 3 10 6 15 13 8 12 1
This could be another approach - n = 4; %%// number of bits you want out = bin2dec(num2str(m(:,end-n+1:end))) Output - out = 0 5 9 4 2 11 7 14 3 10 6 15 13 8 12 1
How to get the unique slices of a matrix?
In matlab, if you have a matrix A you can find the matrix B containing all of the unique rows of A as follows: B = unique(A,'rows'); What I have is a 3d matrix, with rows and columns as the first two dimensions, and one additional dimension ('slices'). How can I get the 3d matrix containing all the unique slices in a matrix A? Here's an example of the kind of functionality I want: >> A % print out A A(:,:,1) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 A(:,:,2) = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 A(:,:,3) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 A(:,:,4) = 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 >> unique(A,'slices'); % get unique slices A(:,:,1) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 A(:,:,2) = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 A(:,:,3) = 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0
I would begin by reshaping A so each slice becomes a row (with the reshape command). Then use unique(A, 'rows'). Finally, reshape the unique rows back to the same shape the slices. For example: % transforming so each row is a slice in row form reshaped_A = reshape(A, [], size(A, 3))'; % getting unique rows unique_rows = unique(reshaped_A, 'rows'); % reshaping back unique_slices = reshape(unique_rows', size(A, 1), size(A, 2), []); Or all in one line: reshape(unique(reshape(A, [], size(A, 3))', 'rows')', size(A, 1), size(A, 2), []) I haven't checked this above code so use with caution! But it should give the idea. EDIT Here it is working on your data (also fixed little bug in above code): >> reshaped_A = reshape(A, [], size(A, 3))' reshaped_A = Columns 1 through 11 16 5 9 4 2 11 7 14 3 10 6 1 0 0 0 0 1 0 0 0 0 1 16 5 9 4 2 11 7 14 3 10 6 0 0 0 1 0 0 1 0 0 1 0 Columns 12 through 16 15 13 8 12 1 0 0 0 0 1 15 13 8 12 1 0 1 0 0 0 Each of these ^^ rows is one of the original slices >> unique_rows = unique(reshaped_A, 'rows') unique_rows = Columns 1 through 11 0 0 0 1 0 0 1 0 0 1 0 1 0 0 0 0 1 0 0 0 0 1 16 5 9 4 2 11 7 14 3 10 6 Columns 12 through 16 0 1 0 0 0 0 0 0 0 1 15 13 8 12 1 These ^^ are the unique slices, but in the wrong shape. >> unique_slices = reshape(unique_rows', size(A, 1), size(A, 2), []) unique_slices(:,:,1) = 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 unique_slices(:,:,2) = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 unique_slices(:,:,3) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1
A very simple and scalable solution would be: A = cat(3, [16 2 3 13;5 11 10 8;9 7 6 12;4 14 15 1], [1 0 0 0;0 1 0 0;0 0 1 0;0 0 0 1], [16 2 3 13;5 11 10 8;9 7 6 12;4 14 15 1], [0 0 0 1;0 0 1 0;0 1 0 0;1 0 0 0]) [n,m,p] = size(A); a = reshape(A,n,[],1); b = reshape(a(:),n*m,[])'; c = unique(b,'rows', 'stable')'; %If the 'stable' option is supported by your version. %If the 'stable' option is not supported, but it's still required, use the index vector option, as required. %i.e., %[c,I,J] = unique(b,'rows'); unique_A = reshape(c,n,m,[]) Results: A(:,:,1) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 A(:,:,2) = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 A(:,:,3) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 A(:,:,4) = 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 unique_A(:,:,1) = 0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 unique_A(:,:,2) = 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 unique_A(:,:,3) = 16 2 3 13 5 11 10 8 9 7 6 12 4 14 15 1 Source: How to find unique pages in a 3d matrix?