【问题标题】:How do I do "map" in octave/matlab? [duplicate]如何在 octave/matlab 中做“地图”? [复制]
【发布时间】:2014-10-10 18:34:47
【问题描述】:

这里有一点八度音程代码

>> [4,5]([1,2,1])
ans =

   4   5   4

我称之为在向量 [1,2,1] 上映射函数 1->4, 2->5。

但我想做的地图是 0->1,25->2,240->9, NaN->0 在一个很长的向量上。

如果向量中有除 0,25,240,NaN 之外的任何值,我也希望它进行轰炸。

我可以看到各种方法来实现这一点,但都不是优雅的,我想知道是否有一种惯用的方式来表达它。

【问题讨论】:

标签: matlab function map octave


【解决方案1】:

如果您可以使用 MATLAB,我建议您使用 containers.Map 范式,它本质上是一个执行键/值查找的关联数组。如果您输入不属于字典的值,它也会吐出错误。因此,您只需为映射中的每个元素提供输入/输出关系。输入就是所谓的键,输出就是所谓的值。

完成后,将输入值的元胞数组提供到字典中/将数组与values 函数关联,然后在完成后将此元胞数组转换回数值向量。从您的输入/输出对来看,您希望输入为double,输出也为double。但是,containers.Map 的问题在于,NaN 不能用作密钥。因此,一种解决方法是将输入数值向量中的每个元素转换为字符键元胞数组,并将输出定义为字符值元胞数组。

我们可以使用arrayfun 来实现这一点,它对数组中的每个值执行操作。这很像for 循环,但特别的是,如果将uni 标志指定为0,则每个对应元素的输出都会转换为字符串。然后,您可以使用这些字符元胞数组创建字典。现在,要对输入进行映射,您还必须将它们转换为字符元胞数组,然后使用values 获取相应的输出,然后使用str2double 转换每个输出元胞元素返回并将其放入数值数组中。

因此,借用 Huntj 的示例输入,您必须这样做:

%// Input-Output relationships
in = [0,25,240,NaN];
out = [1,2,9,0];

%// Test inputs
vector = [0, 25, 240, NaN, 0, 25, 240, NaN];

% // For declaring our dictionary
in_cell = arrayfun(@num2str, in, 'uni', 0);
out_cell = arrayfun(@num2str, out, 'uni', 0);

% // Input test into dictionary
vector_cell = arrayfun(@num2str, vector, 'uni', 0);

% // Create dictionary
dict = containers.Map(in_cell, out_cell);

% // Put in inputs to be mapped
output_cell = values(dict,vector_cell);

% // Convert back to numeric array
output = str2double(output_cell);

这就是我使用上面的代码得到的结果,我们的最终输出存储在output

output =

     1     2     9     0     1     2     9     0

【讨论】:

  • 这不是问题中提到的“非常长的向量”的最快解决方案,但无论如何我发现它非常有趣。
  • @huntj 我同意,但我想我会提出一些建议!
【解决方案2】:

以下代码可能并不优雅,但至少是矢量化的。

vector = [0, 25, 240, NaN, 0, 25, 240, NaN];
old_value = [0, 25, 240, NaN];
new_value = [1,  2,   9,   0];
assert(size_equal(old_value, new_value))
total_mask = false;
for idx = 1:numel(old_value)
    if isnan(old_value(idx))
        partial_mask = isnan(vector);
    else
        partial_mask = vector == old_value(idx);
    endif
    vector(partial_mask) = new_value(idx);
    total_mask |= partial_mask;
endfor
assert(all(total_mask), "some values were unexpected")
vector

给了

    1   2   9   0   1   2   9   0

如果值不在给定的old_values 中,则会产生错误。

编辑[更紧凑,但更需要内存,而不是更快]:

vector = [0, 25, 240, NaN, 0, 25, 240, NaN];
old_values = [0, 25, 240, NaN];
new_values = [1,  2,   9,   0];
mask__ = vector == old_values(:);
mask__(isnan(old_values), :) = isnan(vector);
assert(all(any(mask__, 1)), "some values were unexpected")
vector = cellfun(@(mask_) new_values(mask_), num2cell(mask__, 1))

【讨论】:

  • 我不会称它为“矢量化”,因为您使用的是 for 循环,但代码还是不错的!
  • @rayryeng 诚然,我应该写“部分矢量化”,但它实际上是沿最大维度矢量化的(问题提到了“非常长的矢量”)。所以性能应该已经相当不错了吧?
  • 是的...我同意 :) 无论如何,这可能会被 JIT 加速,因为操作看起来相当良性。
【解决方案3】:

这可以通过逻辑索引轻松完成。使用huntj的起始向量:

allowed = [0, 25, 240];
v = [0; 25; 240; NaN; 0; 25; 240; NaN];

notAllowed = not(any(bsxfun(@eq,v,allowed),2));
notNaN     = not(isnan(v));
if any(notAllowed & notNaN)
    error('Illegal entry');
end

v( v == 0   ) = 1;
v( v == 25  ) = 2;
v( v == 240 ) = 9;
v( isnan(v) ) = 0;

为了使bsxfun() 工作,v 需要是一个列向量。 NaN 未包含在 allowed 表中,因为 NaN == NaN 始终为 false。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多