Referencing value of an array based on the values of another - matlab

I have an array d = [0,1000,2000]. Based on this array, I compute the value of another array, let's say J = [0,5000,8000], where J = 3*d+2000.
Now, during iteration using a for loop, if d=0, I want to extract value of J(1); if d = 1000, then I want to extract values of J(1) and J(2); if d = 2000, I want to extract values of J(1),J(2) and J(3). What would be a generic way to cross-reference values of two arrays?

Assuming your values in d are unique and the length of d and J are identical, use MATLAB's find function, especially using this version with n = 1, and direction = 'first', like so:
idx = find(d == ii, 1, 'first')
Here, ii is the desired value, e.g. 0, 1000, and so on. You'll get the index of the (first) occurrence of the desired value in d. Then, just extract all values from J from 1 to idx:
J(1:idx)
Here's a small test script:
d = [0, 1000, 2000]
J = 3 * d + 2000
% If your counter variable only contains valid values from d
for ii = d
idx = find(d == ii, 1, 'first');
printf('d = %d\n', ii);
printf('Extracted values are: ');
printf('%d ', J(1:idx));
printf('\n \n');
end
% If your counter variable may contain any value
for ii = 0:2000
idx = find(d == ii, 1, 'first');
if (~isempty(idx))
printf('d = %d\n', ii);
printf('Extracted values are: ');
printf('%d ', J(1:idx));
printf('\n \n');
end
end
And, the output looks like this:
d =
0 1000 2000
J =
2000 5000 8000
d = 0
Extracted values are: 2000
d = 1000
Extracted values are: 2000 5000
d = 2000
Extracted values are: 2000 5000 8000
d = 0
Extracted values are: 2000
d = 1000
Extracted values are: 2000 5000
d = 2000
Extracted values are: 2000 5000 8000
Hope that helps!
P.S. I haven't thought about vectorization here, since the question states, that a for loop is used in any case.

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.

Merge array rows based on the first digit in a column

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

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)) = ....

Have to convert Integer to binary

I'm writing a user-defined function to convert integers to binary. The largest number that could be converted with the function should be a binary number with
16 1 s. If a larger number is entered as d, the function should display an error
message. With my code, I'm trying to add the numbers 0 or 1 to my vector x based on the remainder, then I want to reverse my final vector to display a number in binary. Here's what I have:
function [b] = bina(d)
% Bina is a function that converts integers to binary
x = [];
y = 2;
in = d/2;
if d >=(2^16 -1)
fprintf('This number is too big')
else
while in > 1
if in >= 1
r = rem(in,y);
x = [x r]
end
end
end
end
As you insist on a loop:
x = [];
y = 2;
in = d;
if d >=(2^16 -1)
fprintf('This number is too big')
else
ii = 1;
while in > 0
r = logical(rem(in,y^ii));
x = [r x];
in = in - r*2^(ii-1);
ii = ii+1;
end
end
b = x;
You had the right ideas, but you need to update the variables in your while-loop with every iteration. This is mainly in, where you need to subtract the remainder. And just store the binary remainders in your variable x.
You can check your result with
x = double( dec2bin(d, 16) ) - 48
You could also use a for loop, by pre-calculating the number of iterations with
find( d < 2.^(1:16),1)
and then
if d >=(2^16 -1)
fprintf('This number is too big')
else
for ii = 1:find( d < 2.^(1:16),1)
r = logical(rem(in,y^ii));
x = [r x];
in = in - r*2^(ii-1)
end
end