【问题标题】:Add a column with partially matching pattern from a different file从其他文件添加具有部分匹配模式的列
【发布时间】:2016-06-13 19:00:49
【问题描述】:

我有两个结构相似的文件(制表符分隔和很多很多行,第 3 列减去第 2 列 = 1)看起来有点像这样:

文件 1:

1 170023 170024 A -
1 170024 170025 T -
1 170026 170027 A -
1 170028 170029 G -
1 170029 170030 C -
1 170031 170032 C -

文件 2:

1 170023 170024 A
1 170024 170025 T
1 170025 170026 G
1 170026 170027 A
1 170027 170028 G
1 170028 170029 T
1 170029 170030 A
1 170030 170031 G
1 170031 170032 C

我想将文件 2 中的第 4 列(只有字母的列)添加到文件 1。通常,使用 prpaste 很容易,但问题是第 1-3 列不是两个文件中的相同。换句话说,文件的行数不同,文件 2 的行数总是比文件 1 多(特别是,文件 1 中的第 2 列和第 3 列中的所有数字也出现在文件 2 中,但不是反之亦然)。我也知道如何在 R 中执行此操作,但文件太大而无法在 R 中轻松处理,我需要为十几个文件执行该任务。所以我想,bash 或任何命令行软件将是解决这个问题的最有效方法。

当且仅当文件 2 中的列 1-3 与文件 1 中的列 1-3 完全匹配时,我真正想要做的是添加文件 2 中的第 4 列的字母。因此,文件 1 的第 4 列和第 5 列中出现什么符号并不重要。基于上述文件 1 和文件 2 的示例,所需的输出将是:

输出:

1 170023 170024 A - A
1 170024 170025 T - T
1 170026 170027 A - A
1 170028 170029 G - T
1 170029 170030 C - A
1 170031 170032 C - C

如果有人能帮我解决这个问题,我会非常高兴。非常感谢您!

【问题讨论】:

    标签: bash terminal pattern-matching paste data-manipulation


    【解决方案1】:

    我可以考虑并为此使用joinawk 提供一个简单的解决方案。使用awk 可能不是解决问题的最有效方法(可能会因此受到专家的抨击:)),但我能够解决这个问题。

    解决方案 1:-

    您需要做的就是首先使用join,而不指定要加入的任何特定列。它会根据常见的重复列(在本例中为第 1 列)自动连接文件。然后在该输出上,我们可以让awk 播放以打印符合您需要的行并格式化所需的列。

    join file1 file2 | awk '{ if (($2==$6) && ($3==$7)) printf("%s %s %s %s %s %s\n", $1, $2, $3, $4, $5, $8) }'
    

    将输出生成为

    1 170023 170024 A - A
    1 170024 170025 T - T
    1 170026 170027 A - A
    1 170028 170029 G - T
    1 170029 170030 C - A
    1 170031 170032 C - C
    

    解决方案 2:-

    只使用普通的join

    join -j 2 file1 file2 -o 1.1,1.2,1.3,1.4,1.5,2.4
    

    也会产生与您预期相同的输出。

    `join' writes to standard output a line for each pair of input lines that have identical join fields.
    

    我在下面引用joinman 页面内容

    `-j FIELD'
         Equivalent to `-1 FIELD -2 FIELD'.
    
    `-o FIELD-LIST'
    
     Otherwise, construct each output line according to the format in
     FIELD-LIST.  Each element in FIELD-LIST is either the single
     character `0' or has the form M.N where the file number, M, is `1'
     or `2' and N is a positive field number.
    

    因此,该命令首先通过在第 2 列加入 file1file2 并从所见输出中打印所需的列,由 (1.1,1.2,1.3,1.4,@ 指示987654340@,2.4) 应读作file.column。为了更好地理解,我建议先查看不带 -o 选项的输出,以及我是如何根据所见内容构建输出的。

    解决方案 3:-

    使用plain'ol awk,其实我在回答这个问题时自己也学到了一点。

    awk 'FNR==NR{a[$1 FS $2 FS $3]=$4;next} (($1 FS $2 FS $3) in a) {print $0, a[$1 FS $2 FS $3]}' file2 file1
    

    解释:-

    1. FNR==NR{a[$1 FS $2 FS $3]=$4;next} 将处理file2 存储数组a 的条目,下标为column1 空间column2 空间column3,值为column4。

    2. 现在在file1 上,我们需要通过执行(($1 FS $2 FS $3) in a) {print $0, a[$1 FS $2 FS $3]} 来匹配数组中file2 中的那些行,这将为我提供file1 中的所有这些行(我们需要column4 的值),其下标与file2中的一个相同。

    【讨论】:

    • 非常感谢!我仍在运行分析(许多大文件),并将编辑此评论以报告其结果。
    • @AlexDeLarge:那会很有帮助!
    • 因为我有制表符分隔的文件并且新列也应该是制表符分隔的,所以我根据您的第三个解决方案使用了以下命令:awk 'FNR==NR{a[$1 "\t" $2 "\t" $3]=$4;next} (($1 "\t" $2 "\t" $3) in a) {print $0 "\t" a[$1 "\t" $2 "\t" $3]}' file2 file1。再次感谢您!
    猜你喜欢
    • 2011-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-22
    相关资源
    最近更新 更多