打印一列的值是另一列中指定的次数

时间:2012-11-25 00:50:17

标签: perl unix

我有想要在unix中重新格式化的数据,使用第2-3列创建一个新列(在示例中调用时),但是我无法弄清楚如何执行此操作。在不更改列4-7(它们一起用作数据的标识符)的情况下,我想打印第2列中第3列中指定的次数,然后打印一个值(本例中为31)N(=每个标识符的第1列) )减去(每个标识符的第3列的总和)次数。因此,重新格式化的数据将为每个标识符总共包含N行。 开始的数据如下所示:

N   time    awake   line    sex temp    rep
9   15  1   188 f   25  1
9   20  1   188 f   25  1
9   21  1   188 f   25  1
9   28  1   188 f   25  1
10  12  1   205 m   25  1   
10  14  3   205 m   25  1   
10  16  1   205 m   25  1   
10  18  1   205 m   25  1   
10  19  2   205 m   25  1   
10  22  1   205 m   25  1   
10  24  1   205 m   25  1   

重新格式化的数据应该看起来像这样:

line    sex temp    rep when
188 f   25  1   15
188 f   25  1   20
188 f   25  1   21
188 f   25  1   28
188 f   25  1   31
188 f   25  1   31
188 f   25  1   31
188 f   25  1   31
188 f   25  1   31
205 m   25  1   12
205 m   25  1   14
205 m   25  1   14
205 m   25  1   14
205 m   25  1   16
205 m   25  1   18
205 m   25  1   19
205 m   25  1   19
205 m   25  1   22
205 m   25  1   24

我的猜测是它需要某种循环,我认为伪代码看起来像这样:

for (each columns 4-7)
    tot = (column 1)
    rem = tot - sum (column 3)
    for (i=0; i <= column 3; i++)
        print column 2"\n"
    for (j=0; i <= rem; j++)
        print "31\n"

非常感谢任何帮助!

编辑添加: 我尝试从下面的@mvp修改perl代码,但这不太对。我使用awk将原始列4-7重新格式化为一个名为id的字段(和变量)。任何意见?

print "id       when\n"; # output header
my $temp='188.f.25.1';
my $count;
my $rest;
my $total;
while(my $input = <>) {
    my ($n, $time, $awake, $id)
        = split /\s+/, $input; # read each line
    next if $n eq 'N'; # skip input header line
    if ($id eq $temp) {
        $count++;
        for (1..$awake) {print "$id     $time\n";}
        $total = $n;
        next;
    }
    else {
        $rest=$total-$count;
        for (1..$rest) {print "$temp    31\n";}
    }
    $count=0;
    $temp = $id;
    next;
}

修改后的输入文件:

N       time    awake   line.sex.temp.rep
9       15      1       188.f.25.1
9       20      1       188.f.25.1
9       21      1       188.f.25.1
9       28      1       188.f.25.1
10      12      1       205.m.25.1
10      14      3       205.m.25.1
10      16      1       205.m.25.1
10      18      1       205.m.25.1
10      19      2       205.m.25.1
10      22      1       205.m.25.1
10      24      1       205.m.25.1
10      10      1       206.m.25.1
10      14      1       206.m.25.1
10      18      1       206.m.25.1
10      20      1       206.m.25.1
10      24      1       206.m.25.1
10      26      1       206.m.25.1
10      27      1       206.m.25.1
10      28      2       206.m.25.1

3 个答案:

答案 0 :(得分:1)

这是使用awk的一种方式。它使用未修改的输入文件。像:

一样运行
awk -f script.awk file{,} | column -t

script.awk的内容:

BEGIN {
    print "line sex temp rep when"
}

FNR==NR && NR>1 {
    a[$4,$5,$6,$7]+=$3
    next
}

FNR>1 {
    for (i=1;i<=$3;i++) {
        print x=($4 FS $5 FS $6 FS $7), $2
        a[$4,$5,$6,$7]--
        var++
    }

    if (a[$4,$5,$6,$7]==0) { 
        for (i=1;i<=$1-var;i++) {
            print x, "31"
        }
        var=0
    }
}

结果:

line  sex  temp  rep  when
188   f    25    1    15
188   f    25    1    20
188   f    25    1    21
188   f    25    1    28
188   f    25    1    31
188   f    25    1    31
188   f    25    1    31
188   f    25    1    31
188   f    25    1    31
205   m    25    1    12
205   m    25    1    14
205   m    25    1    14
205   m    25    1    14
205   m    25    1    16
205   m    25    1    18
205   m    25    1    19
205   m    25    1    19
205   m    25    1    22
205   m    25    1    24

或者,这是单行:

awk 'BEGIN { print "line sex temp rep when" } FNR==NR && NR>1 { a[$4,$5,$6,$7]+=$3; next } FNR>1 { for (i=1;i<=$3;i++) { print x=($4 FS $5 FS $6 FS $7), $2; a[$4,$5,$6,$7]--; var++ } if (a[$4,$5,$6,$7]==0) { for (i=1;i<=$1-var;i++) print x, "31"; var=0 } }' file{,} | column -t

答案 1 :(得分:0)

这就是我在Perl中的表现:

将其另存为myscript.pl

#!/usr/bin/perl

use strict;
use warnings;

print "line    sex temp    rep when\n"; # output header
while(my $input = <>) {
    my ($n, $time, $awake, $line, $sex, $temp, $rep)
        = split /\s+/, $input;
    next if $n eq 'N'; # skip input header line
    for (1..$awake) {
        print "$line $sex  $temp $rep $time\n";
    }
}

将其称为myscript.pl <a.txt >b.txt

答案 2 :(得分:0)

perl -F -lane 'if($.==1){print "@F[3,4,5,6,1]"}for($i=0;$i<$F[2];$i++){print "@F[3,4,5,6,1]"}' your_file

或者您也可以使用它:

perl -F -lane 'for($i=0;($i<$F[2])||($.==1);$i++){print "@F[3,4,5,6,1]";if($.==1){last}}' your_file

测试如下:

> cat temp
N       time    awake   line    sex     temp    rep
9       15      1       188     f       25      1
9       20      1       188     f       25      1
9       21      1       188     f       25      1
9       28      1       188     f       25      1
10      12      1       205     m       25      1
10      14      3       205     m       25      1
10      16      1       205     m       25      1
10      18      1       205     m       25      1
10      19      2       205     m       25      1
10      22      1       205     m       25      1
10      24      1       205     m       25      1

执行:

> perl -F -lane 'if($.==1){print "@F[3,4,5,6,1]"}for($i=0;$i<$F[2];$i++){print "@F[3,4,5,6,1]"}' temp
line sex temp rep time
188 f 25 1 15
188 f 25 1 20
188 f 25 1 21
188 f 25 1 28
205 m 25 1 12
205 m 25 1 14
205 m 25 1 14
205 m 25 1 14
205 m 25 1 16
205 m 25 1 18
205 m 25 1 19
205 m 25 1 19
205 m 25 1 22
205 m 25 1 24
>