【问题标题】:First columns match; read pattern and replace with specific values in the second file column第一列匹配;读取模式并替换为第二个文件列中的特定值
【发布时间】:2020-05-13 11:18:40
【问题描述】:

我想有条件地使用 file2(tab-delimited) 更新 file1(tab-delimited)。

对于 file1 的每一行,对除第一列之外的每一列执行以下替换,使用 file2 中第一列中与 file1 中第一列中具有相同值的行:

  • 一个。如果; file1中的字段包含字符串“A”,将其替换为file2第二列中的字符串
  • b。否则如果; file1 中的字段包含字符串“B”,将其替换为 file2 第三列中的字符串
  • c。别的; file1 中带有字符串“X”的字段,将其替换为 file2 第四列中的字符串

请协助匹配后正确添加条件。提前谢谢你。

awk 'FNR==NR{a[$1]=$1;next};{print a[$1]}'  file2.txt file1.txt>file3.txt

$ cat replace.awk
{
var1=$2;
var2=$3;
var3=$4;
if ( $0 == "A") $0=var1;
else if ( $0 == "B") $0=var2;
else $0=var3;
print $0,var1,var2,var3;
}

输入文件1

ID  item1   item2   item3   item4 ..
592 A   B   X   B
598 A   B   X   A
612 A   X   A   X
650 A   X   A   B
700 A   X   A   B
822 A   X   A   A
830 A   X   A   A
840 A   X   A   X

输入文件2

ID  var1    var2    var3
568 G   A   NA
570 T   C   NA
592 T   G   NA
598 A   T   NA
612 C   A   NA
650 C   T   NA
700 T   C   NA
822 T   C   NA
830 T   A   NA
840 G   C   NA
900 T   G   NA
1000    A   T   NA
....

预期输出

ID  item1   item2   item3   item4
592 T   G   NA  G
598 A   T   NA  A
612 C   NA  C   NA
650 C   NA  C   T
700 T   NA  T   C
822 T   NA  T   T
830 T   NA  T   T
840 G   NA  G   NA


【问题讨论】:

    标签: perl awk sed grep


    【解决方案1】:
    perl -e'
    
        my %file2;
        {
           my $file2_qfn = shift(@ARGV);
           open(my $fh2, "<", $file2_qfn)
              or die("Can't open \"$file2_qfn\": $!\n");
    
           if (<$fh2>) {  # Skip header.
              while (<$fh2>) {
                 chomp;
                 my @fields = split /\t/;
                 $file2{$fields[0]} = \@fields;
              }
           }
        }
    
        while (<>) {
           if ($. == 1) {  # Handle header.
              print;
              next;
           }
    
           chomp;
           my @fields = split /\t/;
           if ( my $changes = $file2{$fields[0]} ) {
              for (@fields[1..$#fields]) {
                 if    ($_ eq "A") { $_ = $changes->[1]; }
                 elsif ($_ eq "B") { $_ = $changes->[2]; }
                 elsif ($_ eq "X") { $_ = $changes->[3]; }
              }
           }
    
           print(join("\t", @fields), "\n");
        }
    
    ' file2.tsv file1.tsv
    

    上面将整个file2加载到内存中,但如果这两个文件按第一列排序,则很容易避免。

    perl -e'
    
        sub get_row {
           my ($fh) = @_;
           defined( my $line = <$fh> )
              or return undef;
    
           chomp($line);
           return [ split /\t/, $line ];
        }
    
        sub print_row { print(join("\t", @_), "\n"); }
    
        my $file2_qfn = shift(@ARGV);
        open(my $fh2, "<", $file2_qfn)
           or die("Can't open \"$file2_qfn\": $!\n");
    
        my $fh1 = \*ARGV;
    
        my $row1 = get_row($fh1);
        if ($row1) {  # Handle header.
           print_row(@$row1);
           $row1 = get_row($fh1);
        }
    
        my $row2 = get_row($fh2);
        $row2 = get_row($fh2) if $row2;  # Skip header.
    
        while ($row1 && $row2) {
           my $cmp = $row1->[0] <=> $row2->[0];
           if ($cmp <= 0) {
              if ($cmp == 0) {
                 for (@$row1[1..$#$row1]) {
                    if    ($_ eq "A") { $_ = $row2->[1]; }
                    elsif ($_ eq "B") { $_ = $row2->[2]; }
                    elsif ($_ eq "X") { $_ = $row2->[3]; }
                 }
              }
    
              print_row(@$row1);
           }
    
           $row1 = get_row($fh1) if $cmp <= 0;
           $row2 = get_row($fh2) if $cmp >= 0;
        }
    
        while ($row1) {
           print_row(@$row1);
           $row1 = get_row($fh1);
        }
    
    ' file2.tsv file1.tsv
    

    用法(两个版本):

    perl -e'...' -i~ file2.tsv file1.tsv        # Modifies named file in place with backup.
    perl -e'...' -i file2.tsv file1.tsv         # Modifies named file in place without backup.
    perl -e'...' file2.tsv file1.tsv >out.tsv   # Reads from named file, outputs to STDOUT.
    

    【讨论】:

    • 感谢您帮助我恰当地提出我的问题。我似乎没有让程序执行得很好。保存并使用:perl swap_projected_vcf.pl file2.tsv file1.tsv..它在两种情况下都输出原始输入文件。如果我做错了,请告诉我。
    • @Glen Viet,发现问题并修复它们。经过测试。
    • @ikegami,完美。非常感谢。
    【解决方案2】:

    awk 请尝试以下操作:

    awk 'BEGIN {FS=OFS="\t"}
        FNR>1 && FNR==NR {a[$1,"A"]=$2; a[$1,"B"]=$3; a[$1,"X"]=$4; next}
        FNR==1 && FNR!=NR {print}
        FNR>1 {for (i=2; i<=5; i++) $i=a[$1,$i]
            print
        }
    ' file2.txt file1.txt
    

    输出:

    ID      item1   item2   item3   item4
    592     T       G       NA      G
    598     A       T       NA      A
    612     C       NA      C       NA
    650     C       NA      C       T
    700     T       NA      T       C
    822     T       NA      T       T
    830     T       NA      T       T
    840     G       NA      G       NA
    

    [解释]

    • 在读取file2 时,它会创建一个关联base(A、G、T、C 或 NA)的地图 ID(568 等)和item(A、B 或 X)的串联。
    • 它将file1 的第一行打印为标题行。
    • 在读取file1 时,它通过读取关联数组来替换字段 在第一期创建。

    希望这会有所帮助。

    【讨论】:

    • 现场,感谢您的帮助和解释。我希望它可以处理 appx。 1M 记录良好。
    • @GlenViet 谢谢你的回复。我也很好奇我的解决方案是否适用于 1M 记录。如果没有,请告诉我。我会尝试提高性能。 BR。
    【解决方案3】:

    解释请看代码

    #!/usr/bin/perl
    #
    # USAGE:
    #   prog.pl -a fileA -b fileB
    #
    # Description:
    #   Demonstration code for StackOverflow Q59942022
    #
    # Parameters:
    #   -a,--filea  input file with pattern to substitudes
    #   -b,--fileb  input file with values for substitution
    #   -d,--debug  debug flag
    #   -h,--help   brief help message
    #   --man       manual page with more details
    #
    # StackOverflow: 
    #   Question 59942022
    #
    # Author:
    #   Polar Bear
    #
    # Date: Tue Jan 28 3:09:00 PST 2020
    #
    
    use warnings;
    use strict;
    use feature 'say';
    
    use Getopt::Long qw(GetOptions);
    use Pod::Usage;
    use Data::Dumper;
    
    my $debug = 0;
    
    my $fh;         # file handler
    my %opt;        # command line options
    my %data;       # fileA data storage
    my %fileb;      # fileB data storage
    my @order;      # to keep line order
    
    GetOptions(
            'filea|a=s'     => \$opt{data},
            'fileb|b=s'     => \$opt{fileb},
            'debug|d'       => \$opt{debug},
            'help|?'        => \$opt{help},
            'man'           => \$opt{man}
    ) or pod2usage(2);
    
    pod2usage(1) if $opt{help};
    pod2usage(-exitval => 0, -verbose => 2) if $opt{man};
    
    print Dumper(\%opt) if $opt{debug};
    
    open $fh, '<', $opt{data}
            or die "Couldn't open $opt{data}";
    
    map {                                   # process fileA (pattern)
        chomp;
        my @a = split /\t/; 
        push @order, $a[0];                 # preserve line order
        push @{$data{$a[0]}}, @a[1..$#a]
    } <$fh>;
    
    close $fh;
    
    open $fh, '<', $opt{fileb}
            or die "Couldn't open $opt{fileb}";
    
    map {                                   # process fileB (values)
        chomp;
        my @a = split /\t/;
        push @{$fileb{$a[0]}}, @a[1..$#a];
    } <$fh>;
    
    close $fh;
    
    say Dumper(\%data)  if $debug;
    say Dumper(\%fileb) if $debug;
    
    foreach my $k ( @order ) {              # make required substitution
        if( defined $fileb{$k} ) {
            foreach my $i (0..$#{$data{$k}}) {
                $data{$k}[$i] = $fileb{$k}[0] if $data{$k}[$i] eq 'A';
                $data{$k}[$i] = $fileb{$k}[1] if $data{$k}[$i] eq 'B';
                $data{$k}[$i] = $fileb{$k}[2] if $data{$k}[$i] eq 'X';
            }
        }
        say join "\t", $k, @{$data{$k}};    # output result to console
    } 
    
    __END__
    
    =head1 NAME
    
    program - describe program's functionality 
    
    =head1 SYNOPSIS
    
    program.pl [options]
    
     Options:
        -a,--filea  fileA input filename
        -b,--fileb  fileB input filename
        -d,--debug  output debug information
        -?,--help   brief help message
           --man    full documentation
    
    =head1 OPTIONS
    
    =over 4
    
    =item B<-a,--filea>
    
    FileA input filename
    
    =item B<-b,--fileb>
    
    FileB input filename
    
    =item B<-d,--debug>
    
    Print debug information.
    
    =item B<-?,--help>
    
    Print a brief help message and exits.
    
    =item B<--man>
    
    Prints the manual page and exits.
    
    =back
    
    B<This program> reads two input files and substitudes values in first file
    according predefined rules: A - second column, B - third column, X - forth column
    
    =cut
    

    输入文件A

    ID  item1   item2   item3   item4 ..
    592 A   B   X   B
    598 A   B   X   A
    612 A   X   A   X
    650 A   X   A   B
    700 A   X   A   B
    822 A   X   A   A
    830 A   X   A   A
    840 A   X   A   X
    

    输入文件B

    ID  var1    var2    var3
    568 G   A   NA
    570 T   C   NA
    592 T   G   NA
    598 A   T   NA
    612 C   A   NA
    650 C   T   NA
    700 T   C   NA
    822 T   C   NA
    830 T   A   NA
    840 G   C   NA
    900 T   G   NA
    1000    A   T   NA
    

    输出

    ID      item1   item2   item3   item4 ..
    592     T       G       NA      G
    598     A       T       NA      A
    612     C       NA      C       NA
    650     C       NA      C       T
    700     T       NA      T       C
    822     T       NA      T       T
    830     T       NA      T       T
    840     G       NA      G       NA
    

    【讨论】:

    • 感谢您的帮助。它现在执行得很好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 2021-10-16
    • 2018-09-14
    • 2019-01-04
    • 2015-05-12
    相关资源
    最近更新 更多