r #loops #dplyr #tidyverse
#r #циклы #dplyr #tidyverse
Вопрос:
У меня есть dataframe, который содержит много переменных, но я бы хотел сосредоточиться только на одной, ‘X’, как показано ниже:
df <- data.frame("x" = c("NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", 1,"NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1))
Что я хочу сделать, так это заполнить числа в последовательности, начинающейся с 2, таким образом:
- когда есть x== 1, следующий NA должен занимать 2, 3, 4 и так далее, пока снова не появится x == 1, а затем следующий NA должен начать заполняться 2,3,4 и так далее.
Пример вывода:
x = 2,3,4,5,1,2,3,4,5,6,7,1,2,3,4,5,6,7,8,9,1,2,3,4,1,2,3,4,5,1 ......
То, что я пытаюсь сделать, это:
df$Sequence <- ifelse(df1$x!="1" amp; is.na(df1$x), seq(2,100), df1$x)
Но это не возвращает мне ожидаемый результат, почему?
Комментарии:
1. Символьная строка
"NA"
не совпадает сNA
логической константой, которая указывает на пропущенное значение. Поэтому с вашими даннымиis.na()
всегда будет возвращатьсяFALSE
.
Ответ №1:
Поскольку x
начинается с 8 последовательных NA
, все еще неясно, как вменять самые первые строки. Почему ваш вывод примера начинается с 2? Вот как вменить все, начиная с первого вхождения 1. Я изменил пример, разрешив ему начинаться с 1:
library(tidyverse)
df <- data.frame("x" = c(1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1))
df <-
df %>%
na_if("NA") %>%
as_tibble() %>%
mutate(id = row_number())
y <-
df %>%
filter(x == 1) %>%
# calculate block sizes
transmute(
from = id,
to = lead(id),
diff = to - from
) %>%
# vector ends with a 1 and there is no n 1 th element
replace_na(list(diff = 1)) %>%
pull(diff) %>%
map(seq) %>%
flatten() %>%
as.numeric()
y
#> [1] 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 1 2 3 4 5 6
#> [39] 1 2 3 4 5 6 7 8 9 1
df %>% transmute(x, y = y)
#> # A tibble: 48 x 2
#> x y
#> <chr> <dbl>
#> 1 1 1
#> 2 <NA> 2
#> 3 <NA> 3
#> 4 <NA> 4
#> 5 <NA> 5
#> 6 <NA> 6
#> 7 <NA> 7
#> 8 <NA> 8
#> 9 1 1
#> 10 <NA> 2
#> # … with 38 more rows
Создано 2021-10-20 пакетом reprex (v2.0.1)
Ответ №2:
Тот же подход, что и @danlooo, но с использованием data.table вместо tidyverse
df <- data.table(x = as.integer(c(1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", 1,"NA", "NA", "NA", "NA", "NA", 1, "NA", "NA", "NA", "NA", "NA", "NA", "NA", "NA", 1)))
df[, id := .I][!is.na(x), diff := lead(id) - id][is.na(diff), diff := 1L][, y := unlist(mapply(seq, df[x == 1L]$diff))][, c("id", "diff") := NULL]
Если вы хотите узнать, что это такое, вам нужно узнать, как data.table связывается и работает по ссылке.
Я советую вам посмотреть шаг за шагом, чтобы снять цепочку с моего кода следующим образом.
Если вы теперь запустите построчно, вы сможете увидеть, как выглядит таблица df после каждого шага
## tidyverse
# mutate(id = row_number())
## data.table
df[, id := .I] # adds a column id where .I is the row number
## tidyverse
# y <-
# df %>%
# filter(x == 1) %>%
# transmute(
# from = id,
# to = lead(id),
# diff = to - from
# )
## data.table
# instead of creating a new object y first I calculate where available the difference
df[!is.na(x), diff := lead(id) - id] # if x is not NA (x==1 would give same result) create diff column (does the same as the transmute()
## tidyverse
# replace_na(list(diff = 1))
## data.table
df[is.na(diff), diff := 1L] # if diff is NA, assign the value 1 (1L stands for 1 as an integer) to diff
## tidyverse
# pull(diff) %>%
# map(seq) %>%
# flatten()
# as.numeric()
# df %>% transmute(x, y = y)
## data.table
# here I directly add the column y where df[x == 1L]$diff "pulls" the diff column with the rows where x == 1, mapply replaces the map() and in this context I need unlist() instead of flatten()
# no need to make numeric, as I made all integers at start (could have chosen numeric as well)
# I did not leave df, so I did not have to recreate df
df[, y := unlist(mapply(seq, df[x == 1L]$diff))]
# as I did not have to recreate df, I need to clean the "help" columns
df[, c("id", "diff") := NULL]
Комментарии:
1. Спасибо. Но не могли бы вы также включить некоторые пояснения к коду для таких читателей, как я, которым было бы немного сложно понять. Я уверен, что это тоже должно быть отличной работой, но я не смог понять, как работает этот код.
2. Я добавил более подробное объяснение и сравнил шаги с методом tidyverse.
3. вау. это тоже здорово … спасибо, что поделились советами и обошли. потрясающе!