I have a lengthy slice sequence that I need to apply to lots of MATLAB matrices. How can I do this?
i.e. can I simplify,
y(1:some_var*3,1:some_other_var*3,1:another_var*3) = x1(1:some_var*3,1:some_other_var*3,1:another_var*3) .* x2(1:some_var*3,1:some_other_var*3,1:another_var*3) ./ x3(1:some_var*3,1:some_other_var*3,1:another_var*3)
to something like,
inds = slice(1:some_var*3,1:some_other_var*3,1:another_var*3)
y(inds) = x1(inds) .* x2(inds) ./ x3(inds)
like I can do in Python?
In your case, you can create a logical mask:
%# assuming x1,x2,x3,y are all of the same size
mask = false(size(x1));
mask(1:some_var*3,1:some_other_var*3,1:another_var*3) = true;
y(mask) = x1(mask).*x2(mask)./x3(mask);
Other functions that you might want to read about: FIND, SUB2IND
One option is to store each vector of indices in a cell of a cell array, then extract the cell array content as a comma-separated list like so:
inds = {1:some_var*3, 1:some_other_var*3, 1:another_var*3};
y(inds{:}) = x1(inds{:}) .* x2(inds{:}) ./ x3(inds{:});
If you have large matrices, with relatively small/sparse sets of indices, this may be more efficient than using a logical mask as Amro suggested.
Related
I wrote this matlab code in order to concatenate the results of the integration of all the columns of a matrix extracted form a multi matrix array.
"datimf" is a matrix composed by 100 matrices, each of 224*640, vertically concatenated.
In the first loop i select every single matrix.
In the second loop i integrate every single column of the selected matrix
obtaining a row of 640 elements.
The third loop must concatenate vertically all the lines previously calculated.
Anyway i got always a problem with the third loop. Where is the error?
singleframe = zeros(224,640);
int_frame_all = zeros(1,640);
conc = zeros(100,640);
for i=0:224:(22400-224)
for j = 1:640
for k = 1:100
singleframe(:,:) = datimf([i+1:(i+223)+1],:);
int_frame_all(:,j) = trapz(singleframe(:,j));
conc(:,k) = vertcat(int_frame_all);
end
end
end
An alternate way to do this without using any explicit loops (edited in response to rayryeng's comment below. It's also worth noting that using cellfun may not be more efficient than explicitly looping.):
nmats = 100;
nrows = 224;
ncols = 640;
datimf = rand(nmats*nrows, ncols);
% convert to an nmats x 1 cell array containing each matrix
cellOfMats = mat2cell(datimf, ones(1, nmats)*nrows, ncols);
% Apply trapz to the contents of each cell
cellOfIntegrals = cellfun(#trapz, cellOfMats, 'UniformOutput', false);
% concatenate the results
conc = cat(1, cellOfIntegrals{:});
Taking inspiration from user2305193's answer, here's an even better "loop-free" solution, based on reshaping the matrix and applying trapz along the appropriate dimension:
datReshaped = reshape(datimf, nrows, nmats, ncols);
solution = squeeze(trapz(datReshaped, 1));
% verify solutions are equivalent:
all(solution(:) == conc(:)) % ans = true
I think I understand what you want. The third loop is unnecessary as both the inner and outer loops are 100 elements long. Also the way you have it you are assigning singleframe lots more times than necessary since it does not depend on the inner loops j or k. You were also trying to add int_frame_all to conc before int_frame_all was finished being populated.
On top of that the j loop isn't required either since trapz can operate on the entire matrix at once anyway.
I think this is closer to what you intended:
datimf = rand(224*100,640);
singleframe = zeros(224,640);
int_frame_all = zeros(1,640);
conc = zeros(100,640);
for i=1:100
idx = (i-1)*224+1;
singleframe(:,:) = datimf(idx:idx+223,:);
% for j = 1:640
% int_frame_all(:,j) = trapz(singleframe(:,j));
% end
% The loop is uncessary as trapz can operate on the entire matrix at once.
int_frame_all = trapz(singleframe,1);
%I think this is what you really want...
conc(i,:) = int_frame_all;
end
It looks like you're processing frames in a video.
The most efficent approach in my experience would be to reshape datimf to be 3-dimensional. This can easily be achieved with the reshape command.
something along the line of vid=reshape(datimf,224,640,[]); should get you far in this regard, where the 3rd dimension is time. vid(:,:,1) then would display the first frame of the video.
Given the code below:
% Generate some random data
n = 10;
A = cell(n, 1);
for i=1:n
A{i} = timeseries;
A{i}.Data = rand(100, 1);
A{i}.Time = 1:100;
end
I would like to make the sum of Data(end) without explicitly writing a for loop. Is there a smart way to select Data(end) in all cells in a single line? A{:}.Data(end) does not work.
You can do it with cellfun but that is essentially just a for loop wrapped up:
cellfun(#(x) x.Data(end), A)
I prefer Dan's answer, but for reference, I'll post an alternative using arrayfun. This is also just a for loop wrapped up to save keystrokes, but not necessarily time.
sum(arrayfun(#(n) A{n}.Data(end), 1:numel(A)))
You can also extract all of the Data fields into a single matrix, which might be worth it if you're planning on doing multiple operations on it:
A2 = [A{:}];
A3 = [A2.Data];
sum(A3(end,:))
I want to create a MATLAB function to import data from files in another directory and fit them to a given model, but because the data need to be filtered (there's "thrash" data in different places in the files, eg. measurements of nothing before the analyzed motion starts).
So the vectors that contain the data used to fit end up having different lengths and so I can't return them in a matrix (eg. x in my function below). How can I solve this?
I have a lot of datafiles so I don't want to use a "manual" method. My function is below. All and suggestions are welcome.
datafit.m
function [p, x, y_c, y_func] = datafit(pattern, xcol, ycol, xfilter, calib, p_calib, func, p_0, nhl)
datafiles = dir(pattern);
path = fileparts(pattern);
p = NaN(length(datafiles));
y_func = [];
for i = 1:length(datafiles)
exist(strcat(path, '/', datafiles(i).name));
filename = datafiles(i).name;
data = importdata(strcat(path, '/', datafiles(i).name), '\t', nhl);
filedata = data.data/1e3;
xdata = filedata(:,xcol);
ydata = filedata(:,ycol);
filter = filedata(:,xcol) > xfilter(i);
x(i,:) = xdata(filter);
y(i,:) = ydata(filter);
y_c(i,:) = calib(y(i,:), p_calib);
error = #(par) sum(power(y_c(i,:) - func(x(i,:), par),2));
p(i,:) = fminsearch(error, p_0);
y_func = [y_func; func(x(i,:), p(i,:))];
end
end
sample data: http://hastebin.com/mokocixeda.md
There are two strategies I can think of:
I would return the data in a vector of cells instead, where the individual cells store vectors of different lengths. You can access data the same way as arrays, but use curly braces: Say c{1}=[1 2 3], c{2}=[1 2 10 8 5] c{3} = [ ].
You can also filter the trash data upon reading a line, if that makes your vectors have the same length.
If memory is not an major issue, try filling up the vectors with distinct values, such as NaN or Inf - anything, that is not found in your measurements based on their physical context. You might need to identify the longest data-set before you allocate memory for your matrices (*). This way, you can use equally sized matrices and easily ignore the "empty data" later on.
(*) Idea ... allocate memory based on the size of the largest file first. Fill it up with e.g. NaN's
matrix = zeros(length(datafiles), longest_file_line_number) .* NaN;
Then run your function. Determine the length of the longest consecutive set of data.
new_max = length(xdata(filter));
if new_max > old_max
old_max = new_max;
end
matrix(i, length(xdata(filter))) = xdata(filter);
Crop your matrix accordingly, before the function returns it ...
matrix = matrix(:, 1:old_max);
I would like to add a cell array of images together using imadd, but imadd only takes two arguments. Is there a reduce function in MATLAB which I could use add all of these images together without writing a for loop?
images = {im1, im2, im3};
sum = reduce(#imadd, images);
You could just use an array with an extra dimension. E.g. for 2-d (grayscale images)
images = {im1, im2, im3};
imarr = cat(3, images{:});
imsum = sum(imarr, 3);
Of course there's no need to create the cell array in the first place; you could go straight to
imarr = cat(3, im1, im2, im3);
imsum = sum(imarr, 3);
or even
imsum = sum(cat(ndims(im1)+1, im1, im2, im3),ndims(im1)+1);
which also more generally combines any dimensional matrices.
Since you are using the Image Processing Toolbox, the IMLINCOMB function (linear combination of images) can also be used, just give all coefficients as one:
imsum = imlincomb(1,im1, 1,im2, 1,im3)
How would I generate a vector like
x1,x2,x3,x4,...,xn
the problem is concatenate ','and a 'x' char
n=100
A = (1:n);
This is a slight improvement on #Jonas's answer. SPRINTF will do the repeating for you avoiding the need for a mask:
>> n = 5;
>> out = sprintf('x%u,', 1:n);
>> out(end) = []
out =
x1,x2,x3,x4,x5
To generate the string 'x1,x2' etc, you can create a mask for SPRINTF using REPMAT like so:
n = 5;
mask = repmat('x%i,',1,n);
out = sprintf(mask,1:n);
out = out(1:end-1)
out =
x1,x2,x3,x4,x5
Note that in case you actually want to create a vector containing the strings 'x1','x2' etc, you'd use ARRAYFUN to generate a cell array:
out = arrayfun(#(x)sprintf('x%i',x),1:n,'uniformOutput',false)
out =
'x1' 'x2' 'x3' 'x4' 'x5'
The better answer is, don't do it. While you CAN do so, this will likely cause more heartache for you in the future than you want. Having hundreds of such variables floating around is silly, when you can use an array to index the same data. Thus perhaps x{1}, x{2}, ....