sorting an array and keep the index in MATLAB - matlab

I have an array called A, it has 2 Rows and 56 columns ( as shown in the attached image)
the first row represents the values while the second row represents the index for each value.
I want to sort the first row but keep the index for each value (I tried to use Sort function but it doesnt keep the index of each value in the second row).
for example, let's suppose I have this matrix
input
x=[9 2 4 3 ;3 1 8 2 ]
i want the output looks like this
y=[2 3 4 9; 1 2 8 3 ];

There are a couple of ways to achieve this:
[y1,I] = sort(x(1,:))
y2 = x(2,I)
y = [ y1 ; y2 ]
This basically sort the first row of your data, and save the sorting index in I, and then use those index to get the 'sorted' second row, and then just join them
or
y = sortrows(x')'
The ' operator transposes the matrix, which allows you to use sortrows and then use it again to reshape your output matrix.

You can use indices of sorted elements
[S,I]=sort(x(1,:));
result = [S;x(2,I)]
The first row is sorted and indices of the sorted elements is used to order the second row.
result
2 3 4 9
1 2 8 3

You can get the indices directly from sorting the first row. These can be used as an argument in x itself:
x=[9 2 4 3 ;3 1 8 2 ];
%get the indices of the first row:
[~,indices] = sort(x(1,:))
%return x in the order "indices" for each row:
y = [x(1,indices);x(2,indices)]

Related

How to rowwise-sort a matrix containing subgrouped data

In matrix A, every column represents an output variable and every row represents a reading (6 rows in total). Every output has a certain subgroup size (groups of 3 rows). I need A's elements to be sorted in the vertical direction within every subgroup.
A = [ 1 7 4; 4 9 3; 8 5 7; 2 9 1; 7 4 4; 8 1 3];
% consecutive 3 rows is one subgroup, within which sorting is required.
B = [1 5 3; 4 7 4; 8 9 7; 2 1 1; 7 4 3; 8 9 4]; % the expected result.
I was considering something along the lines of B = splitapply(#sort,A,2), but splitapply cannot be called like that. How can I get the desired result?
Please note, the actual matrix contains 8 columns and 300 rows. An example is demonstrated above.
The easiest solution would be to reshape your data, sort, then permute it:
rps = 3; % rows per subgroup
B = permute(sort(reshape(A.',rps,size(A,2),[]),2),[2 1 3]);
The above results in a 3x3x2 arrays, which in my opinion are easier to work with, but if you want the output as in the example, you can do the following:
B = reshape(permute(sort(reshape(A.',rps,size(A,2),[]),2),[2 3 1]),size(A));
Alternatively, you are correct to think that splitapply can be useful here, but it requires a bit more work.
This command works on the sample data and should also work on your full dataset:
b = cell2mat( splitapply( #(x){sort(x,2).'}, A.', repelem( 1:size(A,1)/rps, rps ) ).' );
I'll explain what this does:
repelem( 1:size(A,1)/rps, rps ) returns a row vector of groups. The amount of groups is the total amount of rows divided by the group size. (For good measure, there should be an assertion that this is divisible with no remainder).
splitapply( #(x){sort(x,2).'}, ... since splitapply has to return a scalar object per group, it needs to be told that the output is a cell so that it can return a matrix. (This might not be the best explanation, but if you attempt to run it w/o a cell output, you will get the following error:
The function 'sort' returned a non-scalar value when applied to the 1st group of data.
To compute nonscalar values for each group, create an anonymous function to return each value in a scalar cell:
#(x1){sort(x1)}
I perform several transpose operations since this is what splitapply expects.
I used cell2mat to convert the output cells back to a numeric array.

How to sum aggregate matrix rows in the following way?

I have a matrix of size n-by-3. For some rows of this matrix, the first two columns are identical. I need to keep only one copy of these first-two-element combinations, where the third column will have the sum of 3rd column from rows with identical first-two-columns.
Here's an example of what I want to do:
M = [...
1 2 1
1 2 3
1 2 2
1 2 4
2 3 1
2 3 4
2 3 0];
The final matrix that I need is
R = [...
1 2 1+3+2+4
2 3 1+4+0];
How can this be done? I don't see how I can use the unique command for this.
You may use unique in combination with accumarray. Let's call the initial n x 3 array A:
[C, ~, ic] = unique(A(:,1:2), 'rows');
B = [C, accumarray(ic, A(:,3))];
Explanation:
unique outputs not only unique elements of array (rows in our case thanks to the argument rows), but also two arrays of indexes. The first one is the indexes of the first unique elements in A; I discard it since I don't use it. The second one can be used to reconstruct original array from the output array: A(:, 1:2) = C(ic,:).
accumarray is the generalization of histogram computation, it makes the sum of elements in 2nd argument array for each unique index in the first argument array. In your case, you make the sum over the 3rd column of the original array only.
And that's all in two simple commands!

(MATLAB) How can I copy certain multiple elements from certain rows of a matrix based upon the values of other elements in those rows?

So, I have large matrix (let's say dimensions are 160x6 and the name of the matrix is datamatrix). Next, let's say I have another matrix called datamatrix2 which has dimensions 80x2. Here's what I want to do:
find every row of datamatrix where the value in column 2 is 2 and the value in column 5 is 1,
and then take the value for column 3 and the value from column 6 of each of those rows and place them in column 1 and column 2, respectively, of datamatrix2.
So, for example:
Let's say that row 3 of datamatrix is the first row in datamatrix with a 2 in column 2 and a 1 in column 5. Let's say there is a 3.096 in column 3 of that row and a 10 in column 6 of that row. So, 3.096 would be placed in position 1,1 of data matrix2 and 10 would be placed in position 1,2 of datamatrix2.
Next, let's say that row 25 of datamatrix is the next row in datamatrix with a 2 in column 2 and a 1 in column 5. Let's say there is a 16.432 in column 3 of that row and a 15 in column 6 of that row. So, 16.432 would be placed in position 2,1 of data matrix2 and 15 would be placed in position 2,2 of datamatrix2.
This process would continue until all of the rows of datamatrix with a 2 in column 2 and a 1 in column 5 have been found.
Please let me know if anyone has any suggestions.
Mucho thanks!
G
When I follow your explanation rightly, what you want is:
index = (datamatrix(:,2) == 2) & (datamatrix(:,5) == 1);
datamatrix2 = datamatrix(index,[4,6]);
This solution uses logical indexing.index stores 1's and 0's depending on wheter the condition is fullfilled or not.
To start of with logical indexing it is easiest to start with a vector. Lets take x = [2 5 3 4 6]; and you want the first, second and fifth entry. Then x([1 2 5]) = [2 5 6]; This can also be expressed logically with x(logical([1 1 0 0 1])) = [ 2 5 6]; Notice how we have a true at every position we want.
The same can be applied when we want to access a matrix.
Lets take A = [1 2 3; 4 5 6; 7 8 9] as a sample matrix. The obvious way to access part of this matrix is something like A([1,2],[1,3]) = [1 3; 4 6]. We can now replace the index with a logical notation:
A([1,2],[1,3]) = A(logical([1 1 0]),logical([1 0 1])) = [1 3; 4 6].
Logical and vector notation can also be combined. In my code index is a logical representation of the columns we want, [4 6] is the vector notation of the rows we want. Therefore the right columns and rows will be selected.
I don't really know what exactly you are trying, but lets take a shot
First: find any value (here 6) in any column (here 2) and then take another value of the found rows from any column (here 5) (here with an example magic-matrix)
datamatrix = [magic(5) magic(5)];
myvalue = 2;
values = datamatrix(find(datamatrix(:,2)==myvalue), 5);
If you have 2 conditions for a row, then use this here
values = datamatrix(find((datamatrix(:,column1)==value1).*(datamatrix(:,column2)==value2)),column3);
if you need to get more than one value, say from two different columns, then you just need to replace column3with something like this
column3 --> [col1 col2 col3]
Summarizing, try this code here
values = datamatrix(find((datamatrix(:,2)==2).*(datamatrix(:,5)==1)),[3 6]);
datamatrix2(:,[1 2]) = values
The second statement may fail because of dimension errors. If so, you should maybe provide the datamatrix entries.

find the intersection of a cell array

I have a vector, let say as = [1 3 4] and I have 30 by 30 cell array. I want to check if the elements of vector as intersect with the elements of each cell or not? the cells may be 2D arrays of size n-by-2 (where n= 1 2 3 4,etc).
If the row in a cell has an intersection with that vector, it should return (1 or 0) & if the row has no intersection with that vector, it should return (0 or 1).
Example:
If on of the cell arrays contains [ 1 8 ;6 9] , so the output is [1; 0].
Assuming cellarr to be input cell array, see if this approach works for you -
out = cellfun(#(x) any(any(bsxfun(#eq,x,permute(as,[1 3 2])),2),3),cellarr,'un', 0)
This would give you a cell array of all those matches for each cell. You can see their values with a call to celldisp(out).
Sample run -
as =
1 3 4
-----------------------------------
cellarr{1,1} =
1 8
6 9
cellarr{2,1} =
7 4
2 3
cellarr{1,2} =
4 3
cellarr{2,2} =
7 8
2 3
7 8
-----------------------------------
out{1,1} =
1
0
out{2,1} =
1
1
out{1,2} =
1
out{2,2} =
0
1
0
Edit: If you would like to find indices that do not have any zeros corresponding to their cell values in out, that is we intend to find cells where all rows have at least one element from as, you can use this modified code, assuming ca to be the input cell array (name changed here to keep the code compact) -
mch = cellfun(#(x) all(any(any(bsxfun(#eq,x,permute(as,[1 3 2])),2),3)),ca,'un',0)
%// Find matches for all cells
mch(cellfun('isempty',ca))={false} %// take care of empty cell condition
[row_ind,col_ind] = find(cell2mat(mch)) %// row and column indices of good matches
What about this?
matrices - Calculate intersection of vector subspace by using gauss-algorithm - Mathematics Stack Exchange http://goo.gl/PXCPlH

Generate pairs of points using a nested for loop

As an example, I have a matrix [1,2,3,4,5]'. This matrix contains one column and 5 rows, and I have to generate a pair of points like (1,2),(1,3)(1,4)(1,5),(2,3)(2,4)(2,5),(3,4)(3,5)(4,5).
I have to store these values in 2 columns in a matrix. I have the following code, but it isn't quite giving me the right answer.
for s = 1:5;
for tb = (s+1):5;
if tb>s
in = sub2ind(size(pairpoints),(tb-1),1);
pairpoints(in) = s;
in = sub2ind(size(pairpoints),(tb-1),2);
pairpoints(in) = tb;
end
end
end
With this code, I got (1,2),(2,3),(3,4),(4,5). What should I do, and what is the general formula for the number of pairs?
One way, though is limited depending upon how many different elements there are to choose from, is to use nchoosek as follows
pairpoints = nchoosek([1:5],2)
pairpoints =
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
See the limitations of this function in the provided link.
An alternative is to just iterate over each element and combine it with the remaining elements in the list (assumes that all are distinct)
pairpoints = [];
data = [1:5]';
len = length(data);
for k=1:len
pairpoints = [pairpoints ; [repmat(data(k),len-k,1) data(k+1:end)]];
end
This method just concatenates each element in data with the remaining elements in the list to get the desired pairs.
Try either of the above and see what happens!
Another suggestion I can add to the mix if you don't want to rely on nchoosek is to generate an upper triangular matrix full of ones, disregarding the diagonal, and use find to generate the rows and columns of where the matrix is equal to 1. You can then concatenate both of these into a single matrix. By generating an upper triangular matrix this way, the locations of the matrix where they're equal to 1 exactly correspond to the row and column pairs that you are seeking. As such:
%// Highest value in your data
N = 5;
[rows,cols] = find(triu(ones(N),1));
pairpoints = [rows,cols]
pairPoints =
1 2
1 3
2 3
1 4
2 4
3 4
1 5
2 5
3 5
4 5
Bear in mind that this will be unsorted (i.e. not in the order that you specified in your question). If order matters to you, then use the sortrows command in MATLAB so that we can get this into the proper order that you're expecting:
pairPoints = sortrows(pairPoints)
pairPoints =
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
Take note that I specified an additional parameter to triu which denotes how much of an offset you want away from the diagonal. The default offset is 0, which includes the diagonal when you extract the upper triangular matrix. I specified 1 as the second parameter because I want to move away from the diagonal towards the right by 1 unit so I don't want to include the diagonal as part of the upper triangular decomposition.
for loop approach
If you truly desire the for loop approach, going with your model, you'll need two for loops and you need to keep track of the previous row we are at so that we can just skip over to the next column until the end using this. You can also use #GeoffHayes approach in using just a single for loop to generate your indices, but when you're new to a language, one key advice I will always give is to code for readability and not for efficiency. Once you get it working, if you have some way of measuring performance, you can then try and make the code faster and more efficient. This kind of programming is also endorsed by Jon Skeet, the resident StackOverflow ninja, and I got that from this post here.
As such, you can try this:
pairPoints = []; %// Initialize
N = 5; %// Highest value in your data
for row = 1 : N
for col = row + 1 : N
pairPoints = [pairPoints; [row col]]; %// Add row-column pair to matrix
end
end
We get the equivalent output:
pairPoints =
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
Small caveat
This method will only work if your data is enumerated from 1 to N.
Edit - August 20th, 2014
You wish to generalize this to any array of values. You also want to stick with the for loop approach. You can still keep the original for loop code there. You would simply have to add a couple more lines to index your new array. As such, supposing your data array was:
dat = [12, 45, 56, 44, 62];
You would use the pairPoints matrix and use each column to subset the data array to access your values. Also, you need to make sure your data is a column vector, or this won't work. If we didn't, we would be creating a 1D array and concatenating rows and that's not obviously what we're looking for. In other words:
dat = [12, 45, 56, 44, 62];
dat = dat(:); %// Make column vector - Important!
N = numel(dat); %// Total number of elements in your data array
pairPoints = []; %// Initialize
%// Skip if the array is empty
if (N ~= 0)
for row = 1 : N
for col = row + 1 : N
pairPoints = [pairPoints; [row col]]; %// Add row-column pair to matrix
end
end
vals = [dat(pairPoints(:,1)) dat(pairPoints(:,2))];
else
vals = [];
Take note that I have made a provision where if the array is empty, don't even bother doing any calculations. Just output an empty matrix.
We thus get:
vals =
12 45
12 56
12 44
12 62
45 56
45 44
45 62
56 44
56 62
44 62