For循环:索引调用多个不同列值时如何避免循环?
For Loops: How to avoid loop when index is used to call multiple different column values?
我有一个庞大的医疗记录数据集(2000 万行)。我想创建一个新列,由每个组中特定行的值填充。
数据是什么样的
数据如下所示:
data <- data.frame(
ICUSTAY_ID = c(1,1,5,5,5,5,5,6,6,6,6),
DATA = c(0,0,0,0,1,0,0,0,0,1,0),
OFFSET = c(-20,0,-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, NA, NA, 1, NA, NA, NA,NA,1,NA),
LABRESULT = c(4, 5, 3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
ID DATA OFFSET AA_FIRST LABRESULT
1 0 -20 NA 4.0
1 0 0 NA 5.0
5 0 -1500 NA 3.5
5 0 150 NA 4.1
5 1 155 1 NA
5 0 159 NA 3.0
5 0 300 NA 5.5
6 0 -2000 NA 2.1
6 0 30 NA 2.5
6 1 100 1 NA
6 0 120 NA 3.5
我希望数据看起来像什么
对于每组 ID,我想在该组中找到 AA_FIRST=1 的行(每个 ID 组只有 1 个),找到该行的 OFFSET 值,然后将此 OFFSET 值粘贴到名为 refOFFSET 的新列中,用于 ID 的 all 行。我希望结果如下所示:
ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
1 0 -20 NA 4.0 NA
1 0 0 NA 5.0 NA
5 0 -1500 NA 3.5 155
5 0 150 NA 4.1 155
5 1 155 1 NA 155
5 0 159 NA 3.0 155
5 0 300 NA 5.5 155
6 0 -2000 NA 2.1 100
6 0 30 NA 2.5 100
6 1 100 1 NA 100
6 0 120 NA 3.5 100
ID 组 5 的 AA_FIRST=1 对应于 OFFSET 155,因此 ID=5 的所有行的 refOFFSET 列都填充了 155。
ID 组 6 的 AA_FIRST=1 对应于 OFFSET 100,因此 ID=6 的所有行的 refOFFSET 列都填充了 100。
ID 组不一定有 AA_FIRST=1。 ID组1就是这种情况。ID组1没有任何AA_FIRST=1,所以它的refOFFSET是NA。
并非所有 ID 值都存在。例如ID号2、3、4不存在
我目前的做法
我现在执行此操作的代码由 for 循环和 if/else 语句组成。我想提出一个矢量化或 apply 形式。我的 for 循环需要 2000 万行的时间太长。
data$refOFFSET <- NA #initialize column called refOFFSET
for (i in 1:length(data$ID)){
if (!length(which(data$ID==(data$ID[i]) & data$AA_FIRST==1))) { #if it's integer0
next #go on to next i
}else{
tmpval <- data$OFFSET[which(data$ID==(data$ID[i]) & data$AA_FIRST==1)]}
data$refOFFSET[i] <- tmpval #create column whose value is equal to the reference OFFSET for each ID (i.e. the OFFSET where AA_FIRST=1)
}
问题
有谁知道如何将上述代码写成向量化或应用形式?有什么可以加快计算速度的吗?谢谢!
编辑:我的可重现示例数据和显示的原始数据略有不同。我更正了这个。
我们可以试试dplyr
。通过 ICUSTAY_ID
对数据帧进行分组,我们找到了 AA_FIRST=1
的最小索引,并为整个组使用相应的 OFFSET
值。
library(dplyr)
data %>%
group_by(ICUSTAY_ID) %>%
mutate(refOFFSET = OFFSET[which.min(AA_FIRST == 1)])
# ICUSTAY_ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1 1 0 -20 NA 4.0 NA
#2 1 0 0 NA 5.0 NA
#3 5 0 -1500 NA 3.5 155
#4 5 0 150 NA 4.1 155
#5 5 1 155 1 NA 155
#6 5 0 159 NA 3.0 155
#7 5 0 300 NA 5.5 155
#8 6 0 -2000 NA 2.1 100
#9 6 0 30 NA 2.5 100
#10 6 1 100 1 NA 100
#11 6 0 120 NA 3.5 100
您可以创建自己的函数并像这样使用应用:
my_function<-function(input_vector){
if(is.na(input_vector[4])){return(NA)}
if(input_vector[4]==1){
return(input_vector[3])
}else{retun(NA)}
}
data<- data.frame(
ID = c(5,5,5,5,5,6,6,6,6),
DATA = c(0,0,1,0,0,0,0,1,0),
OFFSET = c(-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, 1, NA, NA, NA,NA,1,NA),
LABRESULT = c(3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
ref_col=apply(data,1,my_function)
data[,'refOFFSET']=ref_col
refOFFSET_val_idx=which(!is.na(ref_col))
refOFFEST_lookup_df=data[refOFFSET_val_idx,c('ID','refOFFSET')]
for(i in 1:nrow(refOFFEST_lookup_df)){
ID_to_change_idx=which(data$ID==refOFFEST_lookup_df[i,'ID'])
data[ID_to_change_idx,'refOFFSET']=refOFFEST_lookup_df[i,'refOFFSET']
}
这是一个使用 data.table
的选项。将'data.frame'转换为'data.table'(setDT(data)
),按"ICUSTAY_ID"分组,我们得到'AA_FIRST'中1值的索引,得到对应的'OFFSET' 值并分配 (:=
) 它以创建 'refOFFSET'。这应该是非常有效的,因为我们正在分配。
library(data.table)
setDT(data)[, refOFFSET := OFFSET[match(1, AA_FIRST)], by = ICUSTAY_ID]
data
# ICUSTAY_ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
# 1: 1 0 -20 NA 4.0 NA
# 2: 1 0 0 NA 5.0 NA
# 3: 5 0 -1500 NA 3.5 155
# 4: 5 0 150 NA 4.1 155
# 5: 5 1 155 1 NA 155
# 6: 5 0 159 NA 3.0 155
# 7: 5 0 300 NA 5.5 155
# 8: 6 0 -2000 NA 2.1 100
# 9: 6 0 30 NA 2.5 100
#10: 6 1 100 1 NA 100
#11: 6 0 120 NA 3.5 100
您还可以对 AA_FIRST == 1
所在的行进行子集并将其保存为查找 table(类似于 Python 中的字典),然后根据 ID
.
data<- data.frame(
ID = c(1,1,5,5,5,5,5,6,6,6,6),
DATA = c(0,0,0,0,1,0,0,0,0,1,0),
OFFSET = c(-20,0,-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, NA, NA, 1, NA, NA, NA, NA, 1, NA),
LABRESULT = c(4.0, 5.0, 3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
dict <- subset(data, data$AA_FIRST==1)[c("ID", "OFFSET")]
data$refOFFSET <- dict[match(data$ID, dict$ID), 2]
使用 match
查找索引似乎解决了您在 Ronak Shah 的解决方案中提到的不兼容大小错误。
data %>%
group_by(ID) %>%
mutate(refOFFSET = OFFSET[match(TRUE, AA_FIRST==1)])
"For a logical vector x with both FALSE and TRUE values, which.min(x)
and which.max(x) return the index of the first FALSE or TRUE,
respectively, as FALSE < TRUE. However, match(FALSE, x) or match(TRUE,
x) are typically preferred, as they do indicate mismatches."
http://stat.ethz.ch/R-manual/R-devel/library/base/html/which.min.html
您应该可以通过过滤然后重新合并数据框来完成。
foo <- data[! is.na(data$AA_FIRST),c('ID','OFFSET')]
colnames(foo) <- c("ID", "refOFFSET")
result <- merge(data, foo, on = "ID")
我有一个庞大的医疗记录数据集(2000 万行)。我想创建一个新列,由每个组中特定行的值填充。
数据是什么样的
数据如下所示:
data <- data.frame(
ICUSTAY_ID = c(1,1,5,5,5,5,5,6,6,6,6),
DATA = c(0,0,0,0,1,0,0,0,0,1,0),
OFFSET = c(-20,0,-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, NA, NA, 1, NA, NA, NA,NA,1,NA),
LABRESULT = c(4, 5, 3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
ID DATA OFFSET AA_FIRST LABRESULT
1 0 -20 NA 4.0
1 0 0 NA 5.0
5 0 -1500 NA 3.5
5 0 150 NA 4.1
5 1 155 1 NA
5 0 159 NA 3.0
5 0 300 NA 5.5
6 0 -2000 NA 2.1
6 0 30 NA 2.5
6 1 100 1 NA
6 0 120 NA 3.5
我希望数据看起来像什么
对于每组 ID,我想在该组中找到 AA_FIRST=1 的行(每个 ID 组只有 1 个),找到该行的 OFFSET 值,然后将此 OFFSET 值粘贴到名为 refOFFSET 的新列中,用于 ID 的 all 行。我希望结果如下所示:
ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
1 0 -20 NA 4.0 NA
1 0 0 NA 5.0 NA
5 0 -1500 NA 3.5 155
5 0 150 NA 4.1 155
5 1 155 1 NA 155
5 0 159 NA 3.0 155
5 0 300 NA 5.5 155
6 0 -2000 NA 2.1 100
6 0 30 NA 2.5 100
6 1 100 1 NA 100
6 0 120 NA 3.5 100
ID 组 5 的 AA_FIRST=1 对应于 OFFSET 155,因此 ID=5 的所有行的 refOFFSET 列都填充了 155。
ID 组 6 的 AA_FIRST=1 对应于 OFFSET 100,因此 ID=6 的所有行的 refOFFSET 列都填充了 100。
ID 组不一定有 AA_FIRST=1。 ID组1就是这种情况。ID组1没有任何AA_FIRST=1,所以它的refOFFSET是NA。
并非所有 ID 值都存在。例如ID号2、3、4不存在
我目前的做法
我现在执行此操作的代码由 for 循环和 if/else 语句组成。我想提出一个矢量化或 apply 形式。我的 for 循环需要 2000 万行的时间太长。
data$refOFFSET <- NA #initialize column called refOFFSET
for (i in 1:length(data$ID)){
if (!length(which(data$ID==(data$ID[i]) & data$AA_FIRST==1))) { #if it's integer0
next #go on to next i
}else{
tmpval <- data$OFFSET[which(data$ID==(data$ID[i]) & data$AA_FIRST==1)]}
data$refOFFSET[i] <- tmpval #create column whose value is equal to the reference OFFSET for each ID (i.e. the OFFSET where AA_FIRST=1)
}
问题
有谁知道如何将上述代码写成向量化或应用形式?有什么可以加快计算速度的吗?谢谢!
编辑:我的可重现示例数据和显示的原始数据略有不同。我更正了这个。
我们可以试试dplyr
。通过 ICUSTAY_ID
对数据帧进行分组,我们找到了 AA_FIRST=1
的最小索引,并为整个组使用相应的 OFFSET
值。
library(dplyr)
data %>%
group_by(ICUSTAY_ID) %>%
mutate(refOFFSET = OFFSET[which.min(AA_FIRST == 1)])
# ICUSTAY_ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1 1 0 -20 NA 4.0 NA
#2 1 0 0 NA 5.0 NA
#3 5 0 -1500 NA 3.5 155
#4 5 0 150 NA 4.1 155
#5 5 1 155 1 NA 155
#6 5 0 159 NA 3.0 155
#7 5 0 300 NA 5.5 155
#8 6 0 -2000 NA 2.1 100
#9 6 0 30 NA 2.5 100
#10 6 1 100 1 NA 100
#11 6 0 120 NA 3.5 100
您可以创建自己的函数并像这样使用应用:
my_function<-function(input_vector){
if(is.na(input_vector[4])){return(NA)}
if(input_vector[4]==1){
return(input_vector[3])
}else{retun(NA)}
}
data<- data.frame(
ID = c(5,5,5,5,5,6,6,6,6),
DATA = c(0,0,1,0,0,0,0,1,0),
OFFSET = c(-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, 1, NA, NA, NA,NA,1,NA),
LABRESULT = c(3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
ref_col=apply(data,1,my_function)
data[,'refOFFSET']=ref_col
refOFFSET_val_idx=which(!is.na(ref_col))
refOFFEST_lookup_df=data[refOFFSET_val_idx,c('ID','refOFFSET')]
for(i in 1:nrow(refOFFEST_lookup_df)){
ID_to_change_idx=which(data$ID==refOFFEST_lookup_df[i,'ID'])
data[ID_to_change_idx,'refOFFSET']=refOFFEST_lookup_df[i,'refOFFSET']
}
这是一个使用 data.table
的选项。将'data.frame'转换为'data.table'(setDT(data)
),按"ICUSTAY_ID"分组,我们得到'AA_FIRST'中1值的索引,得到对应的'OFFSET' 值并分配 (:=
) 它以创建 'refOFFSET'。这应该是非常有效的,因为我们正在分配。
library(data.table)
setDT(data)[, refOFFSET := OFFSET[match(1, AA_FIRST)], by = ICUSTAY_ID]
data
# ICUSTAY_ID DATA OFFSET AA_FIRST LABRESULT refOFFSET
# 1: 1 0 -20 NA 4.0 NA
# 2: 1 0 0 NA 5.0 NA
# 3: 5 0 -1500 NA 3.5 155
# 4: 5 0 150 NA 4.1 155
# 5: 5 1 155 1 NA 155
# 6: 5 0 159 NA 3.0 155
# 7: 5 0 300 NA 5.5 155
# 8: 6 0 -2000 NA 2.1 100
# 9: 6 0 30 NA 2.5 100
#10: 6 1 100 1 NA 100
#11: 6 0 120 NA 3.5 100
您还可以对 AA_FIRST == 1
所在的行进行子集并将其保存为查找 table(类似于 Python 中的字典),然后根据 ID
.
data<- data.frame(
ID = c(1,1,5,5,5,5,5,6,6,6,6),
DATA = c(0,0,0,0,1,0,0,0,0,1,0),
OFFSET = c(-20,0,-1500, 150, 155, 159, 300, -2000, 30, 100, 120),
AA_FIRST = c(NA, NA, NA, NA, 1, NA, NA, NA, NA, 1, NA),
LABRESULT = c(4.0, 5.0, 3.5, 4.1, NA, 3.0, 5.5, 2.1, 2.5, NA, 3.5) )
dict <- subset(data, data$AA_FIRST==1)[c("ID", "OFFSET")]
data$refOFFSET <- dict[match(data$ID, dict$ID), 2]
使用 match
查找索引似乎解决了您在 Ronak Shah 的解决方案中提到的不兼容大小错误。
data %>%
group_by(ID) %>%
mutate(refOFFSET = OFFSET[match(TRUE, AA_FIRST==1)])
"For a logical vector x with both FALSE and TRUE values, which.min(x) and which.max(x) return the index of the first FALSE or TRUE, respectively, as FALSE < TRUE. However, match(FALSE, x) or match(TRUE, x) are typically preferred, as they do indicate mismatches."
http://stat.ethz.ch/R-manual/R-devel/library/base/html/which.min.html
您应该可以通过过滤然后重新合并数据框来完成。
foo <- data[! is.na(data$AA_FIRST),c('ID','OFFSET')]
colnames(foo) <- c("ID", "refOFFSET")
result <- merge(data, foo, on = "ID")