Create new variable that recognizes changes in a code in Matlab - matlab

I have a double matrix B that has in C1 the year 1998 and in C2 a code (unrepeated). Each C2 is given a value in C3.
% C1 C2 C3
B=[ 1998 22 37
1998 34 7
1998 76 12
1998 98 29
1998 107 14
…]
This is how I got to B:
N1=[N{:,1} N{:,2} N{:,3}];
N1=unique(N1(:,1:3),'rows');
N3= unique(N1(:,1:2),'rows');
for m=1:size(N3,1)
N3(m,3)=sum(N1(:,1) == N3(m,1) & N1(:,2)==N3(m,2));
end
B=N3((N3(:,1) == 1998),:);
I have a cell-array A with the years horizontally disposed in R1, un-repeated values in Y, and corresponding codes in the columns that follow. The codes match the ones in C2 from variable B.
A={Y 1996 1997 1998 1999 %R1
1 107 107 22 22
13 98 98 76 1267
… }
Is there a way I could get a new variable that recognizes the change in the codes in variable A, and presents the corresponding values from C3 in B? For instance:
AB={Y Initial C2 Change C2
1 107 14 22 37
13 98 29 76 12 }

AB = {'Y' 'Initial' 'C2' 'Change' 'C2'}; % initialize result cell array
for i=2:size(A,1) % loop through rows of A
indicesOfChanges = find(diff([A{i,2:end}])); % find changes in row
for j=1:length(indicesOfChanges) % for all changes look up the corresponding values
ind1=find(B(:,2)==A{i,indicesOfChanges(j)+1}); % row index in B of value before change
ind2=find(B(:,2)==A{i,indicesOfChanges(j)+2}); % row index in B of value after change
AB{end+1,1} = A{i,1};
AB{end,2} = B(ind1,2);
AB{end,3} = B(ind1,3);
AB{end,4} = B(ind2,2);
AB{end,5} = B(ind2,3);
end
end
Maybe this could be further improved by using a vectorized approach, but as long as your arrays aren't too big it should work fast enough using loops.

Related

How to match and copy time-data from one matrix to another?

In MATLAB (R2015a), I have two large matrices that can be simplified as:
A = [ 24 10 830; 24 15 830; 150 17 945; 231 40 1130; 231 45 1130]
(note that in A, column 3 is the time for the event cataloged in column 1) and
B = [24 13; 150 29; 231 43]
How can I match and copy the time-data from column 3 in matrix A, to a matching and filtered event column in matrix B?
For example, I want the value 24 from first column in B to be matched with value 24 from first column in A, and then copy the corresponding time-data in A's third column (for 24 it's 830, for 150 it's 945 etc.) into B. This should result into our new B with the time-data from A:
B = [24 13 830; 150 29 945; 231 43 1130]
I relatively new to MATLAB so any help is much appreciated!
First find the location of the elements in the first row of B in the first row of A using the ismember function. And then use those locations to construct the new matrix.
[~,Locb] = ismember(B(:,1),A(:,1));
Bnew = [B A(Locb,3)]
Bnew =
24 13 830
150 29 945
231 43 1130
This is a fast way that comes to my mind. There might be some singularities that needed to be checked more thoroughly.

Shifting Index values in matlab

Suppose I have two rows as follows with the data
R1 = 12 13 15 17 200 23
R2 = 32 22 43 67 21 74
I would like to know how to shift the values of the 2nd index and 3rd index of R1 (e.g, 13 15) into the second row of R2 so it becomes
R2 = 32 13 15 67 21 74
It's very simple: R2(2:3) = R1(2:3);
Code sample:
R1 = [12 13 15 17 200 23];
R2 = [32 22 43 67 21 74];
R2(2:3) = R1(2:3);
You can also use the following: R2([2,3]) = R1([2,3]);, in case indexes are not sequential.
In case R1 and R2 are two rows in a matrix, you can use the following sample:
% Create the input matrix A:
R1 = [12 13 15 17 200 23];
R2 = [32 22 43 67 21 74];
A = [R1; R2];
%Copy values from index 2 and 3 of first row to index 2 and 3 of second row:
A(2, [2,3]) = A(1, [2,3]);
In case there are more rows, and you need to "shift" all down, you can use the following example:
%Create sample matrix A (6x6 elements).
A = magic(6);
%"Shift" values of index 2,3 of all rows, one row down:
A(2:end, [2,3]) = A(1:end-1, [2,3]);
Refer here: http://www.mathworks.com/company/newsletters/articles/matrix-indexing-in-matlab.html

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)];

How do I select n elements of a sequence in windows of m ? (matlab)

Quick MATLAB question.
What would be the best/most efficient way to select a certain number of elements, 'n' in windows of 'm'. In other words, I want to select the first 50 elements of a sequence, then elements 10-60, then elements 20-70 ect.
Right now, my sequence is in vector format(but this can easily be changed).
EDIT:
The sequences that I am dealing with are too long to be stored in my RAM. I need to be able to create the windows, and then call upon the window that I want to analyze/preform another command on.
Do you have enough RAM to store a 50-by-nWindow array in memory? In that case, you can generate your windows in one go, and then apply your processing on each column
%# idxMatrix has 1:50 in first col, 11:60 in second col etc
idxMatrix = bsxfun(#plus,(1:50)',0:10:length(yourVector)-50); %'#
%# reshapedData is a 50-by-numberOfWindows array
reshapedData = yourVector(idxMatrix);
%# now you can do processing on each column, e.g.
maximumOfEachWindow = max(reshapedData,[],1);
To complement Kerrek's answer: if you want to do it in a loop, you can use something like
n = 50
m = 10;
for i=1:m:length(v)
w = v(i:i+n);
% Do something with w
end
There's a slight issue with the description of your problem. You say that you want "to select the first 50 elements of a sequence, then elements 10-60..."; however, this would translate to selecting elements:
1-50
10-60
20-70
etc.
That first sequence should be 0-10 to fit the pattern which of course in MATLAB would not make sense since arrays use one-indexing. To address this, the algorithm below uses a variable called startIndex to indicate which element to start the sequence sampling from.
You could accomplish this in a vectorized way by constructing an index array. Create a vector consisting of the starting indices of each sequence. For reuse sake, I put the length of the sequence, the step size between sequence starts, and the start of the last sequence as variables. In the example you describe, the length of the sequence should be 50, the step size should be 10 and the start of the last sequence depends on the size of the input data and your needs.
>> startIndex = 10;
>> sequenceSize = 5;
>> finalSequenceStart = 20;
Create some sample data:
>> sampleData = randi(100, 1, 28)
sampleData =
Columns 1 through 18
8 53 10 82 82 73 15 66 52 98 65 81 46 44 83 9 14 18
Columns 19 through 28
40 84 81 7 40 53 42 66 63 30
Create a vector of the start indices of the sequences:
>> sequenceStart = startIndex:sequenceSize:finalSequenceStart
sequenceStart =
10 15 20
Create an array of indices to index into the data array:
>> index = cumsum(ones(sequenceSize, length(sequenceStart)))
index =
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
>> index = index + repmat(sequenceStart, sequenceSize, 1) - 1
index =
10 15 20
11 16 21
12 17 22
13 18 23
14 19 24
Finally, use this index array to reference the data array:
>> sampleData(index)
ans =
98 83 84
65 9 81
81 14 7
46 18 40
44 40 53
Use (start : step : end) indexing: v(1:1:50), v(10:1:60), etc. If the step is 1, you can omit it: v(1:50).
Consider the following vectorized code:
x = 1:100; %# an example sequence of numbers
nwind = 50; %# window size
noverlap = 40; %# number of overlapping elements
nx = length(x); %# length of sequence
ncol = fix((nx-noverlap)/(nwind-noverlap)); %# number of sliding windows
colindex = 1 + (0:(ncol-1))*(nwind-noverlap); %# starting index of each
%# indices to put sequence into columns with the proper offset
idx = bsxfun(#plus, (1:nwind)', colindex)-1; %'
%# apply the indices on the sequence
slidingWindows = x(idx)
The result (truncated for brevity):
slidingWindows =
1 11 21 31 41 51
2 12 22 32 42 52
3 13 23 33 43 53
...
48 58 68 78 88 98
49 59 69 79 89 99
50 60 70 80 90 100
In fact, the code was adapted from the now deprecated SPECGRAM function from the Signal Processing Toolbox (just do edit specgram.m to see the code).
I omitted parts that zero-pad the sequence in case the sliding windows do not evenly divide the entire sequence (for example x=1:105), but you can easily add them again if you need that functionality...