我有 100 个文件,每个文件有 57,816 行。我想通过基于公共列进行合并来对这些文件进行外部联接。
我正在使用 R 编程,但这非常慢。
fileList <- list.files(, pattern=".txt")
pm_list=lapply(fileList, read.table)
merged_pm=merge_all(pm_list, by = "gene_short_name")
在 bash 中有什么快速的方法可以做到这一点吗?我可以使用的另一种方法是 SQL,但我必须先创建 100 个表,然后在连接之前加载它们,这不是很有效的方法。
每个文件中的行数相等,这就是为什么我想基于公共列进行合并,并且不能使用 R 中的 cbind,因为公共列中的常量在不同文件中有点上下,并且不存在于同一位置在每个文件中。下面是两个示例文件。我想根据“gene_short_name”加入
gene_short_name FPKM56
MT-TF 0.90
MT-TV 0
MT-RNR1 310.015
MT-TL1 0
MT-TM 0
文件2如下:
gene_short_name FPKM53
MT-TF 0
MT-TV 0.344
MT-TM 0.10
MT-TL1 0
MT-RNR1 0
MT-ND2 158.332
答案1
以下脚本应该对作为参数传递的所有制表符分隔文件的第 1 列(字段)执行外连接。它使用加入命令,它对已排序的文件执行外连接,一次 2 个文件。
它将连接文件中的每一行,包括标题行。如果您希望排除标头,请将这两个sort
命令更改为生成省略标头的排序文件的命令。
#!/bin/sh
if test $# -lt 2
then
echo usage: gjoin file1 file2 ...
exit 1
fi
sort -t $'\t' -k 1 "$1" > result
shift
for f in "$@"
do
sort -t $'\t' -k 1 "$f" > temp
join -1 1 -2 1 -t $'\t' result temp > newresult
mv newresult result
done
cat result
rm result temp
如果您有较旧的 shell,$'\t'
则不会被制表符替换,因此您需要使用 'TAB',在引号之间放置文字制表符。
/bin/sh
如果您可以使用现代 shell(例如 bash 或 ksh)来代替 ,则可以进行优化;例如,这些行
sort -t $'\t' -k 1 "$f" > temp
join -1 1 -2 1 -t $'\t' result temp > newresult
可以替换为
join -1 1 -2 1 -t $'\t' result <(sort -t $'\t' -k 1 "$f") > newresult
答案2
由于根据您的帖子,第一列中的键始终相同(仅顺序不同),我认为您可以使用sort
,cut
和更快地完成此操作paste
。您可以对其中一个文件进行排序(不包括前两行),然后对其余文件进行排序(同样,不包括前两行),并仅从每个文件中提取第二列并粘贴结果。例子:
1.txt
:
g_s_n FPKM56
MT-ND2 21.06
MT-TF 0.90
MT-TV 1
MT-RNR1 310.015
MT-TL1 1
MT-TM 1
2.txt
:
g_s_n FPKM53
MT-TF 0
MT-TV 0.344
MT-TM 0.10
MT-TL1 0
MT-RNR1 0
MT-ND2 158.332
3.txt
:
g_s_n FPKM58
MT-RNR1 0.82
MT-TM 7
MT-TF 1.20
MT-TV 4
MT-ND2 4.05
MT-TL1 2
跑步:
paste <({ head -n 2; sort; } <1.txt) <({ head -n 2; sort; } <2.txt | cut -f2) \
<({ head -n 2; sort; } <3.txt | cut -f2)
产生:
g_s_n FPKM56 FPKM53 FPKM58
MT-ND2 21.06 158.332 4.05
MT-RNR1 310.015 0 0.82
MT-TF 0.90 0 1.20
MT-TL1 1 0 2
MT-TM 1 0.10 7
MT-TV 1 0.344 4
它是如何工作的:{ head -n 2; sort; } <1.txt
对第一个文件(前两行除外)进行排序,因此第一列(公共)现在已排序:
g_s_n FPKM56
MT-ND2 21.06
MT-RNR1 310.015
MT-TF 0.90
MT-TL1 1
MT-TM 1
MT-TV 1
其他文件相同:{ head -n 2; sort; } <other_files.txt | cut -f2
,只是这次我们提取第二列(在 之后sort
,所有文件的第一列都是相同的):
FPKM53
158.332
0
0
0
0.10
0.344
和:
FPKM58
4.05
0.82
1.20
2
7
4
这些都是由 合并的paste
。
当然,如果您的 shell 支持进程替换,上面的方法对于有限数量的文件来说效果很好。否则,您将必须编写脚本并使用临时文件(正如 Mark 在他的回答中所做的那样),根据您的系统限制以 10、20 等为一组粘贴文件。