如果满足条件,则用向量填充列

时间:2020-09-17 13:56:17

标签: r dplyr

我正在尝试解决以下问题。 我有一个小标题:

> tibble( signal = c(0,1,0,0,1,0,0,1,1,1,1,1,1,0), days =0)
# A tibble: 14 x 2
   signal  days
    <dbl> <dbl>
 1      0     0
 2      1     0
 3      0     0
 4      0     0
 5      1     0
 6      0     0
 7      0     0
 8      1     0
 9      1     0
10      1     0
11      1     0
12      1     0
13      1     0
14      0     0

我需要通过以下方式填写“天”列:

  • 查找信号== 1并用矢量1,2,3,4一次填充天列
  • 在向量结束后找到下一个信号== 1并再次用向量1,2,3,4填充天列

因此,结果将如下所示:

signal  days
    <dbl> <dbl>
 1      0     0
 2      1     1
 3      0     2
 4      0     3
 5      1     4
 6      0     0
 7      0     0
 8      1     1
 9      1     2
10      1     3
11      1     4
12      1     1
13      1     2
14      0     3

我可以使用for循环来做到这一点,但是很难使用dplyr将其向量化。

感谢任何帮助!

3 个答案:

答案 0 :(得分:3)

data.table::set()

这里有一些基本知识
library(data.table)
i <- 1L
n <- nrow(df)
while (i < n) {
  if (df$signal[i] == 1) {
    k <- min(i+3L, n)
    set(df, i = (i:k), j = "days", 1L:(k-i+1L))
    i <- i+4L
  } else {
    i <- i+1L
  }
}

#    signal days
# 1       0    0
# 2       1    1
# 3       0    2
# 4       0    3
# 5       1    4
# 6       0    0
# 7       0    0
# 8       1    1
# 9       1    2
# 10      1    3
# 11      1    4
# 12      1    1
# 13      1    2
# 14      0    3

答案 1 :(得分:2)

这是一个Rcpp解决方案。尽管它包含一个循环,但是与基于R的循环相比,其开销非常低,并且可能与您将获得的速度一样快:

 Rcpp::cppFunction("IntegerVector fill_column(IntegerVector v) {
  bool flag = false;
  int counter = 1;
  for(int i = 0; i < v.length(); ++i) {
    if(flag){
      v[i] = counter++;
      if(counter == 5) { 
        flag = false;
        counter = 1;
      }
    } else {
      if(v[i] == 1) {
        v[i] = counter++;
        flag = true;
      }
    }
  }
  return v;
  }")

这允许您使用dplyr内部的功能:

df %>% mutate(days = fill_column(signal))

##>  A tibble: 14 x 2
#>    signal  days
#>     <dbl> <int>
#>  1      0     0
#>  2      1     1
#>  3      0     2
#>  4      0     3
#>  5      1     4
#>  6      0     0
#>  7      0     0
#>  8      1     1
#>  9      1     2
#> 10      1     3
#> 11      1     4
#> 12      1     1
#> 13      1     2
#> 14      0     3

答案 2 :(得分:2)

这是通过定义自定义函数f来实现的基础R解决方案,该函数有助于根据给定的signals找出从哪里开始向量

f <- function(signals) {
  k <- 1
  v <- which(signals == 1)
  while (k < length(v)) {
    if (v[k + 1] - v[k] < 4) {
      v <- v[-(k + 1)]
      k <- 1
    } else {
      k <- k + 1
    }
  }
  v
}

然后,如果我们运行以下for循环

for (i in f(df$signal)) {
  fill <- i:min(i + 3, nrow(df))
  df$days[fill] <- seq_along(fill)
}

我们会看到的

> df
   signal days
1       0    0
2       1    1
3       0    2
4       0    3
5       1    4
6       0    0
7       0    0
8       1    1
9       1    2
10      1    3
11      1    4
12      1    1
13      1    2
14      0    3