如何将含重复值的长格式DataFrame转换为二进制DataFrame?
我现在需要把长格式的DataFrame转换成二进制标记的宽格式,但遇到了同一用户对应同一个值重复出现的情况,不知道该怎么处理。
我的示例长格式DataFrame如下:
d_long <- data.frame( nameid = c("sally","sally","sally", "sally","Robert","annie","annie","annie"), value = c("product1","ra","ent","ra","ra","ra","product1","product1") )
对应的数据展示:
nameid value 1 sally product1 2 sally ra 3 sally ent 4 sally ra 5 Robert ra 6 annie ra 7 annie product1 8 annie product1
我想要的预期输出是这样的二进制宽格式:
d_exist <- data.frame( nameid = c("sally","Robert","annie"), product1 = c(1,0,1), ra = c(1,1,1), ent = c(1,0,0) )
对应结果:
nameid product1 ra ent 1 sally 1 1 1 2 Robert 0 1 0 3 annie 1 1 0
我尝试了下面的代码,但出现了报错:
d_long %>% group_by(nameid, value) %>% mutate(count = n()) %>% ungroup() %>% spread(value, count, fill = 0) %>% as.data.frame()
报错信息:
Error: Duplicate identifiers for rows (7, 8), (2, 4)
想请教一下,仅使用 d_long[!duplicated(d_long), ] 这个方法是否可行?
首先,直接用 d_long[!duplicated(d_long), ] 是完全可行的!这个方法能先把nameid和value的重复组合去掉,只保留每组的唯一记录,之后再转宽格式就不会有重复标识符的问题了。
你可以试试这样写:
# 先去重,再转宽格式 d_long %>% distinct(nameid, value) %>% # 这行和d_long[!duplicated(d_long), ]效果一致,更符合tidyverse风格 mutate(flag = 1) %>% spread(value, flag, fill = 0) %>% as.data.frame()
或者用你提到的去重方式:
d_long[!duplicated(d_long), ] %>% mutate(flag = 1) %>% spread(value, flag, fill = 0) %>% as.data.frame()
这两种方式都能得到你想要的二进制标记结果——毕竟我们只需要标记用户是否拥有某个value,不管重复出现多少次,都只记为1。
另外,如果你想用更现代的tidyverse函数,也可以用pivot_wider替代spread(spread已经被软弃用了),写法更直观:
d_long %>% distinct(nameid, value) %>% pivot_wider( names_from = value, values_from = value, values_fn = ~1, values_fill = 0 )
至于你之前报错的原因,是因为group_by+mutate(count = n())之后,原来的重复行依然存在(只是多了count列),比如sally的"ra"有两行,annie的"product1"有两行,这时候spread会因为同一nameid下有重复的行而报错,因为它不知道该怎么处理这些重复的标识符。而先去重之后,每个nameid+value的组合只有一行,转宽格式就不会有冲突了。
还有一种更简洁的基础R方法,用table函数直接统计频次,再转成DataFrame并处理二进制:
tab <- table(d_long$nameid, d_long$value) # 把频次转换成二进制(只要出现过就为1) tab[tab > 0] <- 1 # 转成DataFrame并保留nameid列 as.data.frame.matrix(tab) %>% rownames_to_column("nameid")
这个方法也能快速得到你想要的结果,而且代码量很少。
总结一下:d_long[!duplicated(d_long), ]完全可行,是解决这个问题的有效方法,你也可以根据自己的习惯选择tidyverse风格的distinct或者基础R的table方法。
内容的提问来源于stack exchange,提问作者user8831872

