选择至少x个相同的连续值并删除这些值的终点

时间:2018-06-06 16:13:05

标签: r

我有以下数据集:

> df
      A B C
 [1,] 1 0 0
 [2,] 1 0 0
 [3,] 1 0 0
 [4,] 1 0 1
 [5,] 1 0 1
 [6,] 1 0 1
 [7,] 1 0 1
 [8,] 1 0 0
 [9,] 0 0 0
[10,] 0 0 0
[11,] 0 0 0
[12,] 0 0 0
[13,] 0 0 0

我想对每一列做两件事:首先,我想要将0到0的两个位置内的所有1改变为0.然后,我想选择至少有四个连续的列的区域1秒;换句话说,如果出现一个不在四个或更多1的连续字符串中的1,它将变为0.结果数据集应如下所示:

(a.T @ b).size
(a @ b.T).size

最好的方法是什么?谢谢!

2 个答案:

答案 0 :(得分:1)

这是使用base函数的另一种可能方法。内联代码的解释。

apply(df, 2, function(x) {
    #identify 0 locations, create indices 2 places away from these locations 
    #and set these to 0
    idx <- unique(unlist(lapply(which(x==0L), `+`, -2L:2L)))
    x[idx[idx > 0L & idx <= length(x)]] <- 0L        

    #create run length encoding, filter for those with value=1 but less than 4 
    #and set those lengths to 0
    r <- rle(x)
    r$values[r$lengths < 4L & r$values==1L]  <- 0L
    inverse.rle(r)
})

输出:

      A B C
 [1,] 1 0 0
 [2,] 1 0 0
 [3,] 1 0 0
 [4,] 1 0 1
 [5,] 1 0 1
 [6,] 1 0 1
 [7,] 1 0 1
 [8,] 1 0 0
 [9,] 0 0 0
[10,] 0 0 0
[11,] 0 0 0
[12,] 0 0 0
[13,] 0 0 0

答案 1 :(得分:0)

您可以使用laglead进行比较(第一部分)。

以下是使用您的数据的示例(在对您的问题进行任何修改之前,这是在原始版本上):

library(dplyr)
library(tidyverse)
df <-
as.tibble(df) %>%
  mutate(A_lag=lag(A)) %>%
  mutate(B_lag=lag(B)) %>%
  mutate(C_lag=lag(C)) %>%
  mutate(A_lag2=lag(A,2)) %>%
  mutate(B_lag2=lag(B,2)) %>%
  mutate(C_lag2=lag(C,2)) %>%
  mutate(A_lead=lead(A)) %>%
  mutate(B_lead=lead(B)) %>%
  mutate(C_lead=lead(C)) %>%
  mutate(A_lead2=lead(A,2)) %>%
  mutate(B_lead2=lead(B,2)) %>%
  mutate(C_lead2=lead(C,2)) %>%
  as.data.frame()

a <- df[,c(1,4,7,10,13)]
b <- df[,c(2,5,8,11,14)]
c <- df[,c(3,6,9,12,15)]

df <- data.frame(A=apply(a,1,min,na.rm=T),
           B=apply(b,1,min,na.rm=T),
           C=apply(c,1,min,na.rm=T)
)

这会产生一个如下所示的中间结果表:

   A B C
1  1 0 0
2  1 0 0
3  1 0 0
4  1 0 1
5  1 0 1
6  1 0 1
7  1 0 1
8  1 0 0
9  0 0 0
10 0 1 0
11 0 1 0
12 0 1 0
13 0 1 0

此步骤的结果符合您的逻辑。

然而,下一步似乎你的话要求一件事 - 只保留1个在至少四个1的列式块中 - 但你的例子显示略有不同。

您的示例输出将列B显示为全0,即使最后4行由于前一步骤的逻辑而全部为1。

我已经创建了一些遵循您指定的逻辑的代码。如果您忘记包含细节或者想要稍微不同的东西,这应该会让您足够接近(否则请在评论中告诉我)。

# You could do it without a for loop if need be
myfun <- function(x) {
  for(i in 1:length(x)){
    x[i] <- ifelse((sum(x[i:(max(0,i-3))]) == 4) | (sum(x[i:(min(length(x),i+3))]) == 4),1,0)
  }
  return(x)
}

apply(df,2,myfun)