Using `bsxfun` for non-numeric data - matlab

Is there an equivalent to bsxfun for non-numeric data?
For example, I want to compare all pairs of strings stored in two cell-arrays:
>> a = {'aa', 'bb', 'cc'};
>> b = {'dd', 'aa'};
>> bsxfun( #strcmp, a, b' ); % not working for cells :-(

I like Rody's solution, but you could also do a workaround like this:
ia=(1:length(a)).'; ib=1:length(b);
a=a(:);
bsxfun(#(ii,jj) strcmp( a(ii),b(jj) ) ,ia, ib);

I'm afraid there's no such equivalent for cell-arrays :-(
As far as I can see, you can either:
Follow Oleg's suggestion and use loops
Use existing implementations such as mAryCellFcn or csxfun from the File Exchange.
Roll your own function. For example, here's a variant of Robert's idea that works for inputs of any dimensions (under the restrictions of bsxfun, of course) and an arbitrary binary function func:
function C = mybsxfun(func, A, B)
idx_A = reshape(1:numel(A), size(A));
idx_B = reshape(1:numel(B), size(B));
C = bsxfun(#(ii, jj)func(A(ii), B(jj)), idx_A, idx_B);
If your function can operate on entire cell arrays element-wise, you could perform a singleton expansion on your cell arrays first, and then feed them directly to the function func:
mask = bsxfun(#or, true(size(A)), true(size(B)));
idx_A = bsxfun(#times, mask, reshape(1:numel(A), size(A)));
idx_B = bsxfun(#times, mask, reshape(1:numel(B), size(B)));
C = func(A(idx_A), B(idx_B));
The latter method might be faster if func is optimized for vectorized operations on cell arrays.

How about
[str,ia,ib] = intersect(a,b)
?

As the error message says, broadcasting only works for numeric types. Here are other possible alternatives:
a = {'aa', 'bb', 'cc'};
b = {'dd'; 'aa'};
%# obviously doesnt work
%#bsxfun(#strcmp, a, b)
%# do the singleton expansion ourselves
strcmp(repmat(a,size(b)), repmat(b,size(a)))
%# if you dislike REPMAT, we can use Tony's trick
strcmp(a(ones(size(b)),:), b(:,ones(size(a))))
%# we could also use CELLFUN
cell2mat(cellfun(#(s)strcmp(a,s), b, 'UniformOutput',false))

Related

How to pad cell-arrays in Matlab

Does someone know how to pad cell-arrays? For regular (multi-dimensional) arrays, I can use the function
A = padarray(A,dim,value)
However it won't accept cells...
Been searching but I can't find anything in the docs or google, thought I'd ask before trying to reprogram padarray() to accept cells... if someone knows how to do this, it would be much appreciated.
You can always use cellfun to apply a function to each cell in a cell array:
padize = 2;
A = cellfun( #(x) padarray(x,padsize), A ,'uni', 0);
I'm pretty sure regular Matlab has nothing for cell arrays just like it has nothing for numeric ones (padarray belongs to the Image Processing Toolbox). How difficult it is depends on how generic you need it - the basic symmetric empty-padded fixed-number-of-dimensions case is trivial:
function y = padcell2d(x, r)
y = cell(size(x) + r.*2);
y(r+1:end-r, r+1:end-r) = x;
end
Add complexity as required.
Since it was fun thinking of the simplest way to handle arbitrary dimensions, here's a really hacky way of n-dimensional symmetric empty padding without loops (tested on Octave):
function x = padcell(x, r)
if any(r>0) && ~any(r<0)
if isscalar(r) % otherwise, assume it's a value-per-dimension vector
r = repmat(r, 1, ndims(x));
end
sz = num2cell(size(x) + 2*r);
x{sz{:}} = []; % hooray for comma-separated lists!
x = circshift(x, r);
end
end

arrayfun when each row of the array is an input

My situation is that I would like to map a scalar array A by a function with handle fun sending a row vector to a row vector, to acquire B, such that B(i,:) = fun(A(i,:)).
The most reasonable solution I could think of looks like:
temp = mat2cell(A,ones(1,size(A,1)),size(A,2));
B = cell2mat(cellfun(fun,temp,'UniformOutput',0));
However, the conversion to cells and back seems like overkill (and is assumably computationally expensive). It is also not clear to me why cellfun complains about non-uniform output. Does a more efficient way jump to mind?
There's another solution that employs accumarray. Although not as fancy as bsxfun, it doesn't require declaring a helper function:
subs = ndgrid(1:size(A, 1));
B = accumarray(subs(:), A(:), [], #fun); %// #fun is the function handle
You can do this without using cell arrays at all with bsxfun.
Using Marcin's example data and function:
A =[ 0.5669 0.4315 0.4515 0.7664 0.5923; ...
0.8337 0.7317 0.4898 0.2535 0.7506; ...
0.3321 0.5424 0.4585 0.8004 0.9564];
fun = #(x,y) x*2;
B= bsxfun(fun,A,1);
B =
1.1338 0.8630 0.9030 1.5328 1.1846
1.6674 1.4634 0.9796 0.5070 1.5012
0.6642 1.0848 0.9170 1.6008 1.9128
Edit:
As Eitan noted, fun above may need to be a wrapper on your 'real' anonymous function so it would be more complete to show my solution as:
fun = #(x) x *2; % Replace with actual anonymous function
fun2 = #(x,y) fun(x); % Wrapper on fun to discard unused 2nd parameter
B= bsxfun(fun2,A,1);
I know this is an old post, but in case someone else sees this, Matlab added rowfun to the 2013b release which can evaluate rows of tables and returns a column vector. Here is an example:
f = #(c) sum(c.^2);
x=[1 2;
3 4;
5 6];
z=table2array(rowfun(f,table(x)))
z=
5
25
61
I think you could do as follows, if I understand what u want to do:
A = rand(3, 5);
fun = #(x) x*2;
B = cell2mat(arrayfun(#(i) fun(A(i, :)), 1:size(A, 1), 'UniformOutput', false)');
% example results:
A =
0.5669 0.4315 0.4515 0.7664 0.5923
0.8337 0.7317 0.4898 0.2535 0.7506
0.3321 0.5424 0.4585 0.8004 0.9564
B =
1.1338 0.8630 0.9030 1.5328 1.1845
1.6675 1.4635 0.9795 0.5071 1.5013
0.6642 1.0848 0.9169 1.6008 1.9128
This will apply fun to each element of row in A. This is based on the post here. Also there u find more info and explanation what is happening or alternative ways of applying function to rows in an array.

Combining Anonymous Functions in MATLAB

I have a cell array of anonymous function handles, and would like to create one anonymous function that returns the vector containing the output of each function.
What I have:
ca = {#(X) f(X), #(X)g(X), ...}
What I want:
h = #(X) [ca{1}(X), ca{2}(X), ...]
Yet another way to it:
You can use cellfun to apply a function to each cell array element, which gives you a vector with the respective results. The trick is to apply a function that plugs some value into the function handle that is stored in the cell array.
ca = {#(X) X, #(X) X+1, #(X) X^2};
h=#(x) cellfun(#(y) y(x), ca);
gives
>> h(4)
ans =
4 5 16
You can use str2func to create your anonymous function without having to resort to eval:
ca = {#sin,#cos,#tan}
%# create a string, using sprintf for any number
%# of functions in ca
cc = str2func(['#(x)[',sprintf('ca{%i}(x) ',1:length(ca)),']'])
cc =
#(x)[ca{1}(x),ca{2}(x),ca{3}(x)]
cc(pi/4)
ans =
0.7071 0.7071 1.0000
I found that by naming each function, I could get them to fit into an array. I don't quite understand why this works, but it did.
f = ca{1};
g = ca{2};
h = #(X) [f(X), g(X)];
I feel like there should be an easier way to do this. Because I am dealing with an unknown number of functions, I had to use eval() to create the variables, which is a bad sign. On the other hand, calling the new function works like it is supposed to.

Matlab: how to create infinitly deep for-loops?

So we are given with some w and we want to so something like such pseudocode:
u = zeros(size(w));
for o=1:length(size(w))
for i=1:size(w)(1),
for j=1:size(w)(2),
...
for k=1:size(w)(length(size(w))),
u(i, j, ..., k )=1/(exp((-w(i,j, ..., k )))+25);
end
...
end
end
end
is such thing possible with Matlab and how to do it?
This is certainly possible, but also not recommended, since it's very non-idiomatic.
For simplicity, I assume that there is a typo in your question and that it should say exp(-w(i,j,...)).
Then, u can be calculated as
u = exp(-w);
You can use recursion - write a function, that iterates over the desired variables, and calls itself each time.
#Jonas's answer is the best option, however for a general-purpose code (one that #Jonas's 'matricized' answer does not apply), you can use a code-generation approach, for example:
fcode = fopen('manyForLoopsScript.m','w');
w = rand(2,3,4);
numLoops = length(size(w));
u = zeros(size(w));
% open the 'for' loops
for m = 1 : numLoops
fprintf(fcode, 'for a%d = 1:size(w,%d)\n',m,m);
end
% the actuall 'stuff'
indStr = [];
for m = 1 : numLoops
indStr = [indStr,sprintf('a%d,',m)];
end
indStr = indStr(1:end-1);
fprintf(fcode, ['u(' indStr ') = exp(-w(' indStr '));\n']);
% close the 'for' loops
for m = 1 : numLoops
fprintf(fcode, 'end\n');
end
fclose(fcode);
manyForLoopsScript
One option is to rewrite the function you want to apply to be vectorized, so that it is computed element-wise (the same way EXP built-in function works for scalar/vector/matrix input):
u = 1./(exp(-w)+25);
Otherwise, if you prefer a for-loop, all you need to do is to traverse the multi-dimensional input matrix using linear indices (in a column-major order), apply the function to each element, then RESHAPE the result back into the expected shape:
u = zeros(numel(w),1);
for i=1:numel(w)
u(i) = 1 ./ ( exp(-w(i)) + 25 );
end
u = reshape(u, size(w));
This is basically what the ARRAYFUN functions does for you:
u = arrayfun(#(x) 1./(exp(-x)+25), w);
In the chance that you also need access to the actual indices while looping over the elements of the matrix, you could always get them with IND2SUB (which converts linear indices to subscripts), or even generate all of them with functions like MESHGRID and NDGRID...

How to perform a checkerboard-interpolation in matlab?

I have two matrices A and B containing values for a checkerboard/chessboard-like grid of the form
AxAxAxAx...
xBxBxBxB...
AxAxAxAx...
xBxBxBxB...
...........
...........
Where x represents values not yet known which I want to (linearly) interpolate. What's the easiest way to achieve this?
First thing is probably
C = zeros(size(A)+size(B));
C(1:2:end,1:2:end) = A;
C(2:2:end,2:2:end) = B;
to obtain aforementioned matrix. Now I could loop through all remaining points and take the average of all direct neighbours, for 1) for loops in matlab are slow and 2) there's certainly a way to use interp2, though that seems to require a meshgrid-like grid. So, can this be done easier/faster?
Thanks to woodchips' answer here I found his inpaint_nans, the solution is indeed simple:
C = nan(size(A)+size(B));
C(1:2:end, 1:2:end) = A;
C(2:2:end, 2:2:end) = B;
C = inpaint_nans(C);