I would like to merge 2 vectors according their time values. This should look like this (column 1 = time, column 2 = actual value):
A =
1 234
3 121
4 456
6 6756
B =
2 435
5 90
10 365
Result:
C =
1 234
2 435
3 121
4 456
5 90
6 6756
10 365
Is there an elegant way to realize this in Matlab?
Here's an easy one-liner:
C = sortrows([A;B])
C =
1 234
2 435
3 121
4 456
5 90
6 6756
10 365
Note that this assumes that all of the time values in column 1 are unique. If this is not the case, you can use accumarray:
A =
1 234
3 121
4 456
6 6756
B =
2 435
5 90
10 365
B = [B; 1 512]
B =
2 435
5 90
10 365
1 512
C = [A;B];
D = accumarray(C(:,1),C(:,2));
U = unique(C(:,1));
E = [U,D(U)]
E =
1 746 %// 764 = 234 + 512
2 435
3 121
4 456
5 90
6 6756
10 365
First I would merge these matrices and then sort them by first column.
C = [A; B]
[Y, I] = sort(C(:,1))
C = C(I,:)
First you want to vertical concatenation:
A = [1 234; 3 121; 4 456; 6 6756];
B = [2 435; 5 90; 10 365];
C = vertcat(A,B)
Then you want to sort your answer based on the first column:
[~,inx]=sort(C(:,1));
out = C(inx,:);
>> out =
1 234
2 435
3 121
4 456
5 90
6 6756
10 365
So much more difficult than the 1 liner:
out = sortrows(C,1)
Why Matlab, why don't you have an option in sort to keep the index!
In the general case, you will need to do some form of concatenation and sorting. This is a one liner
C = sort([A,B],1);
Related
I have two matrices A (51 rows X 5100 columns) and B (51rows X 5100 columns) and I want to subtract every row of A with every row of B to obtain another matric C (2601 rows X 5100 columns). How can I have the matrix C?
You can do that by
permuting the matrices' dimensions to obtain 3D arrays of size (in your example) 51 × 1 × 5100 and 1 × 51 × 5100 respectively;
subtracting with implicit expansion, which gives an array of size 51 × 51 × 5100;
reshaping to collapse the first two dimensions into one, which gives the final 51*51 × 5100 matrix.
A = rand(51, 5100); % example matrix
B = rand(51, 5100); % example matrix, same number of columns
C = reshape(permute(A, [1 3 2]) - permute(B, [3 1 2]), [], size(A, 2));
The crux of the problem lies in getting the correct pairs of rows for both matrices. To do this, you could use the meshgrid() function to generate a matrix that varies from 1:n along its rows, and another that varies along its columns (where n is the number of rows).
For example:
mtx1 = reshape(1:9, 3, 3);
mtx2 = reshape(101:109, 3, 3);
n1 = size(mtx1, 1);
n2 = size(mtx2, 1);
[r1, r2] = meshgrid(1:n1, 1:n2);
This gives:
r1 =
1 2 3
1 2 3
1 2 3
r2 =
1 1 1
2 2 2
3 3 3
Next, flatten both r1 and r2:
f1 = r1(:)
f2 = r2(:)
Now, we have:
f1 =
1
1
1
2
2
2
3
3
3
f2 =
1
2
3
1
2
3
1
2
3
We can use f1 and f2 as the indices for our pairs of rows:
mtx1(f1, :) repeats the first row of mtx1 three times, then the second row, then the third row
mtx1(f1, :)
1 4 7
1 4 7
1 4 7
2 5 8
2 5 8
2 5 8
3 6 9
3 6 9
3 6 9
mtx2(f2, :) repeats the entire matrix mtx2 three times
mtx2(f2, :)
101 104 107
102 105 108
103 106 109
101 104 107
102 105 108
103 106 109
101 104 107
102 105 108
103 106 109
Subtract these two, and you get your pairwise difference of rows:
mtx2(f2, :) - mtx1(f1, :)
100 100 100
101 101 101
102 102 102
99 99 99
100 100 100
101 101 101
98 98 98
99 99 99
100 100 100
This also works when mtx1 and mtx2 have different row counts.
I have a matrix A made of 1 2 and 3, e.g.,
A= [ 1 2 2 1;
3 3 1 2;
...
...
1 1 2 2]
now I want to replace 1 2 3 with different values in B according to its row. e.g.,
B= [ 4 5 6;
10 20 30;
...
...
77 88 99]
I want to replace the value in A to B in each row. e.g.,
A= [ 1 2 2 1; replace '1 2 3' with '4 5 6' respectively
3 3 1 2; replace '1 2 3' with '10 20 30' respectively
...
...
1 1 2 2] replace '1 2 3' with '77 88 99' respectively
C will be the matrix with the new assignments that I want.
C= [ 4 5 5 4;
30 30 10 20;
...
...
77 77 88 88]
I can't avoid a loop in this case, here is the quickest way that I can do:
for row_i=1:size(A,1)
C(row_i,:)=B(row_i,A(row_i,:))
end
I hate loops in MATLAB, and the actual size of A and B are large, therefore wonder if anyone can reproduce it without loop will be highly appreciated!
A = [ 1 2 2 1;
3 3 1 2;
1 1 2 2];
B = [ 4 5 6;
10 20 30;
77 88 99];
C = B(sub2ind(size(col), repmat(1:size(A, 1), size(A, 2), 1).', A));
4 5 5 4
30 30 10 20
77 77 88 88
Explanation
You're using A to indicate which column of B to use. And as you said, you want the row to be the same as the row in A. So we just need a row and column index into B for each member of A.
To get the rows, we just need to create a matrix the size of A where every element in each row is equal to the row index. Also we take care to avoid any magic numbers and actually determine the proper size.
rowindex = repmat(1:size(A, 1), size(A, 2), 1).'
1 1 1 1
2 2 2 2
3 3 3 3
Great, now that we have that, we already know the columns. That is simply A!
colindex = A
1 2 2 1
3 3 1 2
1 1 2 2
Now we just need to convert these subscripts to absolute indices using sub2ind and the size of B as a reference.
indices = sub2ind(size(B), rowindex, colindex)
1 4 4 1
8 8 2 5
3 3 6 6
Now we just need to use these to index into B and assign to C.
C = B(indices)
4 5 5 4
30 30 10 20
77 77 88 88
(For my problem, I use a matrix A 4x500000. And the values of A(4,k) varies between 1 and 200).
I give here an example for a case A 4x16 and A(4,k) varies between 1 and 10.
I want first to match a name to the value from 1 to 5 (=10/2):
1 = XXY;
2 = ABC;
3 = EFG;
4 = TXG;
5 = ZPF;
My goal is to find,for a vector X, a matrix M from the matrix A:
A = [20 52 70 20 52 20 52 20 20 10 52 20 11 1 52 20
32 24 91 44 60 32 24 32 32 12 11 32 2 5 24 32
40 37 24 30 11 40 37 40 40 5 10 40 40 3 37 40
2 4 1 3 4 5 2 1 3 3 8 6 7 9 6 10]
A(4,k) takes all values between 1 and 10. These values can be repeated and they all appear on the 4th line.
20
X= 32 =A(1:3,1)=A(1:3,6)=A(1:3,8)=A(1:3,9)=A(1:3,12)=A(1:3,16)
40
A(4,1) = 2;
A(4,6) = 5;
A(4,8) = 1;
A(4,9) = 3;
A(4,12) = 6;
A(4,16) = 10;
for A(4,k) corresponding to X, I associate 2 if A(4,k)<= 5, and 1 if A(4,k)> 5. For the rest of the value of A(4,k) which do not correspond to X, I associate 0:
[ 1 2 3 4 5 %% value of the fourth line of A between 1 and 5
2 2 2 0 2
ZX = 6 7 8 9 10 %% value of the fourth line of A between 6 and 10
1 0 0 0 1
2 2 2 0 2 ] %% = max(ZX(2,k),ZX(4,k))
the ultimate goal is to find the matrix M:
M = [ 1 2 3 4 5
XXY ABC EFG TXG ZPF
2 2 2 0 2 ] %% M(3,:)=ZX(5,:)
Code -
%// Assuming A, X and names to be given to the solution
A = [20 52 70 20 52 20 52 20 20 10 52 20 11 1 52 20
32 24 91 44 60 32 24 32 32 12 11 32 2 5 24 32
40 37 24 30 11 40 37 40 40 5 10 40 40 3 37 40
2 4 1 3 4 5 2 1 3 3 8 6 7 9 6 10];
X = [20 ; 32 ; 40];
names = {'XXY','ABC','EFG','TXG','ZPF'};
limit = 10; %// The maximum limit of A(4,:). Edit this to 200 for your actual case
%// Find matching 4th row elements
matches = A(4,ismember(A(1:3,:)',X','rows'));
%// Matches are compared against all possible numbers between 1 and limit
matches_pos = ismember(1:limit,matches);
%// Finally get the line 3 results of M
vals = max(2*matches_pos(1:limit/2),matches_pos( (limit/2)+1:end ));
Output -
vals =
2 2 2 0 2
For a better way to present the results, you can use a struct -
M_struct = cell2struct(num2cell(vals),names,2)
Output -
M_struct =
XXY: 2
ABC: 2
EFG: 2
TXG: 0
ZPF: 2
For writing the results to a text file -
output_file = 'results.txt'; %// Edit if needed to be saved to a different path
fid = fopen(output_file, 'w+');
for ii=1:numel(names)
fprintf(fid, '%d %s %d\n',ii, names{ii},vals(ii));
end
fclose(fid);
Text contents of the text file would be -
1 XXY 2
2 ABC 2
3 EFG 2
4 TXG 0
5 ZPF 2
A bsxfun() based approach.
Suppose your inputs are (where N can be set to 200):
A = [20 52 70 20 52 20 52 20 20 10 52 20 11 1 52 20
32 24 91 44 60 32 24 32 32 12 11 32 2 5 24 32
40 37 24 30 11 40 37 40 40 5 10 40 40 3 37 40
2 4 1 3 4 5 2 1 3 3 8 6 7 9 6 10]
X = [20; 32; 40]
N = 10;
% Match first 3 rows and return 4th
idxA = all(bsxfun(#eq, X, A(1:3,:)));
Amatch = A(4,idxA);
% Match [1:5; 5:10] to 4th row
idxZX = ismember([1:N/2; N/2+1:N], Amatch)
idxZX =
1 1 1 0 1
1 0 0 0 1
% Return M3
M3 = max(bsxfun(#times, idxZX, [2;1]))
M3 =
2 2 2 0 2
Example, i have an array :
a = [2 3 1 2 4 5 6 4];
b = sort(a);
b = [1 2 2 3 4 4 5 6];
Now i want to combine two value of a and b :
c = [21 32 12 23 44 54 65 46]
then i do the sort of c :
d = [12 21 23 32 44 46 54 65]
and i combine again from c and d (first of c, same value of second c and first d, last d) :
e = [212 321 123 232 444 546 654 465]
then i do the sort of e :
f = [123 212 232 321 444 465 546 654]
and i combine again from e and f :
g = [2123 3212 1232 2321 4444 5465 6546 4654]
so on till the length of a that equals to 8.
Please help me.
Try this:
a = [2 3 1 2 4 5 6 4]
for m=2:8
b = sort(a)
t = round(b-10*floor(b/10))
a = 10*a+t
end
It looks to me like the algorithm just adds the last digit of each sorted list onto the corresponding number in the unsorted list. t is just the last digit in b, then 10*a+t shifts the existing digits in a and puts t at the end. Apologies if I have misunderstood the objective and this is the wrong algorithm, but it works with you example. I guess you will need to convince yourself whether the code does follow your rules.
I am new in matlab and I am not familiar with array of matrices. I have a number of matrices nx6:
<26x6 double>
<21x6 double>
<27x6 double>
<36x6 double>
<29x6 double>
<30x6 double>
....
Each matrix is of this type:
>> Matrix{1,1}
A B C D E F
1 2 6 223 735064.287500000 F11
2 3 6 223 735064.288194445 F12
3 4 6 223 735064.288888889 F13
4 5 6 223 735064.290277778 F14
>> Matrix{2,1}
A B C D E F
1 2 6 223 735064.700694445 F21
2 3 6 223 735064.701388889 F22
3 4 6 223 735064.702083333 F23
4 5 6 223 735064.702777778 F24
>> Matrix{3,1}
A B C D E F
1 2 7 86 735064.3541666666 F31
2 3 7 86 735064.3548611112 F32
3 4 7 86 735064.3555555555 F33
4 5 7 86 735064.3562499999 F34
5 6 7 86 735064.702777778 F35
>> Matrix{4,1}
A B C D E F
1 2 7 86 735064.3569444444 F41
2 3 7 86 735064.3576388888 F42
3 4 7 86 735064.3583333333 F43
4 5 7 86 735064.3590277778 F44
5 6 6 86 735064.702777778 F45
Where E and F are dates in datenum format. Specifically F is the time difference.
Considering all matrices at once, I would like to sum the values of column F among all the matrices that have equal values in columns A, B, D.
For each value of the column D (the number of bus), I would like to obtain a new matrix like the following one:
A B C D H
1 2 6 223 F11+F21
2 3 6 223 F12+F22
3 4 6 223 F13+F23
4 5 6 223 F14+F24
A B C D H
1 2 7 86 F31+F41
2 3 7 86 F32+F42
3 4 7 86 F33+F43
4 5 7 86 F34+F44
5 6 7 86 F35+F45
Thank you in advance for you help!
This approach should get you started. I suggested setting up a matrix that stores the comparison between the columns 1,2 and 4. Based on that matrix you can then generate your output matrix. This saves you nested if statements and checks in your loop.
Here's an example (please note that I changed row 3 of Matrix{1,1}):
Matrix{1,1} = [ ...
1 2 6 223 735064.287500000 1;
2 3 6 223 735064.288194445 2;
3 4 6 223 735064.288888889 3;
4 5 6 223 735064.290277778 4];
Matrix{2,1} = [ ...
1 2 6 223 735064.700694445 10;
2 3 6 223 735064.701388889 10;
2 4 6 223 735064.702083333 10;
4 5 6 223 735064.702777778 10];
COMP = Matrix{1,1}(:,[1:2 4])==Matrix{2,1}(:,[1:2 4]);
a = 1;
for i=1:size(Matrix{1,1},1)
if sum(COMP(i,:)) == 3
SUM{1,1}(a,1:5) = Matrix{1,1}(i,1:5);
SUM{1,1}(a,6) = Matrix{1,1}(i,6) + Matrix{2,1}(i,6);
a = a + 1;
end
end
The matrix COMP stores a 1 for each element that is the same in Matrix{1,1} and Matrix{2,1} when comparing columns 1, 2 and 4.
This reduces the if-statement to a check if all elements in a row agree (hence sum == 3). If that condition is satisfied, a new matrix is generated (SUM{1,1}) which sums the entries in column 6, in this case:
SUM{1,1}(:,6) =
11
12
14