【问题标题】:match and replace different values in same column from list匹配并替换列表中同一列中的不同值
【发布时间】:2014-02-24 03:35:24
【问题描述】:

我想用另一个文件的第二列替换第二列中的“0”数字。例如,输入 1,chr1 第二列是“0”,我想用输入 2 文件的第二列中的“754192”替换它,我想用其他“0”数字从文件,因此对于第二列输入 1 中的 chr2 而不是“0”,它将从输入 2 文件中读取“83616”。两个输入文件都是制表符分隔的。我非常感谢任何 perl/awk 建议。谢谢。

输入 1

chr1    0       121347754       0.004130250308662653
chr1    144009053       249250621       0.12551644444465637
chr2    0       90278124        -0.010306187905371189
chr2    95387134        243199373       -0.011985263787209988
chr3    0       91000000        -0.009726814925670624
chr3    93541117        198022430       -0.014836171641945839
chr4    0       49064792        -0.01315629668533802
chr4    52700771        141568601       0.014452865347266197
chr4    141568601       143871023       0.20834201574325562
chr5    0       46113638        -0.013212060555815697
chr5    49560859        68740653        0.004888067487627268
chr5    70744658        180915260       -0.011330894194543362

输入 2

chr1    754192
chr2    83616
chr3    108226
chr4    90883
chr5    40975
chr6    209980
chr7    67820
chr8    193585
chr9    206255
chr10   126070

输出

chr1    754192       121347754       0.004130250308662653
chr1    144009053       249250621       0.12551644444465637
chr2    83616       90278124        -0.010306187905371189
chr2    95387134        243199373       -0.011985263787209988
chr3    108226       91000000        -0.009726814925670624
chr3    93541117        198022430       -0.014836171641945839
chr4    90883       49064792        -0.01315629668533802
chr4    52700771        141568601       0.014452865347266197
chr4    141568601       143871023       0.20834201574325562
chr5    40975       46113638        -0.013212060555815697
chr5    49560859        68740653        0.004888067487627268
chr5    70744658        180915260       -0.011330894194543362

【问题讨论】:

  • 是制表符分隔数据
  • 你似乎做得很好!请展示您已经尝试过并需要帮助的内容,而不是要求免费代码。

标签: regex perl unix awk


【解决方案1】:

你可以试试这个awk

awk  'NR==FNR{ a[$1]=$2; next;} $2==0{ $2=a[$1]; }1' OFS="\t" input2 input1 

【讨论】:

    【解决方案2】:
    perl -MFile::Slurp -lape'
      BEGIN { %h = map split, read_file(pop); }
      $F[1] ||= $h{$F[0]};
      $_ = join "\t", @F;
    ' input1 input2
    

    输出

    chr1   754192      121347754       0.004130250308662653
    chr1    144009053       249250621       0.12551644444465637
    chr2   83616      90278124        -0.010306187905371189
    chr2    95387134        243199373       -0.011985263787209988
    chr3   108226      91000000        -0.009726814925670624
    chr3    93541117        198022430       -0.014836171641945839
    chr4   90883      49064792        -0.01315629668533802
    chr4    52700771        141568601       0.014452865347266197
    chr4    141568601       143871023       0.20834201574325562
    chr5   40975      46113638        -0.013212060555815697
    chr5    49560859        68740653        0.004888067487627268
    chr5    70744658        180915260       -0.011330894194543362
    

    【讨论】:

    • 你的意思是input2 input1,使用OP的标签?
    • 不需要的时候为什么要啜饮?
    • @TLP read_file() 返回行列表,因此它的工作几乎相同。
    • 你已经有@F,所以使用它:$F[1] = $h{$F[0]} if $F[1] == 0; $_ = join "\t", @F;。您丢失了原始格式,但您始终可以通过 column -t 之类的方式传输输出
    • @glennjackman 如果您喜欢/\s+/,那么您应该查看文档中关于在文字空间' ' 上拆分的内容。
    【解决方案3】:

    这是在 Perl 中执行此操作的一种方法。程序期望这两个文件的路径作为命令行参数。

    use strict;
    use warnings;
    
    my ($file1, $file2) = @ARGV;
    my $fh;
    
    open $fh, '<', $file2 or die qq{Unable to open "$file2" for input: $!};
    my %defaults = map {(split)[0,1]} <$fh>;
    
    open $fh, '<', $file1 or die qq{Unable to open "$file1" for input: $!};
    
    while (<$fh>) {
      my @fields = split;
      $fields[1] ||= $defaults{$fields[0]};
      print join("\t", @fields), "\n";
    }
    

    输出

    chr1  754192  121347754 0.004130250308662653
    chr1  144009053 249250621 0.12551644444465637
    chr2  83616 90278124  -0.010306187905371189
    chr2  95387134  243199373 -0.011985263787209988
    chr3  108226  91000000  -0.009726814925670624
    chr3  93541117  198022430 -0.014836171641945839
    chr4  90883 49064792  -0.01315629668533802
    chr4  52700771  141568601 0.014452865347266197
    chr4  141568601 143871023 0.20834201574325562
    chr5  40975 46113638  -0.013212060555815697
    chr5  49560859  68740653  0.004888067487627268
    chr5  70744658  180915260 -0.011330894194543362
    

    【讨论】:

    • 您的输出与 OP 的预期输出不匹配。例如,比较第一行chr5
    • @ThisSuitIsBlackNot:谢谢。我已经修好了。
    【解决方案4】:

    稍微更程序化的版本(没有错误检查)。

    use Modern::Perl;
    use autodie;
    
    # read input2 into map 
    my %input2 = do { 
      open my $input2, '<', "input2";
      local $/ = undef;
      split( ' ', <$input2> );
    };
    
    open my $input1, '<', "input1";
    while ( <$input1> ) {
      my ($id) = split( ' ' );
      if ( /^\w+\s+0\s/ ) {
        my $replace_with = $input2{$id};
        s/^(\w+\s+)0(\s)/$1$replace_with$2/;
      }
    
      print;
    }
    

    【讨论】:

    • 为什么在你不需要的时候偷吃文件?如果缺少某些值,您只会冒一次性错误的风险,并且不会获得任何收益。此外,除非您希望保留前导空格,否则请使用 ' ' 而不是 /\s+/。此外,\b0\b0.123 匹配——您需要使用 eq==
    • 嗨,你能解释一下为什么我不需要 slurp 文件吗? ( input2 有换行符)。也不确定您对 == 或 eq 的含义。感谢您的反馈。
    • 您可以逐行阅读并分割每一行,例如使用map。实际上,您无论如何都会冒一次性错误的风险,因为split 将去除尾随空格,除非您专门分配一个键值对,即map { my ($k,$v) = split; $k =&gt; $v; } &lt;$input2&gt;。使用eq 将确保字符串相等,== 数字相等,并且您可以将它们应用于从拆分字符串中获得的值。
    【解决方案5】:

    Perl 中的一个内衬:

    $ perl -MFile::Slurp -lape 'BEGIN {$" = "\t"; %input = map { m/([^\s]+)\s*([^\s]+)/ } read_file("input_2")} $F[1] = $input{$F[0]} unless $F[1]' input_1
    

    【讨论】:

    • [^\s] 写得更好\S
    • 已为您修复。另外,恕我直言,您不认为您的答案是对已经给出的答案的抄袭吗?
    • 我进一步缩短了它以跟上 Perl 的精神。您应有的尊重得到了很好的尊重,因为当我打开问题时,回复为零......我在发布我的最终答案之前没有检查/刷新页面,我在它工作之前进行了测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-09-23
    • 1970-01-01
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    • 2020-08-04
    • 1970-01-01
    相关资源
    最近更新 更多