This question already has answers here:
How to create a symmetric matrix where each row/column is a subset of a known vector [duplicate]
(2 answers)
How do I generate the following matrix and vector from the given input data in MATLAB?
(1 answer)
Closed 5 years ago.
I have a vectorb with 30 entries.
I want to avoid using a for loop to construct a matrix like this:
where b_i is the i-th entry of the vector b.
For example, define the vector
b = [2 6 -7 3 1 -4 -1 1 11 8 -4 9 2 0 2 -1 0 4 4 4 2 -4 2 5 1 3 2 -1 1 -2]
where I tried by using for loop is:
A = zeros(5,5);
for i = 1:5
A(i) = b(i+5);
A(i+5) = b(i+6);
A(i+10) = b(i+7);
A(i+15) = b(i+8);
A(i+20) = b(i+9);
end
The result is
Is there a faster and more general method to generate this matrix?
You can use toeplitz:
A=fliplr(toeplitz(b(10:14),b(10:-1:6))
A =
-4 -1 1 11 8
-1 1 11 8 -4
1 11 8 -4 9
11 8 -4 9 2
8 -4 9 2 0
By the way, the indices here are 6 to 14 as in your example, and not 7 to 15 as in the picture. You can change it to your preferred purpose.
Your instinct is to avoid for loops. This is a good intuition to develop as a MATLAB programmer, but it's not always the quickest option. As can be seen in my answer to a very similar question, a for loop may be the quickest way to generate this type of matrix.
Possibly the shortest method to write would use hankel
A = b(hankel(7:11, 11:15));
Output:
>> ans =
-1 1 11 8 -4
1 11 8 -4 9
11 8 -4 9 2
8 -4 9 2 0
-4 9 2 0 2
Equivalent result (but quicker processing as seen in the previously linked answer)
A = hankel(b(7:11), b(11:15));
As Adiel said, there is a difference between the indices you showed in the image of the matrix and the indices you used to create your example. This uses the former.
Not as nice as using toeplitz, but a bit clearer to see what's going on:
b = [2 6 -7 3 1 -4 -1 1 11 8 -4 9 2 0 2 -1 0 4 4 4 2 -4 2 5 1 3 2 -1 1 -2];
n = 5; % window size
% StartIdx = 4; % Starting index of your window
AddVec = repelem(1:n,5)+StartIdx; % create addition vector
IdxVec = repmat(1:n,1,n); % Initialise index vector
IdxVec = AddVec+IdxVec; % add to let the window "slide"
c = b(IdxVec); % create a new vector
d = reshape(c,n,[]) % Reshape to get the desired matrix
d =
6 -7 3 1 -4
-7 3 1 -4 -1
3 1 -4 -1 1
1 -4 -1 1 11
-4 -1 1 11 8
Note that I didn't use a starting index in my run of the matrix. Adjust that parameter according to your needs.
Related
This question already has answers here:
Sorting entire matrix according to one column in matlab
(2 answers)
Closed 4 years ago.
I have this matrix A,
A= [10 -12 4 8;
1 3 11 -2;
1 -9 8 0;
1 1 9 3].
if I use the sortrows function in Matlab B = sortrows(A). I will get this.
B = [1 -9 8 0;
1 1 9 3;
1 3 11 -2;
10 -12 4 8].
The answer I wanted is this.
B = [1 3 11 -2;
1 -9 8 0;
1 1 9 3;
10 -12 4 8]
The thing is, I want to sort my rows here but only based on column 1. if values in column 1 are same, then don't sort them according to column 2(which in genreal this function perform).
I really appreciate, if anyone can help me with this.
Thank you.
Sort the first column only with the function sort keep only the index, then use this index to sort the whole matrix.
A= [10 -12 4 8;
1 3 11 -2;
1 -9 8 0;
1 1 9 3];
[~,ind] = sort(A(:,1))
B = A(ind,:)
or simply use the second argument of the function sortrows which precise the column sorting vector:
B = sortrows(A,1)
Say I have the following matrix:
A(:,1) = [-5 -5 5 5 -5 -5 -5 -5 5 5 5 5]';
A(:,2) = [8 7 3 6 5 9 8 7 9 4 3 2 ]';
I'd like to split it into 4 groups based on the signs in the first column (i.e. everything prior to a sign flip is one group):
1) -5 -5
2) 5 5
3) -5 -5 -5 -5
4) 5 5 5 5
and the corresponding grouping in the second column would then be:
1) 8 7
2) 3 6
3) 5 9 8 7
4) 9 4 3 2
My intuition is to use
diff(A(:,1)) ~= 0
as the first step, but I am unsure how to continue from there. Any help would be appreciated, thanks!
You can use accumarray to create this cell array for you. We first need to assign a unique value to each "group" of consecutive numbers which share a sign. We can then use accumarray to place all elements in a given group into an element of a cell array.
A = cat(1, [-5 -5 5 5 -5 -5 -5 -5 5 5 5 5], [8 7 3 6 5 9 8 7 9 4 3 2 ]).';
% Compute the sign of each element: -1 for negative, 1 for positive
% Repeat first element for diff
S = sign(A([1 1:end],1));
% -1 -1 -1 1 1 -1 -1 -1 -1 1 1 1 1
% Compute element-by-element differences
D = diff(S);
% 0 0 2 0 -2 0 0 0 2 0 0 0
% Convert to a logical matrix which will make any non-zero 1 and any zero stays 0
L = logical(D);
% 0 0 1 0 1 0 0 0 1 0 0 0
% Take the cumulative sum (and add 1) to give each group of elements a unique number
subs = cumsum(L) + 1;
% 1 1 2 2 3 3 3 3 4 4 4 4
% Use this as the first input to accumarray and perform a given action on all elements in
% A(:,2) which share these values. Our action will be to convert to a cell array
result = accumarray(subs, A(:,2), [], #(x){x});
% result{1} =
% 8 7
%
% result{2} =
% 3 6
%
% result{3} =
% 5 9 8 7
%
% result{4} =
% 9 4 3 2
If we really want to reduce it so a single line we could do this.
accumarray(1 + cumsum(logical(diff(sign(A([1 1:end],1))))), A(:,2), [], #(x){x})
Use:
groupsSizes= diff([0;find(conv(A(:,1),[1,-1],'same')~=0)]);
firstGroup = mat2cell(A(:,1),groupsSizes,1);
secondGroup = mat2cell(A(:,2),groupsSizes,1);
Here's another way:
result = mat2cell(A(:), diff([0; find([diff(sign(A(:))); true])]));
This uses mat2cell to split A into pieces and put each into a cell. The length of the pieces is computed using sign to detect the sign and then diff and find to obtain the run lengths.
If I have an arbitrary n*m matrix called data and I would like to take differences of the matrix using gradually bigger steps.
The first case would have a first column equal to data(:,2)-data(:,1), the next column would be data(:,3)-data(:,2) and so on. This can be done with the following function.
data = diff(data,1,2)
Similarly I would also like to take differences based of every second column, so that the first entry would be data(:,3)-data(:,1) and the next data(:,5)-data(:,3) and so on.
This can't be done with diff, but is there any other function or method that can do it without resorting to looping?
I need to do the same thing for every n value up to 50.
Use column indexing to select the "right" columns and then use your favourite diff -
A = randi(9,4,9) %// Input array
stepsize = 2; %// Edit this for a different stepsize
out = diff(A(:,1:stepsize:end),1,2)
Output -
A =
8 9 9 8 3 2 6 8 7
2 5 5 7 5 3 9 6 3
2 7 7 2 4 1 2 4 1
6 2 1 5 4 9 9 3 7
out =
1 -6 3 1
3 0 4 -6
5 -3 -2 -1
-5 3 5 -2
I just wrote a simple wrapper function for the purpose.
function [ out ] = diffhigh( matrix, offset )
matrix_1 = matrix(:,(offset+1):size(matrix,1));
matrix_2 = matrix(:, 1:(size(matrix,1)-offset));
out = matrix_1 - matrix_2;
end
>> a
a =
3 5 1 2 4
1 2 3 4 5
1 4 5 3 2
1 2 4 3 5
2 1 5 3 4
>> diffhigh(a, 2)
ans =
-2 -3 3
2 2 2
4 -1 -3
3 1 1
3 2 -1
>> diffhigh(a, 3)
ans =
-1 -1
3 3
2 -2
2 3
1 3
Let's say I have the following 2 vectors:
a = [1 3 5 7 8 9 10 15 16];
b = [2 4 14];
Is there a function I can use so that, for every element in b, I can find the index of the closest value to that element in a without "going over" the value I'm searching for? The expected output would be:
[1 2 7]
I have found previous answers that address finding the closest value, but not the closest value without exceeding the values being searched for.
Edited: now with a one-liner:
[~,index] = max(repmat(a,numel(b),1) + 0./bsxfun(#le,a,b'), [], 2)
'#% The 0./(0 or 1) creates a NaN mask where the condition
#% isn't met, leaving only the desired values in the matrix
#% max ignores NaNs, conveniently
This isn't a built-in function but it is pretty simple (link on ideone):
a = [1 3 5 7 8 9 10 15 16];
b = [2 4 14];
c = bsxfun(#minus,b',a) #%' transpose b
c(c<0)=nan; #% discard the values in a greater than b
[~,ci] = min(c,[],2) #% min ignores nan
d = a(ci) #% if you want the actual values of a
output:
c =
1 -1 -3 -5 -6 -7 -8 -13 -14
3 1 -1 -3 -4 -5 -6 -11 -12
13 11 9 7 6 5 4 -1 -2
ci =
1
2
7
d =
1 3 10
I have this line of code in MATLAB, which sets these vectors:
x = [2 12 3 8 1 9 2; -3 -2 -1 0 1 2 3]
x =
2 12 3 8 1 9 2
-3 -2 -1 0 1 2 3
Considering the first row as points in y-plane and second row as x-axis in MATLAB plot
Now what line of code in MATLAB will take the maximum number in the first row and set at the middle(0) point in x-axis which will make it look like this
x =
9 2 2 12 3 8 1
-3 -2 -1 0 1 2 3
Please any idea is appreciated, I don't know how best to ask this question, I'm actually trying to edit a plot in MATLAB.
Code:
x = [2 12 3 8 1 9 2; -3 -2 -1 0 1 2 3];
[~,idx] = max(x(1,:));
x(1,:) = circshift(x(1,:),[0 (length(x)+1)/2-idx]);
Output:
x =
9 2 2 12 3 8 1
-3 -2 -1 0 1 2 3