accessing nth column of a cell array in matlab - matlab

I have a cell array with for example 3 cells, in which cells are (3,8), (3,2), (3, 30) matrices, now I want to access the nth column of whole data without converting my cell to matrix, for example, if I search for 8th column, it must be the second column of 3th cell. one way is to convert it into a matrix, but my cell is too long and it gives me out of memory when I try to convert the whole cell to the matrix. then I tried this the code below, but it doesn't work correctly. i want to know what i'm doing wrong.
any help is appreciated.
function [col,i,idx] = find_cellCol(cel, idx)
lgh = length(cel);
i = 1;
me = zeros(2,length(cel));
while( i <= lgh && length(cel{1,i})<=idx)
idx = idx - length(cel{1,i});
i = i+1;
end%end while
if idx == 0
col = cel{1,i-1}(:,end);
else
col = cel{1,i}(:,idx);
end
end

Get only the number of line of each matrix of each cell, then sum those number of line and check on wich cell you reach the 8th line.
%dummy data
x{1} = rand(3,8);
x{2} = rand(3,2);
x{3} = rand(3,20);
val = 8;
csize = cellfun(#(x) size(x,1),x); %get the number of line for each cell
csum = cumsum(csize); % [3,6,9]
ind = find(csum>=val,1); % on which cell do we reach the # line
x{ind}((val-csum(ind))+csize(ind),:) %access the right line
fprintf('Accessing the line %d of the cell %d',(val-csum(ind))+csize(ind),ind)
Which will return:
Accessing the line 2 of the cell 3
EDIT:
The given example mislead me since I was sure that you were trying to access a line (first dimension) and not a column (2nd dimension).
But if you want to access a column you can simply adjust the code above:
val = 8;
csize = cellfun(#(x) size(x,2),x); %get the size of the second dimension now.
csum = cumsum(csize);
ind = find(csum>=val,1);
x{ind}(:,(val-csum(ind))+csize(ind)) %access the right column

Related

How to make calculations on certain cells (within a table) that meet specific criteria?

I have the following code:
L_sum = zeros(height(ABC),1);
for i = 1:height(ABC)
L_sum(i) = sum(ABC{i, ABC.L(i,4:281)});
end
Here my table:
Problem: My sum function sums the entire row values (col. 4-281) per date whereas I only want those cells to be added whose headers are in the cell array of ABC.L, for any given date.
X = ABC.L{1, 1}; gives (excerpt):
Red arrow: what sum function is referencing (L of same date).
Green arrow: what I am trying to reference now (L of previous date).
Thanks for your help
In general, in matlab you dont need to use for loops to do simple operations like selective sums.
Example:
Data=...
[1 2 3;
4 5 6;
7 8 9;
7 7 7];
NofRows=size(Data,1);
RowsToSum=3:NofRows;
ColToSum=[1,3];
% sum second dimension 2d array
Result=sum(Data(RowsToSum,ColToSum), 2)
% table mode
DataTable=array2table(Data);
Result2=sum(DataTable{RowsToSum,ColToSum}, 2)
To do that you need to first extract the columns you want to sum, and then sum them:
% some arbitrary data:
ABC = table;
ABC.L{1,1} = {'aa','cc'};
ABC.L{2,1} = {'aa','b'};
ABC.L{3,1} = {'aa','d'};
ABC.L{4,1} = {'b','d'};
ABC{1:4,2:5} = magic(4);
ABC.Properties.VariableNames(2:5) = {'aa','b','cc','d'}
% summing the correct columns:
L_sum = zeros(height(ABC),1);
col_names = ABC.Properties.VariableNames; % just to make things shorter
for k = 1:height(ABC)
% the following 'cellfun' compares each column to the values in ABC.L{k},
% and returns a cell array of the result for each of them, then
% 'cell2mat' converts it to logical array, and 'any' combines the
% results for all elements in ABC.L{k} to one logical vector:
col_to_sum = any(cell2mat(...
cellfun(#(x) strcmp(col_names,x),ABC.L{k},...
'UniformOutput', false).'),1);
% then a logical indexing is used to define the columns for summation:
L_sum(k) = sum(ABC{k,col_to_sum});
end

Accessing data from cell arrays at the same time

If A is a (x,y) cell array having n cells and each of them is a vector of size (m,n) and of type double.
Example: If A is a 1x2 cell array
A =
[100x1 double] [100x1 double]
Suppose I want to access the first element of each cells at the same time, how can we do so?
Similarly, if we need to access the ith element from every cell, how do we generalise the code?
cell creation with two 1*10 arrays:
A {1} = zeros(1,10) ;
A {2} = zeros (1,10) ;
A =
[1x10 double] [1x10 double]
Adding some data which will be used for fetching later:
A {1}(5) = 5 ;
A {2}(5) = 10 ;
Routine to fetch the data at same index from both arrays inside cell:
cellfun (#(x) x(5),A)
ans =
5 10
As User1551892 suggested, you could use cellfun. Another way is to restruct the cell to a matrix first.
The speed on the operation depends on the number of cells and the size of the matrix within each cell.
% Number of cells
x = 3;
y = 2;
% Size of matrix
m = 1;
n = 100;
% Add some random numbers
A = cell(x,y);
for i = 1:numel(A)
A{i} = round(rand(m,n)*100);
end
% Index to pick in each matrix
idx = 5;
% Convert to matrix
B = [A{:}];
% Pick the number
val = B(idx:(n*m):end);
Doing som tic-toc measurements, the method above is faster for the example values. As long as the one of n or m is small the method is ok.
But if both m and n grows large, cellfun is better (faster)
val = cellfun(#(x) x(idx), A);
An alternative way would be to simply access the cell elements directly , for example we have a cell like you defined
A{1}(1:10) = randi([2 5],1,10);
A{2}(1:10) = randi([2 5],1,10);
now if you want to access the ith elements simply declare i and they will be retrieved in the matrix below
i = 3;
ObsMatrix = [A{1}(i) A{2}(i)]
ObsMatrix =
2 5
If A has unknown number of cell you can simply use a for loop , It will pick ith element from every cell index and put it in ObsMat
i = 3;
for j=1:numel(A)
ObsMat(end + 1) = A{j}(3);
end
cellfun is also a wrapper function for for loop
ObsMat =
2 5

Vectorize MATLAB code

Let's say we have three m-by-n matrices of equal size: A, B, C.
Every column in C represents a time series.
A is the running maximum (over a fixed window length) of each time series in C.
B is the running minimum (over a fixed window length) of each time series in C.
Is there a way to determine T in a vectorized way?
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows %loop over the rows (except row #1).
for col = 1:ncols %loop over the columns.
if C(row, col) > A(row-1, col)
T(row, col) = 1;
elseif C(row, col) < B(row-1, col)
T(row, col) = -1;
else
T(row, col) = T(row-1, col);
end
end
end
This is what I've come up with so far:
T = zeros(m, n);
T(C > circshift(A,1)) = 1;
T(C < circshift(B,1)) = -1;
Well, the trouble was the dependency with the ELSE part of the conditional statement. So, after a long mental work-out, here's a way I summed up to vectorize the hell-outta everything.
Now, this approach is based on mapping. We get column-wise runs or islands of 1s corresponding to the 2D mask for the ELSE part and assign them the same tags. Then, we go to the start-1 along each column of each such run and store that value. Finally, indexing into each such start-1 with those tagged numbers, which would work as mapping indices would give us all the elements that are to be set in the new output.
Here's the implementation to fulfill all those aspirations -
%// Store sizes
[m1,n1] = size(A);
%// Masks corresponding to three conditions
mask1 = C(2:nrows,:) > A(1:nrows-1,:);
mask2 = C(2:nrows,:) < B(1:nrows-1,:);
mask3 = ~(mask1 | mask2);
%// All but mask3 set values as output
out = [zeros(1,n1) ; mask1 + (-1*(~mask1 & mask2))];
%// Proceed if any element in mask3 is set
if any(mask3(:))
%// Row vectors for appending onto matrices for matching up sizes
mask_appd = false(1,n1);
row_appd = zeros(1,n1);
%// Get 2D mapped indices
df = diff([mask_appd ; mask3],[],1)==1;
cdf = cumsum(df,1);
offset = cumsum([0 max(cdf(:,1:end-1),[],1)]);
map_idx = bsxfun(#plus,cdf,offset);
map_idx(map_idx==0) = 1;
%// Extract the values to be used for setting into new places
A1 = out([df ; false(1,n1)]);
%// Map with the indices obtained earlier and set at places from mask3
newval = [row_appd ; A1(map_idx)];
mask3_appd = [mask_appd ; mask3];
out(mask3_appd) = newval(mask3_appd);
end
Doing this vectorized is rather difficult because the current row's output depends on the previous row's output. Doing vectorized operations usually means that each element should stand out on its own using some relationship that is independent of the other elements that surround it.
I don't have any input on how you would achieve this without a for loop but I can help you reduce your operations down to one instead of two. You can do the assignment vectorized per row, but I can't see how you'd do it all in one shot.
As such, try something like this instead:
[nrows, ncols] = size(A);
T = zeros(nrows, ncols);
for row = 2:nrows
out = T(row-1,:); %// Change - Make a copy of the previous row
out(C(row,:) > A(row-1,:)) = 1; %// Set those elements of C
%// in the current row that are larger
%// than the previous row of A to 1
out(C(row,:) < B(row-1,:)) = -1; %// Same logic but for B now and it's
%// less than and the value is -1 instead
T(row,:) = out; %// Assign to the output
end
I'm currently figuring out how to do this with any loops whatsoever. I'll keep you posted.

How to store results created into a cell array during a multiple for loop into a 3d array? In Matlab

In Matlab, at the end of three different for loops (for a=1:240,b=1:5 and c=1:3), I generate a {1,3} cell array where each cell contains a (1,5) array that reports only the last result of the 240 iterations.
How can I generate, apart of this cell array, a (240,5,3) 3d array that stores the result of each iteration?
Or, equivalently, a cell array that stores again the information and then convert it into a (240,5,3) 3d array?
The code would be along the lines of:
%// Size of the problem
Na = 240;
Nb = 5;
Nc = 3;
%// Allocate empty cell array
result = cell(Na, Nb, Nc);
%// Loop
for a = 1:Na
for b = 1:Nb
for c = 1:Nc
%// Here is the code for computing the
%// result x of the last iteration.
result{a,b,c} = x;
end;
end;
end;

3D cell arrays in matlab

I am currently working using matlab, I have uploaded a csv file into a cell array that I have named B. What I now wish to do is to input the information of B into a 3-D cell array, the 3rd dimension of the array being the first column of B which are strings ranging from "chr1" to "chr24". The full length of B is m, and the maximum length of any "chr" is maxlength. I doubt that this is the best way of going about it but here is my code:
for j = 1:m ,
Ind = findstr(B{1}{j}, 'chr');
Num = B{1}{j}(Ind+3:end-1);
cnum = str2num(Num);
for i = 1:24,
if cnum == i;
for k = 2:9 ,
for l = 1:maxlength ,
C{l}{k}{i} = B{k}{j};
C{l}{k}{i}
end
end
end
end
end
The 3-D array that comes out of this does not match the corresponding values in the initial array. I also want to know if this is the right way to create a 3-D array, I can't seem to find anything on the matlab website about them.
Thanks
There are a few possible issues with your approach: First of all, Matlab indexing is different from c-style indexing into tables. myCell{i}{j} is the j-th element of the cell array that is contained in the i-th element of the cell array myCell. If you want to index into a 2-d cell array, you would get the contents of the element in row i, column j as myCell{i,j}.
If the columns 2 through 9 of your .csv file contain all numeric data, it may be a lot more convenient to use either a 1D cell array with an entry for every chromosome, or to use a 2D or 3D numeric array if you get, for each chromosome, a single row, or a table, respectively.
Here's one way to do it
%# convert chromosomes to numbers
chromosomes = B{1};
chromosomes = strrep(chromosomes,'X',25);
chromosomes = strrep(chromosomes,'Y',26);
tmp = regexp(chromsomes,'chr(\d+)','tokens','once');
cnum = cellfun(#(x)str2double(x{1}),tmp);
%# catenate the rest of B into a 2D cell array
allNumbers = cell2mat(cat(2,B{2:end}));
%# now we can make a table with [chromosomeNumber,allOtherNumbers]
finalTable = [chromosomeNumber,allNumbers]
%# alternatively, if there are multiple entries for each chromosome, we can
%# group the data in a cell array, so that the i-th entry corresponds to chr.i
%# for readability, use a loop
outputCell = cell(26,1); %# assume 26 chromosomes
for i=1:26
outputCell{i} = allNumbers(cnum==i,:);
end
I've managed to do this with only two for loops, here is my code:
C = zeros(26,8,maxlength);
next = zeros(1,26);
for j = 1:m ,
Ind = findstr(B{1}{j}, 'chr');
Num = B{1}{j}(Ind+3:end-1);
cnum = str2num(Num);
if Num == 'X'
cnum = 25;
end
if Num == 'Y'
cnum = 26;
end
next(cnum) = next(cnum) + 1;
for k = 2:9 ,
D{cnum}{k-1}{next(cnum)} = B{k}{j};
C(cnum,k-1,next(cnum)) = str2num(B{k}{j});
end
end