做生信分析或者数据科学,最常见的场景就是:你的数据散落在好几张表里。
表 A:存着 100 个病人的年龄、性别、分组信息。 表 B:存着测序仪下机的几万个基因表达量。 现在,你要分析“不同分组病人的 TP53 基因表达量差异”。第一步也是最关键的一步,就是把这两张表依据共同的列(比如病人 ID)合并起来。 R 语言自带的
merge()函数虽然能用,但参数晦涩难记。而dplyr包提供的join家族函数,完美借鉴了 SQL 数据库的设计哲学,逻辑清晰,代码优雅。 今天,我们就通过一套精心设计的示例数据,配合直观的图解,彻底搞懂左连接、内连接、全连接等 6 种核心操作!
许多朋友们在文章发送后不能第一时间接收到相关通知,强烈建议您将我们公众号设置为星标,步骤为 :进入公众号BioGenius班主页→点击右上角三个点→下方第一个选项就是“设置星标”。
为了演示不同 Join 的区别,我们必须构造两张不完全重合的表格。这是现实中最常见的情况。
library(dplyr)# 表1:临床信息表 (左表,Left Table)# 注意:这里有 S1, S2, S3, S4 四个病人clinical_df <- tibble( sample_id = c("S1", "S2", "S3", "S4"), group = c("Ctrl", "Ctrl", "Treat", "Treat"), age = c(25, 30, 45, 50))# 表2:基因表达表 (右表,Right Table)# 注意:S3 的数据缺失了,但多出了一个新病人 S5expression_df <- tibble( sample_id = c("S1", "S2", "S4", "S5"), TP53_exp = c(10.5, 12.1, 9.8, 11.3))print("--- 临床表 (左) ---")print(clinical_df)print("--- 表达表 (右) ---")print(expression_df)关键点:
sample_id。这类 Join 的目的是把右表的列加到左表上。
left_join() —— 生信人的最爱口诀:以左表为尊,右表来依附。这是最最常用的连接方式!我们通常以临床信息表为基准(左表),把表达量信息(右表)匹配过来。
NA(空值)。# 以临床表为主,匹配表达量left_result <- left_join(clinical_df, expression_df, by = "sample_id")print(left_result)# 结果解读:# S1, S2, S4 成功匹配到了表达量。# S3 在左表有,右表没有,所以 TP53_exp 这一列被填上了 NA。# S5 虽然在右表有,但左表没有,所以直接被丢弃。inner_join() —— 极其严格的交集口诀:只取共同语言,谁缺了都不行。
inner_result <- inner_join(clinical_df, expression_df, by = "sample_id")print(inner_result)# 结果解读:# 只有两边都有的 S1, S2, S4 被保留。S3 和 S5 都被踢出了局。# 得到的是一个没有任何 NA 值的干净表格。full_join() —— 我全都要口诀:一个都不能少,没有的填 NA。
NA 补齐。full_result <- full_join(clinical_df, expression_df, by = "sample_id")print(full_result)# 结果解读:# S1-S5 全部都在。S3 缺表达量(NA),S5 缺临床信息(NA, NA)。# 这是最完整的并集表格。现实中很坑爹的情况是:左表叫 sample_id,右表叫 Patient_ID。 这时候需要在 by 参数里手动指定映射关系:
# 假设 expression_df 的列名改了expression_renamed <- rename(expression_df, Patient_ID = sample_id)# 使用命名向量指定:c("左表列名" = "右表列名")left_join(clinical_df, expression_renamed, by = c("sample_id" = "Patient_ID"))这类 Join 非常神奇,它们不会把右表的列加进来,只是拿右表当一个“过滤器”来筛选左表的行。
semi_join() —— 存在的证明口诀:只保留在右表里“有户口”的左表行。我想看看临床表里,有哪些病人在表达表里也有数据?(我只想要人,不想要数据)
semi_result <- semi_join(clinical_df, expression_df, by = "sample_id")print(semi_result)# 结果解读:# 结果还是临床表的 3 列 (sample_id, group, age),没有增加表达列。# 但是行数变少了,只剩下 S1, S2, S4。因为只有这三个人在右表里存在。anti_join() —— 查找缺失值神器口诀:专门挑刺,把右表里“没有户口”的左表行揪出来。这是检查数据缺失最快的方法!我想知道哪些临床病人竟然忘记测序了?
anti_result <- anti_join(clinical_df, expression_df, by = "sample_id")print(anti_result)# 结果解读:# 精准定位到了 S3!因为 S3 在左表,却不在右表。left_join | 我全留 | ||
inner_join | 咱俩都有 | ||
full_join | 咱俩这辈子 | ||
semi_join | |||
anti_join | 查找缺失样本 |
掌握了 dplyr::join 家族,你脑海中处理数据的维度就打开了。不再是单打独斗的一张张孤表,而是一个可以随时关联、随时调用的关系型数据库网络。 赶紧拿你的数据练练手,体验一下数据丝滑对接的快感吧!
下期内容:purrr
此文是在自己的理解和参考网络资料后完成的,若有侵权,请联系删除。

END

加我微信拉你进交流群/VIP群。

——往期推荐——
MetaboAnalyst 6.0代谢组学数据分析超详细教程 | 小白也能轻松上手!
Adobe Illustrator(AI)科研组图排版 | 保姆级教程(内附安装包)
投稿经验 | Frontiers in Microbiology
干货 | RNA-seq之R语言富集分析,KEGG、GO和GSEA实战详解
如果对您有帮助,请帮我们点一个在看,或者转发关注哦~这对我们真的很重要