【问题标题】:Find similar elements of a vector and modify everything inbetween查找向量的相似元素并修改其间的所有内容
【发布时间】:2017-03-09 22:04:09
【问题描述】:

我有一个大数据框 (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)

我基本上想要的结果是一个函数,它搜索数组的第一个和第二个2s,并将此间隔内的所有内容转换为 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 函数中时,它会返回错误:

错误 (which(q %in% 2)[1]):(which(q %in% 2)[2]) : NA/NaN 参数 调用自:FUN(data[replace(posns, !ix, 0)], ...)

我想我在使用rollapply 或数据格式方面存在一些错误,但我不明白可能是什么问题。我考虑过使用rollapply,因为我的数据很长,而且是为不同的用户生成的。因此,我需要一个函数,该函数还可以根据其他变量分割数据,例如User_ID(很像ddply 中的.variablesargument 或data.table 中的by

期待您的支持。

【问题讨论】:

    标签: r for-loop dataframe error-handling rollapply


    【解决方案1】:

    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
    

    【讨论】:

    • 这个问题的解决方案对我的数据集非常有效,并且教会了我关于 rle 的使用!感谢您的支持。
    【解决方案2】:

    您可以尝试在df 中获取2 的索引。 然后获取这些位置之间的差异,从而找到要替换为 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
    

    【讨论】:

    • 感谢您的意见。我实际上考虑过使用索引,您的解决方案从这个角度提供了明确的答案。非常感谢。
    【解决方案3】:

    使用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   
    

    它似乎满足了你的期望。

    【讨论】:

    • 我花了一些时间来理解这一点,但它确实很神奇 - 谢谢。我对 data.table 没有太多经验,所以我期待更多地了解它。感谢您的详尽解释。
    猜你喜欢
    • 2019-07-30
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 2022-01-11
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多