1

This is my sample text file

[s]            [K]        
1              900
2              100 
3              200
8              1000
1              80
55             12
6              90000
5              1

I need to find the max value in column 2 and find the corresponding value in column 1, then subtracting the value from all elements of column 1.

My desired output is like this

[s]            [K]        
-5              900
-4              100 
-3              200
2              1000
-5              80
49             12
0              90000
-1              1

This is my snippet

awk 'function abs(x) {return x<0?-x:x}
FNR==1  { $1=$1; print; next }
{if(max<$2){max=$2;line=$1}}{print $1-line "\t"$2}' test.txt

Somehow this is not giving me correct result. I also want the same thing for minimum value in column 2.

0
$ cat tst.awk
BEGIN { OFS="\t" }
NR==1 { next }
NR==2 { maxVal = $2 }
NR==FNR {
    if ($2 >= maxVal) {
        maxSth = $1
        maxVal = $2
    }
    next
}
{ print (FNR>1 ? ($1 - maxSth) : $1) , $2 }

$ awk -f tst.awk file file
[s]     [K]
-5      900
-4      100
-3      200
2       1000
-5      80
49      12
0       90000
-1      1
  • Great! But I don't understand why someone is continuously down voting every answer? – Mohammed Atta Aug 2 '19 at 8:25
  • There are idiots out there who enjoy doing that. Don't worry about it, just upvote every answer to compensate when you notice it happening as I'm about to do. – Ed Morton Aug 2 '19 at 13:20
  • Serial up or down voting is often caught by scripts running every night, and reversed automatically. – Kusalananda Aug 2 '19 at 13:28
2

You will have to do two passes over your data to first find the minimum and maximum values, and then to do your calculations:

awk 'BEGIN { OFS = "\t" }
     FNR == NR { if (FNR > 1 && (min == "" || $2 < min)) { min = $2; minval = $1 }
                 if (FNR > 1 && (max == "" || $2 > max)) { max = $2; maxval = $1 }
                 next }
     FNR == 1 { print "[s (-max)]", "[s (-min)]", "[K]"; next }
              { print $1 - maxval, $1 - minval, $2 }' file file

For the given data, this would produce

[s (-max)]      [s (-min)]      [K]
-5      -4      900
-4      -3      100
-3      -2      200
2       3       1000
-5      -4      80
49      50      12
0       1       90000
-1      0       1

The awk code have four blocks, and we pass the input file to it twice. The first block (BEGIN) simply sets the output field separator to a tab character.

The second block (FNR == NR) will execute for each line of during the first pass over the file and keeps track of the maximum and minimum values in the 2nd column (max and min) and the corresponding values in the 1st column (maxval and minval), updating them as needed. At the end of this block, next is used to skip the rest of the script for the current input.

NR is the ordinal number for the current record (the "line number") overall, while FNR is the same number for the current file. NR will be the same as FNR only for the first pass over the input file.

The third block (FNR == 1) would be executed when having just read the first line from the second pass over the file. It simply prints out a heading. We're going to calculate both the subtraction of the minval and maxval values from the 1st column's value, so we add a new column header.

The final block is unconditional and would execute for the 2nd line onwards in the 2nd pass through the file and does the actual computation and output of the data.

You can get slightly prettier output by passing the output through column -s $'\t' -t:

[s (-max)]  [s (-min)]  [K]
-5          -4          900
-4          -3          100
-3          -2          200
2           3           1000
-5          -4          80
49          50          12
0           1           90000
-1          0           1
  • +1 for the explicated code. – Jim L. Aug 1 '19 at 22:26
0

maybe something like this? note the processing file twice.

awk 'NR==FNR && NR>1 { max_col2=( max_col2>$2?max_col2:$2 ); max[$2]=$1;  
                       min_col1=( min_col1>$1?min_col1:$1 ); min[$1]=$2; }
     NR!=FNR && FNR>1{ print $1-max[max_col2], $2-min[min_col1]; }' OFS='\t' infile infile
-5      888
-4      88
-3      188
2       988
-5      68
49      0
0       89988
-1      -11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.