发现聚集的NaNs,但只留下孤独的NaNs

时间:2014-04-12 20:06:08

标签: matlab nan

我的数据集不完整,

http://imgur.com/Tpu6Hcf

N = [NaN 1 2 3 NaN 5 6 NaN NaN 7 8 10 12 20 NaN NaN NaN NaN NaN]'

我希望识别一组Nans,也就是说,如果它们的后续数量超过2.我该怎么做?

4 个答案:

答案 0 :(得分:5)

你可以这样做:

aux = diff([0; isnan(N); 0]);
clusters = [find(aux == 1) find(aux == -1) - 1];

然后,群集将是Nx2矩阵,其中N是NaN群集的数量(所有群集),每行都为您提供群集的起始和结束索引。

在这个例子中,那将是:

clusters =

     1     1
     5     5
     8     9
    15    19

这意味着你有4个NaN集群,集群1从索引1到索引1,集群2从5到5,集群3从8到9,集群4从15到19。

如果您只想要具有至少K个NaN的群集,您可以这样做(例如,K = 2):

K = 2;
clusters(clusters(:,2) - clusters(:,1) + 1 >= K, :)

那会给你这个:

ans =

     8     9
    15    19

即,簇8-9和15-19具有2个或更多个NaN。

<强>解释

  • 查找群集

isnan(N)为您提供一个包含NaN为1的逻辑向量:

N --------> NaN 1  2  3 NaN 5  6 NaN NaN 7  8 10 12 20 NaN NaN NaN NaN NaN
isnan(N) ->  1  0  0  0  1  0  0  1   1  0  0  0  0  0  1   1   1   1   1

我们想知道每个序列的起始位置,因此我们使用diff计算每个值减去前一个值,然后给出:

aux = diff(isnan(N));
N ----> NaN 1  2  3 NaN 5  6 NaN NaN 7  8 10 12 20 NaN NaN NaN NaN NaN
aux --> -1  0  0  1 -1  0  1  0  -1  0  0  0  0  1   0   0   0   0

1表示组开始,-1表示组结束。但它错过了第一组开始和最后一组结束,因为第一个1元素不存在(它没有前一个N,因为它是第一个)和最后一个-1 {1}}也不存在(因为1上的最后N之后没有任何内容。一个常见的解决方法是在数组之前和之后添加零,这样就可以得到:

aux = diff([0; isnan(N); 0]);
N ----> NaN 1  2  3 NaN 5  6 NaN NaN 7  8 10 12 20 NaN NaN NaN NaN NaN
aux -->  1 -1  0  0  1 -1  0  1  0  -1  0  0  0  0  1   0   0   0   0  -1

注意两件事:

  1. 如果索引i的差异为1,则N(i)是NaN块的开头。
  2. 如果索引i的差异为-1,则 N(i - 1) 是NaN块的结尾。
  3. 要获得开始和结束,我们使用find来获取aux == 1和aux == -1的索引。因此,我们拨打find两次,并使用[]连接两个来电:

    aux = diff([0; isnan(N); 0]);
    clusters = [find(aux == 1) find(aux == -1) - 1];
    
    • 过滤具有K个或更多元素的群集

    最后一步是找到具有K个或更多元素的集群。为此,我们首先采用聚类矩阵并从第一列中减去第一列,并添加1,如下所示:

    clusters(:,2) - clusters(:,1) + 1
    ans = 
         1
         1
         2
         5
    

    这意味着簇1和2具有1个NaN,簇3具有3个NaN,簇4具有5个NaN。如果我们问哪个值大于或等于K,我们得到这个:

    clusters(:,2) - clusters(:,1) + 1 >= K
    ans =
         0
         0
         1
         1
    

    它是一个逻辑阵列。我们可以使用它来仅索引集群矩阵的1(true)行,如下所示:

    clusters(clusters(:,2) - clusters(:,1) + 1 >= K, :)
    ans =
    
         8     9
        15    19
    

    这就像问:给我们只给出行与这个逻辑向量上的行匹配的簇,并给我们所有列(用:表示)。

答案 1 :(得分:2)

这是一个模块化解决方案:

% the number of NaN you consider as a cluster
num = 3;

% moving average filter
Z = filter(ones(num,1),1,isnan(N));

x = arrayfun(@(x) find(Z == num) - num + x, 1:num,'uni',0)
y = unique(cell2mat(x))

(更新:以下更快的版本)

给出了num = 1

y =     1     5     8     9    15    16    17    18    19

代表num = 2

y =     8     9    15    16    17    18    19

代表num = 3num = 4num = 5

y =    15    16    17    18    19

最后是num = 6 ...以及更多

y =   Empty matrix: 1-by-0

<强>解释

isnan(N)返回一个逻辑数组,其中的位置为NaN

Z = filter(ones(num,1),1,isnan(N));是移动平均滤镜的实现,滤镜窗口为ones(num,1) = [1 1 1]num = 3)。因此,当行中有3个num = 3时,大小为3的过滤器会滑动数组并达到值NaN。 所以它的basicall看起来像:

%//  N   isnan(N)     Z

   NaN          1     1
     1          0     1
     2          0     1
     3          0     0
   NaN          1     1
     5          0     1
     6          0     1
   NaN          1     1
   NaN          1     2
     7          0     2
     8          0     1
    10          0     0
    12          0     0
    20          0     0
   NaN          1     1
   NaN          1     2
   NaN          1     3
   NaN          1     3
   NaN          1     3

现在很容易找到所有 3 find(Z == num)的元素 - 但您还需要之前的所有 2 find(Z == num) - num + 2和以前所有 1 find(Z == num) - num + 1。而不是使用循环arrayfun,这基本上是相同的。结果你会得到一个包含很多索引的矩阵,其中很多都是多余的,但你只需要unique个。我希望现在一切都清楚了。

实际上,将find从arrayfun中取出会快得多,然后甚至可以用bsxfun代替,你可以摆脱cell2mat,这会导致以下形式:

<强>更快

Z = find( filter(ones(num,1),1,isnan(N)) == num ) - num;
y = unique( bsxfun(@plus, Z,1:num) );

或更快的强制性花哨的单行:

y = unique(bsxfun(@plus,find(filter(ones(num,1),1,isnan(N))==num)-num,1:num));

答案 2 :(得分:1)

STRFIND方法

<强>予。 Fancy One Liner:

%%// Given input N
N = [NaN 1 2 3 NaN 5 6 NaN NaN 7 8 10 12 20 NaN NaN NaN NaN NaN]

out = [strfind(num2str(isnan([ 0 N 0]),'%1d'),'011');strfind(num2str(isnan([ 0 N 0]),'%1d'),'110')]'

<强>输出

out =

 8     9
15    19

<强> II。详细解释:

基本上你正在尝试进行滑动窗口检查,在使用双数组时没有直接的方法,但在转换为字符串后,可以使用strfind。这个技巧在这里使用。

我建议按照代码中使用的注释和输出数字来理解它。请注意,对于这种特殊情况,群集意味着一组两个或多个连续的NaN

<强>代码

%%// Given input N
N = [NaN 1 2 3 NaN 5 6 NaN NaN 7 8 10 12 20 NaN NaN NaN NaN NaN]

%%// Set the locations where NaNs are present and then 
%%// append at the start and end with zeros
N2 = isnan([ 0 N 0])

%%// Find the start indices of  all NaN clusters
start_ind = strfind(num2str(N2,'%1d'),'011')

%%// Find the stop indices of  all NaN clusters
stop_ind = [strfind(num2str(N2,'%1d'),'110')]

%%// Put start and stop indices into a Mx2 matrix
out = [start_ind' stop_ind']

<强>输出

N =
       NaN  1  2  3  NaN  5  6  NaN NaN  7  8  10  12  20 NaN NaN NaN NaN NaN

N2 =
     0  1   0  0  0   1   0  0   1   1   0  0   0   0   0  1   1   1   1   1   0

start_ind =
     8    15

stop_ind =
     9    19

out =
     8     9
    15    19

答案 3 :(得分:0)

这使用diffRafael Monteiro's answer,但似乎更简单:

ind = diff([0; isnan(N(:))]);
result = find(ind(1:end-1)==1 & ind(2:end)==0);

在您的示例中,这会给出[8 15]

工作原理ind获取值:

  • 1其中一个(一个或多个)NaN值的运行开始;
  • 0其中NaN和数字相对于之前的值没有变化;
  • -1开始运行(一个或多个)数值。

第二行选择NaN开始运行的位置,以便下一个位置也是NaN。因此,它根据需要为每次运行提供多个NaN