Libsvm Index exceeds matrix dimensions - arrays

The following libsvm matlab code keeps giving me an Index exceeds matrix dimensions after a few loops. Can anyone help me with where the error might be coming from?
testlabel = [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];
model = cell(3,1);
for n=1:3
model{n} = svmtrain(completelabel{n}, completefeatureVector{n}, '-b 1 -t 0');
end
numTest=10;
pr = zeros(numTest,2);
for k=1:numTest
for m=1:3
[~,~,p] = svmpredict(testlabel(m, k), featureVectortest{k}, model{m}, '-b 1');
pr(:,k) = p(:,model{m}.Label==m); %# probability of class==k
end
[~,predictedLabel] = max(pr,[],2);
end

Related

How to balance unique values in an array Matlab

I have a vector
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
1 occurs 17 times
0 occurs 21 times
How can I randomly remove 0s so that both values have equal amounts, such as 1 (17 times) and 0 (17 times)?
This should also work on much bigger matrix.
Starting with your example
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]
You can do the following:
% Get the indices of the value which is more common (`0` here)
zeroIdx = find(~Y); % equivalent to find(Y==0)
% Get random indices to remove
remIdx = randperm(nnz(~Y), nnz(~Y) - nnz(Y));
% Remove elements
Y(zeroIdx(remIdx)) = [];
You could combine the last two lines, but I think it would be less clear.
The randperm line is choosing the correct number of elements to remove from random indices between 1 and the number of zeros.
If the data can only have two values
Values are assumed to be 0 and 1. The most common value is randomly removed to equalize their counts:
Y = [1 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0]; % data
ind0 = find(Y==0); % indices of zeros
ind1 = find(Y==1); % indices of ones
t(1,1:numel(ind0)) = ind0(randperm(numel(ind0))); % random permutation of indices of zeros
t(2,1:numel(ind1)) = ind1(randperm(numel(ind1))); % same for ones. Pads shorter row with 0
t = t(:, all(t,1)); % keep only columns that don't have padding
result = Y(sort(t(:))); % linearize, sort and use those indices into the data
Generalization for more than two values
Values are arbitrary. All values except the least common one are randomly removed to equalize their counts:
Y = [0 1 2 0 2 1 1 2 0 2 1 2 2 0 0]; % data
vals = [0 1 2]; % or use vals = unique(Y), but absent values will not be detected
t = [];
for k = 1:numel(vals) % loop over values
ind_k = find(Y==vals(k));
t(k, 1:numel(ind_k)) = ind_k(randperm(numel(ind_k)));
end
t = t(:, all(t,1));
result = Y(sort(t(:)));

How to change elements of a matrix with reference to a vector of column indices without using for-loop?

I have a matrix
a =
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
and b vector
b =
1 2 3 4 5 5
I want to replace value of each row in a matrix with reference value of b matrix value and finally generate a matrix as follows without using for loop.
a_new =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 1
if first element of b, b(1) = 1 so change take first row of a vector and make first element as 1 because b(1) = 1.
How can I implement this without using for loop?
Sure. You only need to build a linear index from b and use it to fill the values in a:
a = zeros(6,5); % original matrix
b = [1 2 3 4 5 5]; % row or column vector with column indices into a
ind = (1:size(a,1)) + (b(:).'-1)*size(a,1); % build linear index
a(ind) = 1; % fill value at those positions
Same as Luis Mendo's answer, but using the dedicated function sub2ind:
a( sub2ind(size(a),(1:numel(b)).',b(:)) ) = 1
Also via the subscript to indices conversion way,
a = zeros(6,5);
b = [1 2 3 4 5 5];
idx = sub2ind(size(a), [1:6], b); % 1:6 just to create the row index per b entry
a(idx) = 1
any of these methods works in Octave:
bsxfun(#eq, [1:5 5]',(1:5))
[1:5 5].' == (1:5)

Array block splitting in MATLAB

Say we have a vector containing zeros interspersed blocks of ones of varying length, such as:
0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0
I would like to transform this into a 2D array as follows. Each row contains zeros only and one of the blocks. I.e. the number of rows of the 2D array would be the number of blocks at the end. The above array would transform to:
0 0 0 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 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 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 1 1 1 0 0 0
I.e the first block ends up in the first row, the second block in the second row etc.
Question
This exercise is rather trivial using loops. My question is if there is a neat way using MATLAB matrix operations, MATLAB functions and array indexing to do this?
Off the top of my head you could use bwlabel (from the Image Processing Toolbox) to assign each cluster of 1's a unique value. You could then use bsxfun to check equality between the labeled version and the unique labels which will automatically expand it out into a matrix.
a = [0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0];
b = bwlabel(a);
out = bsxfun(#eq, (1:max(b))', b);
Without the image processing toolbox you could do effectively the same thing with:
C = cumsum(~a);
out = bsxfun(#eq, unique(C(logical(a))).', C);
I tried this one
N = 100; % set array size
A = randi(2,N,1)-1; % generate random array filled with 0 and 1
d = diff([0;A]); % if 1 : starting point of block
% if -1 : end point of block
ir = find(A); % Find A==1 which will be row index
ic = cumsum(d(ir)>0); % Set col index
% assemble array
% if you want output as full array
A_new = accumarray([ir,ic],ones(size(ir)),[length(A),ic(end)]);
% if you want output as sparse array
A_new = sparse(ir,ic,ones(size(ir)),length(A),ic(end),length(ir));
% display routine
figure;spy(A,'r');hold on;spy([zeros(size(A)),A_new]);
Turns out it is faster than #Suever 's solution (Compared tic toc time with size 10000, 1000 trial). Also, if you use sparse instead of accumarray, then it is much faster
Suever_time = 7~8 sec
Accumarray = 2~3 sec
Sparse = 0.2~0.3 sec
However, his one is much shorter and neat!

Convert integer to logical array in MATLAB

I want to convert an integer i to a logical vector with an i-th non-zero element. That can de done with 1:10 == 2, which returns
0 1 0 0 0 0 0 0 0 0
Now, I want to vectorize this process for each row. Writing repmat(1:10, 2, 1) == [2 5]' I expect to get
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
But instead, this error occurs:
Error using ==
Matrix dimensions must agree.
Can I vectorize this process, or is a for loop the only option?
You can use bsxfun:
>> bsxfun(#eq, 1:10, [2 5].')
ans =
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
Note the transpose .' on the second vector; it's important.
Another way is to use eye and create a logical matrix that is n x n long, then use the indices to index into the rows of this matrix:
n = 10;
ind = [2 5];
E = eye(n,n) == 1;
out = E(ind, :);
We get:
>> out
out =
0 1 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0
Just another possibility using indexing:
n = 10;
ind = [2 5];
x=zeros(numel(ind),n);
x(sub2ind([numel(ind),n],1:numel(ind),ind))=1;

Select n elements in matrix left-wise based on certain value

I have a logical matrix A, and I would like to select all the elements to the left of each of my 1s values given a fixed distant. Let's say my distance is 4, I would like to (for instance) replace with a fixed value (saying 2) all the 4 cells at the left of each 1 in A.
A= [0 0 0 0 0 1 0
0 1 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 1 0 1]
B= [0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1]
In B is what I would like to have, considering also overwrting (last row in B), and cases where there is only 1 value at the left of my 1 and not 4 as the fixed searching distance (second row).
How about this lovely one-liner?
n = 3;
const = 5;
A = [0 0 0 0 0 1 0;
0 1 0 0 0 0 0;
0 0 0 0 0 0 0;
0 0 0 0 1 0 1]
A(bsxfun(#ne,fliplr(filter(ones(1,1+n),1,fliplr(A),[],2)),A)) = const
results in:
A =
0 0 5 5 5 1 0
5 1 0 0 0 0 0
0 0 0 0 0 0 0
0 5 5 5 5 5 1
here some explanations:
Am = fliplr(A); %// mirrored input required
Bm = filter(ones(1,1+n),1,Am,[],2); %// moving average filter for 2nd dimension
B = fliplr(Bm); %// back mirrored
mask = bsxfun(#ne,B,A) %// mask for constants
A(mask) = const
Here is a simple solution you could have come up with:
w=4; % Window size
v=2; % Desired value
B = A;
for r=1:size(A,1) % Go over all rows
for c=2:size(A,2) % Go over all columns
if A(r,c)==1 % If we encounter a 1
B(r,max(1,c-w):c-1)=v; % Set the four spots before this point to your value (if possible)
end
end
end
d = 4; %// distance
v = 2; %// value
A = fliplr(A).'; %'// flip matrix, and transpose to work along rows.
ind = logical( cumsum(A) ...
- [ zeros(size(A,1)-d+2,size(A,2)); cumsum(A(1:end-d-1,:)) ] - A );
A(ind) = v;
A = fliplr(A.');
Result:
A =
0 2 2 2 2 1 0
2 1 0 0 0 0 0
0 0 0 0 0 0 0
2 2 2 2 2 2 1
Approach #1 One-liner using imdilate available with Image Processing Toolbox -
A(imdilate(A,[ones(1,4) zeros(1,4+1)])==1)=2
Explanation
Step #1: Create a morphological structuring element to be used with imdilate -
morph_strel = [ones(1,4) zeros(1,4+1)]
This basically represents a window extending n places to the left with ones and n places to the right including the origin with zeros.
Step #2: Use imdilate that will modify A such that we would have 1 at all four places to the left of each 1 in A -
imdilate_result = imdilate(A,morph_strel)
Step #3: Select all four indices for each 1 of A and set them to 2 -
A(imdilate_result==1)=2
Thus, one can write a general form for this approach as -
A(imdilate(A,[ones(1,window_length) zeros(1,window_length+1)])==1)=new_value
where window_length would be 4 and new_value would be 2 for the given data.
Approach #2 Using bsxfun-
%// Paramters
window_length = 4;
new_value = 2;
B = A' %//'
[r,c] = find(B)
extents = bsxfun(#plus,r,-window_length:-1)
valid_ind1 = extents>0
jump_factor = (c-1)*size(B,1)
extents_valid = extents.*valid_ind1
B(nonzeros(bsxfun(#plus,extents_valid,jump_factor).*valid_ind1))=new_value
B = B' %// B is the desired output

Resources