简体   繁体   中英

Calculate the percentage difference between two adjacent rows uniquely

I have a tab delimited file such as

Name    S.No    Points  First
Jack    2   98  F
Jones   6   25
Mike    8   11
Jasmine 5   7   
Gareth  1   85  F
Simon   4   76
Mark    11  12
Steve   17  8
Clarke  3   7

I want to calculate top 3 point earners and difference between the first point earner denoted by line F and other two in the group averaged over total points in the group. For example, Jones and Mike in 1st case and Simon and Mark in the second case. I want my output to be

Name    S.No    Points  First
Jack    2   98  F
Jones   6   25  51.77
Mike    8   11  61.70
Gareth  1   85  F
Simon   4   76  4.78
Mark    11  12  44.68

The formula for calculating the percentage is in the last column of output is, for example in case of Jones in Case 1 is

(Jack - Jones)*100/(Jack + Jones + Mike + Jasmine)
(98-25)*100/(98+25+11+7)= 51.77

I can select the first three top rows using

awk '$NF~/E/{c=3;next}c-->0'

However, calculating the difference and the percentage bit cumbersome as I have to sum the denominator before eliminating the top three rows.

refer your awk command, seems the source file has sorted the data by group already.

awk 'NR==FNR{if ($NF=="F") {s=$1}; a[s]+=$3;next}
     { if (FNR=="1") {print;next}
       if ($NF=="F") {s=$1;b=$3;c=3;print;next}
       if (--c>0){ printf "%s %.2f\n", $0,(b-$3)*100/a[s];}
     }' file file

result

Name    S.No    Points  First
Jack    2   98  F
Jones   6   25 51.77
Mike    8   11 61.70
Gareth  1   85  F
Simon   4   76 4.79
Mark    11  12 38.83

Getting a completely different number for Mark:

use 5.010;
use strict;
use warnings;
use autodie;
use List::Util 'sum';

open my $infile, '<', 'foo.txt';
my @groups;
my $group; 
while ( my $line = <$infile> ) {
    chomp $line;
    my ($name, $sno, $points, $first) = split /\t/, $line;
    if ( $first && $first eq 'F' ) {
        $group = [];
        push @groups, $group;
    }
    push @$group, { 'name' => $name, 'sno' => $sno, 'points' => $points, 'first' => $first };
}

say join "\t", qw/Name S.No Points First/;
for my $group (@groups) {
    my $total_points = sum map $_->{'points'}, @$group;
    my $first_points = $group->[0]{'points'};

    say join "\t", @{ $group->[0] }{ qw/name sno points first/ };

    for my $other (1..2) {
        if ( $group->[$other] ) {
            say join "\t", @{ $group->[$other] }{ qw/name sno points/ }, sprintf "%.2f", 100 * ($first_points - $group->[$other]{'points'}) / $total_points;
        }
    }
}

produces:

Name    S.No    Points  First
Jack    2   98  F
Jones   6   25  51.77
Mike    8   11  61.70
Gareth  1   85  F
Simon   4   76  4.79
Mark    11  12  38.83

Here is an awk solution:

awk -f c.awk input.txt input.txt

where c.awk is:

NR==FNR {
    if ($NF=="F") {
        key1=$1
        A[key1]=$3
        B[key1]=$3
    }
    else if (key1)
        B[key1]+=$3
    next
}
key && NF==3 {
    if (++k<3) {
        $4=((A[key]-$3)*100)/B[key]
        print
    }
    next
}

$NF=="F" {
    key=$1
    k=0
}

{print}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM