You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:17:05