Развернуть диапазоны, определенные столбцами "от" и "до"
У меня есть кадр данных, содержащий "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)