Reputation:
I have a dataset df with two variables : one (ascendantly ordered) posixct variable date.time and one numeric variable value. The variable value consists of a series zeros or a series of different positive numbers greaer than zero. The length of each serie is random but greater than one.
Edit: loading the lubridate library
library(lubridate)
The dataset df:
set.seed(10)
df <- data.frame(date.time=seq(ymd_hms("2016-01-01 00:00:00"),ymd_hms("2016-01-01 01:00:00"),length.out = 20),value=c(runif(3,1,3),rep.int(0,4),runif(5,1,3),rep.int(0,4),runif(4,1,3)))
The desired outcome:
desired.outcome <- cbind(df,peak=c(1,1,1,0,0,0,0,2,2,2,2,2,0,0,0,0,3,3,3,3))
I would like to create a third variable called peak that identifies each series of positive numbers greater than zero as a separate "peak". A peak is defined as a series of positive numbers greater than zero that are between two series of zeros.
Upvotes: 2
Views: 66
Reputation: 25225
Another option (in mtd2
) for your consideration:
set.seed(10L)
#generate dataset of 5million rows as OP mentioned
N <- 5e6
df <- data.frame(value=10*runif(N))
#randomly set 25% of values to 0
df[sample(N, 0.25*N), "value"] <- 0
##original dataset of 20 rows
# df <- data.frame(date.time=seq(as.POSIXct("2016-01-01 00:00:00"),as.POSIXct("2016-01-01 01:00:00"),length.out = 20),
# value=c(runif(3,1,3),rep.int(0,4),runif(5,1,3),rep.int(0,4),runif(4,1,3)))
mtd1 <- function() {
inverse.rle(with(a<-rle(df$value>0), modifyList(a, list(values=cumsum(values)*values))))
}
val <- df$value
mtd3 <- function() {
result <- rleidv(val>0)
result[!(val>0)]<-0
result[val>0]<-rleidv(result[val>0])
}
library(data.table)
mtd2 <- function() {
setDT(df)[, peak := (value > 0) * ceiling(rleid(value > 0) / 2)]
}
library(microbenchmark)
microbenchmark(mtd1(), mtd3(), mtd2(), times=5L)
timings:
Unit: milliseconds
expr min lq mean median uq max neval
mtd1() 357.755701 375.957301 517.6211210 610.545700 611.407001 632.439902 5
mtd3() 312.756201 329.190100 385.4440206 329.810201 352.368101 603.095500 5
mtd2() 181.146901 187.001001 256.8531808 215.238501 221.030000 479.849501 5
Upvotes: 0
Reputation: 11480
Maybe not a beauty:
(can't eval your data)
set.seed(10)
value=c(runif(3,1,3),rep.int(0,4),runif(5,1,3),rep.int(0,4),runif(4,1,3))
code:
library(data.table)
result <- rleidv(value>0)
result[!(value>0)]<-0
result[value>0]<-rleidv(result[value>0])
result:
#[1] 1 1 1 0 0 0 0 2 2 2 2 2 0 0 0 0 3 3 3 3
Upvotes: 1
Reputation: 79198
a=rle(df$value>0)
a$values=cumsum(a$values)*a$values
peak=inverse.rle(a)
peak
[1] 1 1 1 0 0 0 0 2 2 2 2 2 0 0 0 0 3 3 3 3
cbind(df,peak)
date.time value peak
1 2016-01-01 00:00:00 2.014956 1
2 2016-01-01 00:03:09 1.613537 1
3 2016-01-01 00:06:18 1.853815 1
4 2016-01-01 00:09:28 0.000000 0
5 2016-01-01 00:12:37 0.000000 0
6 2016-01-01 00:15:47 0.000000 0
7 2016-01-01 00:18:56 0.000000 0
8 2016-01-01 00:22:06 2.386204 2
9 2016-01-01 00:25:15 1.170272 2
10 2016-01-01 00:28:25 1.450873 2
11 2016-01-01 00:31:34 1.549061 2
12 2016-01-01 00:34:44 1.544610 2
13 2016-01-01 00:37:53 0.000000 0
14 2016-01-01 00:41:03 0.000000 0
15 2016-01-01 00:44:12 0.000000 0
16 2016-01-01 00:47:22 0.000000 0
17 2016-01-01 00:50:31 2.231659 3
18 2016-01-01 00:53:41 1.859343 3
19 2016-01-01 00:56:50 2.303311 3
20 2016-01-01 01:00:00 2.135476 3
Doing this in a single line:
inverse.rle(with(a<-rle(df$value>0),modifyList(a,list(values=cumsum(values)*values))))
Upvotes: 4