从向量中提取连续序列
Extract consecutive sequences from vector
我有一个名为 v 的向量,其中包含正值和负值以及零。问题是,如何提取(在列表中)所有连续的正数序列,即用零分隔的正数序列。
这里是 v:
v <- c(-75.09619, -38.31229, 0, 57.17792, 65.55923, 108.52735, 104.29929, 32.47125,0, 0, 0, 0, -26.65008, -49.48638, -79.60670,-90.55343, -34.60761, 0, 21.48842, 38.83820, 42.28727, 0)
输出必须是这样的:
[1] 57.17792, 65.55923, 108.52735, 104.29929, 32.47125
[2] 21.48842 , 38.83820, 42.28727
有什么想法吗?
你可以试试:
indices <- which(v[v>=0]==0)
x <- Map(function(x,y) setdiff(v[v>=0][(y+1):(x-1)],0),
indices[2:length(indices)],
indices[1:(length(indices)-1)])
x[vapply(x,length,1L)>0]
#[[1]]
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#[[2]]
#[1] 21.48842 38.83820 42.28727
我做了什么:
- 从
v
的>=0值形成的向量中取出0位置
- 使用
Map
函数,可以提取向量中两个零之间的部分
- 最后一行的目的是从结果中去除没有值的序列(例如,如果原始向量中有两个或多个零的序列)
这里有一个选项:
ll <- split(v,cumsum(v==0)) ## split data by 0 jump
ll <- lapply(ll,function(x){ ## for each group
x <- x[x!=0] ## remove null values
if(all(x>0) && length(x)>0)x else NA ## check if all values are postifs
})
ll[!is.na(ll)]
# $`1`
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
# $`6`
# [1] 21.48842 38.83820 42.28727
想法:
为每个 0 创建一个组标识符
每组去掉0,检查是否所有值都是正数,否则returns NA
从最终结果中删除缺失的向量。
data.table解决方案
将结果分组在 data.table(data.frame) 中比在列表中更好。我在上面使用了相同的代码,只是将其包装在 data.table 结构中。
library(data.table)
A=data.table(v=v)
A[,{x <- v[v!=0]
if(all(x>0) && length(x)>0)x else NA_real_},
cumsum(v==0)][!is.na(V1)]
# cumsum V1
# 1: 1 57.17792
# 2: 1 65.55923
# 3: 1 108.52735
# 4: 1 104.29929
# 5: 1 32.47125
# 6: 6 21.48842
# 7: 6 38.83820
# 8: 6 42.28727
这是一个使用 split
的简短例子:
split(v[v>0],cumsum(v==0)[v>0])
#$`1`
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
#$`6`
#[1] 21.48842 38.83820 42.28727
这基本上只是将 v
子集化为正数,然后使用 cumsum(v==0)
计数器(它将在一组正值内保持稳定,并在达到紧随其后的 [=15 时增加=]), 到 split
这个组。瞧!
再次尝试使用 rle
:
r <- rle(v>0)
start <- cumsum(r$lengths)[r$values] - r$lengths[r$values] + 1
end <- start + r$lengths[r$values] - 1
Map(function(start,end) v[start:end],start,end)
#[[1]]
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
#[[2]]
#[1] 21.48842 38.83820 42.28727
我会在
中投出一个
v <- c(-75.09619, -38.31229, 0, 57.17792, 65.55923, 108.52735, 104.29929, 32.47125,0, 0, 0, 0, -26.65008, -49.48638, -79.60670,-90.55343, -34.60761, 0,21.48842, 38.83820, 42.28727, 0)
r <- rle(v > 0)
r <- r$lengths[r$values]
(pos <- v[v > 0])
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125 21.48842 38.83820 42.28727
lapply(r, function(x) {
out <- pos[1:x]
pos <<- pos[-(1:x)]
out
})
# [[1]]
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
# [[2]]
# [1] 21.48842 38.83820 42.28727
我有一个名为 v 的向量,其中包含正值和负值以及零。问题是,如何提取(在列表中)所有连续的正数序列,即用零分隔的正数序列。
这里是 v:
v <- c(-75.09619, -38.31229, 0, 57.17792, 65.55923, 108.52735, 104.29929, 32.47125,0, 0, 0, 0, -26.65008, -49.48638, -79.60670,-90.55343, -34.60761, 0, 21.48842, 38.83820, 42.28727, 0)
输出必须是这样的:
[1] 57.17792, 65.55923, 108.52735, 104.29929, 32.47125
[2] 21.48842 , 38.83820, 42.28727
有什么想法吗?
你可以试试:
indices <- which(v[v>=0]==0)
x <- Map(function(x,y) setdiff(v[v>=0][(y+1):(x-1)],0),
indices[2:length(indices)],
indices[1:(length(indices)-1)])
x[vapply(x,length,1L)>0]
#[[1]]
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#[[2]]
#[1] 21.48842 38.83820 42.28727
我做了什么:
- 从
v
的>=0值形成的向量中取出0位置
- 使用
Map
函数,可以提取向量中两个零之间的部分 - 最后一行的目的是从结果中去除没有值的序列(例如,如果原始向量中有两个或多个零的序列)
这里有一个选项:
ll <- split(v,cumsum(v==0)) ## split data by 0 jump
ll <- lapply(ll,function(x){ ## for each group
x <- x[x!=0] ## remove null values
if(all(x>0) && length(x)>0)x else NA ## check if all values are postifs
})
ll[!is.na(ll)]
# $`1`
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
# $`6`
# [1] 21.48842 38.83820 42.28727
想法:
为每个 0 创建一个组标识符
每组去掉0,检查是否所有值都是正数,否则returns NA
从最终结果中删除缺失的向量。
data.table解决方案
将结果分组在 data.table(data.frame) 中比在列表中更好。我在上面使用了相同的代码,只是将其包装在 data.table 结构中。
library(data.table)
A=data.table(v=v)
A[,{x <- v[v!=0]
if(all(x>0) && length(x)>0)x else NA_real_},
cumsum(v==0)][!is.na(V1)]
# cumsum V1
# 1: 1 57.17792
# 2: 1 65.55923
# 3: 1 108.52735
# 4: 1 104.29929
# 5: 1 32.47125
# 6: 6 21.48842
# 7: 6 38.83820
# 8: 6 42.28727
这是一个使用 split
的简短例子:
split(v[v>0],cumsum(v==0)[v>0])
#$`1`
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
#$`6`
#[1] 21.48842 38.83820 42.28727
这基本上只是将 v
子集化为正数,然后使用 cumsum(v==0)
计数器(它将在一组正值内保持稳定,并在达到紧随其后的 [=15 时增加=]), 到 split
这个组。瞧!
再次尝试使用 rle
:
r <- rle(v>0)
start <- cumsum(r$lengths)[r$values] - r$lengths[r$values] + 1
end <- start + r$lengths[r$values] - 1
Map(function(start,end) v[start:end],start,end)
#[[1]]
#[1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
#[[2]]
#[1] 21.48842 38.83820 42.28727
我会在
中投出一个v <- c(-75.09619, -38.31229, 0, 57.17792, 65.55923, 108.52735, 104.29929, 32.47125,0, 0, 0, 0, -26.65008, -49.48638, -79.60670,-90.55343, -34.60761, 0,21.48842, 38.83820, 42.28727, 0)
r <- rle(v > 0)
r <- r$lengths[r$values]
(pos <- v[v > 0])
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125 21.48842 38.83820 42.28727
lapply(r, function(x) {
out <- pos[1:x]
pos <<- pos[-(1:x)]
out
})
# [[1]]
# [1] 57.17792 65.55923 108.52735 104.29929 32.47125
#
# [[2]]
# [1] 21.48842 38.83820 42.28727