Unix下基于列合并多文件:for循环实现及替代方案问询
用For Loop实现多文件合并(附无循环替代方案)
嘿,这个需求太常见了!不管你是要把所有文件的内容按行追加(同时标注来源文件名),还是要把每个文件的数值列合并成宽表(用文件名当列名),我都给你讲清楚怎么实现~
一、用For Loop实现(以Python为例)
先假设你的文件是制表符分隔的文本文件,如果是逗号分隔就把sep='\t'改成sep=','就行。
场景1:把所有文件内容按行追加,新增一列标注文件名
这种情况适合每个文件的行是独立条目,不需要按ID匹配:
import pandas as pd import glob # 批量获取所有N开头的文件(比如N1、N2...) file_list = glob.glob('N*') # 初始化一个空的DataFrame用来存合并结果 merged_df = pd.DataFrame() # 遍历每个文件 for file in file_list: # 读取当前文件,给两列命名(你可以改成自己需要的名字) df = pd.read_csv(file, sep='\t', header=None, names=['列1', '列2']) # 新增一列,值为当前文件名(如果有后缀可以用split('.')[0]去掉) df['来源文件'] = file.split('.')[0] # 把当前文件的内容追加到合并结果里 merged_df = pd.concat([merged_df, df], ignore_index=True) # 保存最终结果 merged_df.to_csv('合并结果.csv', index=False, sep='\t')
场景2:按共同ID合并为宽表,文件名作为数值列的列名
如果每个文件的第一列是共同的标识符(比如基因ID、样本ID),第二列是对应的值,要把所有文件的数值列合并成宽表:
import pandas as pd import glob file_list = glob.glob('N*') # 先读取第一个文件作为基础,把文件名作为第二列的列名 first_file = file_list[0] merged_df = pd.read_csv(first_file, sep='\t', header=None, names=['ID', first_file.split('.')[0]]) # 遍历剩下的所有文件 for file in file_list[1:]: # 读取当前文件,同样用文件名作为数值列的列名 df = pd.read_csv(file, sep='\t', header=None, names=['ID', file.split('.')[0]]) # 按ID列合并(how='outer'保留所有ID,需要只保留共同ID就改成how='inner') merged_df = pd.merge(merged_df, df, on='ID', how='outer') # 保存结果 merged_df.to_csv('合并结果_宽表.csv', index=False, sep='\t')
二、无需For Loop的替代方案
如果不想写显式的for loop,这些方法更简洁:
1. Python:用列表推导式 + pandas.concat/reduce
本质上底层还是循环,但代码里看不到for loop的结构:
对应场景1(追加行):
import pandas as pd import glob file_list = glob.glob('N*') # 用列表推导式一次性读取所有文件并添加文件名列 df_list = [ pd.read_csv(file, sep='\t', header=None, names=['列1', '列2']) .assign(来源文件=file.split('.')[0]) for file in file_list ] # 合并所有DataFrame merged_df = pd.concat(df_list, ignore_index=True) merged_df.to_csv('合并结果.csv', index=False, sep='\t')
对应场景2(合并宽表):
import pandas as pd import glob from functools import reduce file_list = glob.glob('N*') df_list = [ pd.read_csv(file, sep='\t', header=None, names=['ID', file.split('.')[0]]) for file in file_list ] # 用reduce批量合并所有DataFrame merged_df = reduce(lambda left, right: pd.merge(left, right, on='ID', how='outer'), df_list) merged_df.to_csv('合并结果_宽表.csv', index=False, sep='\t')
2. 命令行工具:csvkit(无需写代码)
如果你不想写Python/R代码,装个csvkit工具(用pip install csvkit安装),一行命令搞定:
场景1(追加行+文件名):
csvstack --filenames --names 列1,列2 N* > 合并结果.csv
--filenames会自动新增一列filename,值为每个文件的名字;--names指定列名。
场景2(按第一列合并宽表):
csvjoin --outer -c 1 N* > 合并结果_宽表.csv
-c 1表示按第一列合并;--outer是外连接,保留所有ID。
3. R语言:用tidyverse的map函数
如果你习惯用R,tidyverse的map系列函数可以替代for loop:
场景1(追加行):
library(tidyverse) # 获取所有N开头的文件 file_list <- list.files(pattern = "^N") # 批量读取并合并,自动添加文件名列 merged_df <- map_dfr(file_list, ~{ read_tsv(.x, col_names = c("列1", "列2")) %>% mutate(来源文件 = str_remove(.x, "\\..*")) # 去掉文件名后缀 }) # 保存结果 write_csv(merged_df, "合并结果.csv")
场景2(合并宽表):
library(tidyverse) file_list <- list.files(pattern = "^N") # 批量读取每个文件,然后合并为宽表 merged_df <- map(file_list, ~{ col_name <- str_remove(.x, "\\..*") read_tsv(.x, col_names = c("ID", col_name)) }) %>% reduce(full_join, by = "ID") # full_join对应外连接,换成inner_join就是内连接 write_csv(merged_df, "合并结果_宽表.csv")
内容的提问来源于stack exchange,提问作者Coeus Huo
相关产品推荐
相关产品推荐

