MATLAB中的地图功能?


100

我对MATLAB没有Map函数感到有些惊讶,所以我自己一起破解了一个函数,因为这是我不能没有的。有更好的版本吗?是否缺少我所缺少的针对MATLAB的标准功能编程库?

function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
    results(1,k) = f(list(k));
end

end

用法例如

map( @(x)x^2,1:10)

12
从其他语言到Matlab的第1课:不要用于循环,它们比矢量化解决方案要慢几个数量级。
CookieOfFortune 2009年

15
随着JIT的引入,for循环不再像以前那样受到惩罚。
MatlabDoug

@CookieOfFortune我认为这已经不对了……
Ander Biguri

2
@AnderBiguri我认为他们已经添加了一些改进,但是速度仍然慢得多。
CookieOfFortune

File Exchange上的功能库具有mapfoldl(也称为reduce),select(又名filter)和其他必不可少的东西。推荐(如果您必须使用Matlab)。
艾哈迈德·法西

Answers:


133

简短的答案:内置函数arrayfunmap作用与数字数组的作用完全相同:

>> y = arrayfun(@(x) x^2, 1:10)
y =

     1     4     9    16    25    36    49    64    81   100

还有另外两个内置函数,它们的行为类似:(cellfun对单元数组的元素进行操作)和structfun(对结构的每个字段进行操作)。

但是,如果您利用矢量化功能,特别是使用逐元素算术运算符,则通常不需要这些功能。对于您给出的示例,矢量化解决方案将是:

>> x = 1:10;
>> y = x.^2
y =

     1     4     9    16    25    36    49    64    81   100

某些操作会自动跨元素进行操作(例如将标量值添加到向量中),而其他一些运算符则具有针对逐元素运算的特殊语法(由.运算符之前的a表示)。MATLAB中许多内置函数都设计为使用逐元素运算(通常应用于给定维,例如summean)对向量和矩阵自变量进行运算,因此不需要映射函数。

总结一下,这是将数组中的每个元素平方的一些不同方法:

x = 1:10;       % Sample array
f = @(x) x.^2;  % Anonymous function that squares each element of its input

% Option #1:
y = x.^2;  % Use the element-wise power operator

% Option #2:
y = f(x);  % Pass a vector to f

% Option #3:
y = arrayfun(f, x);  % Pass each element to f separately

当然,对于这样一个简单的操作,选项1是最明智(和最有效)的选择。


2
应该注意的是,选项1不仅更简单,而且更快(与选项3、2​​相比,选项1非常相似)!
Diederick C. Niehorster 2014年

10

除了矢量和逐元素运算,还cellfun可以在单元阵列上映射函数。例如:

cellfun(@upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans = 
    'A'    'B'    'C'

如果'UniformOutput'为true(或未提供),它将尝试根据单元格数组的大小来串联结果,因此

cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC

2

使用Matlab的向量化的一个相当简单的解决方案是:

a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array

现在,输入

c( b ) = a

退货

c = 0    50     0    40     0    30     0    20     0    10

c(b)是对大小为5的向量的引用,其中c的元素位于b给出的索引处。现在,如果将值赋给该参考矢量,则c中的原始值将被覆盖,因为c(b)包含对c中的值的引用,并且没有副本。


1

如果需要的结果是函数数组,则似乎内置的arrayfun不起作用:例如:map(@(x)[xx ^ 2 x ^ 3],1:10)

下面的一些mod可以使这项工作更好:

function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
    if (k==1)
        r1=f(list(k));
        results = zeros(length(r1),length(list));
        results(:,k)=r1;
    else
        results(:,k) = f(list(k));

    end;
end;
end

5
ARRAYFUN适用于您的示例,您只需要包括输入参数..., 'UniformOutput', false);即可创建包含数组的单元格数组输出,然后格式化并组合它们(如果需要)到非单元格数组中。
gnovice 2012年

0

如果matlab没有内置的地图功能,则可能是出于效率方面的考虑。在您的实现中,您使用循环来循环访问列表中的元素,而列表元素通常在matlab世界中不受欢迎。大多数内置的matlab函数都是“向量化的”,即,在整个数组上调用函数要比自己遍历并为每个元素调用函数要有效。

换句话说,这


a = 1:10;
a.^2

比这快得多


a = 1:10;
map(@(x)x^2, a)

假设您对地图的定义。


2
我认为他的意思不是说他希望它一定要循环,而只是要指定为具有将所提供的函数应用于所提供的数组的相应元素的结果数组。我对matlab不太了解,但似乎arrayfun可以完成这项工作。

1
大多数内置的Matlab函数和运算符已经做到了:它们对输入数组的每个元素进行运算,并返回相应的结果数组。
迪马

0

您不需要,map因为应用于值列表的标量函数已应用于每个值,因此其工作原理类似于map。试一试

l = 1:10
f = @(x) x + 1

f(l)

在您的特定情况下,您甚至可以写

l.^2

9
-1:事实并非如此。Matlab没有足够强大的类型系统来指定标量函数。使用向量调用f,并且在您的示例中执行一次向量加法。为了验证这一点,请对您的代码示例进行概要分析(在运行代码之前“概要分析”,然后在其后“概要分析报告”)。您会看到对f的一个调用。
2009年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.