matlab: mapping function over columns (like lisp) - matlab

I need to apply a function over columns of a matrix. Let's say I have this matrix --
>> m = rand(3,3)
m =
0.8626 0.5661 0.8489
0.6830 0.1498 0.1401
0.0857 0.4775 0.3296
and two vectors of lower and upper bounds --
>> mins = [2 3 5]
mins =
2 3 5
>> maxs = [7 11 13]
maxs =
7 11 13
Now what I am doing is to split the matrix into columns --
>> cols = num2cell(m, 1)
cols =
[3x1 double] [3x1 double] [3x1 double]
Now what I was trying is to apply a function over each column that takes the lower and the upper bounds to normalize the column values, more specifically I want to normalize the column m(:,1) with mins(1) and maxs(1), m(:,2) with mins(2) and maxs(2) ... column m(:,n) with mins(n) and maxs(n) and so on. My take was like this --
>> norm_cols = cellfun(#(c, lb, ub) (ub - lb) .* c + lb, cols, mins, maxs);
Error using cellfun
Input #3 expected to be a cell array, was double instead.
My question is such thing doable in matlab? and lets say it's doable, then how do I merge the splitted columns into matrix again?
Moreover, please note that I am aware of loops, but I do not want to use it (as my matrix can grow like 1000x1000), so please do not provide any solution that uses loop. Although I am not sure if function mapping could give better speed up than loops (that's another issue, but not for today).

Well, you could certainly do something like this
>> x = {[1; 2; 3]/10, [4; 5; 6]/10, [7; 8; 9]/10};
>> mins = {1.5 4.5 7.5};
>> maxs = {2.5 5.5 8.5};
>> y = cellfun(#(a, lb, ub) (ub-lb) * x + lb, x, mins, maxs, 'uniform', 0)
y =
[3x1 double] [3x1 double] [3x1 double]
>> [y{:}]
ans =
1.600000000000000 4.900000000000000 8.199999999999999
1.700000000000000 5.000000000000000 8.300000000000001
1.800000000000000 5.100000000000000 8.400000000000000
But it's even cooler to use bsxfun to broadcast your arguments across the rows, without turning it into a cell array first
>> x = [1 4 7; 2 5 8; 3 6 9] / 10;
>> mins = [1.5 4.5 7.5];
>> maxs = [2.5 5.5 8.5];
>> y = bsxfun(#plus, bsxfun(#times, x, maxs-mins), mins);

Related

Find row-wise combinations of a 2 dimensional matrix

I have a matrix:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
I want to find all row-wise combinations of X. i.e.
A(1) = [2, 6, 1; 3, 8, 1; 4, 7, 1]
A(2) = [2, 6, 1; 3, 8, 1; 6, 2, 1]
:
:
:
Here's what I've tried:
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3
[m, n] = size(X);
comb = combnk(1:m, p);
[s, t] = size(comb);
c = [X(comb(:,1), :, :) X(comb(:,2), :, :) X(comb(:,3), :, :)];
This gives me a matrix like:
c = 2 6 1 3 8 1 4 7 1
2 6 1 3 8 1 6 2 1
2 6 1 3 8 1 6 4 1
I want to apply the concatenate matrix option to obtain c to make it dynamic depending on value of p but I'm not sure how to use it. I don't want to use For loops. Please help me out.
This is fully vectorized, so it should be fast:
n = 3; %// number of rows to pick each time
ind = reshape(nchoosek(1:size(X,1), n).', [], 1); %'// indices of combinations
A = permute(reshape(X(ind,:).', size(X,2), n, []), [2 1 3]);
The result is
A(:,:,1)
ans =
2 6 1
3 8 1
4 7 1
A(:,:,2)
ans =
2 6 1
3 8 1
6 2 1
etc.
Should you need the result in the form of a cell array, you can convert A from 3D-array to cell array this way:
A = mat2cell(A, size(A,1), size(A,2), ones(1,size(A,3)));
Your thinking is pretty close. This code does the job. I put comments in code, which should be easy to read.
X = [2,6,1; 3,8,1; 4,7,1; 6,2,1; 6,4,1; 7,3,1; 8,5,1; 7,6,1];
p = 3;
%// List all combinations choosing 3 out of 1:8.
v = nchoosek(1:size(X,1), p);
%// Use each row of v to create the matrices, and put the results in an cell array.
%// This is the A matrix in your question.
A = arrayfun(#(k)X(v(k,:), :), 1:size(v,1), 'UniformOutput', false);
%// And you can concatenate A vertically to get c.
flatA = cellfun(#(x)reshape(x, 1, []), A, 'UniformOutput', false);
c = vertcat(flatA{:});
PS: From my understanding I thought the result you wanted was A, which is an easy to use cell array. But I added an extra step to get c exactly as in your question just in case.
Disclaimer: arrayfun and cellfun are pretty much equivalent to for loop in terms of performance.
You can do it using reshape and a bunch of transposes since Matlab is column-major ordered:
c = reshape(X(comb',:)',9,[])'
or if you want a 3D matrix:
A = permute(reshape(X(comb',:)',3,3,[])', [2,1,3])

How to assemble small n-d arrays into a larger n-d array?

The code below produces a cell array containing n (=210) 2x3x4-shaped n-d arrays.
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
For each i ∈ {1, … ,n}, the contents of the i-th n-d array are all integers whose initial digits match those of i. For example, for i = 123:
>> ndarrays{123}
ans(:,:,1) =
123001 123003 123005
123002 123004 123006
ans(:,:,2) =
123007 123009 123011
123008 123010 123012
ans(:,:,3) =
123013 123015 123017
123014 123016 123018
ans(:,:,4) =
123019 123021 123023
123020 123022 123024
I want to assemble all these n-d arrays into a larger [5x6x7x2x3x4 double] n-d array X in such a way that expressions of the form X(i,j,k,:,:,:) will correspond to one of the original smaller n-d arrays. This last requirement is the one that's giving me difficulty.
I have no problem producing the larger [5x6x7x2x3x4 double] n-d array from the smaller ones, but expressions of the form X(i,j,k,:,:,:) do not produce one of the original smaller n-d arrays.
Below is an example of one of the things I've tried; the last output should match below should match the output shown for ndarrays{123} above, but doesn't:
>> X = reshape(cell2mat(ndarrays), [5:7 2:4]);
>> [idx{1:3}] = ind2sub(5:7, 123);
>> squeeze(X(idx{:}, :, :, :))
ans(:,:,1) =
62001 62003 62005
167001 167003 167005
ans(:,:,2) =
62007 62009 62011
167007 167009 167011
ans(:,:,3) =
62013 62015 62017
167013 167015 167017
ans(:,:,4) =
62019 62021 62023
167019 167021 167023
EDIT: OK, by (pretty much blind) trial-and-error I found that the following does the trick:
tmp = cellfun(#(c) reshape(c, [1 24]), ndarrays, 'un', 0);
X = reshape(cat(1, tmp{:}), [5:7 2:4]);
IOW: linearize the subarrays before passing them to cat(1, ...). I'm surprised that it's necessary to explicitly perform this linearization step: it's what I expect MATLAB to do by default (in cell2mat, for example).
Be that as it may, is there a better (or at least clearer/easier to understand) way to achieve the same effect?
(BTW, for this problem, the initial shape of ndarray is given and not subject to change. IOW, solutions that require modifying the makesubarray function in the example do not fit the situation I'm dealing with.)
EDIT2: In reference to Luis Mendo's answer, here's the output (copy-pasted verbatim from my MATLAB workspace) of a small script (with echo on), showing the sizes of various items:
n = prod(5:7);
makendarray = #(i) reshape(1:24, 2:4) + (i * 1000);
ndarrays = cellfun(makendarray, num2cell(1:n), 'un', 0);
size(ndarrays)
ans =
1 210
size(permute(ndarrays, [2 3 4 1]))
ans =
210 1
size(cell2mat(permute(ndarrays, [2 3 4 1])))
ans =
420 3 4
echo off;
You can achieve it with a little bit of permute and reshape. The logic of this approach can be followed by observing size(X) at the end of each step (indicated in the comments):
X = cell2mat(permute(ndarrays(:), [2 3 4 1])); %// size [2 3 4 210]
X = permute(X, [4 5 6 1 2 3]); %// size [210 1 1 2 3 4]
X = reshape(X, [7 6 5 2 3 4]); %// size [7 6 5 2 3 4]
X = permute(X, [3 2 1 4 5 6]); %// size [5 6 7 2 3 4]

MATLAB: dividing a cell's members into smaller cells according to a vector

I have a cell array of size 1*n (X=cell(1, n)). every cell in this cell array(X), has a different size. So, to be clear, here is an example:
X = {X1, X2, X3} (n=3)
X1 = [1 3 5 9]
X2 = [1 2 3 6 7]
X3 = [1 7 8 9]
I have a vector with length of m. for example:
Y = [0 3 6 9] (m=4)
I want to divide cells in my cell array according to this vector in this way:
X1-1=[1] (because 1>=Y(1) & 1<Y(2)), X1-2=[3 5] (because 3 and 5>=Y(2) & 1<Y(3)), X1-3=[9] (because 9>=Y(3) & 9<Y(4))
and so on for X2 and X3...
and finally I have:
Z = {X1-1 X2-1 X3-1
X1-2 X2-2 X3-2
X1-3 X2-3 X3-3}
Here is my code with for-loops:
Z = cell(size(X,2), size(Y,2));
for i = 1:size(X,2)
T = cell2mat(X(i));
for j = 1:size(Y,2)-1
idx = (T > Y(j) & T < Y(j+1));
Z{i,j} = {T(idx)'};
end
end
Z = cellfun(#(z) cell2mat(z), Z, 'UniformOutput', false);
I want to get the same results without using for-loops. Any vectorization ideas?
You can replace the two for loops with one cellfun and one arrayfun. Not necessarily faster, though:
Z = cellfun(#(Xi) arrayfun(#(k) Xi(Y(k)<=Xi&Xi<Y(k+1)), 1:numel(Y)-1, 'uni', 0).', X, 'uni', 0);
The desired results are stored in Z{1}{1} (corresponding to your X1-1), Z{1}{2} (corresponding to X1-2), etc.
Example: I'm using your data but changing Y to [0 3 6 9.1] (note that with Y = [0 3 6 9] the X1-3 in your example is not correct):
>> Z{1}{1}
ans =
1
>> Z{1}{2}
ans =
3 5
>> Z{1}{3}
ans =
9
>> Z{2}{1}
ans =
1 2

special add in matlab [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to Add a row vector to a column vector like matrix multiplication
I have a nx1 vector and a 1xn vector. I want to add them in a special manner like matrix multiplication in an efficient manner (vectorized):
Example:
A=[1 2 3]'
B=[4 5 6]
A \odd_add B =
[1+4 1+5 1+6
2+4 2+5 2+6
3+4 3+5 3+6
]
I have used bsxfun in MATLAB, but I think it is slow. Please help me...
As mentioned by #b3. this would be an appropriate place to use repmat. However in general, and especially if you are dealing with very large matrices, bsxfun normally makes a better substitute. In this case:
>> bsxfun(#plus, [1,2,3]', [4,5,6])
returns the same result, using about a third the memory in the large-matrix limit.
bsxfun basically applies the function in the first argument to every combination of items in the second and third arguments, placing the results in a matrix according to the shape of the input vectors.
I present a comparison of the different methods mentioned here. I am using the TIMEIT function to get robust estimates (takes care of warming up the code, average timing on multiple runs, ..):
function testBSXFUN(N)
%# data
if nargin < 1
N = 500; %# N = 10, 100, 1000, 10000
end
A = (1:N)';
B = (1:N);
%# functions
f1 = #() funcRepmat(A,B);
f2 = #() funcTonyTrick(A,B);
f3 = #() funcBsxfun(A,B);
%# timeit
t(1) = timeit( f1 );
t(2) = timeit( f2 );
t(3) = timeit( f3 );
%# time results
fprintf('N = %d\n', N);
fprintf('REPMAT: %f, TONY_TRICK: %f, BSXFUN: %f\n', t);
%# validation
v{1} = f1();
v{2} = f2();
v{3} = f3();
assert( isequal(v{:}) )
end
where
function C = funcRepmat(A,B)
N = numel(A);
C = repmat(A,1,N) + repmat(B,N,1);
end
function C = funcTonyTrick(A,B)
N = numel(A);
C = A(:,ones(N,1)) + B(ones(N,1),:);
end
function C = funcBsxfun(A,B)
C = bsxfun(#plus, A, B);
end
The timings:
>> for N=[10 100 1000 5000], testBSXFUN(N); end
N = 10
REPMAT: 0.000065, TONY_TRICK: 0.000013, BSXFUN: 0.000031
N = 100
REPMAT: 0.000120, TONY_TRICK: 0.000065, BSXFUN: 0.000085
N = 1000
REPMAT: 0.032988, TONY_TRICK: 0.032947, BSXFUN: 0.010185
N = 5000
REPMAT: 0.810218, TONY_TRICK: 0.824297, BSXFUN: 0.258774
BSXFUN is a clear winner.
In matlab vectorization, there is no substitute for Tony's Trick in terms of speed in comparison to repmat or any other built in Matlab function for that matter. I am sure that the following code must be fastest for your purpose.
>> A = [1 2 3]';
>> B = [4 5 6];
>> AB_sum = A(:,ones(3,1)) + B(ones(3,1),:);
The speed differential will be much more apparent (at LEAST an order of magnitude) for larger size of A and B. See this test I conducted some time ago to ascertain the superiority of Tony's Trick over repmatin terms of time consumption.
REPMAT is your friend:
>> A = [1 2 3]';
>> B = [4 5 6];
>> AplusB = repmat(A, 1, 3) + repmat(B, 3, 1)
AplusB =
5 6 7
6 7 8
7 8 9

How to get a linear array when indexing 3rd or higher dimensions in multidimensional arrays

Consider having the following multidimensional array:
A = [1 2;3 4];
B = [5 6;7 8];
C = cat(3, A, B);
Well it is like a cube, I want to slice the first row, slice the first column and that's it.
When I do:
C(1,1,:)
I get two separate answers:
C(1,1,1) = 1
C(1,1,2) = 5
And if I do
D = C(1,1,:)
I get D to be a multidimensional.
I want this
D = [1 5];
That's it, how to do this?
Use the squeeze() function to drop the extra dimensions:
>> squeeze(C(1,1,:))'
ans =
1 5