R对比列表识别逐月变化 unnest空列表丢行解决方案
问题原因
unnest_longer()默认会丢弃列表列长度为0的对应行。你的代码中2022-03-01对应的flavors.removed是长度为0的空字符向量,因此整行被过滤,直接导致该月新增的Pumpkin口味记录丢失。
修正方法
在调用unnest_longer()时传入keep_empty = TRUE参数,即可保留空列表对应的行,自动填充NA值;之后过滤掉非首月无移除口味时生成的冗余NA行,就能得到预期的9行完整结果。
完整可运行代码:
library(dplyr) library(tidyr) df %>% group_by(date) %>% summarize(flavors = list(flavor), .groups = "drop") %>% mutate( flavors.added = mapply(setdiff, flavors, lag(flavors)), flavors.removed = mapply(setdiff, lag(flavors), flavors) ) %>% select(-flavors) %>% unnest_longer(flavors.added, keep_empty = TRUE) %>% unnest_longer(flavors.removed, keep_empty = TRUE) %>% pivot_longer(-date, names_to = "type", values_to = "flavor") %>% # 过滤非首月无移除口味的空记录+去重 filter(!(type == "flavors.removed" & is.na(flavor) & date != min(date))) %>% unique() %>% arrange(date, type)
运行结果与预期完全匹配:
# A tibble: 9 x 3 date type flavor <date> <chr> <chr> 1 2022-01-01 flavors.added Almond 2 2022-01-01 flavors.added Apple 3 2022-01-01 flavors.added Apricot 4 2022-01-01 flavors.removed NA 5 2022-02-01 flavors.added Maple 6 2022-02-01 flavors.added Mint 7 2022-02-01 flavors.removed Apple 8 2022-02-01 flavors.removed Apricot 9 2022-03-01 flavors.added Pumpkin
可选优化写法
如果不想额外写过滤逻辑,也可以提前对首月的空值、后续月份的空列表做统一替换,从根源避免长度为0的向量出现:
df %>% group_by(date) %>% summarize(flavors = list(flavor), .groups = "drop") %>% mutate( prev_flavors = lag(flavors), flavors.added = Map(setdiff, flavors, prev_flavors), # 首月lag结果为NULL、无移除口味时直接返回NA,避免空列表 flavors.removed = Map(\(curr, prev) { if (is.null(prev)) return(NA_character_) res <- setdiff(prev, curr) if (length(res) == 0) return(NA_character_) res }, flavors, prev_flavors) ) %>% select(-flavors, -prev_flavors) %>% unnest_longer(flavors.added) %>% unnest_longer(flavors.removed) %>% pivot_longer(-date, names_to = "type", values_to = "flavor") %>% filter(!(is.na(flavor) & type == "flavors.added")) %>% unique() %>% arrange(date, type)
内容的提问来源于stack exchange,提问作者jophuh
相关产品推荐
相关产品推荐

