Storing the digits after the comma in Matlab - matlab

I have a double between 0 and 1 stored in an array in Matlab B.
I want to create a vector t storing the N digits after the comma. If the digits after the comma are <N, then the corresponding element in the vector t should be 0.
Suppose N=10 and B=[0.908789]. Then,
t=[9;0;8;7;8;9;0;0;0;0];
This is the code I am using at the moment
n = fix(rem(B,1)*10^N);
s1 = sprintf('%.0f',n);
ttemp = (s1-'0')';
t=zeros(N,1);
t(1:size(ttemp,1))=ttemp;
but it gives me wrong results.
Indeed, suppose
B=[7.0261e-05] and N=5. The code above gives me
t=[7;0;0;0] without recognising that there e-05.
Any suggestion on how to fix this?

You need to tell sprintf that you'd like all leading 0's to actually be shown if there are fewer than N digits:
Your current way:
sprintf('%.0f', n);
% '7'
The correct way:
s1 = sprintf('%05.f', n);
% '00007'
The general example for any N would be:
s1 = sprintf(['%0', num2str(N), '.f'],n);
The way that you currently have it written, the outpuf of the sprintf command is simply a '7' which when you fill in your output starting at the beginning yields a 7 followed by all 0's (the value you initialized the output to).
If we initialize it to NaN values instead of 0's you can see what the issue is
N = 5;
B = 7.0261e-05;
n = fix(rem(B,1)*10^N);
% 7
s1 = sprintf('%.0f',n);
% '7'
ttemp = (s1 - '0').';
% 7
t = nan(N, 1);
% NaN NaN NaN NaN NaN
t(1:size(ttemp,1)) = ttemp;
% 7 NaN NaN NaN NaN
Alternately, you can keep everything you have and just modify t from the end rather than the beginning
t = zeros(N, 1);
t((end-numel(ttemp)+1):end) = ttemp;
Unsolicited Pointers
' is not the transpose, .' is.
Use numel to determine the number of elements in a vector rather than size since it will work for both row and column vectors

Related

MATLAB: Replace leading zeros of every column with NaN

I have a 3D matrix called mat. Every column may or may not comprise a variable number of leading zeros. I need to replace them with NaNs. It's important to recognize that there might follow even more zeros in any column after the occurence of the first non-zero elements. That is, just indexing ALL zeros in the matrix and replacing them with NaN won't lead to the correct result.
I do have a working solution. However, it contains two for-loops. I am wondering whether it's possible to vectorize and get rid of the loop. In reality, mat could be very big, something like 10000x15x10000. Therefore, I am quite sensitive to execution speed.
Here's my toy example:
% Create test matrix
mat = randi(100,20,5,2);
mat(1:5,1,1) = 0;
mat(1:7,2,1) = 0;
mat(1:3,4,1) = 0;
mat(1:10,5,1) = 0;
mat(1:2,1,2) = 0;
mat(1:3,3,2) = 0;
mat(1:7,4,2) = 0;
mat(1:4,5,2) = 0;
% Find first non-zero element in every column
[~, firstNonZero] = max( mat ~= 0 );
% Replace leading zeros with NaN
% How to vectorize this part???
[nRows, nCols, nPlanes] = size(mat);
for j = 1 : nPlanes
for i = 1 : nCols
mat(1:firstNonZero(1, i, j)-1, i, j) = NaN;
end
end
You could use cumsum to create a cumulative sum down each column, then all leading zeros have a cumulative sum of zero whilst all intermediate zeros have a cumulative sum greater than zero...
mat( cumsum(mat,1) == 0 ) = NaN;
As suggested in the comments, if your mat has negative values then there's a chance the cumulative sum will be 0 later on... use the sum of absolute values instead
mat( cumsum(abs(mat),1) == 0 ) = NaN;
Note that by default, cumsum operates along the first non-singleton dimension, you can use the optional dim argument to specify the dimension. I've used dim=1 to enforce column-wise operation in case your mat could be of height 1, but this is the default for any matrix with height greater than 1.
Note this uses == for comparison, you may want to read Why is 24.0000 not equal to 24.0000 in MATLAB? and use a threshold for your equality comparison.

How to find the nearest match for an integer in a given matrix?

I have two matrices. Matrix A(2048,64) and matrix B(10000,64). Values in each element of these matrices is a binary bit, so each row is a representation of a 64-bit binary value, so each row of the matrix has a magnitude between 2^63 and 2^0; Most Significant Bit to Least Significant Bit, respectively.
Problem:
For each row of A I want to find the value in B which is the closest to it in an absolute, numeric sense.
Consider A(i,1:64) being a binary representation of decimal value Xi, and B(j,1:64) a binary representation of decimal value Yj. So at the first step I want to find the best j such that X1 or A(1,1:64) has the closest numeric value to the element at Yj, i.e. abs(X1-Yj) is minimized among all possible values for j.
The below image, brought from here, describes my problem rather well, but the difference is that each of my values are contained in a row of a matrix containing 64 elements.
I tried to convert the 64-bit values to decimal, however dec2bin supports values up to 56-bit only.
You can divide your 64-bit number into two 32-bit pieces, b1 and b2, convert them to decimal values d1 and d2, then combine them into a uint64 value that has enough precision to hold the result.
bin2uint64 = #(b) uint64(bin2dec(b(:,1:32)))*(2^32) + uint64(bin2dec(b(:,33:64)));
(This assumes that you have your data in the same format required by bin2dec, i.e. a vector of char. If you have a vector of numeric values, just add in a b = char(b+'0');)
Given an initial value
>> b = 1100110010111100101101111010100010101010010011010010000110011010
>> d = bin2uint64(b)
d = 14752868414398472602
>> r = dec2bin(d, 64)
r = 1100110010111100101101111010100010101010010011010010000110011010
>> any(b-r)
ans = 0
Since b-r gives all zeros, the values are identical. You can pass the entire nx64 matrix as b and it will convert all of the values at once.
>> bin2uint64(char(randi([0 1], 20, 64) + '0'))
ans =
4169100589409210726
8883634060077187622
15399652840620725530
12845470998093501747
14561257795005665153
1133198980289431407
13360302497937328511
563773644115232568
8825360015701340662
2543400693478304607
11786523850513558107
8569436845019332309
2720129551425231323
5937260866696745014
4974981393428261150
16646060326132661642
5943867124784820058
2385960312431811974
13146819635569970159
6273342847731389380
You'll notice that I manually converted my random array to char. Assuming your input is numeric, you'll have to convert it first:
Achar = char(A + '0');
Yes, this is a pain, MATLAB should have included a destination type parameter in bin2dec, but they didn't. Now you can use your linked solution to find the matchings.
Converting your values:
Assuming your matrices A and B contain the numeric values 0 and 1, you can easily convert the rows to uint64 data types without precision loss using the bitset and sum functions (and bsxfun for a small efficiency boost):
result = sum(bsxfun(#(bit, V) bitset(uint64(0), bit, V), 64:-1:1, A), 2, 'native');
Compared to the solution from beaker, this one is over 4 times faster for a 10,000 row matrix:
% Sample data:
A = randi([0 1], 10000, 64);
% Test functions:
bin2uint64 = #(b) uint64(bin2dec(b(:,1:32)))*(2^32) + uint64(bin2dec(b(:,33:64)));
beaker_fcn = #(A) bin2uint64(char(A+'0'));
gnovice_fcn = #(A) sum(bsxfun(#(b, V) bitset(uint64(0), b, V), 64:-1:1, A), 2, 'native');
% Accuracy test:
isMatch = isequal(beaker_fcn(A), gnovice_fcn(A)); % Return "true"
% Timing:
timeit(#() beaker_fcn(A))
ans =
0.022865378234183
timeit(#() gnovice_fcn(A))
ans =
0.005434031911843
Computing nearest matches:
You provide a link to some solutions for finding the nearest matches for A in B. However, the fact that you are using unsigned integer types requires some modification. Specifically, order matters when subtracting values due to integer overflow. For example uint64(8) - uint64(1) gives you 7, but uint64(1) - uint64(8) gives you 0.
Here's the modified solution for unsigned integers, applied to the sample data you provide:
A = uint64([1 5 7 3 2 8]);
B = uint64([4 12 11 10 9 23 1 15]);
delta = bsxfun(#(a, b) max(a-b, b-a), A(:), reshape(B, 1, []));
[~, index] = min(delta, [], 2);
result = B(index)
result =
1×6 uint64 row vector
1 4 9 4 1 9 % As expected!

Index a vector by a matrix of conditions to obtain multiple selections of the target?

I have a vector T of length n and m other vectors of the same length with 0 or 1 used as condition to select elements of T. The condition vectors are combined into a matrix I of size n x m.
Is there a one liner to extract a matrix M of values from Tsuch that the i-th column of M are those elements in T that are selected by the condition elements of the i-th column in I?
Example:
T = (1:10)'
I = mod(T,2) == 0
T(I)'
yields
2 4 6 8 10
However
I = mod(T,2:4) == 0
T(I)'
yields an error in the last statement. I see that the columns might select a different number of elements which results in vectors of different lengths (as in the example). However, even this example doesn't work:
I = zeros(10,2)
I(:,1) = mod(T,2)==0
I(:,2) = mod(T,2)==1
Is there any way to achieve the solution in a one liner?
The easiest way I can think of to do something like this is to take advantage of the element-wise multiplication operator .* with your matrix I. Take this as an example:
% these lines are just setup of your problem
m = 10;
n = 10;
T = [1:m]';
I = randi([0 1], m, n);
% 1 liner to create M
M = repmat(T, 1, n) .* I;
What this does is expand T to be the same size as I using repmat and then multiplies all the elements together using .*.
Here is a one linear solution
mat2cell(T(nonzeros(bsxfun(#times,I,(1:numel(T)).'))),sum(I))
First logical index should be converted to numeric index for it we multiply T by each column of I
idx = bsxfun(#times,I,(1:numel(T)).');
But that index contain zeros we should extract those values that correspond to 1s in matrix I:
idx = nonzeros(idx);
Then we extract repeated elements of T :
T2 = T(idx);
so we need to split T2 to 3 parts size of each part is equal to sum of elements of corresponding column of I and mat2cell is very helpful
result = mat2cell(T2,sum(I));
result
ans =
{
[1,1] =
2
4
6
8
10
[2,1] =
3
6
9
[3,1] =
4
8
}
One line solution using cellfun and mat2cell
nColumns = size(I,2); nRows = size(T,1); % Take the liberty of a line to write cleaner code
cellfun(#(i)T(i),mat2cell(I,nRows,ones(nColumns,1)),'uni',0)
What is going on:
#(i)T(i) % defines a function handle that takes a logical index and returns elements from T for those indexes
mat2cell(I,nRows,ones(nColumns,1)) % Split I such that every column is a cell
'uni',0 % Tell cellfun that the function returns non uniform output

Mean and Standard Deviation of a column, ignoring zero values - Matlab

I am trying to find the mean of a column however I am having trouble getting an output for a function I created. My code is below, I cannot see what mistake I have made.
for j=1:48;
C_f2 = V(V(:,3) == j,:);
C_f2(C_f2==0)=NaN;
m=mean(C_f2(:,4));
s=std(C_f2(:,4));
row=[j,m,s];
s1=[s1;row];
end
I have checked the matrix, C_f2 and that is full of values so should not be returning NaN. However my output for the matrix s1 is
1 NaN NaN
2 NaN NaN
3 NaN NaN
. ... ...
48 NaN NaN
Can anyone see my issue? Help would me much appreciated!
The matrix C_f2 looks like,
1 185 01 5003
1 185 02 5009
. ... .. ....
1 259 48 5001
On line 3 you set all values which are zero to NaN. The mean function will return NaN as mean if any element is NaN. If you want to ignore the NaN values, you have to use the nanmean function, which comes with the Statistics toolbox. See the following example:
a = [1 NaN 2 3];
mean(a)
ans =
NaN
nanmean(a)
ans =
2
If you don't have the Statistics toolbox, you can exclude NaN elements with logical indexing
mean(a(~isnan(a)))
ans =
2
or it is possibly the easiest, if you directly exlude all elements which are zero instead of replacing them by NaN.
mean(a(a~=0))
Your line C_f2(C_f2==0)=NaN; will put NaNs into C_f2. Then, your mean and std operations will see those NaNs and output NaNs themselves.
To have the mean and std ignore NaN, you need to use the alternate version nanmean and nanstd.
These are part of a toolbox, however, so you might not have them if you just have the base Matlab installation.
Don't set it to NaN, any NaN involved computation without additional rules will return NaN,
use find to correctly index the none zero part of your column
say column n is your input
N = n(find(n~=0))
now do your Mu calculation
To compute the mean and standard deviation of each column excluding zeros:
A = [1 2;
3 0;
4 5;
6 7;
0 0]; %// example data
den = sum(A~=0); %// number of nonzero values in each column
mean_nz = bsxfun(#rdivide, sum(A), den);
mean2_nz = bsxfun(#rdivide, sum(A.^2), den);
std_nz = sqrt(bsxfun(#times, mean2_nz-mean_nz.^2, den./(den-1)));
The results for the example are
mean_nz =
3.5000 4.6667
std_nz =
2.0817 2.5166
The above uses the "corrected" definition of standard deviation (which divides by n-1, where n is the number of values). If you want the "uncorrected" version (i.e. divide by n):
std_nz = sqrt(mean2_nz-mean_nz.^2);

Vectorizing the Notion of Colon (:) - values between two vectors in MATLAB

I have two vectors, idx1 and idx2, and I want to obtain the values between them. If idx1 and idx2 were numbers and not vectors, I could do that the following way:
idx1=1;
idx2=5;
values=idx1:idx2
% Result
% values =
%
% 1 2 3 4 5
But in my case, idx1 and idx2 are vectors of variable length. For example, for length=2:
idx1=[5,9];
idx2=[9 11];
Can I use the colon operator to directly obtain the values in between? This is, something similar to the following:
values = [5 6 7 8 9 9 10 11]
I know I can do idx1(1):idx2(1) and idx1(2):idx2(2), this is, extract the values for each column separately, so if there is no other solution, I can do this with a for-loop, but maybe Matlab can do this more easily.
Your sample output is not legal. A matrix cannot have rows of different length. What you can do is create a cell array using arrayfun:
values = arrayfun(#colon, idx1, idx2, 'Uniform', false)
To convert the resulting cell array into a vector, you can use cell2mat:
values = cell2mat(values);
Alternatively, if all vectors in the resulting cell array have the same length, you can construct an output matrix as follows:
values = vertcat(values{:});
Try taking the union of the sets. Given the values of idx1 and idx2 you supplied, run
values = union(idx1(1):idx1(2), idx2(1):idx2(2));
Which will yield a vector with the values [5 6 7 8 9 10 11], as desired.
I couldn't get #Eitan's solution to work, apparently you need to specify parameters to colon. The small modification that follows got it working on my R2010b version:
step = 1;
idx1 = [5, 9];
idx2 = [9, 11];
values = arrayfun(#(x,y)colon(x, step, y), idx1, idx2, 'UniformOutput', false);
values=vertcat(cell2mat(values));
Note that step = 1 is actually the default value in colon, and Uniform can be used in place of UniformOutput, but I've included these for the sake of completeness.
There is a great blog post by Loren called Vectorizing the Notion of Colon (:). It includes an answer that is about 5 times faster (for large arrays) than using arrayfun or a for-loop and is similar to run-length-decoding:
The idea is to expand the colon sequences out. I know the lengths of
each sequence so I know the starting points in the output array. Fill
the values after the start values with 1s. Then I figure out how much
to jump from the end of one sequence to the beginning of the next one.
If there are repeated start values, the jumps might be negative. Once
this array is filled, the output is simply the cumulative sum or
cumsum of the sequence.
function x = coloncatrld(start, stop)
% COLONCAT Concatenate colon expressions
% X = COLONCAT(START,STOP) returns a vector containing the values
% [START(1):STOP(1) START(2):STOP(2) START(END):STOP(END)].
% Based on Peter Acklam's code for run length decoding.
len = stop - start + 1;
% keep only sequences whose length is positive
pos = len > 0;
start = start(pos);
stop = stop(pos);
len = len(pos);
if isempty(len)
x = [];
return;
end
% expand out the colon expressions
endlocs = cumsum(len);
incr = ones(1, endlocs(end));
jumps = start(2:end) - stop(1:end-1);
incr(endlocs(1:end-1)+1) = jumps;
incr(1) = start(1);
x = cumsum(incr);