Matlab find the smallest number within variables - matlab

Say I have 10 variables which have different numbers within them(num1 = 0.4123,num2 = 0.6223,num3 etc.). How can i find the variable which has the smallest number? Do i need to use recursive loop or is there any easy way to do it?

Having a bunch of numbers delineated by separate variable names is awful. I would recommend doing what #LuisMendo suggested and place them all into an array.
However, if you don't want to punch in all of those numbers into a single array, you can cheat and save all variables that start with num, then load it back into MATLAB inside a struct. Convert the struct into an array by converting it into a cell array, and then a numeric array.
Once you do that, use the min call that he was talking about. In other words:
save('temp.mat', 'num*'); %// Save all variables with num from workspace to file
s = load('temp.mat'); %// Reload back in as a structure
vals = cell2mat(struct2cell(s)); %// Convert from structure to numeric array
[~,idx] = min(vals); %// Find value that was the minimum
f = fieldnames(s); %// Get all of the variable names
disp(f{idx}); %// Display the variable that has the minimum
idx would be the number that resulted in the minimum. If you want to display the actual name of the variable that resulted in the minimum, you can use fieldnames to retrieve a list of all of the variables from the structure, then index into that with the minimum value to get the result.
Or, if you can bear the typing, just do this:
vals = [num1 num2 num3 num4 num5 num6 num7 num8 num9 num10];
[~,idx] = min(vals);
disp(['num' num2str(idx)]);
The first way would be preferable if you have a lot of variables that you want to find the min of. If this is the case, you should consider placing all of the values in an array and reformulate your code. Having a lot of variables declared in your code makes it unmanageable.

It would be much better to have all those variables as entries of a numeric vector:
num = [0.4123 0.6223];
Then you would use
[~, result] = min(num);
to get the index of the minimum element.

Related

Printing progress in command window

I'd like to use fprintf to show code execution progress in the command window.
I've got a N x 1 array of structures, let's call it myStructure. Each element has the fields name and data. I'd like to print the name side by side with the number of data points, like such:
name1 number1
name2 number2
name3 number3
name4 number4
...
I can use repmat N times along with fprintf. The problem with that is that all the numbers have to come in between the names in a cell array C.
fprintf(repmat('%s\t%d',N,1),C{:})
I can use cellfun to get the names and number of datapoints.
names = {myStucture.name};
numpoints = cellfun(#numel,{myStructure.data});
However I'm not sure how to get this into a cell array with alternating elements for C to make the fprintf work.
Is there a way to do this? Is there a better way to get fprintf to behave as I desire?
You're very close. What I would do is change your cellfun call so that the output is a cell array instead of a numeric array. Use the 'UniformOutput' flag and set this to 0 or false.
When you're done, make a new cell array where both the name cell array and the size cell array are stacked on top of each other. You can then call fprintf once.
% Save the names in a cell array
A = {myStructure.name};
% Save the sizes in another cell array
B = cellfun(#numel, {myStructure.data}, 'UniformOutput', 0);
% Create a master cell array where the first row are the names
% and the second row are the sizes
out = [A; B];
% Print out the elements side-by-side
fprintf('%s\t%d\n', out{:});
The trick with the third line of code is that when you unroll the cell array using {:}, this creates a comma-separated list unrolled in column-major format, and so doing out{:} actually gives you:
A{1}, B{1}, A{2}, B{2}, ..., A{n}, B{n}
... which provides the interleaving you need. Therefore, providing this order into fprintf coincides with the format specifiers that are specified and thus gives you what you need. That's why it's important to stack the cell arrays so that each column gives the information you need.
Minor Note
Of course one should never forget that one of the easiest ways to tackle your problem is to just use a simple for loop. Even though for loops are considered bad practice, their performance has come a long way throughout MATLAB's evolution.
Simply put, just do this:
for ii = 1 : numel(myStructure)
fprintf('%s\t%d\n', myStructure(ii).name, numel(myStructure(ii).data));
end
The above code is arguably more readable in comparison to what we did above with cell arrays. You're accessing the structure directly rather than having to create intermediate variables for the purpose of calling fprintf once.
Example Run
Here's an example of this running. Using the data shown below:
clear myStructure;
myStructure(1).name = 'hello';
myStructure(1).data = rand(5,1);
myStructure(2).name = 'hi';
myStructure(2).data = zeros(3,3);
myStructure(3).name = 'huh';
myStructure(3).data = ones(6,4);
I get the following output after running the printing code:
hello 5
hi 9
huh 24
We can see that the sizes are correct as the first element in the structure is simply a random 5 element vector, the second element is a 3 x 3 = 9 zeroes matrix while the last element is a 6 x 4 = 24 ones matrix.

Using Matlab to randomly split an Excel Sheet

I have an Excel sheet containing 1838 records and I need to RANDOMLY split these records into 3 Excel Sheets. I am trying to use Matlab but I am quite new to it and I have just managed the following code:
[xlsn, xlst, raw] = xlsread('data.xls');
numrows = 1838;
randindex = ceil(3*rand(numrows, 1));
raw1 = raw(:,randindex==1);
raw2 = raw(:,randindex==2);
raw3 = raw(:,randindex==3);
Your general procedure will be to read the spreadsheet into some matlab variables, operate on those matrices such that you end up with three thirds and then write each third back out.
So you've got the read covered with xlsread, that results in the two matrices xlsnum and xlstxt. I would suggest using the syntax
[~, ~, raw] = xlsread('data.xls');
In the xlsread help file (you can access this by typing doc xlsread into the command window) it says that the three output arguments hold the numeric cells, the text cells and the whole lot. This is because a matlab matrix can only hold one type of value and a spreadsheet will usually be expected to have text or numbers. The raw value will hold all of the values but in a 'cell array' instead, a different kind of matlab data type.
So then you will have a cell array valled raw. From here you want to do three things:
work out how many rows you have (I assume each record is a row) by using the size function and specifying the appropriate dimension (again check the help file to see how to do this)
create an index of random numbers between 1 and 3 inclusive, which you can use as a mask
randindex = ceil(3*rand(numrows, 1));
apply the mask to your cell array to extract the records matching each index
raw1 = raw(:,randindex==1); % do the same for the other two index values
write each cell back to a file
xlswrite('output1.xls', raw1);
You will probably have to fettle the arguments to get it to work the way you want but be sure to check the doc functionname page to get the syntax just right. Your main concern will be to get the indexing correct - matlab indexes row-first whereas spreadsheets tend to be column-first (e.g. cell A2 is column A and row 2, but matlab matrix element M(1,2) is the first row and the second column of matrix M, i.e. cell B1).
UPDATE: to split the file evenly is surprisingly more trouble: because we're using random numbers for the index it's not guaranteed to split evenly. So instead we can generate a vector of random floats and then pick out the lowest 33% of them to make index 1, the highest 33 to make index 3 and let the rest be 2.
randvec = rand(numrows, 1); % float between 0 and 1
pct33 = prctile(randvec,100/3); % value of 33rd percentile
pct67 = prctile(randvec,200/3); % value of 67th percentile
randindex = ones(numrows,1);
randindex(randvec>pct33) = 2;
randindex(randvec>pct67) = 3;
It probably still won't be absolutely even - 1838 isn't a multiple of 3. You can see how many members each group has this way
numel(find(randindex==1))

Matlab -- Copy Structure Array without For Loop

I have a fairly simple question in Matlab. I want to copy n items of structure array (sumRT.P) to a matrix (m). In C, I would just use a for loop, like this:
for i = 1:n
m(i) = sumRT(i).P;
end
But I bet there's a simpler way to copy an array in Matlab (that's the whole point of language right?). I tried this:
m = sumRT(1:n).P;
But this just copies the first item in sumRT.P to m, resulting in a 1 X 1 matrix. Note, if I type, sumRT(2).P for example, I can see the second item. Same for any number up to n. Why is this wrong and how do I fix it?
It depends on the data types in your structure array. If they are types of variables, or if they are variables of the same size in arrays of different dimensions, then you can't put them into an array, but you can make them into a cell:
m={sumRT(1:n).P}
and cells are pretty simple to deal with, so this oughtn't be a big problem.
If they are all scalar numerical values, you can create a matrix:
m=cell2mat({sumRT(1:n).P})
Try the following:
m = squeeze(cell2mat(struct2cell(sumRT(1:n))));
This converts the struct array to a cell array, and then to a (numeric) array, and then squeezes it by remoiving singleton dimensions.
Example:
>> sumRT(1).P = 10; sumRT(2).P = 20; sumRT(3).P = 30;
>> n = 2; %// copy first two elements only
>> m = squeeze(cell2mat(struct2cell(sumRT(1:n))))
m =
10
20

Accessing data in structures without loops

I have a set of strings vals, for example:
vals = {'AD', 'BC'}
I also have a struct info, inside of which are structs nested in fields corresponding to the elements in the array vals (that would be 'AD' and 'BC' in this example), each in turn storing a number in a field named lastcontract.
I can use a for loop to extract lastcontract for each of the vals like this:
for index = 1:length(vals)
info.(vals{index}).lastcontract
end
I'd like to find a way of doing this without a loop if at all possible, but I'm not having luck. I tried:
info.(vals{1:2}).lastcontract
without success. I think arrayfun may be the appropriate way, but I can't figure out the right syntax.
It is actually possible here to manage without an explicit loop (nor arrayfun/cellfun):
C = struct2cell(info); %// Convert to cell array
idx = ismember(fieldnames(info), vals); %// Find fields
C = [C{idx}]; %// Flatten to structure array
result = [C.lastcontract]; %// Extract values
P.S
cellfun would be more appropriate here than arrayfun, because you iterate vals (a cell array). For the sake of practice, here's a solution with cellfun:
result = cellfun(#(x)info.(x).lastcontract, vals);

Mean value of multiple columns

I have searched a lot to find the solution, but nothing really works for me I think.
I have n data files containing two columns each (imported using uigetfile). To extract the data, I use a for loop like this:
for i=1:n
data{i}=load(filename{i});
x{i}=data{i}(:,1);
y{i}=data{i}(:,2);
end
Now, I want to get the mean value for each row of all the (let's say) x-values. E.g.:
x{1} = [1,4,7,8]
x{2} = [1,2,6,9]
Then I want something like
x_mean = [1,3,6.5,8.5]
I have tried (where k is number of rows)
for i=1:n
data{i}=load(filename{i});
x{i}=data{i}(:,1);
y{i}=data{i}(:,2);
j=1:k
x_mean=sum(x{i}(j))/n
end
But I can't use multiple counters in a for loop (as I understand). Moreover, I don't use mean as I don't see how I can use it in this case.
If someone could help me, it would be great!
You can capture the contents of each numeric array in the cell x into a new numeric array x_num like so:
x_num = [x{:}]
Computing the mean is then as simple as
mean_x = mean( [x{:}] )
For your example, that gives you the mean of all numbers in all arrays in x, which will therefore be a scalar.
If you want to compute the mean of all the rows (column-wise mean), as your example would indicate), you have to concatenate your arrays vertically, which you can do with cat:
mean_x_columnwise = mean( cat(1,x{:}) )
If you want to take the mean over all the columns (row-wise mean), you should only have to tell mean that you are looking at a different dimension:
mean_x_rowwise = mean( cat(1,x{:}), 2)