I've two matrix a and b and I'd like to combine the rows in a way that in the first row I got no duplicate value and in the second value, columns in a and b which have the same row value get the maximum value in new matrix. i.e.
a = 1 2 3
8 2 5
b = 1 2 5 7
2 4 6 1
Desired output
c = 1 2 3 5 7
8 4 5 6 1
Any help is welcomed,please.( the case for accumulation is asked here)
Accumarray accepts functions both anonymous as well as built-in functions. It uses sum function as default. But you could change this to any in-built or anonymous functions like this:
In this case you could use max function.
in = horzcat(a,b).';
[uVal,~,idx] = unique(in(:,1));
out = [uVal,accumarray(idx,in(:,2),[],#max)].'
Based upon your previous question and looking at the help file for accumarray, which has this exact example.
[ii, ~, kk] = unique([a(1,:) b(1,:)]);
result = [ ii; accumarray(kk(:), [a(2,:) b(2,:)], [], #max).'];
The only difference is the anonymous function.
Related
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.
Would someone please show me how I can go about changing this code from an iterated to a vectorized implementation to speed up performance in Matlab? It takes approximately 8 seconds per i for i=1:20 on my machine currently.
classEachWordCount = zeros(nwords_train, nClasses);
for i=1:nClasses % (20 classes)
for j=1:nwords_train % (53975 words)
classEachWordCount(j,i) = sum(groupedXtrain{i}(groupedXtrain{i}(:,2)==j,3));
end
end
If context is helpful basically groupedXtrain is a cell of 20 matrices which represent different classes, where each class matrix has 3 columns: document#,word#,wordcount, and unequal numbers of rows (tens of thousands). I'm trying to figure out the count total of each word, for each class. So classEachWordCount should be a matrix of size 53975x20 where each row represents a different word and each column a different label. There's got to be a built-in function to assist in something like this, right?
for example groupedXtrain{1} might start off like:
doc#,word#,wordcount
1 1 3
1 2 1
1 4 3
1 5 1
1 8 2
2 2 1
2 5 4
2 6 2
As is mentioned in the comments, you can use accumarray to sum up the values in the third column for each unique value in the second column for each class
results = zeros(nwords_train, numel(groupedXtrain));
for k = 1:numel(groupedXtrain)
results(:,k) = accumarray(groupedXtrain{k}(:,2), groupedXtrain{k}(:,3), ...
[nwords_train 1], #sum);
end
idx=randperm(5)
idx=[1,3,4,2,5]
I know this works like that but I'm curious about is there anyway to get something like this.
idx=[1,3,4,2,5,5,3,2,4,1]
adding one set of array after one array
Is there any way to make that?
One vectorized way would be to create a random array of size (m,n), sort it along each row and get the argsort indices. Each row of those indices would represent a group of randperm values. Here, m would be the number of groups needed and n being the number of elements in each group.
Thus, the implementation would look something like this -
[~,idx] = sort(rand(2,5),2);
out = reshape(idx.',1,[])
Sample run -
>> [~,idx] = sort(rand(2,5),2);
>> idx
idx =
5 1 3 2 4
4 3 2 5 1
>> out = reshape(idx.',1,[])
out =
5 1 3 2 4 4 3 2 5 1
You can use the modulo operation:
n = 5 %maximum value
r = 2 %each element are repeated r times.
res = mod(randperm(r*n),n)+1
So if I have a matrix s;
s = [4;5;9;12;3]
and I want to calculate the difference between an entry and it's previous entry plus add the previous difference such that I'll get
s = [ 4 0; 5 1; 9 5; 12 8; 3 -1]
I'm quite new to matlab. I understand a for loop would be required to go through the original matrix
The second column of your result seems to be essentially cumsum(diff(s)). However, that's not "the difference between an entry and its previous entry plus the previous difference"; it's the cumulative sum of differences.
So, if what you want in the second column is the cumulative sum of differences:
result = [s [0; cumsum(diff(s))]];
In matlab you have a lot of functions for working directly with matrix, the one that feeds here is diff and cumsum please visit the matlab documentation, and the functions for concatening like horzcat or vertcat int his case manually to get what you need work like this:
>> s = [4;5;9;12;3]
s =
4
5
9
12
3
Get the vector my_cum_diff which is the difference between elements in a vector
my_cum_diff = [0; cumsum(diff(s))]
my_cum_diff = [0; cumsum(diff(s))]
my_cum_diff =
0
1
5
8
-1
finally concat the two vectors
final_s=[s my_cum_diff]
final_s =
4 0
5 1
9 5
12 8
3 -1
I have two arrays, x and y. x is the input of the function and y is the function values.
For example, x = [ 1 2 3 4 5 6 7 8 9 10], y = [ 3 6 2 4 1 6 7 0 1 8 ]. Both are the same length.
Suppose I have an another array z containing [ 2 3 8 9 10 3] (not the same length as x and y),
Is there any functions that produce the output [6 2 0 1 8 2] (return value at corresponding indices) without using for-loop through each element of array?
Thank you so much
edit1* How can I do if the numbers in the arrays are not integer?
y(z)
That's all you need......
I think that you just want:
y(z);
This will return the z'th elements of the y vector. You may want
y(x(z));
Which will return the same result in your example, since x is just the value 1 through 10.
With both of these z can contain only positive integers, and in the second case x must also contain only positive integers.
If you are using a MATLAB version newer than 2008b, you can use the containers.Map class to do what you want, even with non-integer, non-consecutive or non-numeric values:
x = [ 1 2 3 4 5 6 7 8 9 10];
y = [ 3 6 2 4 1 6 7 0 1 8 ];
z = [ 2 3 8 9 10 3];
F = containers.Map(x,y);
% for a single element:
Fz1 = F(z(1))
% for multiple elements at the same time, you need to use arrayfun
Fz = arrayfun(#(x)(F(x)),z)
The Map class actually creates a so-called hashmap, so you can map almost any value to other values (e.g. strings, cells, array, ...).
When the item is not present, it will return an error.
If you cannot use MATLAB 2008b or newer, there are three possibilities for non-integer domain values.
Use an interpolation method such as interp1. That might give false values (at values that weren't provided beforehand). You can check for that case by using ismember(z, x).
Secondly, you could invent your own scheme from non-integers to integers (e.g. if all your values are multiples of 0.5, multiply by 2) and use the solution Oli has shown.
The other solution is to use structs to mimic the behavior of a map. Then you only need a conversion from your domain values to valid field names (i.e. strings that are valid variable names in MATLAB, that may be possible by using the genvarname function).
These last two solutions are somewhat dirty and prone to errors if you don't take rounding into consideration. So I see them only as a last resort.