MatLab: Find numeric values in cell array - matlab

In my cell array test = cell(1,2,20,14); I want to find numeric values in the subset test(:,1,1,1).
For example test(:,:,1,1) looks like this:
>> test(:,:,1,1)
ans =
[ 0] [0.1000] [57]
[0.9000] [0.9500] [73]
I want to find the index of the cell containing 0.9 in the first column, so I can access the third column (in this case value 73). I tried:
find(test{:,:,1,1} == 0.9) which gives:
Error using == Too many input arguments..
How can I find the respective index?
Thanks.

Try this to access that third column value directly -
cell2mat(test(vertcat(test{:,1,1,1})==0.9,3,1,1))
Edit 1: If you would like to test out for match w.r.t. the first two columns of test's subset, use this -
v1 = reshape(vertcat(test{:,[1 2],1,1}),[],2)
cell2mat(test(ismember(v1,[0.9 0.95],'rows'),3,1,1))

Just add brackets [] around test{:,:,1,1}. This wraps the different cell values together to one vector/matrix.
Like this:
[index1, index2] = find([test{:,:,1,1}] == 0.9)

Related

find strings in cell array using wildcards

I have a cell array of which the last part looks like this:
Columns 8372 through 8375
{'w20091231_2000.nc'} {'w20091231_2020.nc'} {'w20091231_2040.nc'} {'w20091231_2100.nc'}
Columns 8376 through 8379
{'w20091231_2120.nc'} {'w20091231_2140.nc'} {'w20091231_2200.nc'} {'w20091231_2220.nc'}
Columns 8380 through 8383
{'w20091231_2240.nc'} {'w20091231_2300.nc'} {'w20091231_2320.nc'} {'w20091231_2340.nc'}
Columns 8384 through 8387
{'wD1.nc'} {'wD2.nc'} {'wD3.nc'} {'wD4.nc'}
Now I want to rearrange this array so that it only contains the last four strings.{'wD1.nc'} {'wD2.nc'} {'wD3.nc'} {'wD4.nc'}
I tried
IndexC = strfind(names,'wD*.nc');
Index = find(not(cellfun('isempty',IndexC)))
and
Index = find(contains(names,'wD*.nc'));
names2=names(Index)
both work if wD*.nc is wD4.nc but then of course I only select the one value and not the four that I want.
How do I get to use the * ?
I had to do some googling but found this https://www.mathworks.com/matlabcentral/answers/77039-comparing-strings-with-wildcards , and something like the following seems to work:
IndexC = regexp(names, regexptranslate('wildcard', 'wD*.nc'));
Index = find(not(cellfun('isempty',IndexC)));
names2=names(Index)
In one line using regexp with the match option:
x = regexp([x{:}],'wD\d+\.nc','match')

Sort all dimensions of a cell array from min to max in MATLAB

I have created a cell array called Items of size (10,5). It contains the following as the first column in the cell array:
Item name Price (in $) Shipping (in $) Total price (in $) Total price (in €)
I have it all filled up, but what I need to do is to sort the cell array according to the total price in € from the smallest to the largest, but I can't seem to find a way to do it. I tried sort(Items,5); in order to sort according to the values in € but it returns an error. It could be useful to find a way to make the sorting automatic, so if I wanted to add more items it would still sort them in the global list.
sortrows will likely do exactly what you want to do. It will sort based on a specific column, assuming the datatype is constant in the entire column.
>> a ={'a',8,9;'b',5,6;'c',2,3};
>> a_sorted = sortrows(a,3)
a_sortred =
'c' [2] [3]
'b' [5] [6]
'a' [8] [9]
Edit
From your comments below, you can easily just sort the array first and then add a row to the cell array the same way you would combine regular arrays. Documentation
>> a = {7,8,9;4,5,6;1,2,3};
>> a_sorted = sortrows(a,3);
>> headers = {'col1','col2','col3'};
>> a_header = [headers;a_sorted]
a_header =
'col1' 'col2' 'col3'
[ 1] [ 2] [ 3]
[ 4] [ 5] [ 6]
[ 7] [ 8] [ 9]
EDIT #2
You can round the values that you are presenting using the second argument of the round function. After you round it, you can change the format of how things are displayed. Normally it is set as short, which is 4 decimal places. If you set it to shortg it will show as few decimals as possible, up to 4.
>> a = [1.23456789;2.3456789;3.456789]
a =
1.2346
2.3457
3.4568
>> a_rounded = round(a,2)
a_rounded =
1.2300
2.3500
3.4600
>> format shortg
>> a_rounded
a_rounded =
1.23
2.35
3.46
If changing the format is not an option, you could always just convert the number into a string and then display that. That gets a little more complicated, but a quick google will help you there.
EDIT #3
I did not know this existed before, but you can apparently use the format style called bank. This will display all numbers as two decimal points even if they are 0.
First place all of the prices in a separate array, sort on this array individually then use the indices of sorting to rearrange the rows of your cell array.
Try something like this:
price = [Items{:,5}];
[~,ind] = sort(price);
ItemsSorted = Items(ind,:);
Alternatively you can use the sortrows function that MZimmerman6 mentioned and operate along the fifth column of your cell array. I wasn't aware it worked on cell arrays, so I learned something new!

Remove rows from a matrix

I have the array "A" with values:
101 101
0 0
61.6320000000000 0.725754779522671
73.7000000000000 0.830301150185882
78.2800000000000 0.490917508345341
81.2640000000000 0.602561200211232
82.6880000000000 0.435568593909153
And I wish to remove this first row and retain the shape of the array (2 columns), thus creating the array
0 0
61.6320000000000 0.725754779522671
73.7000000000000 0.830301150185882
78.2800000000000 0.490917508345341
81.2640000000000 0.602561200211232
82.6880000000000 0.435568593909153
I have used A = A(A~=101); , which removes the values as required - however it packs the array down to one column.
The best way is:
A = A(2:end, :)
But you can also do
A(1,:) = []
however it is slightly less efficient (see Deleting matrix elements by = [] vs reassigning matrix)
If you are looking to delete rows that equal a certain number try
A = A(A(:,1)~=101,:)
Use all or any if you want to delete row if either all or any column equals your value:
A = A(all(A~=101,2),:)

find value in a string of cell considering some margin

Suppose that I have a string of values corresponding to the height of a group of people
height_str ={'1.76000000000000';
'1.55000000000000';
'1.61000000000000';
'1.71000000000000';
'1.74000000000000';
'1.79000000000000';
'1.74000000000000';
'1.86000000000000';
'1.72000000000000';
'1.82000000000000';
'1.72000000000000';
'1.63000000000000'}
and a single height value.
height_val = 177;
I would like to find the indices of the people that are in the range height_val +- 3cm.
To find the exact match I would do like this
[idx_height,~]=find(ismember(cell2mat(height_str),height_val/100));
How can I include the matches in the previous range (174-180)?
idx_height should be = [1 5 6 7]
You can convert you strings into an numeric array (as #Divakar mentioned) by
height = str2num(char(height_str))*100; % in cm
Then just
idx_height = find(height>=height_val-3 & height<=height_val+3);
Assuming that the precision of heights stays at 0.01cm, you can use a combination of str2double and ismember for a one-liner -
idx_height = find(ismember(str2double(height_str)*100,[height_val-3:height_val+3]))
The magic with str2double is that it works directly with cell arrays to get us a numeric array without resorting to a combined effort of converting that cell array to a char array and then to a numeric array.
After the use of str2double, we can use ismember as you tried in your problem to get us the matches as a logical array, whose indices are picked up with find. That's the whole story really.
Late addition, but for binning my first choice would be to go with bsxfun and logical operations:
idx_height = find(bsxfun(#le,str2double(height_str)*100,height_val+3) & ...
bsxfun(#ge,str2double(height_str)*100,height_val-3))

Complementary array Matlab

We've got an array of values, and we would like to create another array whose values are not in the first one.
Example:
load('internet.mat')
The first column contains the values in MBs, we have thought in something like:
MB_no = setdiff(v, internet(:,1))
where v is a 0 vector whose length equals to the number of rows in internet.mat. But it just doesn't work.
So, how do we do this?
You need to specify the range of possible values to define what values are not in internet . Say the range is v = 1:10 then setdiff(v,internet(:,1)) will give you the values in 1:10 that are not in the first column of internet.
It seems as if you don't want the first column.
You can simply do:
MB_no=internet(:,2:end);
assuming internet(:,1) has only positive integers and you wish to find which are the integers in [1,...,max( internet(:,1) )] that do not appear in that range you can simply do
app = [];
app( internet(:,1) ) = 1;
MB_no = find( app == 0 );
This is somewhat like bucket sort.