在列中找到最接近的先前负值

时间:2018-10-08 20:03:43

标签: r dplyr

我有一个数据帧df

library(tidyverse)
t <- c(103,104,108,120,127,129,140,142,150,151,160,177,178,183,186,187,191,194,198,199)
w <- c(1,1,1,-1,-1,-1,-1,-1,1,1,-1,-1,1,1,1,-1,1,1,-1,-1)

df <- data_frame(t, w)

> dput(df)
structure(list(t = c(103, 104, 108, 120, 127, 129, 140, 142, 
150, 151, 160, 177, 178, 183, 186, 187, 191, 194, 198, 199), 
w = c(1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 
-1, 1, 1, -1, -1)), .Names = c("t", "w"), row.names = c(NA, 
-20L), class = c("tbl_df", "tbl", "data.frame"))

> df
# A tibble: 20 x 2
       t     w
   <dbl> <dbl>
 1   103  1.00
 2   104  1.00
 3   108  1.00
 4   120 -1.00
 5   127 -1.00
 6   129 -1.00
 7   140 -1.00
 8   142 -1.00
 9   150  1.00
10   151  1.00
11   160 -1.00
12   177 -1.00
13   178  1.00
14   183  1.00
15   186  1.00
16   187 -1.00
17   191  1.00
18   194  1.00
19   198 -1.00
20   199 -1.00

现在,如果w中的值大于零,请找到 最近的负数 w ,并将对应的t值之间的差异分配给新列d。否则,d等于零。即所需的输出应如下所示:

       t     w   d
     103  1.00  NA   (there is no previous w < 0)
     104  1.00  NA   (there is no previous w < 0)
     108  1.00  NA   (there is no previous w < 0)
     120 -1.00   0
     127 -1.00   0
     129 -1.00   0
     140 -1.00   0
     142 -1.00   0
     150  1.00   8   = 150 - 142
     151  1.00   9   = 151 - 142
     160 -1.00   0
     177 -1.00   0
     178  1.00   1   = 178 - 177
     183  1.00   6   = 183 - 177
     186  1.00   9   = 186 - 177
     187 -1.00   0
     191  1.00   4   = 191 - 187
     194  1.00   7   = 194 - 187
     198 -1.00   0
     199 -1.00   0

(上面的NA可能也为零。)

从昨天开始,我试图使用findInterval()which()等来解决此问题,但没有成功。我在想的另一种方法是在lag()函数中引入可变移位...

理想情况下,我希望有一个类似tidyverse的解决方案。

任何帮助将不胜感激。 预先谢谢你!

2 个答案:

答案 0 :(得分:8)

使用data.table (因为tidyverse当前没有非等额联接):

library(data.table)
DT = data.table(df)

DT[, v := 0]
DT[w > 0, v := 
  DT[w < 0][.SD, on=.(t < t), mult="last", i.t - x.t]
]

      t  w  v
 1: 103  1 NA
 2: 104  1 NA
 3: 108  1 NA
 4: 120 -1  0
 5: 127 -1  0
 6: 129 -1  0
 7: 140 -1  0
 8: 142 -1  0
 9: 150  1  8
10: 151  1  9
11: 160 -1  0
12: 177 -1  0
13: 178  1  1
14: 183  1  6
15: 186  1  9
16: 187 -1  0
17: 191  1  4
18: 194  1  7
19: 198 -1  0
20: 199 -1  0

它将新列初始化为0,然后将其替换为w > 0的行子集。替换使用数据子集.SD(其中w > 0与表的部分w < 0DT[w < 0]的联接)。连接语法为x[i, on=, j],在这种情况下...

  • x = DT[w < 0]
  • i = .SD = DT[w > 0]

联接使用i的每一行根据x中的规则在on=中查找行。当找到多个匹配项时,我们仅采用最后一个(mult = "last")。

j是我们使用联接执行的操作,这里计算两列之间的差。为了消除每个表中的列的歧义,我们使用前缀x.*i.*


使用cummax。我不确定这是否能一概而论,但适用于以下示例:

DT[, v := t - cummax(t*(w < 0))]
DT[cumsum(w < 0) == 0, v := NA]

我想这要求t列以升序排序。

答案 1 :(得分:4)

一种奇特的方式:

首先,在中间列(t2)中使用NA(如果为正数)和t(如果为负)

df <- mutate(df, t2 = case_when(w > 0 ~ as.numeric(NA), TRUE ~ t)) 

#fill NA in t2 so that for each row, t2 is value of t when w was last neg
df <- fill(df, t2)

#> df
# A tibble: 20 x 3
#       t     w    t2
#   <dbl> <dbl> <dbl>
# 1   103     1    NA
# 2   104     1    NA
# 3   108     1    NA
# 4   120    -1   120
# 5   127    -1   127
# 6   129    -1   129
# 7   140    -1   140
# 8   142    -1   142
# 9   150     1   142
#10   151     1   142
#11   160    -1   160
#12   177    -1   177
#13   178     1   177
#14   183     1   177
#15   186     1   177
#16   187    -1   187
#17   191     1   187
#18   194     1   187
#19   198    -1   198
#20   199    -1   199

然后从t减去t2

df$d <- with(df, t - t2)

#> df
# A tibble: 20 x 4
#       t     w    t2     d
#   <dbl> <dbl> <dbl> <dbl>
# 1   103     1    NA    NA
# 2   104     1    NA    NA
# 3   108     1    NA    NA
# 4   120    -1   120     0
# 5   127    -1   127     0
# 6   129    -1   129     0
# 7   140    -1   140     0
# 8   142    -1   142     0
# 9   150     1   142     8
#10   151     1   142     9
#11   160    -1   160     0
#12   177    -1   177     0
#13   178     1   177     1
#14   183     1   177     6
#15   186     1   177     9
#16   187    -1   187     0
#17   191     1   187     4
#18   194     1   187     7
#19   198    -1   198     0
#20   199    -1   199     0