Создать различное количество строк для каждого идентификатора в зависимости от условия

#r

#r

Вопрос:

У меня есть фрейм данных с одной строкой на идентификатор. Я пытаюсь создать несколько строк для каждого идентификатора с той же информацией, кроме start и stop .

Если возраст> = 36, этот идентификатор должен иметь в общей сложности 9 строк. start должно быть 27, 28, …, 35. stop должно быть 28, 29, …, 36.

Если 27> возраст < 36, у этого идентификатора должно быть ( age — 27) всего строк. Например, если age равно 30, то этот идентификатор должен иметь в общей сложности 3 строки.

Если возраст = 27, у этого идентификатора должно быть всего 1 строка. Самый младший возраст в моем наборе данных — 27 лет, поэтому каждый идентификатор должен содержать не менее 1 строки.

Некоторые данные:

 df$id <- c(1, 2, 3)
df$age <- c(27, 40, 30)
time.columns <- c("start", "stop")
df[, time.columns] <- NA
 

Я хочу, чтобы это выглядело так:

 id age start stop 
1  27  27    28 
2  40  27    28
2  40  28    29
2  40  29    30 
2  ..  ..    ..
2  40  35    36
3  30  27    28 
3  30  28    29 
3  30  29    30 
 

Комментарии:

1. @akrun Это выглядит так stop <- start 1 .

2. Да, это верно. stop должно быть start 1

3. Исходя из ожидаемого результата, я думаю, что это 13 строк?

Ответ №1:

Вы могли бы написать базовую if else функцию для значений «age» и использовать ее для вычисления столбцов «start». Что-то вроде этого должно помочь вам начать:

 expander <- function(value) {
  if (value >= 36) {
    start = 27:35
  } else if (value == 27) {
    start = 27
  } else if (value > 27 amp; value < 36) {
    start = 27   sequence(value - 27) - 1
  }
}
 

Вот как вы можете это использовать:

 # Sample data
df <- data.frame(id = c(1, 2, 3, 4),
                 age = c(27, 40, 30, 29))

library(dplyr)
library(tidyr)

df %>% 
  group_by(id) %>% 
  mutate(start = list(expander(age))) %>% 
  unnest(cols = c(start)) %>% 
  mutate(stop = start   1)
# # A tibble: 15 x 4
# # Groups:   id [4]
#       id   age start  stop
#    <dbl> <dbl> <dbl> <dbl>
#  1     1    27    27    28
#  2     2    40    27    28
#  3     2    40    28    29
#  4     2    40    29    30
#  5     2    40    30    31
#  6     2    40    31    32
#  7     2    40    32    33
#  8     2    40    33    34
#  9     2    40    34    35
# 10     2    40    35    36
# 11     3    30    27    28
# 12     3    30    28    29
# 13     3    30    29    30
# 14     4    29    27    28
# 15     4    29    28    29
 

Если вы хотите полностью придерживаться базы R, вы можете сделать:

 x <- with(df, sapply(age, expander))

within(df[rep(1:nrow(df), lengths(x)), ], {
    start = unlist(x)
    stop = start   1
})
##     id age stop start
## 1    1  27   28    27
## 2    2  40   28    27
## 2.1  2  40   29    28
## 2.2  2  40   30    29
## 2.3  2  40   31    30
## 2.4  2  40   32    31
## 2.5  2  40   33    32
## 2.6  2  40   34    33
## 2.7  2  40   35    34
## 2.8  2  40   36    35
## 3    3  30   28    27
## 3.1  3  30   29    28
## 3.2  3  30   30    29
## 4    4  29   28    27
## 4.1  4  29   29    28
 

Комментарии:

1. Спасибо — почему нам нужно использовать list() функцию расширения?

2. @user12310746, он создает list столбец, который можно unnest отредактировать позже. mutate ожидается, что результирующее количество строк будет таким же, как и входные данные, но это не относится к этой функции.

Ответ №2:

Вот data.table вариант

 setDT(df)[
  ,
  .(start = 27:pmin(pmax(age - 1, 27), 35)),
  .(id, age)
][
  ,
  stop := start   1
][]
 

что дает

     id age start stop
 1:  1  27    27   28
 2:  2  40    27   28
 3:  2  40    28   29
 4:  2  40    29   30
 5:  2  40    30   31
 6:  2  40    31   32
 7:  2  40    32   33
 8:  2  40    33   34
 9:  2  40    34   35
10:  2  40    35   36
11:  3  30    27   28
12:  3  30    28   29
13:  3  30    29   30
 

Ответ №3:

Поскольку «начало» остается фиксированным, мы создаем «остановку» на основе минимального значения 36 и «возраста», создаем последовательность из «start» и «stop», затем unnest list столбец

 library(dplyr)
library(purrr)
df %>%
   mutate(start = 27, stop = map2(start 1, pmin(36, age), 
    ~ if(.y > .x) seq(.x, .y) else .x)) %>% 
   unnest(c(stop)) %>%
   group_by(id) %>%
   mutate(start = first(start)   row_number() - 1)
 

-вывод

 # A tibble: 13 x 4
# Groups:   id [3]
#      id   age start  stop
#   <int> <dbl> <dbl> <dbl>
# 1     1    27    27    28
# 2     2    40    27    28
# 3     2    40    28    29
# 4     2    40    29    30
# 5     2    40    30    31
# 6     2    40    31    32
# 7     2    40    32    33
# 8     2    40    33    34
# 9     2    40    34    35
#10     2    40    35    36
#11     3    30    27    28
#12     3    30    28    29
#13     3    30    29    30
 

данные

 df <- structure(list(id = 1:3, age = c(27, 40, 30)), class = "data.frame", 
row.names = c(NA, 
-3L))