【问题标题】:find longest sequence of non nan values but allow for threshold找到最长的非 nan 值序列,但允许阈值
【发布时间】:2015-09-08 11:40:42
【问题描述】:

是否可以找到向量的非 nan 值但也允许 n 个 nan?例如,如果我有以下数据:

X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24]; %// input array
thres = 1; % this is the number of nans to allow

我只想保留最长的非 nan 值序列,但允许在数据中保留 'n' 个 nan。所以,假设我愿意保留 1 nan,我会输出

X_out = [8 10 11 nan 9 14 6 1 4 23 24]; %// output array

也就是说,开头的两个 nan 已被删除,因为它们超过了上面 'thres' 中的值,但第三个 nan 是独立的,因此可以保留在数据中。我想开发一种可以将 thres 定义为任何值的方法。

我可以用

找到非 nan 值
Y = ~isnan(X); %// convert to zeros and ones

有什么想法吗?

【问题讨论】:

  • 这听起来很棘手。您可以获取每个nan,将它们一个接一个地替换为非nan,并在每个修改后的向量中搜索最长的非nan 序列,然后选择最长的。但是这样比较麻烦,不知道有没有更简单的方法。哦,我的例子假设thres=1。您必须进行统计组合才能将nans 与非nans 切换。
  • 删除数据时,是删除到最后一个NaN还是删除到Vector的开头?
  • 删除到最后一个 nan。

标签: matlab


【解决方案1】:

为了找到最多包含thresholdNaN 的最长序列,我们必须找到所述序列的开始和结束。

要生成所有可能的起点,我们可以使用hankel

H = hankel(X)

H =

    18     3   NaN   NaN     8    10    11   NaN     9    14     6     1     4    23    24
     3   NaN   NaN     8    10    11   NaN     9    14     6     1     4    23    24     0
   NaN   NaN     8    10    11   NaN     9    14     6     1     4    23    24     0     0
   NaN     8    10    11   NaN     9    14     6     1     4    23    24     0     0     0
     8    10    11   NaN     9    14     6     1     4    23    24     0     0     0     0
    10    11   NaN     9    14     6     1     4    23    24     0     0     0     0     0
    11   NaN     9    14     6     1     4    23    24     0     0     0     0     0     0
   NaN     9    14     6     1     4    23    24     0     0     0     0     0     0     0
     9    14     6     1     4    23    24     0     0     0     0     0     0     0     0
    14     6     1     4    23    24     0     0     0     0     0     0     0     0     0
     6     1     4    23    24     0     0     0     0     0     0     0     0     0     0
     1     4    23    24     0     0     0     0     0     0     0     0     0     0     0
     4    23    24     0     0     0     0     0     0     0     0     0     0     0     0
    23    24     0     0     0     0     0     0     0     0     0     0     0     0     0
    24     0     0     0     0     0     0     0     0     0     0     0     0     0     0 

现在我们需要找到每一行中最后一个有效元素。 为此,我们可以使用cumsum:

C = cumsum(isnan(H),2)

C =

     0     0     1     2     2     2     2     3     3     3     3     3     3     3     3
     0     1     2     2     2     2     3     3     3     3     3     3     3     3     3
     1     2     2     2     2     3     3     3     3     3     3     3     3     3     3
     1     1     1     1     2     2     2     2     2     2     2     2     2     2     2
     0     0     0     1     1     1     1     1     1     1     1     1     1     1     1
     0     0     1     1     1     1     1     1     1     1     1     1     1     1     1
     0     1     1     1     1     1     1     1     1     1     1     1     1     1     1
     1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0

每一行的终点都是那个,C中对应的元素最多是threshold

threshold = 1;

T = C<=threshold

T =

 1     1     1     0     0     0     0     0     0     0     0     0     0     0     0
 1     1     0     0     0     0     0     0     0     0     0     0     0     0     0
 1     0     0     0     0     0     0     0     0     0     0     0     0     0     0
 1     1     1     1     0     0     0     0     0     0     0     0     0     0     0
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1
 1     1     1     1     1     1     1     1     1     1     1     1     1     1     1

最后一个有效元素是通过以下方式找到的:

[~,idx]=sort(T,2);
lastone=idx(:,end)

lastone =

 3     2     1     4    15    15    15    15    15    15    15    15    15    15    15

我们必须确保尊重每一行的实际长度:

lengths = length(X):-1:1;
real_length = min(lastone,lengths);
[max_length,max_idx] = max(real_length)


max_length =

     11


max_idx =

     5

如果有更多相同最大长度的序列,我们只取第一个并显示它:

selected_max_idx = max_idx(1);
H(selected_max_idx, 1:max_length)


ans =

 8    10    11   NaN     9    14     6     1     4    23    24

完整脚本

X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24];

H = hankel(X);
C = cumsum(isnan(H),2);

threshold = 1;

T = C<=threshold;
[~,idx]=sort(T,2);
lastone=idx(:,end)';

lengths = length(X):-1:1;
real_length = min(lastone,lengths);
[max_length,max_idx] = max(real_length);

selected_max_idx = max_idx(1);
H(selected_max_idx, 1:max_length)

【讨论】:

  • 抱歉,这个算法似乎不够健壮,例如X = [18 3 nan nan nan nan 9 14 6 1 4 23 24]threshold = 3
  • @macduf 我得到NaN NaN NaN 9 14 6 1 4 23 24,你会期待什么?
  • @macduff tbh,OP 对删除的确切位置/程度不是很清楚,所以我认为这不是稳健性问题。
  • 当然 - 我知道问题是“找到最长的非 nan 值序列”,因此将 NaN 从结果中排除。但它是一种虚拟的东西,核心是找到序列。很好地解决了这个棘手的问题。
  • 这是一个相当长、复杂的方法,应该是一个简单的算法。当然我偏向于rle(见我的回答),但你真的不需要求助于这么大的矩阵。
【解决方案2】:

方法一:卷积

一种可能的方法是将Y = double(~isnan(X));n 的窗口进行卷积,其中n 递减,直到找到可接受的子序列。 “可接受”表示子序列至少包含n-thres个,即卷积至少给出n-thres

Y = double(~isnan(X));
for n = numel(Y):-1:1 %// try all possible sequence lengths
    w = find(conv(Y,ones(1,n),'valid')>=n-thres); %// is there any acceptable subsequence?
    if ~isempty(w)
        break
    end
end
result = X(w:w+n-1);

方法二:累计和

Yn 的窗口进行卷积(如方法1)相当于计算Y 的累积总和,然后用n 间距取差。这在操作数量方面更有效。

Y = double(~isnan(X));
Z = cumsum(Y);
for n = numel(Y):-1:1
    w = find([Z(n) Z(n+1:end)-Z(1:end-n)]>=n-thres);
    if ~isempty(w)
        break
    end
end
result = X(w:w+n-1);

方法 3:2D 卷积

这实质上是一次计算方法 1 中循环的所有迭代。

Y = double(~isnan(X));
z = conv2(Y, tril(ones(numel(Y))));
[nn, ww] = find(bsxfun(@ge, z, (1:numel(Y)).'-thres)); %'
[n, ind] = max(nn);
w = ww(ind)-n+1;
result = X(w:w+n-1);

【讨论】:

  • 累积总和是我的目标,但这编码要好得多。
【解决方案3】:

让我们试试我最喜欢的工具:RLE。 Matlab 没有直接功能,所以使用我发布的“seqle”来交换中心。 Seqle 的默认设置是返回运行长度编码。所以:

>> foo = [ nan 1 2 3 nan nan 4 5 6 nan 5 5 5 ];

>> seqle(isnan(foo))
ans = 
    run: [1 3 2 3 1 3]
    val: [1 0 1 0 1 0]

“run”表示当前运行的长度; “val”表示值。在这种情况下,val==1 表示值为nanval==0 表示数值。您可以看到提取满足条件val==0 | run &lt; 2 的最长“运行”值序列以连续获得不超过一个nan 相对容易。然后只需获取该运行的累积索引,这就是您想要的 foo 的子集。

编辑: 可悲的是,肉眼发现的微不足道的东西可能并不那么容易通过代码提取。我怀疑有一种更快的方法可以使用longrun 标识的索引来获得所需的子序列。

>> foo = [ nan 1 2 3 nan nan 4 5 6 nan nan 5 5 nan 5 nan 4 7 4 nan ];
>>  sfoo= seqle(isnan(foo))
sfoo = 
    run: [1 3 2 3 2 2 1 1 1 3 1]
    val: [1 0 1 0 1 0 1 0 1 0 1]
>> longrun = sfoo.run<2 |sfoo.val==0
longlong = 
    run: [2 1 1 1 6]
    val: [1 0 1 0 1]
% longrun identifies which indices might be part of a run
% longlong identifies the longest sequence of valid run 
>> longlong = seqle(longrun)
>> lfoo = find(sfoo.run<2 |sfoo.val==0);
>> sbar = seqle(lfoo,1);
>> maxind=find(sbar.run==max(sbar.run),1,'first');
>> getlfoo = lfoo( sum(sbar.run(1:(maxind-1)))+1 ); 
% first value in longrun , which is  part of max run
% getbar finds end of run indices
>> getbar = getlfoo:(getlfoo+sbar.run(maxind)-1);
>> getsbar = sfoo.run(getbar);
% retrieve indices of input vector 
>> startit = sum(sfoo.run(1:(getbar(1)-1))) +1;
>> endit = startit+ ((sum(sfoo.run(getbar(1):getbar(end ) ) ) ) )-1;
>> therun = foo( startit:endit )
therun =
     5     5   NaN     5   NaN     4     7     4   NaN

【讨论】:

  • 你能发一个完整的例子吗?
  • @m.s.很丑,但这是一种方法。
【解决方案4】:

嗯,不喜欢挑战的人,我的解决方案不如m.s.的,不过倒是可以替代。

X = [18 3 nan nan 8 10 11 nan 9 14 6 1 4 23 24]; %// input array
thresh =1;
X(isnan(X))= 0 ;

for i = 1:thresh
    Y(i,:) = circshift(X',-i); %//circular shift
end 

由于某种原因,Matlab 反转“'”使格式看起来很奇怪。

D = X + sum(Y,1);

Discard = find(D==0)+thresh; %//give you the index of the part that needs to be discarded

chunk = find(X==0); %//Segment the Vector into segments delimited by NaNs

seriesOfZero = circshift(chunk',-1)' - chunk;

bigchunk =[1 chunk( find(seriesOfZero ~= 1)) size(X,2)]; %//Convert series of NaNs into 1 chunk

[values,DiscardChunk] = intersect(bigchunk,Discard);
DiscardChunk =  sort(DiscardChunk,'descend')

for t = 1:size(DiscardChunk,2)
  X(bigchunk(DiscardChunk(t)-1):bigchunk(DiscardChunk(t))) = []; %//Discard the data
end
X(X == 0) = NaN
%//End of Code

8 10 11 NaN 9 14 6 1 4 23 24

当: X = [18 3 楠楠 8 10 11 楠楠 9 14 6 1 楠楠楠 4 23 24]; %// 输入数组 阈值 =2;

8 10 11 NaN 4 23 24

【讨论】:

  • 我刚刚用你的第二个例子(X = [18 3 nan nan nan 8 10 11 nan nan 9 14 6 1 nan nan nan 4 23 24];)尝试了你的代码,但结果是18 3 NaN NaN NaN 8 10 11 NaN 4 23 24;并使用thresh=2 运行第一个示例导致Index exceeds matrix dimensions.
  • @m.s.在这里可以正常工作,你清除之前的结果了吗?
  • @m.s.很奇怪,我有 R2011b,它适用于这两个示例。是什么破坏了它?
  • 我有 R2015a,不知道是什么原因造成的问题
  • @m.s.可能是功能错误 - 我怀疑是 circshift,但我不确定......但它在 2011b 上工作,这很奇怪。
猜你喜欢
  • 1970-01-01
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-15
  • 1970-01-01
  • 2011-07-27
  • 1970-01-01
相关资源
最近更新 更多