Как управлять именами новых переменных после распространения tidyr?

У меня есть dataframe с панелью: 2 наблюдения для каждой единицы с двух лет:

library(tidyr)
mydf <- data.frame(
    id = rep(1:3, rep(2,3)), 
    year = rep(c(2012, 2013), 3), 
    value = runif(6)
)
mydf
#  id year      value
#1  1 2012 0.09668064
#2  1 2013 0.62739399
#3  2 2012 0.45618433
#4  2 2013 0.60347152
#5  3 2012 0.84537624
#6  3 2013 0.33466030

Я хотел бы изменить эти данные на широкий формат, который можно легко сделать с помощью tidyr::spread. Однако, как значения year переменный являются числом, имена моих новых переменных становятся числа, а что делает его дальнейшее использование сложнее.

spread(mydf, year, value)
#  id       2012      2013
#1  1 0.09668064 0.6273940
#2  2 0.45618433 0.6034715
#3  3 0.84537624 0.3346603

Я знаю, что я могу легко переименовать столбцы. Однако, если я хотел бы изменить форму в цепочке с другими операциями, это становится неудобным. Например, следующая строка, очевидно, не имеет смысла.

library(dplyr)
mydf %>% spread(year, value) %>% filter(2012 > 0.5)

Следующие работы, но не настолько кратки:

tmp <- spread(mydf, year, value)
names(tmp) <- c("id", "y2012", "y2013")
filter(tmp, y2012 > 0.5)

Любая идея, как я могу изменить имена новых переменных в spread?

Ответ 1

Я знаю, что прошло несколько лет с тех пор, как этот вопрос был задан изначально, но для потомков я хочу также выделить аргумент sep в spread. Если нет NULL, он будет использоваться в качестве разделителя между именем ключа и значениями:

mydf %>% 
 spread(key = year, value = value, sep = "")
#  id   year2012  year2013
#1  1 0.15608322 0.6886531
#2  2 0.04598124 0.0792947
#3  3 0.16835445 0.1744542

Это не совсем то, что хотелось в вопросе, но достаточно для моих целей. Смотрите ?spread.

Обновление с tidyr 1.0.0: в tidyr 1.0.0 добавлен pivot_widerpivot_longer), который обеспечивает больший контроль в этом отношении с аргументами names_sep и names_prefix. Так что теперь вызов будет:

mydf %>% 
  pivot_wider(names_from = year, values_from = value,
              names_prefix = "year")
# # A tibble: 3 x 3
#        id year2012 year2013
#     <int>    <dbl>    <dbl>
#   1     1    0.347    0.388
#   2     2    0.565    0.924
#   3     3    0.406    0.296

Чтобы получить именно то, что изначально было нужно (с префиксом "y"), вы, конечно, теперь можете получить его напрямую, просто имея names_prefix = "y".

names_sep используется в случае, если вы собрали несколько столбцов, как показано ниже, где я добавил четверти к данным:

# Add quarters to data
mydf2 <- data.frame(
  id = rep(1:3, each = 8), 
  year = rep(rep(c(2012, 2013), each = 4), 3), 
  quarter  = rep(c("Q1","Q2","Q3","Q4"), 3),
  value = runif(24)
)
head(mydf2)
# id year quarter     value
# 1  1 2012      Q1 0.8651470
# 2  1 2012      Q2 0.3944423
# 3  1 2012      Q3 0.4580580
# 4  1 2012      Q4 0.2902604
# 5  1 2013      Q1 0.4751588
# 6  1 2013      Q2 0.6851755

mydf2 %>% 
  pivot_wider(names_from = c(year, quarter), values_from = value,
              names_sep = "_m", names_prefix = "y")
# # A tibble: 3 x 9
#      id y2012_mQ1 y2012_mQ2 y2012_mQ3 y2012_mQ4 y2013_mQ1 y2013_mQ2 y2013_mQ3 y2013_mQ4
#   <int>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
# 1     1     0.865     0.394     0.458    0.290      0.475     0.685     0.213     0.920
# 2     2     0.566     0.614     0.509    0.0515     0.974     0.916     0.681     0.509
# 3     3     0.968     0.615     0.670    0.748      0.723     0.996     0.247     0.449

Ответ 2

Вы можете использовать backticks для имен столбцов, начиная с цифр, и filter должен работать как ожидалось

  mydf %>%
      spread(year, value) %>%
      filter('2012' > 0.5)
  #  id      2012      2013
  #1  3 0.8453762 0.3346603

Или другой вариант будет использовать unite для объединения двух столбцов в один столбец после создания второго столбца 'year1' со строкой 'y'.

  mydf %>%
     mutate(year1='y') %>%
     unite(yearN, year1, year) %>%
     spread(yearN, value) %>%
     filter(y_2012 > 0.5)
 #   id    y_2012    y_2013
 #1  3 0.8453762 0.3346603

Даже мы можем изменить столбец "год" в mutate, используя paste

 mydf %>%
     mutate(year=paste('y', year, sep="_")) %>%
     spread(year, value) %>%
     filter(y_2012 > 0.5)

Ответ 3

Другой вариант - использовать setNames() как следующую вещь в трубе:

mydf %>%
    spread(mydf, year, value) %>%
    setNames( c("id", "y2012", "y2013") ) %>%
    filter(y2012 > 0.5)

Единственная проблема с использованием setNames заключается в том, что вы должны точно знать, какими будут ваши столбцы при их spread(). В большинстве случаев это не проблема, особенно если вы работаете полуинновативно.

Но если вам не хватает пары ключ/значение в ваших исходных данных, есть вероятность, что она не будет отображаться в виде столбца, и вы можете в конечном итоге называть свои столбцы неправильно, даже не зная об этом. Конечно, setNames() ошибку, если количество имен не совпадает с количеством столбцов, поэтому вы получили встроенную ошибку проверки ошибок.

Тем не менее, удобство использования setNames() перевешивало риск чаще, чем не для меня.

Ответ 4

Используя преемника spread() pivot_wider(), мы можем дать префикс созданным столбцам:

library(tidyr)
set.seed(1)
mydf <- data.frame(
  id = rep(1:3, rep(2,3)), 
  year = rep(c(2012, 2013), 3), 
  value = runif(6)
)

pivot_wider(mydf, names_from = "year", values_from = "value", names_prefix = "y")
#> # A tibble: 3 x 3
#>      id y2012 y2013
#>   <int> <dbl> <dbl>
#> 1     1 0.266 0.372
#> 2     2 0.573 0.908
#> 3     3 0.202 0.898

Created on 2019-09-14 by the reprex package (v0.3.0)

Ответ 5

Rename() в dplyr должен сделать свое дело

library(tidyr); library(dplyr)
mydf %>%
  spread(year,value)%>%
  rename(y2012 = '2012',y2013 = '2013')%>%
  filter(y2012>0.5)