强制“独特”将NaNs视为模糊不清

时间:2014-08-26 23:49:15

标签: matlab

我有一个代表分子状态的长数字矩阵。子集可能如下所示:

 states = [...
  1     1     1     1
  1     1     1     1
  1     0     1     1
NaN     0   NaN   NaN
  1     0     1     0
  1     0     1     1
NaN   NaN   NaN   NaN
  1     0     1     1
NaN   NaN   NaN   NaN
  1     1     0     0
 ];

其中NaN值适用于表示未知的状态。实际上,此列表可能包含数十万个值。如果我使用unique命令获取唯一状态,结果看起来像

K>>unique(states,'rows')

ans = 

     1     0     1     0
     1     0     1     1
     1     1     0     0
     1     1     1     1
   NaN     0   NaN   NaN
   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN

因为" unique将NaN值视为不同的"。

如何按下此输出,使NaN值不明显?因此[NaN NaN NaN NaN][NaN 0 NaN NaN]不同,但[NaN NaN NaN NaN] == [NaN NaN NaN NaN]

3 个答案:

答案 0 :(得分:2)

这是一个部分(并且不令人满意)的答案,适用于这个特定的实例,但不是一般的:

states(isnan(states)) = inf;
uniqueStates = unique(states,'rows');
uniqueStates(~isfinite(uniqueStates)) = nan;

显然MATLAB不会将Inf值视为不同。我不打算在我的应用程序中包含任何Inf值,但我当然可以想象一个同时包含InfNaN值的情况,在这种情况下,这会使它们混乱

实际答案:

所以@Louis Mendo删除了他的评论,这使我得出了最终答案,但这似乎很有力:

function C = nanUnique(varargin)

A = varargin{1};
dummyVal = ceil(max(A(isfinite(A(:))))) + 1;
A(isnan(A)) = dummyVal;
C = unique(A,varargin{2:end});
C(C==dummyVal) = nan;

简而言之,找到最大值(即无穷大)。向上舍入的值+ 1是一个整数(没有浮点错误)并保证是唯一的。将所有NaN替换为虚拟值,运行unique,然后将NaN放回原来的位置。

答案 1 :(得分:2)

<强>代码

%// Get unique rows with in-built "unique" that considers NaN as distinct
unq1 = unique(states,'rows');

%// Detect nans
unq1_nans = isnan(unq1);

%// Find nan equalities across rows
unq1_nans_roweq = bsxfun(@plus,unq1_nans,permute(unq1_nans,[3 2 1]))==2;

%// Find non-nan equalities across rows
unq1_nonans_roweq = bsxfun(@eq,unq1,permute(unq1,[3 2 1]));

%// Find "universal" (nan or non-nan) equalities across rows
unq1_univ_roweq = unq1_nans_roweq | unq1_nonans_roweq;

%// Remove non-unique rows except the first non-unique match as with 
%// the default functionality of MATLAB's in-built unique function
out = unq1(~any(triu(squeeze(sum(unq1_univ_roweq,2)==size(states,2)),1),1),:);

示例#1

输入 -

states =
    3.0000    1.0000    7.0000    8.0000
    8.0000         0    1.0000    6.0000
       Inf         0       NaN       NaN
    5.0000         0    1.0000         0
       Inf         0       NaN       NaN
    7.0000         0    5.0000    1.0000
       NaN       NaN   11.2000       Inf
       NaN       NaN   15.0000       NaN
       NaN       NaN   11.2000       Inf

使用MATLAB的内置unique + 'rows' -

的中间结果
unq1 =
    3.0000    1.0000    7.0000    8.0000
    5.0000         0    1.0000         0
    7.0000         0    5.0000    1.0000
    8.0000         0    1.0000    6.0000
       Inf         0       NaN       NaN
       Inf         0       NaN       NaN
       NaN       NaN   11.2000       Inf
       NaN       NaN   11.2000       Inf
       NaN       NaN   15.0000       NaN

请注意,两个具有相同值的行 - [Inf 0 NaN NaN]仍在显示,同样我们还有另一个相同的对 - [NaN NaN 11.2000 Inf]。我们需要为这两对中的每一对保留一个唯一的行。让我们看看我们的代码如何执行 -

out =
    3.0000    1.0000    7.0000    8.0000
    5.0000         0    1.0000         0
    7.0000         0    5.0000    1.0000
    8.0000         0    1.0000    6.0000
       Inf         0       NaN       NaN
       NaN       NaN   11.2000       Inf
       NaN       NaN   15.0000       NaN

没关系!

示例#2

对于最终测试,让我们测试一下输入数组中有大数字的情况,如下所示 -

states =
            3            1            7            8
            8            0            1            6
          Inf            0          NaN          NaN
            5            0            1            0
          Inf            0          NaN          NaN
            7            0            5            1
          NaN          NaN       1e+100          Inf
          NaN          NaN           15          NaN
          NaN          NaN       1e+100          Inf

unique + 'rows'的中间结果为 -

unq1 =
            3            1            7            8
            5            0            1            0
            7            0            5            1
            8            0            1            6
          Inf            0          NaN          NaN
          Inf            0          NaN          NaN
          NaN          NaN           15          NaN
          NaN          NaN       1e+100          Inf
          NaN          NaN       1e+100          Inf

因此,我们的代码必须删除最后两行中的一行。

out =
            3            1            7            8
            5            0            1            0
            7            0            5            1
            8            0            1            6
          Inf            0          NaN          NaN
          NaN          NaN           15          NaN
          NaN          NaN       1e+100          Inf

确实如此!

答案 2 :(得分:1)

建议的其他解决方案更简单。 但是你可以考虑继承double类型。 您需要通过创建自己的eq()方法覆盖==运算符,该方法应使用isequaln()(NaN值被视为相等)。 无论如何,需要考虑的事情。