R:在NA之后保留时间序列中的第一个值并将3个连续值设置为NA
R: Keep first values in time series after NA and set 3 consecutive values to NA
我有一个数据框,其中的列中包含许多时间序列(示例为 col1 中的一个序列)。我想遍历每个时间序列并保留出现在 NA 之后的第一个值,并将 仅连续的 3 个值设置为 NA (col2 中所需结果的示例)。理想情况下,这应该适用于数据框中的所有或指定(例如 [2:30])列。
不考虑只有3个连续值到NA的情况。
示例数据
df <- data.frame(
col1 = c(7.00, NA, NA, 1.00, 2.00, 5.00, NA, 5.00, 7.00, NA, NA, 1.00, NA, 2.00, NA, NA, 1.00, 3.00, 4.00, 5.00, 6.00, 7.00, NA, 7.00, NA),
col2 = c(7.00, NA, NA, 1.00, NA, NA, NA, 5.00, NA, NA, NA, 1.00, NA, 2.00, NA, NA, 1.00, NA, NA, NA, 6.00, NA, NA, 7.0, NA),
stringsAsFactors = FALSE)
感谢您的帮助。
这是一种使用 dplyr
的方法。
首先,每当我们有一个跟在 NA
之后的有效值时,我都会创建一个新组。然后在每个组中,我将第 2 行到第 4 行设置为 NA
,否则使用 col1
.
library(dplyr)
df %>%
mutate(new_grp = if_else(!is.na(col1) & is.na(lag(col1)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(grp) %>%
# Modified below per OP clarification: treat each group of 4 entries
# following an NA like the first four, with one value and then 3 NAs.
# Uses modulo 4, where any row with a remainder of 2, 3, or 0 (ie row 4, etc.) will get NA
mutate(col2b = case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ col1)) %>%
ungroup()
输出:
row col1 col2 col2b
1 7 7 7
2 NA NA NA
3 NA NA NA
4 1 1 1
5 2 NA NA
6 5 NA NA
7 NA NA NA
8 5 5 5
9 7 NA NA
10 NA NA NA
11 NA NA NA
12 1 1 1
13 NA NA NA
14 2 2 2
15 NA NA NA
16 NA NA NA
17 1 1 1
18 3 NA NA
19 4 NA NA
20 5 NA NA
21 6 6 6
22 7 NA NA # Modified per OP clarification
23 NA NA NA
24 7 7 7
25 NA NA NA
编辑:应用于多个/所有列
如果您所有的列都是同一类型,这应该可以一次转换所有列。它的工作原理是使用 tidyr
将数据从宽格式收集到 "long" 格式,然后执行与以前相同的计算,然后传播回宽格式。
df %>%
mutate(row = row_number()) %>%
tidyr::gather(col, value, -row) %>%
group_by(col) %>%
mutate(new_grp = if_else(!is.na(value) & is.na(lag(value)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(col, grp) %>%
mutate(value = case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ value)) %>%
ungroup() %>%
tidyr::spread(col, value) %>%
select(-row, -new_grp, -grp)
如果您的数据框使用不同的类型,我认为它会变得更加复杂,除非我缺少更简单的替代方法。我从一种使用 "tidyeval" 的方法开始,让您可以使用函数以编程方式更改一个指定的列。这之后的最后一步可以使用 purrr
将函数应用于所有列。
NA_2to4 <- function(df_name, col_to_change) {
col_quo <- enquo(col_to_change)
df_name %>%
mutate(new_grp = if_else(!is.na(!!col_quo) & is.na(lag(!!col_quo)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(grp) %>%
mutate(!!col_quo := case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ !!col_quo)) %>%
ungroup() %>%
select(-new_grp, -grp)
}
以下是将其应用于特定列的方法:
df %>%
NA_2to4(colA) %>%
NA_2to4(colB)
我想有一种方法可以使用 purr::map
应用于所有列,但我现在不确定语法。
我有一个数据框,其中的列中包含许多时间序列(示例为 col1 中的一个序列)。我想遍历每个时间序列并保留出现在 NA 之后的第一个值,并将 仅连续的 3 个值设置为 NA (col2 中所需结果的示例)。理想情况下,这应该适用于数据框中的所有或指定(例如 [2:30])列。
示例数据
df <- data.frame(
col1 = c(7.00, NA, NA, 1.00, 2.00, 5.00, NA, 5.00, 7.00, NA, NA, 1.00, NA, 2.00, NA, NA, 1.00, 3.00, 4.00, 5.00, 6.00, 7.00, NA, 7.00, NA),
col2 = c(7.00, NA, NA, 1.00, NA, NA, NA, 5.00, NA, NA, NA, 1.00, NA, 2.00, NA, NA, 1.00, NA, NA, NA, 6.00, NA, NA, 7.0, NA),
stringsAsFactors = FALSE)
感谢您的帮助。
这是一种使用 dplyr
的方法。
首先,每当我们有一个跟在 NA
之后的有效值时,我都会创建一个新组。然后在每个组中,我将第 2 行到第 4 行设置为 NA
,否则使用 col1
.
library(dplyr)
df %>%
mutate(new_grp = if_else(!is.na(col1) & is.na(lag(col1)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(grp) %>%
# Modified below per OP clarification: treat each group of 4 entries
# following an NA like the first four, with one value and then 3 NAs.
# Uses modulo 4, where any row with a remainder of 2, 3, or 0 (ie row 4, etc.) will get NA
mutate(col2b = case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ col1)) %>%
ungroup()
输出:
row col1 col2 col2b
1 7 7 7
2 NA NA NA
3 NA NA NA
4 1 1 1
5 2 NA NA
6 5 NA NA
7 NA NA NA
8 5 5 5
9 7 NA NA
10 NA NA NA
11 NA NA NA
12 1 1 1
13 NA NA NA
14 2 2 2
15 NA NA NA
16 NA NA NA
17 1 1 1
18 3 NA NA
19 4 NA NA
20 5 NA NA
21 6 6 6
22 7 NA NA # Modified per OP clarification
23 NA NA NA
24 7 7 7
25 NA NA NA
编辑:应用于多个/所有列
如果您所有的列都是同一类型,这应该可以一次转换所有列。它的工作原理是使用 tidyr
将数据从宽格式收集到 "long" 格式,然后执行与以前相同的计算,然后传播回宽格式。
df %>%
mutate(row = row_number()) %>%
tidyr::gather(col, value, -row) %>%
group_by(col) %>%
mutate(new_grp = if_else(!is.na(value) & is.na(lag(value)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(col, grp) %>%
mutate(value = case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ value)) %>%
ungroup() %>%
tidyr::spread(col, value) %>%
select(-row, -new_grp, -grp)
如果您的数据框使用不同的类型,我认为它会变得更加复杂,除非我缺少更简单的替代方法。我从一种使用 "tidyeval" 的方法开始,让您可以使用函数以编程方式更改一个指定的列。这之后的最后一步可以使用 purrr
将函数应用于所有列。
NA_2to4 <- function(df_name, col_to_change) {
col_quo <- enquo(col_to_change)
df_name %>%
mutate(new_grp = if_else(!is.na(!!col_quo) & is.na(lag(!!col_quo)), 1, 0),
grp = cumsum(new_grp)) %>%
group_by(grp) %>%
mutate(!!col_quo := case_when(row_number() %% 4 %in% c(2:3, 0) ~ NA_real_,
TRUE ~ !!col_quo)) %>%
ungroup() %>%
select(-new_grp, -grp)
}
以下是将其应用于特定列的方法:
df %>%
NA_2to4(colA) %>%
NA_2to4(colB)
我想有一种方法可以使用 purr::map
应用于所有列,但我现在不确定语法。