将长格式DataFrame转为宽格式以去除重复Person.Id记录
将重复Person.Id的DataFrame转换为宽格式
问题描述
你有一个包含重复Person.Id的DataFrame,每个居住过的城市对应一行记录,需要将其转换为宽格式,用二进制(1/0)标记用户是否居住过某城市,同时保留Current_Patient、DOB等唯一信息列,消除重复的Person.Id。
原始DataFrame:
> dput(df) structure(list(Person.Id = c(123L, 345L, 345L, NA), City_lived = c("NY", "NY", "Boston", NA), Current_Patient = c("Yes", "Yes", "Yes", NA), DOB = c("11/20/97", "10/10/92", "10/10/92", NA)), class = "data.frame", row.names = c(NA, -4L))
期望输出:
> dput(df2) structure(list(Person.Id = c(123L, 345L), City_lived_Boston = c(0L, 1L), City_lived_NY = c(1L, 1L), Current_Patient = c("Yes", "Yes"), DOB = c("11/20/97", "10/10/92")), class = "data.frame", row.names = c(NA, -2L))
解决方案
方法1:使用tidyverse的pivot_wider(推荐)
pivot_wider是tidyverse生态中专门用于长转宽的函数,语法清晰易读:
library(tidyverse) # 加载原始数据 df <- structure(list(Person.Id = c(123L, 345L, 345L, NA), City_lived = c("NY", "NY", "Boston", NA), Current_Patient = c("Yes", "Yes", "Yes", NA), DOB = c("11/20/97", "10/10/92", "10/10/92", NA)), class = "data.frame", row.names = c(NA, -4L)) # 转换为宽格式 df2 <- df %>% # 过滤掉Person.Id为NA的无效行 filter(!is.na(Person.Id)) %>% pivot_wider( # 指定唯一标识列(每个Person.Id对应的唯一信息) id_cols = c(Person.Id, Current_Patient, DOB), # 将City_lived的取值转为列名 names_from = City_lived, # 给新列添加前缀,匹配期望格式 names_prefix = "City_lived_", # 基于City_lived列生成值 values_from = City_lived, # 用length判断是否存在该城市(存在则标记为1) values_fn = ~ifelse(length(.x) > 0, 1L, 0L), # 不存在的城市标记为0 values_fill = 0L ) # 查看结果 dput(df2)
方法2:使用reshape2的dcast
如果你习惯使用传统的reshape2包,也可以用dcast实现:
library(reshape2) # 加载原始数据 df <- structure(list(Person.Id = c(123L, 345L, 345L, NA), City_lived = c("NY", "NY", "Boston", NA), Current_Patient = c("Yes", "Yes", "Yes", NA), DOB = c("11/20/97", "10/10/92", "10/10/92", NA)), class = "data.frame", row.names = c(NA, -4L)) # 过滤无效行 df_filtered <- df[!is.na(df$Person.Id), ] # 转宽格式 df2 <- dcast( df_filtered, Person.Id + Current_Patient + DOB ~ City_lived, fun.aggregate = length, value.var = "City_lived", fill = 0 ) # 给城市列添加前缀,匹配期望格式 colnames(df2) <- ifelse(colnames(df2) %in% c("Person.Id", "Current_Patient", "DOB"), colnames(df2), paste0("City_lived_", colnames(df2))) # 查看结果 dput(df2)
关键说明
- 过滤无效行:原始数据中存在
Person.Id为NA的行,这部分数据无意义,先通过filter或索引过滤掉。 - 唯一标识列:
Person.Id、Current_Patient、DOB是每个用户的唯一信息,必须作为id_cols(或dcast的行变量),确保转换后每个Person.Id只保留一行。 - 二进制标记:通过
values_fn = length或~ifelse(length(.x) > 0, 1L, 0L)实现“是否居住过某城市”的标记,存在则为1,不存在则为0。
内容的提问来源于stack exchange,提问作者Jamie
相关产品推荐
相关产品推荐

