Развернуть диапазоны, определенные столбцами "от" и "до"

У меня есть кадр данных, содержащий "name" президентов США, годы, когда они начинаются и заканчиваются в офисе (столбцы "from" и "to"). Вот пример:

name           from  to
Bill Clinton   1993 2001
George W. Bush 2001 2009
Barack Obama   2009 2012

... и вывод из dput:

dput(tail(presidents, 3))
structure(list(name = c("Bill Clinton", "George W. Bush", "Barack Obama"
), from = c(1993, 2001, 2009), to = c(2001, 2009, 2012)), .Names = c("name", 
"from", "to"), row.names = 42:44, class = "data.frame")

Я хочу создать кадр данных с двумя столбцами ("name" и "year"), с каждой строкой на каждый год, когда президент находился на своем посту. Таким образом, мне нужно создать регулярную последовательность с каждым годом от "from" до "to". Здесь я ожидал:

name           year
Bill Clinton   1993
Bill Clinton   1994
...
Bill Clinton   2000
Bill Clinton   2001
George W. Bush 2001
George W. Bush 2002
... 
George W. Bush 2008
George W. Bush 2009
Barack Obama   2009
Barack Obama   2010
Barack Obama   2011
Barack Obama   2012

Я знаю, что могу использовать data.frame(name = "Bill Clinton", year = seq(1993, 2001)) для расширения возможностей для одного президента, но я не могу понять, как итерации для каждого президента.

Как мне это сделать? Я чувствую, что должен это знать, но я рисую пробел.

Обновление 1

Хорошо, я пробовал оба решения, и я получаю сообщение об ошибке:

foo<-structure(list(name = c("Grover Cleveland", "Benjamin Harrison", "Grover Cleveland"), from = c(1885, 1889, 1893), to = c(1889, 1893, 1897)), .Names = c("name", "from", "to"), row.names = 22:24, class = "data.frame")
ddply(foo, "name", summarise, year = seq(from, to))
Error in seq.default(from, to) : 'from' must be of length 1

Ответ 1

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

library(plyr)
ddply(presidents, "name", summarise, year = seq(from, to))
#              name year
# 1    Barack Obama 2009
# 2    Barack Obama 2010
# 3    Barack Obama 2011
# 4    Barack Obama 2012
# 5    Bill Clinton 1993
# 6    Bill Clinton 1994
# [...]

и если важно сортировать данные по годам, вы можете использовать функцию arrange:

df <- ddply(presidents, "name", summarise, year = seq(from, to))
arrange(df, df$year)
#              name year
# 1    Bill Clinton 1993
# 2    Bill Clinton 1994
# 3    Bill Clinton 1995
# [...]
# 21   Barack Obama 2011
# 22   Barack Obama 2012

Изменить 1: Следуя @edgester "Обновление 1", более подходящий подход состоит в том, чтобы использовать adply для учета президентов с непоследовательными терминами:

adply(foo, 1, summarise, year = seq(from, to))[c("name", "year")]

Ответ 2

Здесь a data.table решение. У этого есть хорошая (если незначительная) особенность, оставляя президентов в их приказанном порядке:

library(data.table)
dt <- data.table(presidents)
dt[, list(year = seq(from, to)), by = name]
#               name year
#  1:   Bill Clinton 1993
#  2:   Bill Clinton 1994
#  ...
#  ...
# 21:   Barack Obama 2011
# 22:   Barack Obama 2012

Изменить: Чтобы обрабатывать президентов с не последовательными терминами, используйте это вместо:

dt[, list(year = seq(from, to)), by = c("name", "from")]

Ответ 3

Здесь a dplyr решение:

library(dplyr)

# the data
presidents <- 
structure(list(name = c("Bill Clinton", "George W. Bush", "Barack Obama"
), from = c(1993, 2001, 2009), to = c(2001, 2009, 2012)), .Names = c("name", 
"from", "to"), row.names = 42:44, class = "data.frame")

# the expansion of the table
presidents %>%
    rowwise() %>%
    do(data.frame(name = .$name, year = seq(.$from, .$to, by = 1)))

# the output
Source: local data frame [22 x 2]
Groups: <by row>

             name  year
            (chr) (dbl)
1    Bill Clinton  1993
2    Bill Clinton  1994
3    Bill Clinton  1995
4    Bill Clinton  1996
5    Bill Clinton  1997
6    Bill Clinton  1998
7    Bill Clinton  1999
8    Bill Clinton  2000
9    Bill Clinton  2001
10 George W. Bush  2001
..            ...   ...

h/t: fooobar.com/questions/413258/...

Ответ 4

Другое решение base:

l <- mapply(`:`, d$from, d$to)
data.frame(name = d$name[rep(1:nrow(d), lengths(l))], year = unlist(l))
#              name year
# 1    Bill Clinton 1993
# 2    Bill Clinton 1994
# ...snip
# 8    Bill Clinton 2000
# 9    Bill Clinton 2001
# 10 George W. Bush 2001
# 11 George W. Bush 2002
# ...snip
# 17 George W. Bush 2008
# 18 George W. Bush 2009
# 19   Barack Obama 2009
# 20   Barack Obama 2010
# 21   Barack Obama 2011
# 22   Barack Obama 2012

Ответ 5

Вот быстрое решение base- R, где Df - ваш data.frame:

do.call(rbind, apply(Df, 1, function(x) {
  data.frame(name=x[1], year=seq(x[2], x[3]))}))

Он дает некоторые предупреждения о именах строк, но, похоже, возвращает правильный data.frame.

Ответ 6

Другой вариант использования tidyverse может быть gather данные в длинном формате, group_by name и создать последовательность между from и to настоящего времени.

library(tidyverse)

presidents %>%
  gather(key, date, -name) %>%
  group_by(name) %>%
  complete(date = seq(date[1], date[2]))%>%
  select(-key) 

# A tibble: 22 x 2
# Groups:   name [3]
#   name          date
#   <chr>        <dbl>
# 1 Barack Obama  2009
# 2 Barack Obama  2010
# 3 Barack Obama  2011
# 4 Barack Obama  2012
# 5 Bill Clinton  1993
# 6 Bill Clinton  1994
# 7 Bill Clinton  1995
# 8 Bill Clinton  1996
# 9 Bill Clinton  1997
#10 Bill Clinton  1998
# … with 12 more rows

Ответ 7

Альтернативный tidyverse подход с использованием unnest и map2.

library(tidyverse)

presidents %>%
  unnest(year = map2(from, to, seq)) %>%
  select(-from, -to)

#              name  year
# 1    Bill Clinton  1993
# 2    Bill Clinton  1994
...
# 21   Barack Obama  2011
# 22   Barack Obama  2012

Ответ 8

Использование by создать с by списка L из data.frames, один data.frame за президента, а затем rbind их вместе. Пакеты не используются.

L <- by(presidents, presidents$name, with, data.frame(name, year = from:to))
do.call("rbind", setNames(L, NULL))

Если вы не возражаете против имен строк, то последняя строка может быть уменьшена до:

do.call("rbind", L)