将半长数据转换为宽数据。

4

我很确定应该有一个简单的替代方法,但我无法想出来。当前正在使用的是for循环,这不是最优解。 我的数据框看起来像这样:

NAME <- c("ABC", "ABC", "ABC", "DEF", "GHI", "GHI", "JKL", "JKL", "JKL", "MNO")
YEAR <- c(2012, 2013, 2014, 2012, 2012, 2013, 2012, 2014, 2016, 2013)
MARKS <- c(45, 75, 95, 91, 75, 76, 85, 88, 89, 77)
MAXIMUM <- c(95, NA, NA, 91, 76, NA, 89, NA, NA, 77)

DF <- data.frame(
  NAME,
  YEAR,
  MARKS,
  MAXIMUM
)

> DF
   NAME YEAR MARKS MAXIMUM
1   ABC 2012    45      95
2   ABC 2013    75      NA
3   ABC 2014    95      NA
4   DEF 2012    91      91
5   GHI 2012    75      76
6   GHI 2013    76      NA
7   JKL 2012    85      89
8   JKL 2014    88      NA
9   JKL 2016    89      NA
10  MNO 2013    77      77

我想每行只有一个名称,每年的详细信息(YEAR、MARKS和MAXIMUM列)应该被展开为单独的表头。我尝试使用了tidyr::pivot_wider函数,但没有成功。
这里是所需输出的示例图: enter image description here
4个回答

5

也许你可以按照 row_number() 的顺序首先按 NAME 枚举,然后使用 pivot_wider

library(tidyverse)

DF %>%
  group_by(NAME) %>%
  mutate(n = row_number()) %>%
  pivot_wider(NAME, names_from = n, values_from = c(YEAR, MARKS, MAXIMUM))

输出

  NAME  YEAR_1 YEAR_2 YEAR_3 MARKS_1 MARKS_2 MARKS_3 MAXIMUM_1 MAXIMUM_2 MAXIMUM_3
  <chr>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>   <dbl>     <dbl>     <dbl>     <dbl>
1 ABC     2012   2013   2014      45      75      95        95        NA        NA
2 DEF     2012     NA     NA      91      NA      NA        91        NA        NA
3 GHI     2012   2013     NA      75      76      NA        76        NA        NA
4 JKL     2012   2014   2016      85      88      89        89        NA        NA
5 MNO     2013     NA     NA      77      NA      NA        77        NA        NA

或者,如@RobertoT所提到的,您可以将YEAR转换为因子,然后排列您的YEAR值。使用complete函数,您可以填充缺失的YEAR值。最后使用select函数对列进行排序。

DF$YEAR_FAC = factor(DF$YEAR)

DF %>%
  group_by(NAME) %>%
  complete(YEAR_FAC, fill = list(YEAR = NA)) %>%
  mutate(n = row_number()) %>%
  pivot_wider(NAME, names_from = n, values_from = c(YEAR, MARKS, MAXIMUM)) %>%
  select(NAME, ends_with(as.character(1:nlevels(DF$YEAR_FAC))))

输出

  NAME  YEAR_1 MARKS_1 MAXIMUM_1 YEAR_2 MARKS_2 MAXIMUM_2 YEAR_3 MARKS_3 MAXIMUM_3 YEAR_4 MARKS_4 MAXIMUM_4
  <chr>  <dbl>   <dbl>     <dbl>  <dbl>   <dbl>     <dbl>  <dbl>   <dbl>     <dbl>  <dbl>   <dbl>     <dbl>
1 ABC     2012      45        95   2013      75        NA   2014      95        NA     NA      NA        NA
2 DEF     2012      91        91     NA      NA        NA     NA      NA        NA     NA      NA        NA
3 GHI     2012      75        76   2013      76        NA     NA      NA        NA     NA      NA        NA
4 JKL     2012      85        89     NA      NA        NA   2014      88        NA   2016      89        NA
5 MNO       NA      NA        NA   2013      77        77     NA      NA        NA     NA      NA        NA

3

除了@Ben+1的解决方案外,我们还可以使用我最近学到的代码来对列进行排序 将两个具有交替列位置的数据框组合在一起

DF %>%
  group_by(NAME) %>%
  mutate(n = row_number()) %>%
  pivot_wider(NAME, names_from = n, values_from = c(YEAR, MARKS, MAXIMUM)) %>% 
  select(-NAME) %>% 
  dplyr::select(all_of(c(matrix(names(.), ncol = 3, byrow = TRUE))))    

  NAME  YEAR_3 MARKS_3 MAXIMUM_3 YEAR_1 MARKS_1 MAXIMUM_1 YEAR_2 MARKS_2 MAXIMUM_2
  <chr>  <dbl>   <dbl>     <dbl>  <dbl>   <dbl>     <dbl>  <dbl>   <dbl>     <dbl>
1 ABC     2014      95        NA   2012      45        95   2013      75        NA
2 DEF       NA      NA        NA   2012      91        91     NA      NA        NA
3 GHI       NA      NA        NA   2012      75        76   2013      76        NA
4 JKL     2016      89        NA   2012      85        89   2014      88        NA
5 MNO       NA      NA        NA   2013      77        77     NA      NA        NA

2
我认为之前的所有答案都忽略了期望输出是基于“YEAR”作为一个因素。每行期望输出有4个分组列,而不是3个。因此,您需要避免在同一列中混合不同年份。
您可以为每一行分配一个数字-“grp”-基于“Year”的级别作为“factor()”。此外,如果您先进行较长的旋转,您可以按照您想要的方式排列值,然后将所有内容扩展到更宽,以便列按您的期望排序:
library(tidyverse)
DF %>% 
  mutate(grp = as.integer(factor(DF$YEAR,unique(DF$YEAR)))) %>% 
  pivot_longer(cols=c('YEAR','MARKS','MAXIMUM'), names_to = 'COLNAMES', values_to= 'COL_VALUES') %>%
  arrange(NAME,grp) %>% 
  pivot_wider(names_from = c(COLNAMES,grp), values_from= COL_VALUES, names_sep = '')

输出:

# A tibble: 5 x 13
  NAME  YEAR1 MARKS1 MAXIMUM1 YEAR2 MARKS2 MAXIMUM2 YEAR3 MARKS3 MAXIMUM3 YEAR4 MARKS4 MAXIMUM4
  <chr> <dbl>  <dbl>    <dbl> <dbl>  <dbl>    <dbl> <dbl>  <dbl>    <dbl> <dbl>  <dbl>    <dbl>
1 ABC    2012     45       95  2013     75       NA  2014     95       NA    NA     NA       NA
2 DEF    2012     91       91    NA     NA       NA    NA     NA       NA    NA     NA       NA
3 GHI    2012     75       76  2013     76       NA    NA     NA       NA    NA     NA       NA
4 JKL    2012     85       89    NA     NA       NA  2014     88       NA  2016     89       NA
5 MNO      NA     NA       NA  2013     77       77    NA     NA       NA    NA     NA       NA

然而,我建议您跟踪年份,以免使tibble更加混乱。
DF$YEAR = factor(DF$YEAR)

DF %>% 
  pivot_longer(cols=c('MARKS','MAXIMUM'), names_to = 'COLNAMES', values_to= 'COL_VALUES') %>%
  arrange(NAME,YEAR) %>% 
  pivot_wider(names_from = c(COLNAMES,YEAR), values_from= COL_VALUES)

# A tibble: 5 x 9
  NAME  MARKS_2012 MAXIMUM_2012 MARKS_2013 MAXIMUM_2013 MARKS_2014 MAXIMUM_2014 MARKS_2016 MAXIMUM_2016
  <chr>      <dbl>        <dbl>      <dbl>        <dbl>      <dbl>        <dbl>      <dbl>        <dbl>
1 ABC           45           95         75           NA         95           NA         NA           NA
2 DEF           91           91         NA           NA         NA           NA         NA           NA
3 GHI           75           76         76           NA         NA           NA         NA           NA
4 JKL           85           89         NA           NA         88           NA         89           NA
5 MNO           NA           NA         77           77         NA           NA         NA           NA

使用因子来匹配预期输出是一个很好的观点。 - Ben
感谢您建议为列命名,以免混淆。 - Frodo

0

这里有一个使用data.table的版本:

library(data.table)
DT <- setDT(DF)

# numerotate the line
DT[,I := .I - .I[1] + 1,by = NAME]
# melt to have only three columns
tmp <- melt(DT,measure.vars = c("YEAR","MARKS","MAXIMUM"))
# transforming to wide
dcast(tmp,
      NAME ~ paste0(variable,I), 
      value.var = "value")


   NAME MARKS1 MARKS2 MARKS3 MAXIMUM1 MAXIMUM2 MAXIMUM3 YEAR1 YEAR2 YEAR3
1:  ABC     45     75     95       95       NA       NA  2012  2013  2014
2:  DEF     91     NA     NA       91       NA       NA  2012    NA    NA
3:  GHI     75     76     NA       76       NA       NA  2012  2013    NA
4:  JKL     85     88     89       89       NA       NA  2012  2014  2016
5:  MNO     77     NA     NA       77       NA       NA  2013    NA    NA

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