Tracking changes in a cell in Matlab - matlab

I have a cell with 3 different columns. The first is a simple ranking, the second is a code composed by X elements and the third is a code composed by Y elements that usually is the same for a certain combination of numbers in column two. So if in column two you have the number 345, it is likely that in column three you will always have 798. The thing is that sometimes it changes. So what I have, for instance, is:
1 453 4789
1 56 229
1 453 1246 %here the corresponding code has changed
2 43 31
2 453 1246 %here the code did not change
3 56 31 %here the corresponding code has changed (it was 229 previously)
What I want to have at the end is a new cell with three columns, only descriminating the cases in which a change in the code of the third column (correspondent to the code form the second column) was observed. For instance, in this simple example I would get:
1 453 1246
3 56 31

If you have your data in a matrix A you can use sorting:
[~, I] = sort(A(:,2));
B = A(I,:);
code_diff = logical(diff(B(:, 2)));
value_diff = logical(diff(B(:, 3)));
value_diff(code_diff) = false;
rows = sort(I([false; value_diff]));
ans = A(rows, :);
If the "codes" in the second column are all smallish integers, another possibility is to use a lookup table:
n = size(A, 1);
m = max(A(:, 2));
mask = false(n, 1);
lookup = inf(m, 1);
for i = 1:n
code = A(i,2);
if isinf(lookup(code))
lookup(code) = A(i,3);
elseif lookup(code) ~= A(i,3)
mask(i) = true;
lookup(code) = A(i,3);
end
end
ans = A(mask, :);

Assuming the values are in a matrix, this is a possible solution:
CJ2 = [1 453 4789
1 56 229
1 453 1246
2 43 31
2 453 1246
3 56 31];
changes = zeros(size(CJ2));
nChanges = 0;
for i = 2:size(CJ2,1)
pos = find(CJ2(1:i-1,2) == CJ2(i,2), 1, 'last');
if ~isempty(pos) && CJ2(pos,3) ~= CJ2(i,3)
nChanges = nChanges + 1;
changes(nChanges, :) = CJ2(i,:);
end
end
changes = changes(1:nChanges, :);
changes
Results:
>> changes
changes =
1 453 1246
3 56 31

Related

Find Array around Maximum Values of an Array

i have a quite complicated question here which i am working on. It's extremely difficult to describe in words, so i will try to explain it with an example.
Assume i have a matrix of values:
A =
[31 85 36 71 51]
[12 33 74 39 12]
[67 11 13 14 18]
[35 36 84 33 57]
Now, i want to first find a maximum vector in the first dimension, which is easy:
[max_vector,~] = max(A,[],1);
max_vector=[67,85, 84, 71,57]
Now i want to get a "slimmed" matrix with values around the maxima (periodical indices):
Desired_Matrix =
[12 36 36 33 18]
[67 85 84 71 57]
[35 33 13 39 51]
This is the matrix with the vectors around the maximum values of matrix A. Can someone tell me how to do this without using a double for loop?
Thank you!
% Input.
A = [31 85 36 71 51; 12 33 74 39 12; 67 11 13 14 18; 35 36 84 33 57]
% Dimensions needed.
nRows = size(A, 1);
nCols = size(A, 2);
% Get maxima and corresponding indices in input.
[max_vector, ind] = max(A);
% Get neighbouring indices.
ind = [ind - 1; ind; ind + 1];
% Modulo indices to prevent dimension overflow.
ind = mod(ind, nRows);
% Correct zero indices.
ind(ind == 0) = nRows;
% Calculate correct indices in A.
temp = repmat(0:nRows:nRows*(nCols-1), 3, 1);
ind = ind + temp;
% Output.
B = A(ind)
Since we have max indices per column, but later want to access these elements in the original array A, we need proper linear indices for A. Here, the trick is to add the number of rows multiplied by the column index (starting by 0). The easiest way to understand might be to remove the semicolons, and inspect the intermediate values of ind.
#HansHirse's answer is more efficient, as it does not create an intermediate matrix.
Try this:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = bsxfun(#plus, bsxfun(#plus, ind_max, (0:2).'), (0:size(A_ext,2)-1)*size(A_ext,1));
result = reshape(A_ext(ind_lin), 3, []);
For Matlab R2016b or newer, you can simplify the third line:
[~, ind_max] = max(A,[],1);
A_ext = A([end 1:end 1],:);
ind_lin = ind_max + (0:2).' + (0:size(A_ext,2)-1)*size(A_ext,1);
result = reshape(A_ext(ind_lin), 3, []);
Here is another solution. This is similar to HansHirse's answer, with two improvements:
Slightly more elegantly handles the modular indexing
Is more flexible for specifying which neighbours your want
Code:
% Input
A = [31 85 36 71 51;
12 33 74 39 12;
67 11 13 14 18;
35 36 84 33 57];
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc] = size(A);
% Get max indices
[~,idx] = max( A, [], 1 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + p.' - 1, nr ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
B = A(idx + (0:nr:nr*nc-1));
You can use the Image Processing Toolbox to generate the result, though is less efficient than other solutions.
[~,idx] = max(A, [], 1);
d = imdilate( idx == (1:size(A,1) ).', [1;1;1], 'full');
p = padarray(A, 1, 'circular');
Desired_Matrix = reshape(p(d), 3, []);
just for your information, here is the generalized form for the 3D-Case:
A = zeros(3,5,5);
for id = 1: 20
A(:,:,id) = id;
if id == 10
A(:,:,id) = 100;
end
end
% Relative rows of neighbours, i.e. this is [-1, 0, 1] for +/- one row
p = -1:1;
% Get A row and column counts for ease
[nr, nc, nz] = size(A);
% Get max indices
[~,idx] = max( A, [], 3 );
% Handle overflowing indices to wrap around rows
% You don't have to redefine "idx", could use this directly in the indexing line
idx = mod( idx + reshape(p,1,1,3) - 1, nz ) + 1;
% Output B. The "+ ... " is to convert to linear indices, as "idx"
% currently just refers to the row number.
INDICES = ((idx-1) * (nr*nc)+1 )+ reshape(0:1:nc*nr-1,nr,nc);
B = A(INDICES);

How to create a vector in Matlab with two different step sizes?

I want to create a vector in Matlab with two step sizes that will alternate.
vector =
0 50 51 101 102 152 etc.
So the step size is 50 and 1 which will alternate. How to write a script that will create this vector?
Code
N = 6; %// Number of elements needed in the final output
sz1 = 50; %// Stepsize - 1
sz2 = 4; %// Stepsize - 2
startval = 8; %// First element of the output
vector = reshape(bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2)),1,[])
Output
vector =
8 58 62 112 116 166
Note: For your problem, you need to use sz2 = 1 and startval = 0 instead.
Explanation
Internally it creates two matrices which when "flattened-out" to form vectors would resemble the two vectors that you have pointed out in the comments. You can get those two matrices with the following two sets of conditions.
Set #1: If you keep N = 6, sz1 = 50, sz2 = 0 and startval = 0 -
bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2))
gives us -
0 50 100
50 100 150
Set #2: If you keep N = 6, sz1 = 0, sz2 = 1 and startval = 0 -
bsxfun(#plus,[startval startval+sz1]',[0:N/2-1]*(sz1+sz2))
gives us -
0 1 2
0 1 2
Good thing about bsxfun is that these two matrices can be summed internally to give the final output -
0 51 102
50 101 152
Since, you needed the output as a vector, we need to flatten it out using reshape -
reshape(...,1,[])
giving us -
0 50 51 101 102 152
Thus, we have the final code that was listed earlier.
Here are some ideas I've been playing around with:
Initialization (comparable to Divakar's answer):
N = 6; %// Number of pairs in the final output
firstStep = 50;
secndStep = 1;
startVal = 8; %// First element of the output
And then:
%// Idea 1:
V1 = [startVal cumsum(repmat([firstStep,secndStep],[1,N])) + startVal];
%// Idea 2:
trimVec = #(vec)vec(1:end-1);
V2 = trimVec(circshift(kron((startVal:firstStep:startVal + ...
N*firstStep),[1,1]),[0,-1]) + kron((0:N),[1,1]));
Note that both of these vectors result in length = 2*N + 1.
The thing I'd like to point out is that if you create your vector of differences (e.g. [1 50 1 50 ...]), cumsum() really does the trick from there (you can also have more than 2 step sizes if you choose).
Let the input data be
step1 = 50;
step2 = 1;
start = 0;
number = 9; %// should be an odd number
Then:
n = (number-1)/2;
vector = cumsum([start reshape(([repmat(step1,1,n); repmat(step2,1,n)]),1,[])]);
The result in this example is
vector =
0 50 51 101 102 152 153 203 204

overlapping feature values and efficient features calculation [duplicate]

If I have a matrix
F=[ 24 3 17 1;
28 31 19 1;
24 13 25 2;
47 43 39 1;
56 41 39 2];
in the first three columns I have feature values a forth column is for class labels. my problem is to get rid of same feature values when class label is different for that particular values.
like for F matrix I have to remove the rows 1,3,4 and 5 ,because for first column there are 2 different values in column four and same is for third column (39 and 39)as class label again got changed.
so output should look like
F=[28 31 19 1];
The straightforward approach would be iterating over the columns, counting the number of different classes for each value, and removing the rows for values associated to more than one class.
Example
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
%// Iterate over columns
for col = 1:size(F, 2) - 1
%// Count number of different classes for each value
[vals, k, idx] = unique(F(:, col));
count = arrayfun(#(x)length(unique(F(F(:, col) == x, end))), vals);
%// Remove values associated to more than one class
F(count(idx) > 1, :) = [];
end
This results in:
F =
28 31 19 1
Another take at the problem, without arrayfun (edited)
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
Separate both classes:
A1 = F(F(:,4)==1,1:3);
A2 = F(F(:,4)==2,1:3);
Replicate them to a 3D matrix to compare each line of class1 with each line of class2:
B2 = repmat(shiftdim(A2',-1),size(A1,1),1);
B1 = repmat(A1,[1,1,size(A2,1)]);
D4 = squeeze(sum(B1 == B2,2));
remove rows duplicated rows
A1(logical(sum(D4,2)),:) = [];
A2(logical(sum(D4,1)),:) = [];
reconstruct original matrix
R = [A1 ones(size(A1,1),1);A2 2*ones(size(A2,1),1)];

overlapping feature values values in matlab

If I have a matrix
F=[ 24 3 17 1;
28 31 19 1;
24 13 25 2;
47 43 39 1;
56 41 39 2];
in the first three columns I have feature values a forth column is for class labels. my problem is to get rid of same feature values when class label is different for that particular values.
like for F matrix I have to remove the rows 1,3,4 and 5 ,because for first column there are 2 different values in column four and same is for third column (39 and 39)as class label again got changed.
so output should look like
F=[28 31 19 1];
The straightforward approach would be iterating over the columns, counting the number of different classes for each value, and removing the rows for values associated to more than one class.
Example
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
%// Iterate over columns
for col = 1:size(F, 2) - 1
%// Count number of different classes for each value
[vals, k, idx] = unique(F(:, col));
count = arrayfun(#(x)length(unique(F(F(:, col) == x, end))), vals);
%// Remove values associated to more than one class
F(count(idx) > 1, :) = [];
end
This results in:
F =
28 31 19 1
Another take at the problem, without arrayfun (edited)
F = [24 3 17 1; 28 31 19 1; 24 13 25 2; 47 43 39 1; 56 41 39 2];
Separate both classes:
A1 = F(F(:,4)==1,1:3);
A2 = F(F(:,4)==2,1:3);
Replicate them to a 3D matrix to compare each line of class1 with each line of class2:
B2 = repmat(shiftdim(A2',-1),size(A1,1),1);
B1 = repmat(A1,[1,1,size(A2,1)]);
D4 = squeeze(sum(B1 == B2,2));
remove rows duplicated rows
A1(logical(sum(D4,2)),:) = [];
A2(logical(sum(D4,1)),:) = [];
reconstruct original matrix
R = [A1 ones(size(A1,1),1);A2 2*ones(size(A2,1),1)];

Matrix "Zigzag" Reordering

I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.