使用tidyverse将长格式转宽格式时遇问题求助
问题重现
用户尝试用tidyverse的pivot_wider将长格式数据转宽格式,代码如下:
library(tidyverse) df <- data.frame(continent = c("europe", "africa"), region = c("north", "west", "south"), number = 1:18) df_long <- df |> pivot_wider(names_from = region, values_from = number)
运行后出现警告及错误,输出为列表列:
Warning message:
Values fromnumberare not uniquely identified; output will contain list-cols.
• Usevalues_fn = listto suppress this warning.
• Usevalues_fn = {summary_fun}to summarise duplicates.
• Use the following dplyr code to identify duplicates.
{data} %>%
dplyr::group_by(continent, region) %>%
dplyr::summarise(n = dplyr::n(), .groups = "drop") %>%
dplyr::filter(n > 1L)
Error in exists(cacheKey, where = .rs.WorkingDataEnv, inherits = FALSE) :
invalid first argument
Error in assign(cacheKey, frame, .rs.CachedDataEnv) :
attempt to use zero-length variable namehead(df_long)
A tibble: 2 × 4continent north west south
1 europe <int [3]> <int [3]> <int [3]>
2 africa <int [3]> <int [3]> <int [3]>
问题原因
首先看数据构造:continent只有2个值,region有3个值,number是1:18,这意味着continent和region的组合会重复3次(2*3=6组,18/6=3)。pivot_wider要求每行的names_from与values_from的对应组合必须唯一,否则无法将多个值塞进单个单元格,只能用列表列存储,同时触发重复值警告。
报错信息里的invalid first argument和zero-length variable name是RStudio缓存的小问题,核心矛盾还是重复值导致的列表列输出。
解决方案
根据需求不同,有三种常见处理方式:
1. 保留所有重复值,添加唯一标识符
如果要保留所有number值,先给每个continent-region分组添加序号,让continent+region+id成为唯一组合:
df |> group_by(continent, region) |> mutate(id = row_number()) |> # 给每组添加行序号 ungroup() |> pivot_wider(names_from = region, values_from = number)
输出示例:
# A tibble: 6 × 4 continent id north west south <chr> <int> <int> <int> <int> 1 europe 1 1 4 7 2 europe 2 2 5 8 3 europe 3 3 6 9 4 africa 1 10 13 16 5 africa 2 11 14 17 6 africa 3 12 15 18
2. 对重复值进行汇总
如果不需要保留所有值,可对每个continent-region组合的number做汇总(求和、均值、最大/最小值等),用values_fn指定汇总函数:
# 求和示例 df |> pivot_wider(names_from = region, values_from = number, values_fn = sum)
输出:
# A tibble: 2 × 4 continent north west south <chr> <int> <int> <int> 1 europe 6 15 24 2 africa 33 42 51
其他汇总函数如mean、max、min可直接替换sum使用。
3. 仅保留每个组合的第一个值
如果只需要每个continent-region组合的第一个值,用values_fn = first:
df |> pivot_wider(names_from = region, values_from = number, values_fn = first)
输出:
# A tibble: 2 × 4 continent north west south <chr> <int> <int> <int> 1 europe 1 4 7 2 africa 10 13 16
内容的提问来源于stack exchange,提问作者always.learning

