"Find and replace" from a matrix MATLAB - matlab

I have a matrix that is like the following:
a = [10 0; 12 5; 10 0; 12 0; 15 0; 15 2];
a =
10 0
12 5
10 0
12 0
15 0
15 2
I am looking to create a new matrix which find and replaces the zeros with a value that is dependent on the first column's value. The key is this matrix:
Key =
10 100
12 200
15 300
If the value is already greater than zero in the first column I would like to leave it. The output would look like this:
Output =
10 100
12 5
10 100
12 200
15 300
15 2

You can do it in one line using logical indexing smartly:
a(~a(:,2),2)=arrayfun(#(x)Key(Key(:,1)==x,2),a(~a(:,2),1))
%a =
% 10 100
% 12 5
% 10 100
% 12 200
% 15 300
% 15 2

Related

How to shift non circularly in Matlab

I am trying to shift non circularly in MATLAB so even if I shift outside of the index it will add 0s to correct it. I tried following the answer in How do I shift columns (left or right) in a matrix? but had no success.
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35]
d = 3; % shift; positive/negative for right/left
result = zeros(size(data), 'like', data); % preallocate with zeros
result(:,max(1,1+d):min(end,end+d)) = data(:,max(1,1-d):min(end,end-d)); % write values
In my output results is nothing but the same size but all zeroes
Desired output:
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35
You can do it by creating a matrix result, the final size, filled with zeros, then copying the original data into the final result, making sure you place the data at the right indices.
What you have in your example code is not right for what you ask. If I run it,the final result is padded fine but truncated at the size of the original data matrix. This is how some matrix are shifted (with the shifted columns dropped altogether), but that's not what you asked.
A simple way to do it, is to create a padding matrix of the proper size, then simply concatenate it with your original data matrix. This can be done as below:
%% Initial data
data = [1 2 3 4 5; 11 12 13 14 15; 21 22 23 24 25; 31 32 33 34 35] ;
d = 3 ;
%% shift and pad with zeros
nrows = size(data,1) ; % Number of rows in [data]
pad = zeros( nrows , abs(d) ) ; % create padding matrix
if d>0
result = [pad data] ; % Concatenate the filler matrix on the left
else
result = [data pad] ; % Concatenate the filler matrix on the right
end
And just to be sure:
>> result
result =
0 0 0 1 2 3 4 5
0 0 0 11 12 13 14 15
0 0 0 21 22 23 24 25
0 0 0 31 32 33 34 35
If you want to reuse the same way than in your example code, you have to adjust it a bit to allow for the new columns:
%% create result and copy data
result = zeros( size(data,1) , size(data,2)+abs(d) ) ;
colStart = max(1,1+d) ;
result(:,colStart:colStart+size(data,2)-1) = data ;
This will create the same result matrix as above.

Reshaping a vector into a larger matrix with arbitrary m and n

I'm attempting to create a function that takes a vector of any length and uses its entries to generate a matrix of size mxn, where m and n are arbitrary numbers. If the matrix has a greater number of entries that the original vector, the entries should repeat. E.g. A vector, (1,2,3,4) would make a 3x3 matrix (1,2,3;4,1,2;3,4,1).
So far I have this function:
function A = MyMatrix(Vector,m,n)
A = reshape([Vector,Vector(1:(m*n)-length(Vector))],[m,n]);
end
which is successful in some cases:
>> m=8;n=5;Vector=(1:20);
>> A = MyMatrix(Vector,m,n)
A =
1 9 17 5 13
2 10 18 6 14
3 11 19 7 15
4 12 20 8 16
5 13 1 9 17
6 14 2 10 18
7 15 3 11 19
8 16 4 12 20
However this only works for values of m and n that multiply to a number less than or equal to twice the number of entries in 'Vector', so 40 in this case. When mn is larger than 40, this code yields:
>> m=8;n=6;Vector=(1:20);
>> A = MyMatrix(Vector,m,n)
Index exceeds the number of array elements (20).
Error in MyMatrix (line 3)
A = reshape([Vector,Vector(1:(m*n)-length(Vector))],[m,n]);
I have tried to create a workaround using functions such as repmat, however, so far I have not been able to create a matrix with larger m and n.
You only need to
index the vector using "modular", 1-based indexing;
reshape it taking into account that Matlab is column-major, so you need to swap m and n;
transpose to swap m and n back.
V = [10 20 30 40 50 60]; % vector
m = 4; % number of rows
n = 5; % number of columns
A = reshape(V(mod(0:m*n-1, numel(V))+1), n, m).';
This gives
A =
10 20 30 40 50
60 10 20 30 40
50 60 10 20 30
40 50 60 10 20

Matlab isn't incrementing my variable

I have the following matrix declared in Matlab:
EmployeeData =
1 20 100000 42 14
2 15 95000 35 14
3 18 70000 28 14
4 10 85000 35 14
5 10 40000 21 12
6 4 45000 14 8
7 3 50000 21 10
8 5 55000 21 14
9 1 25000 14 7
10 2 50000 21 9
42 4 100000 42 10
Where column 1 represents ID numbers, 2 represents years, 3 is salary, 4 is vacation days, and 5 is sick days. I am trying to find the maximum value of a column (in this case the salary column), and print out the ID associated with that value. If more than one employee has the maximum value, all the IDs with that maximum are supposed to be shown. So here is how I naively implemented a way to do it:
>> maxVal = [];
>> j = 1;
>> for i = EmployeeData(:, 3)
if i == max(EmployeeData(:, 3))
maxVal = [maxVal EmployeeData(j, 1)];
end
j = j + 1;
end
But it shows maxVal to be [] in my workspace variables, instead of [1 42] as I expected. Upon inserting a disp(i) in the for loop above the if to debug, I get the following output:
100000
95000
70000
85000
40000
45000
50000
55000
25000
50000
Just like I expected. But when I switch out that disp(i) with a disp(j), I get this for my output:
1
What am I doing wrong? Should this not work?
MATLAB for loops operate on rows, not columns. You should try replacing your for loop with:
for i = EmployeeData(:, 3)' % NOTE THE TRANSPOSE
...
end
EDIT: Note that you can do what you're trying to do without a forloop:
maxVal = EmployeeData(EmployeeData(:,3) == max(EmployeeData(:,3)),1);
Is this what you want?
>> EmployeeData(EmployeeData(:,3)==max(EmployeeData(:,3)),1)
ans =
1
42

Setting a specified number of rows in each column and block of elements to zero

Is there any easy way to set a specified number of rows k in nth column to zero ? Its a bit tricky question to explain so I guess its best to look at the example.
Lets say I have:
A =
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
I wish to set rows, like this: [row1 col1] = 0; [row2 col1] = 0, and then [row3 col2]= 0; row4 col2]= 0 and so on, so my output is:
k = 2
B =
0 2 3
0 5 6
7 0 9
10 0 12
13 14 0
16 17 0
Do you have any suggestions/solutions how this could be solved with a for loop, or maybe there is another way?
and how this solution could be extend further to something like this:
A =
1 2 3 1 2 3
4 5 6 4 5 6
7 8 9 7 8 9
10 11 12 10 11 12
13 14 15 13 14 15
16 17 18 16 17 18
B =
0 0 3 1 2 3
0 0 6 4 5 6
7 8 0 0 8 9
10 11 0 0 3 1
13 14 15 13 0 0
16 17 18 16 0 0
One approach -
k = 2;
row1 = 1:size(A,1)
col1 = ceil([1:size(A,1)]./k)
A(sub2ind(size(A),row1,col1))=0
For the edited question, use kron like this -
k = 2;
a1 = eye(size(A)./k);
b1 = ones(k,k);
A(logical(kron(a1,b1)))=0
That isn't a problem. We can figure out exactly which rows and columns you want to set to 0 based on this value k, then use sub2ind to get a single index to access into your matrix. This will be in column-major format. Then you can use this and set all of your values to zero. Here is an example. We need to know the width and height of your matrix first before we do this:
rows = [row1 row2 row3];
cols = [col1 col2 col3];
%// Get column major indices
ind = sub2ind([height width], rows, cols);
%// Set the values in this matrix to 0.
B(ind) = 0;
Now with your example, we need to access all of the rows. However, for the columns, we need to access k elements in each column and ensuring they don't overlap. As such, we can do it like so:
k = 2;
B = reshape(1:18, 6, 3).';
rows = 1 : 6;
cols = ceil(rows / k);
ind = sub2ind([rows cols], rows, cols);
B(ind) = 0;
You would thus get:
B =
0 2 3
0 5 6
7 0 9
10 0 12
13 14 0
16 17 0
The following code is executable in matlab, and does what you want:
% Create A-matrix
A = reshape(1:18,6,3)
% Set specified datapoints to zero
A([1,2],[1,1]) = 0
Alternatively, you can set each element separately
A(1,1) = 0
A(1,2) = 0
And the most general way, with k and n:
A([1:k],n) = 0
For first question:
A(mod(0:numel(A)-1, size(A,1)+k) < k) = 0;
For second question, including first question as a particular case:
c = repmat({ones(k,size(B,2)/size(B,1)*k)}, size(B,1)/k, 1);
B = B.*~blkdiag(c{:})

How can I divide each row of a matrix by a fixed row?

Suppose I have a matrix like:
100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60
...
I wish to divide each row by the second row (each element by the corresponding element), so I'll get:
100 100 100 100 100 100
1 1 1 1 1 1
10 10 10 10 10 10
...
Hw can I do it (without writing an explicit loop)?
Use bsxfun:
outMat = bsxfun (#rdivide, inMat, inMat(2,:));
The 1st argument to bsxfun is a handle to the function you want to apply, in this case right-division.
Here's a couple more equivalent ways:
M = [100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60];
%# BSXFUN
MM = bsxfun(#rdivide, M, M(2,:));
%# REPMAT
MM = M ./ repmat(M(2,:),size(M,1),1);
%# repetition by multiplication
MM = M ./ ( ones(size(M,1),1)*M(2,:) );
%# FOR-loop
MM = zeros(size(M));
for i=1:size(M,1)
MM(i,:) = M(i,:) ./ M(2,:);
end
The best solution is the one using BSXFUN (as posted by #Itamar Katz)
You can now use array vs matrix operations.
This will do the trick :
mat = [100 200 300 400 500 600
1 2 3 4 5 6
10 20 30 40 50 60];
result = mat ./ mat(2,:)
which will output :
result =
100 100 100 100 100 100
1 1 1 1 1 1
10 10 10 10 10 10
This will work in Octave and Matlab since R2016b.