【问题标题】:print value of one column the number of times specified in another column打印一列的值在另一列中指定的次数
【发布时间】:2012-11-26 22:17:45
【问题描述】:

我想在 unix 中重新格式化数据,将第 2-3 列创建一个新列(在示例中称为 when),但我无法弄清楚如何执行此操作。在不更改作为数据标识符的第 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 的字段(和变量)。有cmets吗?

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

【问题讨论】:

  • 那么对于新的数据格式,你想要的输出是什么?
  • @Kenosis 所需的输出实际上是相同的,除了最初所需的原始输出的前四列将是一个由句点分隔的单个字段,我会使用 awk 重新分隔(由于我是新手,无法真正弄清楚如何一次完成所有操作)

标签: perl unix


【解决方案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

【讨论】:

  • 您介意评论一下这是做什么的吗?:a[$4,$5,$6,$7]--
  • @suegene:没问题; a[$4,$5,$6,$7]-- 只是将包含第 4、5、6 和 7 列的多维数组递减(减一)。
  • 谢谢,我想我终于明白了,直到现在才意识到数组的结构。非常好!
【解决方案2】:

这就是我在 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 &lt;a.txt &gt;b.txt

【讨论】:

  • 您是否介意扩展您的答案以包括如何读取数据文件(我们可以称为是a.txt)并输出到重新格式化的文件(b.txt)?
  • 新增用法,请看我修改后的答案
  • 我已经运行了脚本,到目前为止它看起来像是一个部分解决方案。它没有解决打印行以在重新格式化文件的最后一列中填充值“31”的问题。这是我得到的输出(对不起,不知道如何让它正确显示):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 米 25 1 22 205 米 25 1 24 206 米 25 1 10 206 米 25 1 14 206 米 25 1 18 206 米 25 1 20 206 米 25 1 24 206 米 25 1 26 206 米 25 1 27 206 米 25 1 206 米 25 1 28
  • 我明白了,我完全错过了那部分。我可以尝试为你添加这个,或者你可以自己做 - 你的伪代码已经有了它:-)
  • 啊,如果你能添加它,我肯定会很感激,同时我会尝试自己做,但我猜我对 perl 语法的不熟悉会妨碍我一点点。具体来说,我完全不确定您将如何获得第 3 列的总和。
【解决方案3】:
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
> 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-07
    • 2022-12-06
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2020-04-17
    • 2022-11-12
    相关资源
    最近更新 更多