Sorting array of objects by property in MATLAB? [duplicate] - arrays

This question already has an answer here:
Matlab - sort cell array of objects by property
(1 answer)
Closed 8 years ago.
I have an array of objects, each with various properties, and I would like to figure out how to run "sort" on the array, specifying a specific property of each object to sort by.
For example, say my objects are "chairs" and I want to sort by the property of numlegs (number of legs), then I would be able to run a sort function on an array of chairs, and it would sort them by the number of legs they have like "sort(chairs,numlegs)". Is there any way to do this?
Thanks!

Sorting object array
define a class
_
classdef SimpleClass
properties
id
numlegs
end
methods
function obj = SimpleClass(id,numlegs)
if nargin > 0
obj.id = id;
obj.numlegs = numlegs;
end
end
end
end
sort
_
chairs = SimpleClass.empty(20,0);
for ii = 1:20
chairs(ii) = SimpleClass(ii, randi(4,1));
end
[~, ind] = sort([chairs.numlegs]);
chairs_sorted = chairs(ind);
Output
_
>> [chairs_sorted.numlegs]
ans =
Columns 1 through 10
1 1 1 1 1 1 1 1 2 3
Columns 11 through 20
3 3 3 3 3 3 3 4 4 4
>> [chairs_sorted.id]
ans =
Columns 1 through 10
3 5 8 9 10 11 17 19 12 1
Columns 11 through 20
2 4 6 7 14 15 20 13 16 18
Sorting structure array
chairs = struct('id',num2cell(1:20), 'numlegs',num2cell(randi(4, 1, 20)));
[~, ind] = sort([chairs.numlegs]);
chairs_sorted = chairs(ind);

Related

Reverse array order in Matlab

If I have an array
>> c = 1:10
c =
1 2 3 4 5 6 7 8 9 10
How do I reverse the last five elements, so that my new array is
c =
1 2 3 4 5 10 9 8 7 6
Thank you,
Need to use array merge and flip for subarray
A = 1:10
A = [A(1:5),fliplr(A(6:10))]
You can try the following code, it uses the increment index :
d = [c(1:5), c(10:-1:6)]

Matlab delete specific elements from an array

I have for example a=[1 2 3 4 5 6 7 8 9 10]; and I have to delete each 2 following numbers from 3.
like at the end it should be a=[1 4 7 10];
How to do this without a for loop.
And also if there is a way to guarantee that at the end the resulting array will have exact number of entries, like here it should be a with 4 entries at the end.
But for example we have b=[1 2 3 4 5 6 7 8 9 ]; and if I want make sure that at the end I still have 4 entries in the rest array, so that b can't be equal to [1 4 7] because I need 4 entries for sure.
You can use indexing for this:
A = 1:10;
B = A(1:3:end)
B =
[1 4 7 10]
Or, if you really want to remove elements:
A = 1:10;
A(2:3:end) = [];
A(3:3:end) = [];
For your second question regarding length checking, it's unclear what you're asking. Would an if comparison be enough ?
if numel(A) ~= 4
% ... Handle unexpected values here
end
Best,
As you mentioned in the question and in the comments that you need 4 elements at the end and if elements are less than 4 then you want to include the last element/s of b, the following should work:-
b=[1 2 3 4 5 6 7 8 9]
b_req=b(1:3:end);
temp=length(b_req);
if temp<4 b_req(end+1:4)=b(end-3+temp:end); % for including the elements of b so that total elements are 4 at the end
elseif temp>4 b_req=b_req(1:4); % for removing the extra elements
end
b_req
Output:-
b =
1 2 3 4 5 6 7 8 9
b_req =
1 4 7 9
and
if instead b=[1 2 3 4 5 6 7 8 9 10]; then the same code gives what you require, i.e. b_req = [1 4 7 10]
This code speaks for itself:
a = 1:15; % some vector
% returns every third element after the first one:
third_elemets = a(1:3:end);
% returns the missing elements for the vector to be in size 4 from the end of a
last_elements = a(end-3+length(third_elemets):end);
% take maximum 4 elements from a
logic_ind = true(min(4,length(third_elemets)),1);
% and concatanate them with last_elements (if there are any)
a = [third_elemets(logic_ind) last_elements]
and under the assumption that whenever there are less than 4 elements you simply take the last one(s) - it should always work.

Sort and keep index of a n-dimension array -- MATLAB

I have a 12-D array and am using each dimension as an index value in an optimization problem.
A(:,:,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10)
each index value i is a value from 1 to 5.
I want to sort A from greatest to least and keep track of the indices so I know which indices correspond to to what value of A.
So my ideal output would be a 2 column cell/array with one column being the value and the other other column being the index values.
For a simple 3D example: say I have a 3D array: A(:,:,i1).
Where:
A(:,:,1) = 2
A(:,:,2) = 6
A(:,:,3) = 13
A(:,:,4) = 11
A(:,:,5) = 5
I would like my output to be:
13 3
11 4
6 2
5 5
2 1
EDIT:
assume I have 1x1x3x3 sized input such that
A(1,1,1,1) = 3
A(1,1,2,1) = 1
A(1,1,3,1) = 23
A(1,1,1,2) = 12
A(1,1,2,2) = 9
A(1,1,3,2) = 8
A(1,1,1,3) = 33
A(1,1,2,3) = 14
A(1,1,3,3) = 6
the expected output would be:
33 [1,1,1,3]
23 [1,1,3,1]
14 [1,1,2,3]
12 [1,1,1,2]
9 [1,1,2,2]
8 [1,1,3,2]
6 [1,1,3,3]
3 [1,1,1,1]
1 [1,1,2,1]
This should be a generic code for any multi-dimensional input array -
%// Sort A and get the indices
[sorted_vals,sorted_idx] = sort(A(:),'descend');
%// Set storage for indices as a cell array and then store sorted indices into it
c = cell([1 numel(size(A))]);
[c{:}] = ind2sub(size(A),sorted_idx);
%// Convert c to the requested format and concatenate with cell arary version of
%// sorted values for the desired output
out = [num2cell(sorted_vals) mat2cell([c{:}],ones(1,numel(A)),numel(size(A)))];
The generic code owes its gratitude to this fine solution.
I guess this is what you want:
b=A(:);
[sorted_b,ind]=sort(b,'descend');
[dim1,dim2,dim3,dim4]=ind2sub(size(A),ind);
%arranging in the form you want
yourCell=cell(size(b,1),2);
yourCell(:,1)=mat2cell(sorted_b,ones(size(b,1),1),1);
%arranging indices -> maybe vectorized way is there for putting values in "yourCell"
for i=1:size(b,1)
yourCell{i,2}=[dim1(i) dim2(i) dim3(i) dim4(i)];
end
For the array A, given by you, my output looks like:
33 [1,1,1,3]
23 [1,1,3,1]
14 [1,1,2,3]
12 [1,1,1,2]
9 [1,1,2,2]
8 [1,1,3,2]
6 [1,1,3,3]
3 [1,1,1,1]
1 [1,1,2,1]
which matches with your output.

Matlab: how to rank 2D array and mark the ranking in the other 2D array?

I am considering an easy algorithm to rank my 2D array and mark their rank in the same size of the 2D array.
For example, I have a matrix in below:
[0 2 15 34;
0 15 21 24;
0 3 5 8;
1 14 23 29]
The output should be as follow:
[1 5 10 16;
1 10 12 14;
1 6 7 8;
4 9 13 15]
I am kind of new to matlab, I not sure if the matlab have the functionality to directly do it. Or it would be even better if you could provide some ideas for implementing the algorithm. Thank you very much!
If I understand correctly, you want to replace each element by its rank. I offer three ways to do it; the third seems to be what you want.
Let your example data be defined as
data = [0 2 15 34;
0 15 21 24;
0 3 5 8;
1 14 23 29];
This assigns equal ranks to equal data values (as in your example), but doesn't skip ranks in that case (your example seems to do so):
[~, ~, vv] = unique(data(:));
result = reshape(vv, size(data));
With your example data, this gives
result =
1 3 8 13
1 8 9 11
1 4 5 6
2 7 10 12
This assigns different ranks to equal data values (so skipping ranks is out of the question):
[~, vv] = sort(data(:));
[~, vv] = sort(vv);
result = reshape(vv, size(data));
With your example data,
result =
1 5 11 16
2 10 12 14
3 6 7 8
4 9 13 15
This assigns equal ranks to equal data values, and in that case it skips ranks:
[~, vv] = sort(data(:));
[~, vv] = sort(vv);
[~, jj, kk] = unique(data(:), 'first');
result = reshape(vv(jj(kk)), size(data));
With your example data,
result =
1 5 10 16
1 10 12 14
1 6 7 8
4 9 13 15
Another approach, single-line: for each entry, find how many other entries are smaller, and add 1:
result = reshape(sum(bsxfun(#lt,data(:),data(:).'))+1, size(data));

Element-wise array replication according to a count [duplicate]

This question already has answers here:
Repeat copies of array elements: Run-length decoding in MATLAB
(5 answers)
Closed 8 years ago.
My question is similar to this one, but I would like to replicate each element according to a count specified in a second array of the same size.
An example of this, say I had an array v = [3 1 9 4], I want to use rep = [2 3 1 5] to replicate the first element 2 times, the second three times, and so on to get [3 3 1 1 1 9 4 4 4 4 4].
So far I'm using a simple loop to get the job done. This is what I started with:
vv = [];
for i=1:numel(v)
vv = [vv repmat(v(i),1,rep(i))];
end
I managed to improve by preallocating space:
vv = zeros(1,sum(rep));
c = cumsum([1 rep]);
for i=1:numel(v)
vv(c(i):c(i)+rep(i)-1) = repmat(v(i),1,rep(i));
end
However I still feel there has to be a more clever way to do this... Thanks
Here's one way I like to accomplish this:
>> index = zeros(1,sum(rep));
>> index(cumsum([1 rep(1:end-1)])) = 1;
index =
1 0 1 0 0 1 1 0 0 0 0
>> index = cumsum(index)
index =
1 1 2 2 2 3 4 4 4 4 4
>> vv = v(index)
vv =
3 3 1 1 1 9 4 4 4 4 4
This works by first creating an index vector of zeroes the same length as the final count of all the values. By performing a cumulative sum of the rep vector with the last element removed and a 1 placed at the start, I get a vector of indices into index showing where the groups of replicated values will begin. These points are marked with ones. When a cumulative sum is performed on index, I get a final index vector that I can use to index into v to create the vector of heterogeneously-replicated values.
To add to the list of possible solutions, consider this one:
vv = cellfun(#(a,b)repmat(a,1,b), num2cell(v), num2cell(rep), 'UniformOutput',0);
vv = [vv{:}];
This is much slower than the one by gnovice..
What you are trying to do is to run-length decode. A high level reliable/vectorized utility is the FEX submission rude():
% example inputs
counts = [2, 3, 1];
values = [24,3,30];
the result
rude(counts, values)
ans =
24 24 3 3 3 30
Note that this function performs the opposite operation as well, i.e. run-length encodes a vector or in other words returns values and the corresponding counts.
accumarray function can be used to make the code work if zeros exit in rep array
function vv = repeatElements(v, rep)
index = accumarray(cumsum(rep)'+1, 1);
vv = v(cumsum(index(1:end-1))+1);
end
This works similar to solution of gnovice, except that indices are accumulated instead being assigned to 1. This allows to skip some indices (3 and 6 in the example below) and remove corresponding elements from the output.
>> v = [3 1 42 9 4 42];
>> rep = [2 3 0 1 5 0];
>> index = accumarray(cumsum(rep)'+1, 1)'
index =
0 0 1 0 0 2 1 0 0 0 0 2
>> cumsum(index(1:end-1))+1
ans =
1 1 2 2 2 4 5 5 5 5 5
>> vv = v(cumsum(index(1:end-1))+1)
vv =
3 3 1 1 1 9 4 4 4 4 4

Resources