如何在另一列上有条件地按组执行列的连续计数
How to Perform Consecutive Counts of Column by Group Conditionally Upon Another Column
我正在尝试从按 PatientID 列分组的 Noshow 列中获取连续计数。我使用的以下代码非常接近我希望获得的结果。但是,使用求和函数 returns 求整个组的总和。我希望 sum 函数只对当前行和上面有“1”的行求和。基本上,我试图计算患者在每一行中没有显示他们的预约的连续次数,然后在他们显示时重置为 0。似乎只需要对我的以下代码进行一些调整。但是,我似乎无法在本网站的任何地方找到答案。
transform(df, ConsecNoshows = ifelse(Noshow == 0, 0, ave(Noshow, PatientID, FUN = sum)))
以上代码产生以下输出:
#Source: local data frame [12 x 3]
#Groups: ID [2]
#
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 4
#3 1 0 0
#4 1 1 4
#5 1 1 4
#6 1 1 4
#7 2 0 0
#8 2 0 0
#9 2 1 3
#10 2 1 3
#11 2 0 0
#12 2 1 3
这就是我想要的:
#Source: local data frame [12 x 3]
#Groups: ID [2]
#
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 0
#3 1 0 1
#4 1 1 0
#5 1 1 1
#6 1 1 2
#7 2 0 0
#8 2 0 0
#9 2 1 0
#10 2 1 1
#11 2 0 2
#12 2 1 0
[更新]我希望连续计数向下偏移一行。
感谢您提前提供的任何帮助!
对连续值进行分组的最直接的方法是使用 data.table
中的 rleid
,这是 data.table
包中的一个选项,您可以通过 [=14] 对数据进行分组=] 以及 rleid
的 Noshow
变量。而且您还需要 cumsum
函数来获取 Noshow
变量的累计和而不是 sum
:
library(data.table)
setDT(df)[, ConsecNoshows := ifelse(Noshow == 0, 0, cumsum(Noshow)), .(PatientID, rleid(Noshow))]
df
# PatientID Noshow ConsecNoshows
# 1: 1 0 0
# 2: 1 1 1
# 3: 1 0 0
# 4: 1 1 1
# 5: 1 1 2
# 6: 1 1 3
# 7: 2 0 0
# 8: 2 0 0
# 9: 2 1 1
#10: 2 1 2
#11: 2 0 0
#12: 2 1 1
这是另一种(类似的)data.table
方法
library(data.table)
setDT(df)[, ConsecNoshows := seq(.N) * Noshow, by = .(PatientID, rleid(Noshow))]
df
# PatientID Noshow ConsecNoshows
# 1: 1 0 0
# 2: 1 1 1
# 3: 1 0 0
# 4: 1 1 1
# 5: 1 1 2
# 6: 1 1 3
# 7: 2 0 0
# 8: 2 0 0
# 9: 2 1 1
# 10: 2 1 2
# 11: 2 0 0
# 12: 2 1 1
这基本上是按 Noshow
的 PatientID
和 "run-length-encoding" 分组,并使用组大小创建序列,同时乘以 Noshow
以便仅保留以下值Noshow == 1
我们可以使用 base R
中的 rle
(未使用包)。使用 ave
,我们按 'PatientID' 分组,得到 'Noshow' 的 rle
,将 'lengths' 的 sequence
乘以 'values'由 'lengths' 复制以获得预期的输出。
helperfn <- function(x) with(rle(x), sequence(lengths) * rep(values, lengths))
df$ConsecNoshows <- with(df, ave(Noshow, PatientID, FUN = helperfn))
df$ConsecNoshows
#[1] 0 1 0 1 2 3 0 0 1 2 0 1
由于 OP 似乎正在使用 'tbl_df',dplyr
中的解决方案是
library(dplyr)
df %>%
group_by(PatientID) %>%
mutate(ConsecNoshows = helperfn(Noshow))
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 1
#3 1 0 0
#4 1 1 1
#5 1 1 2
#6 1 1 3
#7 2 0 0
#8 2 0 0
#9 2 1 1
#10 2 1 2
#11 2 0 0
#12 2 1 1
我会创建一个辅助函数,然后使用您最熟悉的任何实现:
sum0 <- function(x) {x[x == 1]=sequence(with(rle(x), lengths[values == 1]));x}
#base R
transform(df1, Consec = ave(Noshow, PatientID, FUN=sum0))
#dplyr
library(dplyr)
df1 %>% group_by(PatientID) %>% mutate(Consec=sum0(Noshow))
#data.table
library(data.table)
setDT(df1)[, Consec := sum0(Noshow), by = PatientID]
# PatientID Noshow Consec
# <int> <int> <int>
# 1 1 0 0
# 2 1 1 1
# 3 1 0 0
# 4 1 1 1
# 5 1 1 2
# 6 1 1 3
# 7 2 0 0
# 8 2 0 0
# 9 2 1 1
# 10 2 1 2
# 11 2 0 0
# 12 2 1 1
我正在尝试从按 PatientID 列分组的 Noshow 列中获取连续计数。我使用的以下代码非常接近我希望获得的结果。但是,使用求和函数 returns 求整个组的总和。我希望 sum 函数只对当前行和上面有“1”的行求和。基本上,我试图计算患者在每一行中没有显示他们的预约的连续次数,然后在他们显示时重置为 0。似乎只需要对我的以下代码进行一些调整。但是,我似乎无法在本网站的任何地方找到答案。
transform(df, ConsecNoshows = ifelse(Noshow == 0, 0, ave(Noshow, PatientID, FUN = sum)))
以上代码产生以下输出:
#Source: local data frame [12 x 3]
#Groups: ID [2]
#
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 4
#3 1 0 0
#4 1 1 4
#5 1 1 4
#6 1 1 4
#7 2 0 0
#8 2 0 0
#9 2 1 3
#10 2 1 3
#11 2 0 0
#12 2 1 3
这就是我想要的:
#Source: local data frame [12 x 3]
#Groups: ID [2]
#
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 0
#3 1 0 1
#4 1 1 0
#5 1 1 1
#6 1 1 2
#7 2 0 0
#8 2 0 0
#9 2 1 0
#10 2 1 1
#11 2 0 2
#12 2 1 0
[更新]我希望连续计数向下偏移一行。
感谢您提前提供的任何帮助!
对连续值进行分组的最直接的方法是使用 data.table
中的 rleid
,这是 data.table
包中的一个选项,您可以通过 [=14] 对数据进行分组=] 以及 rleid
的 Noshow
变量。而且您还需要 cumsum
函数来获取 Noshow
变量的累计和而不是 sum
:
library(data.table)
setDT(df)[, ConsecNoshows := ifelse(Noshow == 0, 0, cumsum(Noshow)), .(PatientID, rleid(Noshow))]
df
# PatientID Noshow ConsecNoshows
# 1: 1 0 0
# 2: 1 1 1
# 3: 1 0 0
# 4: 1 1 1
# 5: 1 1 2
# 6: 1 1 3
# 7: 2 0 0
# 8: 2 0 0
# 9: 2 1 1
#10: 2 1 2
#11: 2 0 0
#12: 2 1 1
这是另一种(类似的)data.table
方法
library(data.table)
setDT(df)[, ConsecNoshows := seq(.N) * Noshow, by = .(PatientID, rleid(Noshow))]
df
# PatientID Noshow ConsecNoshows
# 1: 1 0 0
# 2: 1 1 1
# 3: 1 0 0
# 4: 1 1 1
# 5: 1 1 2
# 6: 1 1 3
# 7: 2 0 0
# 8: 2 0 0
# 9: 2 1 1
# 10: 2 1 2
# 11: 2 0 0
# 12: 2 1 1
这基本上是按 Noshow
的 PatientID
和 "run-length-encoding" 分组,并使用组大小创建序列,同时乘以 Noshow
以便仅保留以下值Noshow == 1
我们可以使用 base R
中的 rle
(未使用包)。使用 ave
,我们按 'PatientID' 分组,得到 'Noshow' 的 rle
,将 'lengths' 的 sequence
乘以 'values'由 'lengths' 复制以获得预期的输出。
helperfn <- function(x) with(rle(x), sequence(lengths) * rep(values, lengths))
df$ConsecNoshows <- with(df, ave(Noshow, PatientID, FUN = helperfn))
df$ConsecNoshows
#[1] 0 1 0 1 2 3 0 0 1 2 0 1
由于 OP 似乎正在使用 'tbl_df',dplyr
中的解决方案是
library(dplyr)
df %>%
group_by(PatientID) %>%
mutate(ConsecNoshows = helperfn(Noshow))
# PatientID Noshow ConsecNoshows
# <int> <int> <int>
#1 1 0 0
#2 1 1 1
#3 1 0 0
#4 1 1 1
#5 1 1 2
#6 1 1 3
#7 2 0 0
#8 2 0 0
#9 2 1 1
#10 2 1 2
#11 2 0 0
#12 2 1 1
我会创建一个辅助函数,然后使用您最熟悉的任何实现:
sum0 <- function(x) {x[x == 1]=sequence(with(rle(x), lengths[values == 1]));x}
#base R
transform(df1, Consec = ave(Noshow, PatientID, FUN=sum0))
#dplyr
library(dplyr)
df1 %>% group_by(PatientID) %>% mutate(Consec=sum0(Noshow))
#data.table
library(data.table)
setDT(df1)[, Consec := sum0(Noshow), by = PatientID]
# PatientID Noshow Consec
# <int> <int> <int>
# 1 1 0 0
# 2 1 1 1
# 3 1 0 0
# 4 1 1 1
# 5 1 1 2
# 6 1 1 3
# 7 2 0 0
# 8 2 0 0
# 9 2 1 1
# 10 2 1 2
# 11 2 0 0
# 12 2 1 1