-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
lead-lag.R
164 lines (144 loc) · 3.7 KB
/
lead-lag.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#' Compute lagged or leading values
#'
#' Find the "previous" (`lag()`) or "next" (`lead()`) values in a vector. Useful
#' for comparing values behind of or ahead of the current values.
#'
#' @param x A vector
#' @param n Positive integer of length 1, giving the number of positions to
#' lag or lead by
#' @param default The value used to pad `x` back to its original size after the
#' lag or lead has been applied. The default, `NULL`, pads with a missing
#' value. If supplied, this must be a vector with size 1, which will be cast
#' to the type of `x`.
#' @param order_by An optional secondary vector that defines the ordering to use
#' when applying the lag or lead to `x`. If supplied, this must be the same
#' size as `x`.
#' @param ... Not used.
#'
#' @return
#' A vector with the same type and size as `x`.
#'
#' @name lead-lag
#' @examples
#' lag(1:5)
#' lead(1:5)
#'
#' x <- 1:5
#' tibble(behind = lag(x), x, ahead = lead(x))
#'
#' # If you want to look more rows behind or ahead, use `n`
#' lag(1:5, n = 1)
#' lag(1:5, n = 2)
#'
#' lead(1:5, n = 1)
#' lead(1:5, n = 2)
#'
#' # If you want to define a value to pad with, use `default`
#' lag(1:5)
#' lag(1:5, default = 0)
#'
#' lead(1:5)
#' lead(1:5, default = 6)
#'
#' # If the data are not already ordered, use `order_by`
#' scrambled <- slice_sample(
#' tibble(year = 2000:2005, value = (0:5) ^ 2),
#' prop = 1
#' )
#'
#' wrong <- mutate(scrambled, previous_year_value = lag(value))
#' arrange(wrong, year)
#'
#' right <- mutate(scrambled, previous_year_value = lag(value, order_by = year))
#' arrange(right, year)
NULL
#' @export
#' @rdname lead-lag
lag <- function(x, n = 1L, default = NULL, order_by = NULL, ...) {
if (inherits(x, "ts")) {
abort("`x` must be a vector, not a <ts>, do you want `stats::lag()`?")
}
check_dots_empty0(...)
check_number_whole(n)
if (n < 0L) {
abort("`n` must be positive.")
}
shift(x, n = n, default = default, order_by = order_by)
}
#' @export
#' @rdname lead-lag
lead <- function(x, n = 1L, default = NULL, order_by = NULL, ...) {
check_dots_empty0(...)
check_number_whole(n)
if (n < 0L) {
abort("`n` must be positive.")
}
shift(x, n = -n, default = default, order_by = order_by)
}
shift <- function(x,
...,
n = 1L,
default = NULL,
order_by = NULL,
error_call = caller_env()) {
check_dots_empty0(...)
if (!is.null(order_by)) {
out <- with_order(
order_by = order_by,
fun = shift,
x = x,
n = n,
default = default,
error_call = error_call
)
return(out)
}
obj_check_vector(x, call = error_call)
check_number_whole(n)
n <- vec_cast(n, integer(), call = error_call)
if (!is.null(default)) {
vec_check_size(default, size = 1L, call = error_call)
default <- vec_cast(
x = default,
to = x,
x_arg = "default",
to_arg = "x",
call = error_call
)
}
lag <- n >= 0L
n <- abs(n)
size <- vec_size(x)
if (n > size) {
n <- size
}
if (is.null(default)) {
shift_slice(x, n, size, lag)
} else {
shift_c(x, n, size, lag, default)
}
}
shift_slice <- function(x, n, size, lag) {
loc_default <- vec_rep(NA_integer_, n)
if (lag) {
loc <- seq2(1L, size - n)
loc <- vec_c(loc_default, loc)
vec_slice(x, loc)
} else {
loc <- seq2(1L + n, size)
loc <- vec_c(loc, loc_default)
vec_slice(x, loc)
}
}
shift_c <- function(x, n, size, lag, default) {
default <- vec_rep(default, n)
if (lag) {
loc <- seq2(1L, size - n)
x <- vec_slice(x, loc)
vec_c(default, x, .ptype = x)
} else {
loc <- seq2(1L + n, size)
x <- vec_slice(x, loc)
vec_c(x, default, .ptype = x)
}
}