1

I need to find the max and min of each entry in my file:

#subset of my file NEUTRON 20.900103 PION- 0.215176 PION- 22.716532 NEUTRON 8.043279 PION+ 1.374297 PION- 0.313350 PION+ 0.167848 

How could I loop through the file and find the min and max for each Name when there are multiple entry names. I have used awk already to count each entry and have no repeats, but each repeat of each name contains a number with it and that number is what im trying to filter out the max and min of each entry. ex output from whole file:

Name Count Minimum Maximum -------- ----- --------- --------- KAON- 1 5.489958 5.489958 NEUTRON 2 8.043279 20.900103 PHOTON 10 0.034664 1.897264 PION- 5 0.192247 22.716532 PION+ 7 0.167848 7.631051 PROTON 1 1.160216 1.160216 
6
  • 1
    Well I recommended SQL in your last Q. Unless you vote me up there, I will not tell you what the aggregate functions for min and max are called ;) Commented Oct 11, 2019 at 17:23
  • Welcome to unix.stackexchange.com ! Do you only have value >= 0 in column 2 ? Or can they be negative too ? Commented Oct 11, 2019 at 17:30
  • @Cbhihe Every value is greater than 0 Commented Oct 11, 2019 at 17:44
  • 1
    bash is just your shell; it is not (good for) a text processor Commented Oct 11, 2019 at 18:01
  • 1
    You can call any command from bash (or zsh or tcsh or any shell); the shell itself does not matter here. Commented Oct 11, 2019 at 18:10

2 Answers 2

5
awk '{ count[$1]++ min[$1]=(!($1 in min) || $2<min[$1]) ? $2 : min[$1] max[$1]=(!($1 in max) || $2>max[$1]) ? $2 : max[$1] } END { print "Name","Count","Minimum","Maximum" print "----","-----","-------","-------" for(i in count) print i,count[i],min[i],max[i] }' file | column -t 

The logic of the minimum array value assignment is:
If the name of the first field doesn't exist in the array (!($1 in min)) or (||) the second field is smaller than the current array value ($2<min[$1]), then (?) assign the new value $2, else (:) assign the old value min[$1].

The | column -t is used to pretty-print the result as table. You can remove it if you don't need it.

Output:

Name Count Minimum Maximum ---- ----- ------- ------- PION+ 2 0.167848 1.374297 PION- 3 0.215176 22.716532 NEUTRON 2 8.043279 20.900103 
1
  • 1
    Always try to write software using positive rather than negative logic since positives are always easier to understand than negatives and cannot lead to obscure double negatives. min[$1]=(($1 in min) && (min[$1]<$2) ? min[$1] : $2) avoids the negative !. Similarly for max of course. Commented Oct 12, 2019 at 20:13
3

As pointed out by Jeff Schaller, bash is not a text processor, even though what you ask for is not particularly difficult to achieve. So here is a way to do it,... for whatever it's worth.

$ awk '!/^#.*/ { ((++cnt[$1])); if (cnt[$1]==1) {min[$1]=max[$1]=$2} else if ($2 < min[$1]) { min[$1]=$2} else if ($2 > max[$1]) {max[$1]=$2} } END { printf "%-10s%7s%10s%12s\n","Name","Count","Minimum", "Maximum"; for (i in cnt) printf "%-10s%7d%10.6g%12.8g\n", i,cnt[i],min[i],max[i]; }' testdata 

Output:

Name Count Minimum Maximum PION+ 2 0.167848 1.374297 PION- 3 0.215176 22.716532 NEUTRON 2 8.04328 20.900103 

Explanation:

  • skip any line (record) that start with a #
  • increment cnt[$1] by 1, for each record, starting from 0 at onset of awk program's execution
  • if first field value "$1" of current record was never seen before, initialize min and max value for that value to the value "$2".
  • in other cases (when value "$1" was seen at least once in a previous record), update min and max values
  • finally print with formating to respect the aspect of your example output in OP.
2
  • I am getting %7d 0 1 %7d 0 1 %7d 0 7 %7d 0 5 %7d 0 2 %7d 0 10 as an output from this Commented Oct 11, 2019 at 19:52
  • @fgdark. Strange ! One character dropped out when I copied-pasted the one-liner from my terminal. It was an "s" in the formatting string. I must have done something by mistake. Corrected. Commented Oct 11, 2019 at 20:59

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.