R:选择高于某个阈值的 n 个连续行中的第一行
R: Selecting first of n consecutive rows above a certain threshold value
我有一个包含 MRN、日期和测试值的数据框。
我需要 select 每个 MRN 的所有 第 行 three个连续值大于0.5.
这是数据的示例版本:
MRN Collected_Date ANC
1 001 2015-01-02 0.345
2 001 2015-01-03 0.532
3 001 2015-01-04 0.843
4 001 2015-01-05 0.932
5 002 2015-03-03 0.012
6 002 2015-03-05 0.022
7 002 2015-03-06 0.543
8 002 2015-03-07 0.563
9 003 2015-08-02 0.343
10 003 2015-08-03 0.500
11 003 2015-08-04 0.734
12 003 2015-08-05 0.455
13 004 2014-01-02 0.001
14 004 2014-01-03 0.500
15 004 2014-01-04 0.562
16 004 2014-01-05 0.503
示例代码:
df <- data.frame(MRN = c('001','001','001','001',
'002','002','002','002',
'003','003','003','003',
'004','004','004','004'),
Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015',
'03-03-2015','03-05-2015','03-06-2015','03-07-2015',
'08-02-2015','08-03-2015','08-04-2015','08-05-2015',
'01-02-2014','01-03-2014','01-04-2014','01-05-2014'),
format = '%m-%d-%Y'),
ANC = as.numeric(c('0.345','0.532','0.843','0.932',
'0.012','0.022','0.543','0.563',
'0.343','0.500','0.734','0.455',
'0.001','0.500','0.562','0.503')))
目前,我正在使用一种非常笨拙的方法,使用滞后函数来计算日期差异,然后过滤所有 >= 0.5 的值,然后对值求和,这有助于 select 日期第三个值。然后我减去两天得到第一个值的日期:
df %>% group_by(MRN) %>%
mutate(., days_diff = abs(Collected_Date[1] - Collected_Date)) %>%
filter(ANC >= 0.5) %>%
mutate(days = days_diff + lag((days_diff))) %>%
filter(days == 5) %>%
mutate(Collected_Date = Collected_Date - 2) %>%
select(MRN, Collected_Date)
输出:
来源:本地数据框[2 x 2]
团体:MRN
MRN Collected_Date
1 001 2015-01-03
2 004 2014-01-03
一定有更简单/更优雅的方法。此外,如果测试日期之间存在差距,它也不会给出准确的结果。
这个例子我想要的输出是:
MRN Collected_Date ANC
1 001 2015-01-03 0.532
2 004 2014-01-03 0.500
所以如果至少三个连续的测试值 >= 0.5,则应返回第一个值的日期。
如果没有至少三个连续值 >= 0.5,则应返回 NA。
非常感谢任何帮助!
非常感谢!
最简单的方法是结合使用 zoo
库和 dplyr
。在 zoo
包中有一个名为 rollapply
的函数,我们可以使用它来计算 window 时间的函数值。
在此示例中,我们可以应用 window 计算接下来三个值中的最小值,然后应用指定的逻辑。
df %>% group_by(MRN) %>%
mutate(ANC=rollapply(ANC, width=3, min, align="left", fill=NA, na.rm=TRUE)) %>%
filter(ANC >= 0.5) %>%
filter(row_number() == 1)
# MRN Collected_Date ANC
# 1 001 2015-01-03 0.532
# 2 004 2014-01-03 0.500
在上面的代码中,我们使用了 rollapply
来计算接下来 3 项中的最小值。要了解其工作原理,请比较以下内容:
rollapply(1:6, width=3, min, align="left", fill=NA) # [1] 1 2 3 4 NA NA
rollapply(1:6, width=3, min, align="center", fill=NA) # [1] NA 1 2 3 4 NA
rollapply(1:6, width=3, min, align="right", fill=NA) # [1] NA NA 1 2 3 4
因此在我们的示例中,我们从左侧对齐,因此它从当前位置开始并期待接下来的 2 个值。
最后我们通过适当的值进行过滤,并对每组进行第一个观察。
这是一个 ddply
解决方案(抱歉,我不是最新的 %>%
语法,但也许它也可以应用)。
我不确定它是否是你所说的 "elegant",但在第二次阅读时它会有意义(对我来说这比单行更重要),并且是对缺失日期等具有鲁棒性
关键是使用rle
(运行长度编码)来寻找ANC >= 0.5
的'runs',其中运行的长度至少为3 . 这会处理 'consecutive' 部分。我们将其保存到 r
.
然后r.i
给出第一个运行中长度为3或更大的索引,其中运行的值为TRUE
。
要获取 x
中的索引,您只需 sum
运行 长度,但不包括我们感兴趣的 运行,然后加 1 得到开始(即 sum(r$lengths[1:(r.i - 1)])
和 +1
)。
ddply(df,
.(MRN),
function (x) {
r <- rle(x$ANC >= 0.5) # find 'runs' of x$ANC >= 0.5
# find index of first run of length >=3 with ANC >= .5
r.i <- which(r$lengths >= 3 & r$values)[1]
if (!is.na(r.i)) {
# get index of first row in that run and return it.
return(x[sum(r$lengths[seq_len(r.i - 1)]) + 1, ])
}
return(NULL)
})
如果您提取例如x <- subset(df, MRN == '001')
并逐步查看 r
、r.i
的样子。
我们可以创建一个辅助函数,给定一个向量 x
returns 一个表示高于给定阈值的连续值数量的向量:
high_run <- function(x, threshold) {
high <- x >= threshold
streak <- high[1]
for(h in high[2:length(high)]){
streak <- c(streak, streak[length(streak)]*h + h)
}
run
}
以及一个函数,其中 returns 特定长度的第一个 运行 的起始索引:
high_run_start <- function(x, threshold, run){
match(run, high_run(x, threshold)) - run + 1
}
然后我们可以使用后一个函数 select 原始数据帧的适当行:
> df %>% group_by(MRN) %>%
+ filter(row_number()==high_run_start(ANC,0.5,3))
Source: local data frame [2 x 3]
Groups: MRN
MRN Collected_Date ANC
1 001 2015-01-03 0.532
2 004 2014-01-03 0.500
基本方法:
使用rle
找到3个或更多的序列并抓住第一个
df <- data.frame(MRN = c('001','001','001','001','002','002','002','002','003','003','003','003','004','004','004','004'), Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015', '03-03-2015','03-05-2015','03-06-2015','03-07-2015', '08-02-2015','08-03-2015','08-04-2015','08-05-2015', '01-02-2014','01-03-2014','01-04-2014','01-05-2014'), format = '%m-%d-%Y'), ANC = as.numeric(c('0.345','0.532','0.843','0.932', '0.012','0.022','0.543','0.563', '0.343','0.500','0.734','0.455', '0.001','0.500','0.562','0.503')))
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x)
cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ]
# MRN Collected_Date ANC
# 2 001 2015-01-03 0.532
# 14 004 2014-01-03 0.500
也许这个版本更容易理解
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x) {
r <- rle(x >= .5)
r <- rep(r$lengths, r$lengths)
cumsum(r == 3 & x >= .5) == 1
}))), ]
编辑
df <- df[c(1:4,4,4,4,5,5,5,5:16), ]
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x)
cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ]
# MRN Collected_Date ANC
# 2 001 2015-01-03 0.532
# 14 004 2014-01-03 0.500
我有一个包含 MRN、日期和测试值的数据框。
我需要 select 每个 MRN 的所有 第 行 three个连续值大于0.5.
这是数据的示例版本:
MRN Collected_Date ANC
1 001 2015-01-02 0.345
2 001 2015-01-03 0.532
3 001 2015-01-04 0.843
4 001 2015-01-05 0.932
5 002 2015-03-03 0.012
6 002 2015-03-05 0.022
7 002 2015-03-06 0.543
8 002 2015-03-07 0.563
9 003 2015-08-02 0.343
10 003 2015-08-03 0.500
11 003 2015-08-04 0.734
12 003 2015-08-05 0.455
13 004 2014-01-02 0.001
14 004 2014-01-03 0.500
15 004 2014-01-04 0.562
16 004 2014-01-05 0.503
示例代码:
df <- data.frame(MRN = c('001','001','001','001',
'002','002','002','002',
'003','003','003','003',
'004','004','004','004'),
Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015',
'03-03-2015','03-05-2015','03-06-2015','03-07-2015',
'08-02-2015','08-03-2015','08-04-2015','08-05-2015',
'01-02-2014','01-03-2014','01-04-2014','01-05-2014'),
format = '%m-%d-%Y'),
ANC = as.numeric(c('0.345','0.532','0.843','0.932',
'0.012','0.022','0.543','0.563',
'0.343','0.500','0.734','0.455',
'0.001','0.500','0.562','0.503')))
目前,我正在使用一种非常笨拙的方法,使用滞后函数来计算日期差异,然后过滤所有 >= 0.5 的值,然后对值求和,这有助于 select 日期第三个值。然后我减去两天得到第一个值的日期:
df %>% group_by(MRN) %>%
mutate(., days_diff = abs(Collected_Date[1] - Collected_Date)) %>%
filter(ANC >= 0.5) %>%
mutate(days = days_diff + lag((days_diff))) %>%
filter(days == 5) %>%
mutate(Collected_Date = Collected_Date - 2) %>%
select(MRN, Collected_Date)
输出:
来源:本地数据框[2 x 2] 团体:MRN
MRN Collected_Date
1 001 2015-01-03
2 004 2014-01-03
一定有更简单/更优雅的方法。此外,如果测试日期之间存在差距,它也不会给出准确的结果。
这个例子我想要的输出是:
MRN Collected_Date ANC
1 001 2015-01-03 0.532
2 004 2014-01-03 0.500
所以如果至少三个连续的测试值 >= 0.5,则应返回第一个值的日期。
如果没有至少三个连续值 >= 0.5,则应返回 NA。
非常感谢任何帮助!
非常感谢!
最简单的方法是结合使用 zoo
库和 dplyr
。在 zoo
包中有一个名为 rollapply
的函数,我们可以使用它来计算 window 时间的函数值。
在此示例中,我们可以应用 window 计算接下来三个值中的最小值,然后应用指定的逻辑。
df %>% group_by(MRN) %>%
mutate(ANC=rollapply(ANC, width=3, min, align="left", fill=NA, na.rm=TRUE)) %>%
filter(ANC >= 0.5) %>%
filter(row_number() == 1)
# MRN Collected_Date ANC
# 1 001 2015-01-03 0.532
# 2 004 2014-01-03 0.500
在上面的代码中,我们使用了 rollapply
来计算接下来 3 项中的最小值。要了解其工作原理,请比较以下内容:
rollapply(1:6, width=3, min, align="left", fill=NA) # [1] 1 2 3 4 NA NA
rollapply(1:6, width=3, min, align="center", fill=NA) # [1] NA 1 2 3 4 NA
rollapply(1:6, width=3, min, align="right", fill=NA) # [1] NA NA 1 2 3 4
因此在我们的示例中,我们从左侧对齐,因此它从当前位置开始并期待接下来的 2 个值。
最后我们通过适当的值进行过滤,并对每组进行第一个观察。
这是一个 ddply
解决方案(抱歉,我不是最新的 %>%
语法,但也许它也可以应用)。
我不确定它是否是你所说的 "elegant",但在第二次阅读时它会有意义(对我来说这比单行更重要),并且是对缺失日期等具有鲁棒性
关键是使用rle
(运行长度编码)来寻找ANC >= 0.5
的'runs',其中运行的长度至少为3 . 这会处理 'consecutive' 部分。我们将其保存到 r
.
然后r.i
给出第一个运行中长度为3或更大的索引,其中运行的值为TRUE
。
要获取 x
中的索引,您只需 sum
运行 长度,但不包括我们感兴趣的 运行,然后加 1 得到开始(即 sum(r$lengths[1:(r.i - 1)])
和 +1
)。
ddply(df,
.(MRN),
function (x) {
r <- rle(x$ANC >= 0.5) # find 'runs' of x$ANC >= 0.5
# find index of first run of length >=3 with ANC >= .5
r.i <- which(r$lengths >= 3 & r$values)[1]
if (!is.na(r.i)) {
# get index of first row in that run and return it.
return(x[sum(r$lengths[seq_len(r.i - 1)]) + 1, ])
}
return(NULL)
})
如果您提取例如x <- subset(df, MRN == '001')
并逐步查看 r
、r.i
的样子。
我们可以创建一个辅助函数,给定一个向量 x
returns 一个表示高于给定阈值的连续值数量的向量:
high_run <- function(x, threshold) {
high <- x >= threshold
streak <- high[1]
for(h in high[2:length(high)]){
streak <- c(streak, streak[length(streak)]*h + h)
}
run
}
以及一个函数,其中 returns 特定长度的第一个 运行 的起始索引:
high_run_start <- function(x, threshold, run){
match(run, high_run(x, threshold)) - run + 1
}
然后我们可以使用后一个函数 select 原始数据帧的适当行:
> df %>% group_by(MRN) %>%
+ filter(row_number()==high_run_start(ANC,0.5,3))
Source: local data frame [2 x 3]
Groups: MRN
MRN Collected_Date ANC
1 001 2015-01-03 0.532
2 004 2014-01-03 0.500
基本方法:
使用rle
找到3个或更多的序列并抓住第一个
df <- data.frame(MRN = c('001','001','001','001','002','002','002','002','003','003','003','003','004','004','004','004'), Collected_Date = as.Date(c('01-02-2015','01-03-2015','01-04-2015','01-05-2015', '03-03-2015','03-05-2015','03-06-2015','03-07-2015', '08-02-2015','08-03-2015','08-04-2015','08-05-2015', '01-02-2014','01-03-2014','01-04-2014','01-05-2014'), format = '%m-%d-%Y'), ANC = as.numeric(c('0.345','0.532','0.843','0.932', '0.012','0.022','0.543','0.563', '0.343','0.500','0.734','0.455', '0.001','0.500','0.562','0.503')))
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x)
cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ]
# MRN Collected_Date ANC
# 2 001 2015-01-03 0.532
# 14 004 2014-01-03 0.500
也许这个版本更容易理解
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x) {
r <- rle(x >= .5)
r <- rep(r$lengths, r$lengths)
cumsum(r == 3 & x >= .5) == 1
}))), ]
编辑
df <- df[c(1:4,4,4,4,5,5,5,5:16), ]
df[as.logical(with(df, ave(ANC, MRN, FUN = function(x)
cumsum(x >= .5 & with(rle(x >= .5), rep(lengths, lengths)) >= 3) == 1))), ]
# MRN Collected_Date ANC
# 2 001 2015-01-03 0.532
# 14 004 2014-01-03 0.500