Merge array rows based on the first digit in a column - matlab

I have two arrays, A and B. The first digit of each row is the serial number.
How do I combine A and B to have an array C, such that all rows in A with the same serial number in B are concatenated horizontally?
A = [ 12345;
47542;
32673;
65436;
75343;
23496;
54765 ]
B = [ 23566;
33425;
65438;
75354 ]
y = ismember(A(:,1), B(:,1), 'rows');
t=find(y);
C= [A(t,1:12),B(t,1:12)];
I need C to be:
C = [ 12345, 00000;
23496, 23566;
32673, 33425;
47542, 00000;
54765, 00000;
65436, 00000;
75343, 75354]

My approach would be the following, extract the leading digits of both arrays and compare those:
a=num2str(A)-'0';
b=num2str(B)-'0';
[ida,idb]=ismember(a(:,1),b(:,1));
Now get the sorting index of A
[~,ids]=sort(a(:,1));
Create output array
C=zeros(size(A,1),2);
Finally assign and sort output
C(:,1)=A;
C(ida,2)=B(idb(idb>0));
%sort result
C=C(ids,:)

If it's only the first digit, we only need to check if the first digit (i.e. floor(A/1e4)) matches 0 to 9, and index accordingly...
% Add some zeros at the front to make indexing work with the unmatched ismember outputs
Az = [zero(; A]; Bz = [0; B];
% Find the indices for 0 to 9 within the first digits of A and B
[~,ia] = ismember( 0:9, floor( A/1e4 ) );
[~,ib] = ismember( 0:9, floor( B/1e4 ) );
% Assign to C and discard unmatched rows
C = [Az(ia+1), Bz(ib+1)];
C( all( C==0, 2 ), : ) = [];
Note that keeping things numeric with the floor operation should always be preferable to flipping between numeric and character data with things like num2str...
Edit
You changed the scope of the question by commenting with new data. Here is the same method, written to be more generic so it handles A and B with more columns and different magnitude IDs
% Add some zeros at the front to make indexing work with the unmatched ismember outputs
Az = [zeros(1,size(A,2)); A]; Bz = [zeros(1,size(A,2)); B];
% Function for getting first digit
f = #(x) floor(x./(10.^floor(log10(x))));
% Find the indices for 0 to 9 within the first digits of A and B
[~,ia] = ismember( 0:9, f(A(:,1)) );
[~,ib] = ismember( 0:9, f(B(:,1)) );
% Assign to C and discard unmatched rows
C = [Az(ia+1,:), Bz(ib+1,:)];
C( all( C==0, 2 ), : ) = [];

First of all, the whole script. At first glance, I couldn't find a solution without using loops.
A = [ 12345;
47542;
32673;
65436;
75343;
23496;
54765; ]
B = [ 23566;
33425;
65438;
75354; ]
A = sort(A); % Sort A and B.
B = sort(B);
A_str = int2str(A); % Convert integers to chars.
B_str = int2str(B);
A_sn = A_str(:, 1); % Extract first columns.
B_sn = B_str(:, 1); % Basically, these are the serial numbers.
C = zeros(size(A, 1), size(A, 2) * 2); % Initialize C.
C(:, 1) = A; % First column of C is just A.
for i = 1:length(A_sn) % For all serial numbers in A...
for j = 1:length(B_sn) % For all serial numbers in B...
if (A_sn(i) == B_sn(j)) % Check if serial number in B equals the serial number in A.
C(i, 2) = B(j); % If so, set i-th row in C to the corresponding value in B.
end
end
end
C
Results in:
A =
12345
47542
32673
65436
75343
23496
54765
B =
23566
33425
65438
75354
C =
12345 0
23496 23566
32673 33425
47542 0
54765 0
65436 65438
75343 75354

Related

Merging and adding two column vectors in a specific way in MATLAB

I have two column vectors "b" and "c"
b = [0;1;0;1;1;3;1;2;0;1]
c = [0.25; 0.21; 0.33; 0.22;0.24]
I need the output vector
output = [0; 0.25; 0.25; 0.46; 0.79;0.79; 1.01; 1.01; 1.01; 1.25]
Whenever there is 0 or any number (except 1) in index position 1 of b, it will assign 0 to output vector.
Whenever it finds the number 1 for the first time in b (in our case at 2nd index of b), it will take 0.25 from c and assign to the output vector.
It will retain the same number in other index positions of output vector until there is zero or any number (except 1) in b, and whenever it finds the 1 in b, it will take second index of c and will add like (0.25+0.21 = 0.46).
Again it found the 1 at index position 5 of b, so it will take 3rd index element of c and add like (0.46+0.33). The process continues.
The size of the b and output vector are same...
Also, the number of 1s in b are equal to the number of elements in c
Also, this is just an example, the actual size of the column vector "b'' will be like 400x1. So the suggested answer using loop indexing will be good enough.
You can write this logic as follows in a loop, see the comments for details:
% Define inputs
b = [0;1;0;1;1;3;1;2;0;1];
c = [0.25; 0.21; 0.33; 0.22;0.24];
% Create helper arrays
output = zeros(size(b)); % Output defaults to [0, 0, ...]
cSum = cumsum(c); % Cumulative sum of "c"
cIdx = 0; % Current index in "cSum" for output
% Loop over the elements in "b"
for ii = 1:numel(b)
if b(ii) == 1
% If b(ii)=1 then we want to move to the next element in "cSum"
cIdx = cIdx + 1;
end
if cIdx > 0
% If there has been at least one "1" in b so far then the output
% should be taken from "cSum"
output(ii) = cSum(cIdx);
end
end
Alternatively, you could do this without a loop, but with the same underlying logic. Using cumsum(b==1) gives you the index for which element of c you want to use for the output. This gives the same result:
bIdx = cumsum(b==1)+1;
cSum = [0; cumsum(c)];
output = cSum(bIdx);
In your example, these both gives this as desired:
output = [0; 0.25; 0.25; 0.46; 0.79;0.79; 1.01; 1.01; 1.01; 1.25]
Both of these assume the number of 1s in b is less than or equal to the number of elements in c, or they will error.

How to combine two matrices column wise to strings separated by comma?

I have two matrices
A = [ 1 3
4 3]
B = [ 2 1
4 1 ]
I want to combine A and B to produce the string array
C = [ "1,2" "3,1"
"4,1" "3,1" ]
How can I do this in MATLAB? I tried it this way
for i = 1: 4;
for j = 1: 4;
fprintf('%0.2f,%0.2f\n',A(i,j),B(i,j) )
end
end
Appreciate your suggestions !
A = [1 3; 4 3];
B = [2 1; 4 1];
C = A + "," + B
C =
% 2×2 string array
% "1,2" "3,1"
% "4,4" "3,1"
The first thing to note, is that there is a difference between strings "string" and character arrays 'character array'. Whereas strings are one entity, the character array is an array of characters.
Thus you can make the following assignment
A(1) = "Hello";
but not
B(1) = 'Hello';
because the B(1) is one value, and 'Hello' is 5 values (H,e,l,l,o).
Secondly, you cannot use fprintf as you suggest in the comments as it only prints (as in its name) and the variable returned by fprintf is the number of characters printed. Instead, to construct the string use strcat together with num2str, such that you get:
A = rand(2); %some matrices
B = rand(2);
for i = 1:2
for j = 1:2
C(i,j) = strcat(num2str(A(i,j)),",",num2str(B(i,j)));
end
end
EDIT: If you are anyway going to interchange the comma for \pm in LaTeX, you can just do it when constructing C by using
C(i,j) = strcat(num2str(A(i,j)),"\pm",num2str(B(i,j)))
instead.

Multidimensional data storage and interpolation

I have a function (so to speak, i actually have data with this characteristic) with one variable x and several parameters a, b and c, so y = f(x, a, b, c).
Now i want to interpolate within families of parameters (for example for variations of a).
I'm currently doing this for data with one parameter (here, y is the data matrix)
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1]; % parameter
for i = 1:length(a)
y(:, i) = x.^2 + a(i);
end
% interpolate:
yi = interp1(a, y.', 0.5);
This works fine, but how do i expand this to more dimensions?
My current data format is like this: Each column of my data matrix represents one specific set of parameters, so for example:
0 0 0 0
1 1 1 1
2 2 2 2
3 3 3 3
where the first column denotes a = 0, b = 0, the second a = 1, b = 0, the third a = 0, b = 1 and the last a = 1, b = 1 (values are just for clarification, this is not on purpose binary. Also, the data columns are obviously not the same).
This data format is just the consequence of my data aquisition scheme, but i'm happy to change this into something more useful. Whatever works.
Works well for me:
% generate variable and data
x = linspace(0, 1, 100);
a = [0, 1, 2]; % parameter
b = [3, 4, 5]; % parameter
c = [6, 7, 8]; % parameter
% Create grid
[X,A,B,C]=ndgrid(x,a,b,c);
% define function
foo = #(x,p1,p2,p3) p1.*x.^2 + p2.*x + p3;
% evaluate function
Y = foo(X,A,B,C);
% interpolate:
yi = interpn(X,A,B,C,Y,x,1,4,6);
#zlon's answer works fine for the interpolation part, here i want to show how to convert the data from the format i provided to the needed format for the interpolation.
The two-dimensional matrix must be transformed into a N-dimensional one. Since the columns are not necessarily in order, we need to find the right ones. This is what i did:
First, we need to know the parameter set of each column:
a = [ 2, 2, 1, 0, 0, 1 ];
b = [ 1, 0, 0, 1, 0, 1 ];
These vectors length match the number of columns in the data matrix. The first column for example now contains the data for a = 2 and b = 1.
Now we can generate the new table:
A = -Inf;
i = 1;
while true
A = min(a(a > A)); % find next a
if isempty(A)
break
end
idxa = find(a == A); % store possible indices
B = -Inf;
j = 1;
while true
B = min(b(b > B))); % find next b
if isempty(B)
break
end
idxb = find(b == B); % store possible indices
% combine both indices
idx = intersect(idxa, idxb);
% save column in new data table
data(:, i, j) = olddata(:, idx);
% advance
j = j + 1;
end
i = i + 1;
end

Exclude value by the calculation

I have an array A (I have written so as to make it similar to the matrix that I am using) :
%%%%%%%%%%%%% This is Matrix %%%%%%%%%%%%%%%%%%%%
a = 3; b = 240; c = 10; d = 30; e = 1;
mtx1 = a.*rand(30,1) + a;
mtx2 = round((b-c).*rand(30,1));
mtx3 = round((d-e).*rand(30,1));
mtx4 = -9999.*ones(30,1);
A = [mtx1 mtx2 mtx3 mtx4];
for i = 10:12
for ii = 17 :19
A(i,:)= -9999;
A(ii,:)= 999;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
I would calculate some statistical values, excluding from the calculation the values **-9999 and 999.
the statistical values must be calculated with respect to each column.
the columns represent respectively: the wind speed, direction, and
other parameters
I wrote a code but it is not correct
[nr,ncc]=size(A);
for i=1:ncc
B = A(:,i); %// Temp Vector
Oup=1; Odw=1; %// for Vector Control
while Oup>0 %// || Odw>0 % Oup>0 OR Odw>0 , Oup>0 && (AND) Odw>0
B=sort(B,'descend');
U = find(B<999 & B>-9999); % find for each column of the temp
%vector
Oup = length(U); % Calculates the length
B(U)=[]; % Delete values -9999 and 9999
end
% calculates parameters with the vector temp
count(i)=length(B);
med(i)=mean(B);
devst(i)=std(B);
mediana(i)=median(B);
vari(i)=var(B);
kurt(i)=kurtosis(B);
Asimm(i)=skewness(B);
Interv(i)=range(B);
Mass(i)=max(B);
Mini(i)=min(B);
if length(B)<nr
B(length(B)+1:nr)=nan;
end
C(:,i)=B(:); %//reconstruction of the original matrix
end
would you have any suggestions?
If your data set is in A, and you want to operate on it with a function f, just use logical indexing, i.e.:
f(A( ~(A==999 & A==-9999) )) =...
Alternatively, use find and linear indexing:
ind = find( ~(A==999 & A==-9999) );
f(A(ind)) = ....

Matlab if loop function, How to improve this function?

I am new to matlab and I am trying to figure how to write the following function in a smart/efficient way.
First I create a matrix y with entries HL, and every line is a permutation of a predefined length n.
For example, for n=3 I get the first line of the matrix y : H H H. Then I want for each line, to create a matrix of size n x n where for example the entry (1,2) corresponds to a variable which links entry y(1,1) with y(1,2) , and entry y(3,2) corresponds to a variable which links entry y(1,3) with y(1,2). Is this possible?
function A1=mystupidfunction(s , n)
%n is the number of agents and s is the number of state, this function
%returns the matrix of influence. Stupid code must be improved to work for
%n agents
x = 'HL'; %// Set of possible types
K = n; %// Length of each permutation
%// Create all possible permutations (with repetition) of letters stored in x
C = cell(K, 1); %// Preallocate a cell array
[C{:}] = ndgrid(x);
y = cellfun(#(x){x(:)}, C);
y = [y{:}];
A1 = sym('A1',[n n], 'positive' );
syms H L A_HH A_HL A_LH A_LL
for k=s
for i=1:n
for j=1:n
if ( y(k,1)==H) && ( y(k,2)==H) && (y(k,3)==H)
A1(i,j)=A_HH
elseif ( y(k,1)==L) && ( y(k,2)==L) && (y(k,3)==L)
A1(i,j)=A_LL
elseif ( y(k,1)==H) && ( y(k,2)==L) && (y(k,3)==L)
A1(1,1)=A_HH
A1(1,2)=A_HL
A1(1,3)=A_HL
A1(2,1)=A_LH
A1(3,1)=A_LH
A1(2,2)=A_LL
A1(2,3)=A_LL
A1(3,3)=A_LL
A1(3,2)=A_LL
elseif ( y(k,1)==H) && ( y(k,2)==H) && (y(k,3)==L)
A1(1,1)=A_HH
A1(1,2)=A_HH
A1(1,3)=A_HL
A1(2,1)=A_HH
A1(3,1)=A_LH
A1(2,2)=A_HH
A1(2,3)=A_HL
A1(3,3)=A_LL
A1(3,2)=A_LH
elseif ( y(k,1)==L) && ( y(k,2)==L) && (y(k,3)==H)
A1(1,1)=A_LL
A1(1,2)=A_LL
A1(1,3)=A_LH
A1(2,1)=A_LL
A1(3,1)=A_LH
A1(2,2)=A_LL
A1(2,3)=A_LH
A1(3,3)=A_HH
A1(3,2)=A_HL
elseif ( y(k,1)==L) && ( y(k,2)==H) && (y(k,3)==H)
A1(1,1)=A_LL
A1(1,2)=A_LH
A1(1,3)=A_LH
A1(2,1)=A_HL
A1(3,1)=A_HL
A1(2,2)=A_HH
A1(2,3)=A_HH
A1(3,3)=A_HH
A1(3,2)=A_HH
elseif ( y(k,1)==L) && ( y(k,2)==H) && (y(k,3)==L)
A1(1,1)=A_LL
A1(1,2)=A_LH
A1(1,3)=A_LL
A1(2,1)=A_HL
A1(3,1)=A_LL
A1(2,2)=A_HH
A1(2,3)=A_HL
A1(3,3)=A_LL
A1(3,2)=A_LH
elseif ( y(k,1)==H) && ( y(k,2)==L) && (y(k,3)==H)
A1(1,1)=A_HH
A1(1,2)=A_HL
A1(1,3)=A_HH
A1(2,1)=A_LH
A1(3,1)=A_HH
A1(2,2)=A_LL
A1(2,3)=A_HL
A1(3,3)=A_HH
A1(3,2)=A_HL
else A(i,j)=0
end
end
end
end
For example when n=3 and s=1, then the function returns:
A =
[ A_HH, A_HH, A_HH]
[ A_HH, A_HH, A_HH]
[ A_HH, A_HH, A_HH]
notes:
C = cell(K, 1); %// Preallocate a cell array
[C{:}] = ndgrid(x); %// Create K grids of values
y = cellfun(#(x){x(:)}, C); %// Convert grids to column vectors
y = [y{:}];
the output is for n=3 :
y =
HHH
LHH
HLH
LLH
HHL
LHL
HLL
LLL
s is just a scalar that indicates the number of line in the matrix y (which corresponds to a "state")
if you only have 2 states H and L, as you said in comment you could just as well use 0 and 1, so a binary logic.
To get your y combinations, just count in binary until you have the right number of digit and convert each bit to the state you decide:
So for order n=3 you would have:
n = 3 ;
a = de2bi( (0:2^n-1).' )
which gives you all 3 digits binary combinations:
a =
0 0 0
1 0 0
0 1 0
1 1 0
0 0 1
1 0 1
0 1 1
1 1 1
If you want them in ascii format, you can then convert them:
b = char( ones(size(a))* 'L') ;
b(~a) = 'H' ;
To obtain:
b =
HHH
LHH
HLH
LLH
HHL
LHL
HLL
LLL
So if you want to keep a "ascii" character logic (and output), this could do the job:
function out = combiadjacent( k , n )
yb = de2bi( (0:2^n-1).' ) ; %'// get the combinations
y = char( ones(size(yb))* 'L') ;
y(~yb) = 'H' ;
A = char( zeros(n,2*n) ) ;
for col=1:n
Acol = [col col+1] + (col-1) ;
A(:,Acol) = [y(k,col)*ones(n,1) y(k,:).'] ;
end
out = reshape( cellstr( reshape(A.',2,[]).' ) , n , n ).' ;
Will give you a cell array as output like:
>> A = combiadjacent( 4 , 3 )
A =
'LL' 'LL' 'HL'
'LL' 'LL' 'HL'
'LH' 'LH' 'HH'
However, if you are not picky about the exact output format, the solution below will probably run significantly faster:
function out = combiadjacent( k , n )
y = de2bi( (0:2^n-1).' ) ; %'// get the combinations
b = ones(n,1)*y(k,:) ;
%// use that to get a cell array of characters
out = cellfun( #(a,b) char(cat(2,a+65,b+65)) , num2cell(b) ,num2cell(b.'),'uni',0) ;
%'// or use that to get a cell array of double in output (even faster)
%// out = cellfun( #(a,b) cat(2,a,b) , num2cell(b) ,num2cell(b.'),'uni',0)
%// or that to get a cell array of boolean
%// out = cellfun( #(a,b) logical(cat(2,a,b)) , num2cell(b) ,num2cell(b.'),'uni',0) ;
Which will give you
>> A = combiadjacent( 4 , 3 )
A =
'BB' 'BB' 'AB'
'BB' 'BB' 'AB'
'BA' 'BA' 'AA'
note: Whatever solution you choose, it would be prudent to add a checking condition for k to make sure the user will not request a line which doesn't exist.