如何从不同文件中提取特定列并在一个文件中输出?

时间:2015-09-24 10:46:00

标签: perl awk sed

我在一个目录中有12个文件,每个文件有4列。第一列是基因名称,其余3是计数列。所有文件都在同一目录中。我想为每个文件提取1,4列(总共12个文件)并将它们粘贴到一个输出文件中,因为第一列对于每个文件都是相同的,输出文件应该只有第一列一次,其余的将是然后是每个文件的第4列。每个文件的第一列是相同的。我不想在这里使用R.我是awk的忠实粉丝。所以我尝试了类似下面的东西,但它不起作用

我的输入文件看起来像 输入文件1

ZYG11B  8267    16.5021 2743.51
ZYG11A  4396    0.28755 25.4208
ZXDA    5329    2.08348 223.281
ZWINT   1976    41.7037 1523.34
ZSCAN5B 1751    0.0375582   1.32254
ZSCAN30 4471    4.71253 407.923
ZSCAN23 3286    0.347228    22.9457
ZSCAN20 4343    3.89701 340.361
ZSCAN2  3872    3.13983 159.604
ZSCAN16-AS1 2311    1.1994  50.9903

输入文件2

ZYG11B  8267    18.2739 2994.35
ZYG11A  4396    0.227859    19.854
ZXDA    5329    2.44019 257.746
ZWINT   1976    8.80185 312.072
ZSCAN5B 1751    0   0
ZSCAN30 4471    9.13324 768.278
ZSCAN23 3286    1.03543 67.4392
ZSCAN20 4343    3.70209 318.683
ZSCAN2  3872    5.46773 307.038
ZSCAN16-AS1 2311    3.18739 133.556

输入文件3

ZYG11B  8267    20.7202 3593.85
ZYG11A  4396    0.323899    29.8735
ZXDA    5329    1.26338 141.254
ZWINT   1976    56.6215 2156.05
ZSCAN5B 1751    0.0364084   1.33754
ZSCAN30 4471    6.61786 596.161
ZSCAN23 3286    0.79125 54.5507
ZSCAN20 4343    3.9199  357.177
ZSCAN2  3872    5.89459 267.58
ZSCAN16-AS1 2311    2.43055 107.803

上述所需的输出

ZYG11B  2743.51 2994.35 3593.85
    ZYG11A  25.4208 19.854  29.8735
    ZXDA    223.281 257.746 141.254
    ZWINT   1523.34 312.072 2156.05
    ZSCAN5B 1.32254 0   1.33754
    ZSCAN30 407.923 768.278 596.161
    ZSCAN23 22.9457 67.4392 54.5507
    ZSCAN20 340.361 318.683 357.177
    ZSCAN2  159.604 307.038 267.58
    ZSCAN16-AS1 50.9903 133.556 107.803

这里你可以看到每个文件和第4列的第一列,因为每个文件的第一列是相同的,所以我只想保留一次,休息时,每个文件的第4列都会有。我刚刚显示了3个文件。它应该同时适用于目录中的所有文件,因为所有文件都有类似的命名约定,如file1_quant.genes.sf file2_quant.genes.sf,file3_quant.genes.sf

每个文件都有相同的第一列,但在休息列中有不同的计数。我的想法是创建一个输出文件,该文件应该包含所有文件的第1列和第4列。

awk '{print $1,$2,$4}' *_quant.genes.sf > genes.estreads

任何抬头?

6 个答案:

答案 0 :(得分:3)

如果我理解正确的话,你要找的是每个键一行,从多个文件中整理出来。

此作业所需的工具是关联数组。我认为awk可以,但我不是100%肯定。我可能会在perl中解决它:

#!/usr/bin/perl
use strict;
use warnings;

# an associative array, or hash as perl calls it
my %data;

#iterate the input files (sort might be irrelevant here) 
foreach my $file ( sort glob("*_quant.genes.sf") ) {
    #open the file for reading. 
    open( my $input, '<', $file ) or die $!;
    #iterate line by line. 
    while (<$input>) {
        #extract the data - splitting on any whitespace. 
        my ( $key, @values ) = split; 
        #add'column 4' to the hash (of arrays)
        push( @{$data{$key}}, $values[2] );  
    }
    close($input);
}

#start output 
open( my $output, '>', 'genes.estreads' ) or die;
#sort, because hashes are explicitly unordered. 
foreach my $key ( sort keys %data ) { 
    #print they key and all the elements collected. 
    print {$output} join( "\t", $key, @{ $data{$key} } ), "\n";
}
close($output);

使用上面指定的数据,这会产生:

ZSCAN16-AS1 50.9903 133.556 107.803
ZSCAN2  159.604 307.038 267.58
ZSCAN20 340.361 318.683 357.177
ZSCAN23 22.9457 67.4392 54.5507
ZSCAN30 407.923 768.278 596.161
ZSCAN5B 1.32254 0   1.33754
ZWINT   1523.34 312.072 2156.05
ZXDA    223.281 257.746 141.254
ZYG11A  25.4208 19.854  29.8735
ZYG11B  2743.51 2994.35 3593.85

答案 1 :(得分:1)

以下是您在awk中执行此操作的方法:

awk 'BEGIN{FS = " "};{print $1, $4}' *|awk 'BEGIN{FS = " "};{temp = x[$1];x[$1] = temp  " "  $2;};END {for(xx in x) print xx,x[xx]}'

看起来很神秘,我只是使用关联数组。

<小时/> 以下是解决方案:

  1. 只需打印键和值,每行一个。

    print $1, $2

  2. 将数据存储在关联数组中,不断更新

    temp = x[$1];x[$1] = temp " " $2;}

  3. 显示:

    for(xx in x) print xx,x[xx]

  4. 示例运行:

    [cloudera@quickstart test]$ cat f1
    A k1
    B k2
    
    [cloudera@quickstart test]$ cat f2
    A k3
    B k4
    C k1
    
    [cloudera@quickstart test]$ awk 'BEGIN{FS = " "};{print $1, $2}' *|awk 'BEGIN{FS = " "};{temp = x[$1];x[$1] = temp  " "  $2;};END {for(xx in x) print xx,x[xx]}'
    A  k1 k3
    B  k2 k4
    C  k1
    

    作为旁注,该方法应该让人联想到Map Reduce范例。

答案 2 :(得分:1)

awk '{E[$1]=E[$1] "\t" $4}END{for(K in E)print K E[K]}' *_quant.genes.sf > genes.estreads

订单是读取文件时的外观顺序(因此通常基于1个已读取的文件)

答案 3 :(得分:0)

如果所有文件中的第一列相同,则可以使用paste

paste <(tabify f1 | cut -f1,4) \
      <(tabify f2 | cut -f4)   \
      <(tabify f3 | cut -f4)

tabify将连续空格更改为标签:

sed 's/ \+/\t/g' "$@"

和f1,f2,f3是输入文件的名称。

答案 4 :(得分:0)

以下是在Perl中执行此操作的另一种方法:

 perl -lane '$data{$F[0]} .= " $F[3]"; END { print "$_ $data{$_}" for keys %data }' input_file_1 input_file_2 input_file_3

答案 5 :(得分:0)

这是使用awk执行此操作的另一种方法。它支持使用多个文件。

awk 'FNR==1{f++}{a[f,FNR]=$1}{b[f,FNR]=$4}END { for(x=1;x<=FNR;x++){printf("%s ",a[1,x]);for(y=0;y<=ARGC;y++)printf("%s ",b[y,x]);print ""}}' input1.txt input2.txt input3.txt 

该行代码,给出以下输出

ZYG11B  2743.51 2994.35 3593.85  
ZYG11A  25.4208 19.854 29.8735  
ZXDA  223.281 257.746 141.254  
ZWINT  1523.34 312.072 2156.05  
ZSCAN5B  1.32254 0 1.33754  
ZSCAN30  407.923 768.278 596.161  
ZSCAN23  22.9457 67.4392 54.5507  
ZSCAN20  340.361 318.683 357.177  
ZSCAN2  159.604 307.038 267.58  
ZSCAN16-AS1  50.9903 133.556 107.803