Matlab: insert multiple elements at specified positions - arrays

I have a large array A, in which I wish to insert values at specific positions. These values and positions are stored in array B. Can I execute this assignment using indexing, i.e. without resorting to a for-loop or arrayfun?
Example:
% target array
A = [NaN NaN;
NaN NaN];
% r c value
B = [1 1 17;
1 2 13;
2 2 21];
% ?? Mystery operation ??
% Desired result:
A = [17 13;
NaN 21];

This is a vectorize solution:
accumarray(B(:,1:2),B(:,3),[],[],NaN)
referring to documentation of accumarray we can use signature accumarray(subs,val,sz,fun,fillval) that first two arguments are subs= [row and column indices] and val=value of matrix and 5th argument fillval:
fills all elements of A that are not referred to by any subscript in subs with the scalar value fillval
so we use NaN for the 5th argument.
or you can use signature accumarray(subs,val,sz,fun,fillval,issparse) if you want to create sparse matrix.

Solution
Another approach might be with the use of linear indices:
% Define data
A = [NaN NaN;
NaN NaN];
B = [1 1 17;
1 2 13;
2 2 21];
% Create linear indices for matrix A, with rows from B(:, 1) and columns from B(:, 2)
indices = sub2ind(size(A), B(:, 1), B(:, 2));
% Replace the data in A with values from B(:, 3)
A(indices) = B(:, 3);

Related

Sum up vector values till threshold, then start again

I have a vector a = [1 3 4 2 1 5 6 3 2]. Now I want to create a new vector 'b' with the cumsum of a, but after reaching a threshold, let's say 5, cumsum should reset and start again till it reaches the threshold again, so the new vector should look like this:
b = [1 4 4 2 3 5 6 3 5]
Any ideas?
You could build a sparse matrix that, when multiplied by the original vector, returns the cumulative sums. I haven't timed this solution versus others, but I strongly suspect this will be the fastest for large arrays of a.
% Original data
a = [1 3 4 2 1 5 6 3 2];
% Threshold
th = 5;
% Cumulative sum corrected by threshold
b = cumsum(a)/th;
% Group indices to be summed by checking for equality,
% rounded down, between each cumsum value and its next value. We add one to
% prevent NaNs from occuring in the next step.
c = cumsum(floor(b) ~= floor([0,b(1:end-1)]))+1;
% Build the sparse matrix, remove all values that are in the upper
% triangle.
S = tril(sparse(c.'./c == 1));
% In case you use matlab 2016a or older:
% S = tril(sparse(bsxfun(#rdivide,c.',c) == 1));
% Matrix multiplication to create o.
o = S*a.';
By normalizing the arguments of cumsum with the threshold and flooring you can get grouping indizes for accumarray, which then can do the cumsumming groupwise:
t = 5;
a = [1 3 4 2 1 5 6 3 2];
%// cumulative sum of normalized vector a
n = cumsum(a/t);
%// subs for accumarray
subs = floor( n ) + 1;
%// cumsum of every group
aout = accumarray( subs(:), (1:numel(subs)).', [], #(x) {cumsum(a(x))});
%// gather results;
b = [aout{:}]
One way is to use a loop. You create the first cumulative sum cs, and then as long as elements in cs are larger than your threshold th, you replace them with elements from the cumulative sum on the rest of the elements in a.
Because some elements in a might be larger than th, this loop will be infinite unless we also eliminate these elements too.
Here is a simple solution with a while loop:
a = [1 3 4 2 1 5 6 3 2];
th = 5;
cs = cumsum(a);
while any(cs>th & cs~=a) % if 'cs' has values larger that 'th',
% and there are any values smaller than th left in 'a'
% sum all the values in 'a' that are after 'cs' reached 'th',
% excluding values that are larger then 'th'
cs(cs>th & cs~=a) = cumsum(a(cs>th & cs~=a));
end
Calculate the cumulative sum and replace the indices value obeying your condition.
a = [1 3 4 2 1 5 6 3 2] ;
b = [1 4 4 2 3 5 6 3 5] ;
iwant = a ;
a_sum = cumsum(a) ;
iwant(a_sum<5) = a_sum(a_sum<5) ;

Create N x 2 array from N x 1 array-Matlab

I have a 1D array (say A) of size N (i.e N x 1; N-rows, 1 Column). Now I want to create an array of size N x 2 (N-rows, 2-columns) with the array A as one column and the other column with a same element (0 in the given example below).
For e.g If
A =[1;2;3;4;5];
I'd like to create a matrix B which is
B=[0 1; 0 2; 0 3; 0 4; 0 5]
How do I do this in Matlab?
You can also abuse bsxfun for a one-liner -
bsxfun(#times,[0,1],A)
Or matrix-multiplication for that implicit expansion -
A*[0,1]
You can initialize B to be an Nx2 array of all zeros and then assign the second column to the values in A.
A = [1;2;3;4;5];
B = zeros(numel(A), 2);
B(:,2) = A;
% 0 1
% 0 2
% 0 3
% 0 4
% 0 5
If you actually just want zeros in that first column, you don't even have to initialize B as MATLAB will automatically fill in the unknown values with 0.
% Make sure B isn't already assigned to something
clear B
% Assign the second column of uninitialized variable B to be equal to A
B(:,2) = A;
You can try this approach
B=[zeros(length(A),1) A]

Matlab - Sort into deciles each column

Suppose I have a matrix A [m x 1], where m is not necessarily even. I to create a matrix B also [m x 1] which tells me the decile of the elements in A (i.e. matrix B has numbers from 1 to 10).
I know I can use the function sort(A) to get the position of the elements in A and from there I can manually get deciles. Is there another way of doing it?
I think one possibility would be B = ceil(10 * tiedrank(A) / length(A) . What do you think? Are there any issues with this?
Also, more generally, if I have a matrix A [m x n] and I want to create a matrix B also [m x n], in which each column of B should have the decile of the corresponding column in A , is there a way of doing it without a for loop through the columns?
Hope the problem at hand is clear. So far I have been doing it using the sort function and then manually assigning the deciles, but it is very inefficient.
This is how I would do it:
N = 10;
B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A));
This counts, for each element, how many elements are less than or equal to it; and then rounds the results to 10 values.
Depending on how you define deciles, you may want to change #le to #lt, or ceil to floor. For numel(A) multiple of N, the above definition gives exactly numel(A)/N values in each of the N quantiles. For example,
>> A = rand(1,8)
A =
0.4387 0.3816 0.7655 0.7952 0.1869 0.4898 0.4456 0.6463
>> N = 4;
>> B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A))
B =
2 1 4 4 1 3 2 3

How to check if all the entries in columns of a matrix are equal (in MATLAB)?

I have a matrix of growing length for example a 4-by-x matrix A where x is increasing in a loop. I want to find the smallest column c where all columns before that, each, carry one single number. The matrix A can look like:
A = [1 2 3 4;
1 2 3 5;
1 2 3 1;
1 2 3 0];
where c=3, and x=4.
At each iteration of the loop where A grows in length, the value of index c grows as well. Therefore, at each iteration, I want to update the value of c. How efficiently can I code this in Matlab?
Let's say you had the matrix A and you wanted to check a particular column iito see if all its elements are the same. The code would be:
all(A(:, ii)==A(1, ii)) % checks if all elements in column are same as first element
Also, keep in mind that once the condition is broken, x cannot be updated anymore. Therefore, your code should be:
x = 0;
while true
%% expand A by one column
if ~all(A(:, x)==A(1, x)) % true if all elements in column are not the same as first element
break;
end
x = x+1;
end
You could use this:
c = find(arrayfun(#(ind)all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
This finds the first column where not all values are the same. If you run this in a loop, you can detect when entries in a column start to differ:
for x = 1:maxX
% grow A
c = find(arrayfun(#(ind)~all(A(1, ind)==A(:, ind)), 1:x), 1, 'first');
% If c is empty, all columns have values equal to first row.
% Otherwise, we have to subtract 1 to get the number of columns with equal values
if isempty(c)
c = x;
else
c = c - 1;
end
end
Let me give a try as well:
% Find the columns which's elements are same and sum the logical array up
c = sum(A(1,:) == power(prod(A,1), 1/size(A,1)))
d=size(A,2)
To find the last column such that each column up to that one consists of equal values:
c = find(any(diff(A,1,1),1),1)-1;
or
c = find(any(bsxfun(#ne, A, A(1,:)),1),1)-1;
For example:
>> A = [1 2 3 4 5 6;
1 2 3 5 5 7;
1 2 3 1 5 0;
1 2 3 0 5 8];
>> c = find(any(diff(A,1,1),1),1)-1
c =
3
You can try this (easy and fast):
Equal_test = A(1,:)==A(2,:)& A(2,:)==A(3,:)&A(3,:)==A(4,:);
c=find(Equal_test==false,1,'first')-1;
You can also check the result of find if you want.

adding values to diagonals of matrix using element-wise addition in matlab

I am writing a script that operates on matrices, and I have run into the problem of needing to add the sum of the diagonals of a previous matrix to the diagonal elements of a new matrix. The code I have so far for this particular function (described in more detail below) is:
t = 1;
for k = (m-1):-1:-(m-1)
C = bsxfun(#plus, diag(B, k), d);
g(t) = sum(diag(B, k));
t = t + 1;
end
where d is a 1x3 array, and C is supposed to be a 3x3 array; however, C is being output as a 1x3 array in such a way that the first diagonal is being summed and added to d, then the main diagonal is being summed and added to d, and the final diagonal is being summed and added to d.
Is there a way I can get the values of C to be such that the first diagonal is the sum of it's individual elements added to the last element of d, the main diagonal's individual elements added to the middle element of d, and the bottom diagonal's elements added to the first element of d? (while still working for any array size?)
Here is a picture that describes what I'm trying to achieve:
Thanks!
You can use toeplitz to generate a matrix containing the values that need to be added to your original matrix:
M = [5 5 5; 7 7 7; 9 9 9]; %// data matrix
v = [1 11 4 3 2]; %// data vector
S = toeplitz(v);
S = S(1:(numel(v)+1)/2, (numel(v)+1)/2:end);
result = M+S;
Or, as noted by #thewaywewalk, you can do this more directly as follows:
M = [5 5 5; 7 7 7; 9 9 9]; %// data matrix
v = [1 11 4 3 2]; %// data vector
result = M + toeplitz(v(size(M,1):-1:1), v(size(M,2):end));
Assuming B to be a square shaped matrix, listed in this post would be one bsxfun based vectorized approach. Here's the implementation -
N = size(B,1) %// Store size of B for later usage
%// Find a 2D grid of all indices with kth column representing kth diagonal of B
idx = bsxfun(#plus,[N-numel(B)+1:N+1:N]',[0:2*N-2]*N) %//'
%// Mask of all valid indices as we would see many from the 2D grid
%// going out of bounds of 2D array, B
mask = idx>numel(B) | idx<1
%// Set all out-of-bounds indices to one, so that in next step
%// we could index into B in a vectorized manner and sum those up with d
idx(mask)=1
sum1 = bsxfun(#plus,B(idx),d(:).') %//'
%// Store the summations at proper places in B with masking again
B(idx(~mask)) = sum1(~mask)
Sample run -
B =
1 9 0
7 9 4
6 8 7
d =
4 9 5 8 2
B =
6 17 2
16 14 12
10 17 12
Code:
The following code adds the sums of the diagonals of A to the corresponding diagonals in the matrix B. The code works for matrices A, B of equal size, not necessarily square.
A = magic(4);
B = magic(4);
D = bsxfun(#minus, size(A,2)+(1:size(A,1)).', 1:size(A,2)); %'
sumsDiagsA = accumarray(D(:), A(:)); %// Compute sums of diagonals (your 'd')
B = B + sumsDiagsA(D); %// Add them to the matrix
Explanation:
First we build a matrix that numbers all diagonals beginning from the rightmost diagonal:
>> D = bsxfun(#minus, size(A,2)+(1:size(A,1)).', 1:size(A,2))
D =
4 3 2 1
5 4 3 2
6 5 4 3
7 6 5 4
Then we compute sumsDiagsA as the sum of the diagonals via accumarray:
sumsDiagsA = accumarray(D(:), A(:));
The variable sumsDiagsA is what you refer to as d in your code.
Now we use indexing to the vector containing the sums and add them to the matrix B:
C = B + sumsDiagsA(D);
Assuming you have already computed your vector d, you don't need the accumarray-step and all you need to do is:
D = bsxfun(#minus, size(B,2)+(1:size(B,1)).', 1:size(B,2)); %'
C = B + d(D);

Resources