合并一些制表符分隔的文件

合并一些制表符分隔的文件

我有 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 等为一组粘贴文件。

相关内容