查找向量的相似元素并修改其间的所有内容
Find similar elements of a vector and modify everything inbetween
我有一个大数据框 (df),其二项式值范围从 1 到 2。数据中也包含 NA。作为一个实际示例,我将创建一个包含用户数据子集的短向量:
df <- c(NA,NA,2,1,1,1,2,1,2,2,1,1,1,NA,2,2,1,2,1,1,1,2)
我基本上想要的结果是搜索数组的第一个和第二个 2
s 并将此间隔内的所有内容转换为 2 的函数。不过,如果第二个和第一个 2 的位置 > 3 然后值保持原样并且不执行任何更改。
除上述之外,函数还必须针对 df
的每个值进行循环。例如,再次考虑以下情况:
df <- c(NA,NA,2,1,1,1,2,1,2,2,1,1,1,NA,2,2,1,2,1,1,1,2)
函数应该有这样的结果:
df_outcome <- c(NA,NA,2,1,1,1,2,2,2,2,1,1,1,NA,2,2,2,2,1,1,1,2)
请注意,在 df_outcome
中,第一个和第二个 2 之间的值没有合并,因为它们的位置差异大于 3。另一方面,其他非2值也相应改变。
我尝试过的(但不能正常工作):
在 zoo
包中的 rollapply
的帮助下,我尝试创建一个函数来查找数组的第一个和第二个 2 并执行如上所述的修改。
func <- function (q) {
for (i in (which(q %in% 2)[1]):(which(q %in% 2)[2])) {
q[i]<-2
}
return(q)
}
然后我使用 rollapply
嵌套它,这样我就可以为每个循环指定一个特定的宽度加上其他参数,例如结果索引的位置(左)。
df_outcome<-rollapply(df, width = 3, FUN = func, fill = NA, partial = TRUE, align = "left")
问题在于,如果将用户生成的函数应用于向量,它就会起作用。然而,当作为参数嵌套在 rollapply
函数中时,它 returns 一个错误:
Error in (which(q %in% 2)[1]):(which(q %in% 2)[2]) : NA/NaN argument
Called from: FUN(data[replace(posns, !ix, 0)], ...)
我想我在使用 rollapply
或数据格式方面有一些错误,但我不明白可能是什么问题。我考虑过使用rollapply
,因为我的数据很长,而且是为不同的用户生成的。因此,我需要一个函数,它也可以根据 User_ID
等其他变量拆分数据(很像 ddply
中的 .variables
参数或 by
中的 by
28=].
期待您的支持。
使用data.table
(但可能有更好的解决方案):
df<-c(NA, NA, 2, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, NA, 2, 2, 1, 2, 1, 1, 1, 2)
dt<-data.table(val=df)
dt[,`:=`(id=rleid(val), p=shift(val,type="lag"), n=shift(val,type="lead"))]
dt[,`:=`(s=.N, f=p[1], e=n[.N]), by=id]
dt[f==2 & e==2 & s<3, val:=2]
详情:
创建df
测试数据有两个连续的1
df<-c(NA, NA, 2, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, NA, 2, 2, 1, 2, 1,
1, 1, 2)
dt<-data.table(val=df)
创建一个 rleid
val,滞后并引导 val(用于下一步)
dt[,`:=`(id=rleid(val), p=shift(val, type="lag"), n=shift(val, type="lead"))]
然后通过id(group)得到组的大小,这个组的上一个和下一个值
dt[,`:=`(s=.N, f=p[1], e=n[.N]), by=id]
现在根据您的要求进行过滤(上一个下一个为 2,大小小于 3)并将 val 设置为 2
dt[f==2 & e==2 & s<3, val:=2]
最后给出
dt[,val]
[1] NA NA 2 2 2 2 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
与来源相比:
[1] NA NA 2 1 1 2 2 1 2 2 1 1 1 NA 2 2 1 2 1 1 1 2
它似乎满足了您的期望。
您可以尝试获取 2
在 df
中的索引。
然后得到这些位置之间的差异,从而找到要替换为 2
:
的值的索引
# position of the 2s
pos_df_2 <- which(df==2)
# which of the difference in positions are less than 3
wh_pos2_inf3 <- which(c(FALSE, diff(pos_df_2)<=3))
# get all indices between positions that are separated by less than 3 elements
ind_to_replace <- unique(unlist(sapply(wh_pos2_inf3, function(x) {pos_df_2[x-1]:pos_df_2[x]})))
# replace the elements by 2
df[ind_to_replace] <- 2
df
#[1] NA NA 2 1 1 1 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
rle
的解决方案:
rldf <- rle(df)
rllag <- c(tail(rldf$values,-1), NA)
rllead <- c(NA, head(rldf$values,-1))
rldf$values[which(rldf$values == 1 & rllag == 2 & rllead == 2 & rldf$lengths < 3)] <- 2
df_out <- inverse.rle(rldf)
给出:
> df_out
[1] NA NA 2 1 1 1 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
> identical(df_outcome,df_out)
[1] TRUE
我有一个大数据框 (df),其二项式值范围从 1 到 2。数据中也包含 NA。作为一个实际示例,我将创建一个包含用户数据子集的短向量:
df <- c(NA,NA,2,1,1,1,2,1,2,2,1,1,1,NA,2,2,1,2,1,1,1,2)
我基本上想要的结果是搜索数组的第一个和第二个 2
s 并将此间隔内的所有内容转换为 2 的函数。不过,如果第二个和第一个 2 的位置 > 3 然后值保持原样并且不执行任何更改。
除上述之外,函数还必须针对 df
的每个值进行循环。例如,再次考虑以下情况:
df <- c(NA,NA,2,1,1,1,2,1,2,2,1,1,1,NA,2,2,1,2,1,1,1,2)
函数应该有这样的结果:
df_outcome <- c(NA,NA,2,1,1,1,2,2,2,2,1,1,1,NA,2,2,2,2,1,1,1,2)
请注意,在 df_outcome
中,第一个和第二个 2 之间的值没有合并,因为它们的位置差异大于 3。另一方面,其他非2值也相应改变。
我尝试过的(但不能正常工作):
在 zoo
包中的 rollapply
的帮助下,我尝试创建一个函数来查找数组的第一个和第二个 2 并执行如上所述的修改。
func <- function (q) {
for (i in (which(q %in% 2)[1]):(which(q %in% 2)[2])) {
q[i]<-2
}
return(q)
}
然后我使用 rollapply
嵌套它,这样我就可以为每个循环指定一个特定的宽度加上其他参数,例如结果索引的位置(左)。
df_outcome<-rollapply(df, width = 3, FUN = func, fill = NA, partial = TRUE, align = "left")
问题在于,如果将用户生成的函数应用于向量,它就会起作用。然而,当作为参数嵌套在 rollapply
函数中时,它 returns 一个错误:
Error in (which(q %in% 2)[1]):(which(q %in% 2)[2]) : NA/NaN argument Called from: FUN(data[replace(posns, !ix, 0)], ...)
我想我在使用 rollapply
或数据格式方面有一些错误,但我不明白可能是什么问题。我考虑过使用rollapply
,因为我的数据很长,而且是为不同的用户生成的。因此,我需要一个函数,它也可以根据 User_ID
等其他变量拆分数据(很像 ddply
中的 .variables
参数或 by
中的 by
28=].
期待您的支持。
使用data.table
(但可能有更好的解决方案):
df<-c(NA, NA, 2, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, NA, 2, 2, 1, 2, 1, 1, 1, 2)
dt<-data.table(val=df)
dt[,`:=`(id=rleid(val), p=shift(val,type="lag"), n=shift(val,type="lead"))]
dt[,`:=`(s=.N, f=p[1], e=n[.N]), by=id]
dt[f==2 & e==2 & s<3, val:=2]
详情:
创建df
测试数据有两个连续的1
df<-c(NA, NA, 2, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, NA, 2, 2, 1, 2, 1,
1, 1, 2)
dt<-data.table(val=df)
创建一个 rleid
val,滞后并引导 val(用于下一步)
dt[,`:=`(id=rleid(val), p=shift(val, type="lag"), n=shift(val, type="lead"))]
然后通过id(group)得到组的大小,这个组的上一个和下一个值
dt[,`:=`(s=.N, f=p[1], e=n[.N]), by=id]
现在根据您的要求进行过滤(上一个下一个为 2,大小小于 3)并将 val 设置为 2
dt[f==2 & e==2 & s<3, val:=2]
最后给出
dt[,val]
[1] NA NA 2 2 2 2 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
与来源相比:
[1] NA NA 2 1 1 2 2 1 2 2 1 1 1 NA 2 2 1 2 1 1 1 2
它似乎满足了您的期望。
您可以尝试获取 2
在 df
中的索引。
然后得到这些位置之间的差异,从而找到要替换为 2
:
# position of the 2s
pos_df_2 <- which(df==2)
# which of the difference in positions are less than 3
wh_pos2_inf3 <- which(c(FALSE, diff(pos_df_2)<=3))
# get all indices between positions that are separated by less than 3 elements
ind_to_replace <- unique(unlist(sapply(wh_pos2_inf3, function(x) {pos_df_2[x-1]:pos_df_2[x]})))
# replace the elements by 2
df[ind_to_replace] <- 2
df
#[1] NA NA 2 1 1 1 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
rle
的解决方案:
rldf <- rle(df)
rllag <- c(tail(rldf$values,-1), NA)
rllead <- c(NA, head(rldf$values,-1))
rldf$values[which(rldf$values == 1 & rllag == 2 & rllead == 2 & rldf$lengths < 3)] <- 2
df_out <- inverse.rle(rldf)
给出:
> df_out
[1] NA NA 2 1 1 1 2 2 2 2 1 1 1 NA 2 2 2 2 1 1 1 2
> identical(df_outcome,df_out)
[1] TRUE