在满足特定条件时,用NaN或Inf替换值

时间:2016-12-27 12:15:31

标签: matlab multidimensional-array replace vectorization nan

我创建了以下三维模型矩阵:

mockup(:,:,1) = ...
    [100, 100, 100; ...
    103, 95, 100; ...
    101, 85, 100; ...
    96, 90, 102; ...
    91, 89, 99; ...
    97, 91, 97; ...
    105, 83, 100];

mockup(:,:,2) = ...
    [50, NaN, NaN; ...
    47, NaN, 40; ...
    45, 60, 45; ...
    47, 65, 45; ...
    51, 70, 45; ...
    54, 65, 50; ...
    62, 80, 55];

我还定义了percentTickerAvailable = 0.5

因此,这些列代表三种不同资产的股票价格。为了进一步处理,我需要以下列方式操作NaN值。

  1. 如果任何给定ROW中NaN的百分比大于1 - percentTickerAvailable,则用NaN替换这些特定行中的所有值。也就是说,如果没有足够的资产在该特定行中有价格,则完全忽略该行。
  2. 如果任何给定ROW中NaN的百分比小于或等于1 - percentTickerAvailable,请用-inf替换相应的NaN。
  3. 要明确的是,“任何给定行中的NaN百分比”计算如下: 任何给定ROW中的NaN数除以列数。

    调整后的模型矩阵应如下所示:

    mockupAdj(:,:,1) = ...
        [100, 100, 100; ...
        103, 95, 100; ...
        101, 85, 100; ...
        96, 90, 102; ...
        91, 89, 99; ...
        97, 91, 97; ...
        105, 83, 100];
    
    mockupAdj(:,:,2) = ...
        [NaN, NaN, NaN; ...
        47, -inf, 40; ...
        45, 60, 45; ...
        47, 65, 45; ...
        51, 70, 45; ...
        54, 65, 50; ...
        62, 80, 55];
    

    到目前为止,我做了以下事情:

    function vout = ranking(vin, percentTickerAvailable)
    
    percentNonNaN = 1 - sum(isnan(vin), 2) / size(vin, 2);
    NaNIdx = percentNonNaN < percentTickerAvailable;
    infIdx = percentNonNaN > percentTickerAvailable & ...
        percentNonNaN < 1;
    [~, ~, numDimVin] = size(vin);
    
    for i = 1 : numDimVin
        vin(NaNIdx(:,:,i) == 1, :, i) = NaN;
    end
    
    about = vin;
    
    end % EoF
    

    通过调用mockupAdj = ranking(mockup, 0.5),这已经将mockup(1,:,2)中的第一行正确转换为{'NaN', 'NaN', 'NaN'}。但是,我正在努力争取第二点。使用infIdx我已经成功识别出与第二个条件对应的行。但我不知道如何正确使用该信息,以便用mockup(2,2,2)替换-inf中的单个NaN。

    任何提示都受到高度赞赏。

3 个答案:

答案 0 :(得分:4)

这是可以使用矢量化解决的一个很好的例子。我提供了两个版本的代码,一个使用现代语法(包括隐式扩展),另一个使用旧版本的MATLAB。

有几点需要注意:

  • NaN替换阶段,我使用&#34;技巧&#34;其中0/0的评估结果为NaN
  • Inf替换阶段,我使用逻辑屏蔽/索引来访问vin中的正确元素。

R2016b及更新版本:

function vin = ranking (vin, percentTickerAvailable)
  % Find percentage of NaNs on each line:
  pNaN = mean(isnan(vin), 2, 'double');
  % Fills rows with NaNs:
  vin = vin + 0 ./ (1 - ( pNaN >= percentTickerAvailable));
  % Replace the rest with -Inf
  vin(isnan(vin) & pNaN < percentTickerAvailable) = -Inf;
end 

在R2016b之前:

function vin = rankingOld (vin, percentTickerAvailable)
  % Find percentage of NaNs on each line:
  pNaN = mean(isnan(vin), 2, 'double');
  % Fills rows with NaNs:
  vin = bsxfun(@plus, vin, 0 ./ (1 - ( pNaN >= percentTickerAvailable)));
  % Replace the rest with -Inf
  vin(bsxfun(@and, isnan(vin), pNaN < percentTickerAvailable)) = -Inf;
end

答案 1 :(得分:3)

1)

  

任何给定行中NaN的百分比应小于1

......你在谈论比率吗?在这种情况下,这是一个无用的检查,因为它总是如此。或者谈论百分比?在这种情况下,您的代码不会按照您的描述进行操作。我的猜测是比率。

2)根据我的猜测,我有一个跟进问题:按照你的描述,不应该模拟(2,2,2)留下NaN?该行中有33%(<50%)的NaN,因此不符合您的条件2.

3)基于我认为合乎逻辑的答案,我会更改percentNaN = sum(isnan(vin), 2) / size(vin, 2);以提高可读性,并NaNIdx = percentNaN > percentTickerAvailable;相应地更改。vin(isnan(vin)) = -inf; 现在只需在循环前添加一行:

startDate

为什么呢?因为像这样你用-inf替换所有的NaN。稍后,通过循环将那些尊重条件1的条件再次覆盖到NaN。你不需要InfIdx。

4)请注意,您的功能目前无法返回vout。让它回归vin,你就没事了。

答案 2 :(得分:1)

您还可以使用逻辑索引来完成此任务:

x(:,:,1) = ...
    [100, 100, 100; ...
    103, 95, 100; ...
    101, 85, 100; ...
    96, 90, 102; ...
    91, 89, 99; ...
    97, 91, 97; ...
    105, 83, 100];

x(:,:,2) = ...
    [50, NaN, NaN; ...
    47, NaN, 40; ...
    45, 60, 45; ...
    47, 65, 45; ...
    51, 70, 45; ...
    54, 65, 50; ...
    62, 80, 55];

    % We fix the threshold
    tres = 0.5; %fix the threshold.

    % We check if a value = NaN or not.
    in  = isnan(x);
    % Which line have more than 50% of NaN ?.
    ind = (sum(in,2)./(size(x,2)))>0.5
    % We generate an index
    [x1,~,x3] = ind2sub(size(ind),ind);
    % We set the NaN index to 0 if the line contains less than 50 % of NaN.
    in(x1,:,x3) = 0;

    % We calculate the new values.
    x(in) = -inf;
    x(x1,:,x3) = NaN;