如何展开具有重复标识符的列?

15
我有以下的tibble:
structure(list(age = c("21", "17", "32", "29", "15"), 
               gender = structure(c(2L, 1L, 1L, 2L, 2L), .Label = c("Female", "Male"), class = "factor")), 
          row.names = c(NA, -5L), class = c("tbl_df", "tbl", "data.frame"), .Names = c("age", "gender"))

    age gender
  <chr> <fctr>
1    21   Male
2    17 Female
3    32 Female
4    29   Male
5    15   Male

我正在尝试使用tidyr::spread来实现这个目标:

  Female Male
1    NA     21
2    17     NA
3    32     NA
4    NA     29
5    NA     15

我认为spread(gender, age)会起作用,但是我收到一个错误信息,内容如下:
Error: Duplicate identifiers for rows (2, 3), (1, 4, 5)

你为什么想要弄乱你的数据? - F. Privé
1
@F.Privé 只是在练习,我只是想了解为什么它不起作用。 - Dambo
1
添加一个唯一索引,例如 df %>% mutate(i = row_number()) %>% spread(gender, age)。在这种情况下并不是非常有用,但在更复杂的上下文中有时会用到。 - alistaire
@alistaire 谢谢,你能简要解释一下为什么我的解决方案是有歧义的吗?我不明白为什么tidyr需要那些行号来展开。 - Dambo
我在答案中添加了更详细的解释。vignette('tidy-data', 'tidyr') 也可能会有所帮助。 - alistaire
显示剩余2条评论
1个回答

21

现在你有两个女性age值和三个男性age值,没有其他变量阻止它们合并成一行,就像spread试图对具有类似/无索引值的值做的那样:

library(tidyverse)

df <- data_frame(x = c('a', 'b'), y = 1:2)

df    # 2 rows...
#> # A tibble: 2 x 2
#>       x     y
#>   <chr> <int>
#> 1     a     1
#> 2     b     2

df %>% spread(x, y)    # ...become one if there's only one value for each.
#> # A tibble: 1 x 2
#>       a     b
#> * <int> <int>
#> 1     1     2

spread 不像 dcast 那样应用函数来合并多个值,因此必须对行进行索引,以便在一个位置只有一个或零个值,例如:

df <- data_frame(i = c(1, 1, 2, 2, 3, 3), 
                 x = c('a', 'b', 'a', 'b', 'a', 'b'), 
                 y = 1:6)

df    # the two rows with each `i` value here...
#> # A tibble: 6 x 3
#>       i     x     y
#>   <dbl> <chr> <int>
#> 1     1     a     1
#> 2     1     b     2
#> 3     2     a     3
#> 4     2     b     4
#> 5     3     a     5
#> 6     3     b     6

df %>% spread(x, y)    # ...become one row here.
#> # A tibble: 3 x 3
#>       i     a     b
#> * <dbl> <int> <int>
#> 1     1     1     2
#> 2     2     3     4
#> 3     3     5     6

如果您的值不能自然地由其他列索引,您可以添加一个唯一的索引列(例如通过添加行号作为列),这将阻止 spread 尝试折叠行:

df <- structure(list(age = c("21", "17", "32", "29", "15"), 
                     gender = structure(c(2L, 1L, 1L, 2L, 2L), 
                                        .Label = c("Female", "Male"), class = "factor")), 
                row.names = c(NA, -5L), 
                class = c("tbl_df", "tbl", "data.frame"), 
                .Names = c("age", "gender"))

df %>% mutate(i = row_number()) %>% spread(gender, age)
#> # A tibble: 5 x 3
#>       i Female  Male
#> * <int>  <chr> <chr>
#> 1     1   <NA>    21
#> 2     2     17  <NA>
#> 3     3     32  <NA>
#> 4     4   <NA>    29
#> 5     5   <NA>    15

如果想要之后移除它,请添加select(-i)。在这种情况下,这不会产生非常有用的数据框,但在更复杂的重塑过程中可能非常有用。

为什么 tidyr 不能帮我做到这一点? df %>% group_by_at(vars(-age)) %>% mutate(row_id=1:n()) %>% ungroup() %>% spread(key=gender, value=age) %>% select(-row_id) - dmi3kno
@dmi3kno 它可以吗?还是你的意思是为什么 spread 不能自己做到这一点?如果是这样,那是因为行顺序不是一个变量,因此在数据中17和21之间没有固有的联系,直到你通过索引添加一个。 - alistaire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接