在R中使用pivot_wider返回列表而非数据框的原因
问题背景
我在R中有一个名为df的数据框:
df # A tibble: 20 × 2 # Groups: Professional_Assistance_Diet, How_successful_diet [9] var val <fct> <fct> 1 No Successful 2 I do not know Neutral 3 I do not know Very Successful 4 No Successful 5 No Successful 6 I do not know Very Successful 7 No Neutral 8 I do not know Successful 9 I do not know Neutral 10 Yes Very Successful 11 Yes Successful 12 No Not Successful 13 I do not know Very Successful 14 I do not know Not Successful 15 No Not Successful 16 I do not know Neutral 17 I do not know Successful 18 I do not know Neutral 19 No Neutral 20 Yes Very Successful
尝试用pivot_wider转换宽表时,得到如下结果:
df %>% pivot_wider( names_from = var, values_from = val ) # A tibble: 1 × 3 No `I do not know` Yes <list> <list> <list> 1 <fct [7]> <fct [10]> <fct [3]> Warning message: Values from `val` are not uniquely identified; output will contain list-cols. • Use `values_fn = list` to suppress this warning. • Use `values_fn = {summary_fun}` to summarise duplicates. • Use the following dplyr code to identify duplicates. {data} |> dplyr::summarise(n = dplyr::n(), .by = c(var)) |> dplyr::filter(n > 1L)
问题:为什么转换后返回列表列而非常规数据框?
附数据结构:
df = structure(list(var = structure(c(3L, 2L, 2L, 3L, 3L, 2L, 3L, 2L, 2L, 4L, 4L, 3L, 2L, 2L, 3L, 2L, 2L, 2L, 3L, 4L), levels = c("", "I do not know", "No", "Yes"), class = "factor"), val = structure(c(4L, 3L, 5L, 4L, 4L, 5L, 3L, 4L, 3L, 5L, 4L, 2L, 5L, 2L, 2L, 3L, 4L, 3L, 3L, 5L), levels = c("Not at all Successful", "Not Successful", "Neutral", "Successful", "Very Successful"), class = "factor")), class = c("grouped_df", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -20L), groups = structure(list( Professional_Assistance_Diet = structure(c(2L, 2L, 2L, 2L, 3L, 3L, 3L, 4L, 4L), levels = c("", "i do not know", "No", "Yes"), class = "factor"), How_successful_diet = structure(c(2L, 3L, 4L, 5L, 2L, 3L, 4L, 4L, 5L), levels = c("Not at all Successful", "Not Successful", "Neutral", "Successful", "Very Successful" ), class = "factor"), .rows = structure(list(14L, c(2L, 9L, 16L, 18L), c(8L, 17L), c(3L, 6L, 13L), c(12L, 15L), c(7L, 19L), c(1L, 4L, 5L), 11L, c(10L, 20L)), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame" ), row.names = c(NA, -9L), .drop = TRUE))
原因分析
核心原因是缺少唯一的行标识变量,pivot_wider无法确定同一var类别下的多个val值对应到宽表的哪一行,只能将同一var下的所有val打包成列表存入单元格。
具体细节:
- 你的数据是分组数据框(
grouped_df),但分组列未被用作pivot_wider的行标识; - 仅指定
names_from和values_from时,函数找不到能区分行的唯一依据,只能把重复类别下的所有值合并为列表; - 警告信息也明确提示:
Values from val are not uniquely identified; output will contain list-cols,直接点出了问题本质。
解决方法
根据需求不同,有两种常用处理方式:
1. 保留原始行结构
如果想让每一行原始数据对应宽表的一行,可以先添加唯一行号作为标识:
library(tidyverse) df %>% ungroup() %>% # 先取消分组避免干扰 mutate(row_id = row_number()) %>% # 添加唯一行号 pivot_wider( names_from = var, values_from = val, values_fill = NA # 缺失值用NA填充 )
2. 汇总统计结果
如果需要统计每个var类别下不同val的出现频次,可指定values_fn使用汇总函数:
df %>% ungroup() %>% pivot_wider( names_from = var, values_from = val, values_fn = list(val = table) # 统计各val的出现次数 )
也可以直接生成交叉表:
table(df$var, df$val)
内容的提问来源于stack exchange,提问作者Homer Jay Simpson
相关产品推荐
相关产品推荐

