it-swarm-eu.dev

Existuje způsob, jak získat min, max, medián a průměr seznamu čísel jedním příkazem?

Mám v seznamu seznam čísel, jeden na řádek. Jak mohu získat minimální, maximální, střední a průměr hodnoty? Chci použít výsledky v bash skriptu.

I když moje okamžitá situace je pro celá čísla, řešení pro čísla s plovoucí desetinnou čárkou by bylo užitečné po řádku, ale jednoduchá metoda celočíselného zpracování je v pořádku.

101
Peter.O

Můžete použít programovací jazyk R .

Zde je rychlý a špinavý R skript:

#! /usr/bin/env Rscript
d<-scan("stdin", quiet=TRUE)
cat(min(d), max(d), median(d), mean(d), sep="\n")

Všimněte si "stdin" V scan, což je speciální název souboru, který se má číst ze standardního vstupu (tj. Z potrubí nebo přesměrování).

Nyní můžete přesměrovat svá data přes stdin do skriptu R:

$ cat datafile
1
2
4
$ ./mmmm.r < datafile
1
4
2
2.333333

Funguje také pro pohyblivé body:

$ cat datafile2
1.1
2.2
4.4
$ ./mmmm.r < datafile2
1.1
4.4
2.2
2.566667

Pokud nechcete psát soubor skriptu R, můžete v příkazovém řádku pomocí Rscript:

$ Rscript -e 'd<-scan("stdin", quiet=TRUE)' \
          -e 'cat(min(d), max(d), median(d), mean(d), sep="\n")' < datafile
1
4
2
2.333333

Přečtěte si jemné příručky R na http://cran.r-project.org/manuals.html .

Bohužel plný odkaz je k dispozici pouze v PDF. Dalším způsobem, jak číst odkaz, je zadání ?topicname Do výzvy interaktivní R relace.


Pro úplnost: existuje příkaz R, který vydá všechny požadované hodnoty a další. Bohužel v lidsky přátelském formátu, který je obtížné programově analyzovat.

> summary(c(1,2,4))
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  1.000   1.500   2.000   2.333   3.000   4.000 
54
lesmana

Vlastně udržuji malý awk program, abych dal součet, počet dat, minimální datum, maximální datum, průměr a střední hodnotu jednoho sloupce číselných dat (včetně záporných čísel):

#!/bin/sh
sort -n | awk '
  BEGIN {
    c = 0;
    sum = 0;
  }
  $1 ~ /^(\-)?[0-9]*(\.[0-9]*)?$/ {
    a[c++] = $1;
    sum += $1;
  }
  END {
    ave = sum / c;
    if( (c % 2) == 1 ) {
      median = a[ int(c/2) ];
    } else {
      median = ( a[c/2] + a[c/2-1] ) / 2;
    }
    OFS="\t";
    print sum, c, ave, median, a[0], a[c-1];
  }
'

Výše uvedený skript čte ze stdin a tiskne sloupce výstupu oddělené tabulátory na jeden řádek.

55
Bruce Ediger

S GNU datamash :

$ printf '1\n2\n4\n' | datamash max 1 min 1 mean 1 median 1
4   1   2.3333333333333 2
53
cuonglm

Min, max a průměr jsou docela snadno se s awk:

% echo -e '6\n2\n4\n3\n1' | awk 'NR == 1 { max=$1; min=$1; sum=0 }
   { if ($1>max) max=$1; if ($1<min) min=$1; sum+=$1;}
   END {printf "Min: %d\tMax: %d\tAverage: %f\n", min, max, sum/NR}'
Min: 1  Max: 6  Average: 3,200000

Vypočítání mediánu je o něco složitější, protože musíte třídit čísla a chvíli je ukládat do paměti nebo je číst dvakrát (poprvé je spočítat, za druhé - získat střední hodnotu). Zde je příklad, který ukládá všechna čísla do paměti:

% echo -e '6\n2\n4\n3\n1' | sort -n | awk '{arr[NR]=$1}
   END { if (NR%2==1) print arr[(NR+1)/2]; else print (arr[NR/2]+arr[NR/2+1])/2}' 
3
20
gelraen

Minimální:

jq -s min

Maximum:

jq -s max

Medián:

sort -n|awk '{a[NR]=$0}END{print(NR%2==1)?a[int(NR/2)+1]:(a[NR/2]+a[NR/2+1])/2}'

Průměrný:

jq -s add/length

V jq the -s (--Slurp) volba vytvoří pole pro vstupní řádky po analýze každého řádku jako JSON nebo v tomto případě jako čísla.

20
nisetama

pythonpy funguje dobře pro tuto věc:

cat file.txt | py --ji -l 'min(l), max(l), numpy.median(l), numpy.mean(l)'
18
RussellStewart

A Perl jedno- (dlouhá) vložka, včetně mediánu:

cat numbers.txt \
| Perl -M'List::Util qw(sum max min)' -MPOSIX -0777 -a -ne 'printf "%-7s : %d\n"x4, "Min", min(@F), "Max", max(@F), "Average", sum(@F)/@F,  "Median", sum( (sort {$a<=>$b} @F)[ int( $#F/2 ), ceil( $#F/2 ) ] )/2;'

Používané speciální možnosti jsou:

  • -0777: přečte celý soubor najednou místo řádek po řádku
  • -a: autosplit do pole @F

Čitelnější verze skriptu stejného by byla:

#!/usr/bin/Perl

use List::Util qw(sum max min);
use POSIX;

@F=<>;

printf "%-7s : %d\n" x 4,
    "Min", min(@F),
    "Max", max(@F),
    "Average", sum(@F)/@F,
    "Median", sum( (sort {$a<=>$b} @F)[ int( $#F/2 ), ceil( $#F/2 ) ] )/2;

Pokud chcete desetinná čísla, nahraďte %d s něčím jako %.2f.

7
mivk
nums=$(<file.txt); 
list=(`for n in $nums; do printf "%015.06f\n" $n; done | sort -n`); 
echo min ${list[0]}; 
echo max ${list[${#list[*]}-1]}; 
echo median ${list[${#list[*]}/2]};
7
NotANumber

Simple-r je odpověď:

r summary file.txt
r -e 'min(d); max(d); median(d); mean(d)' file.txt

Používá prostředí R ke zjednodušení statistické analýzy.

6
user48270

Jen kvůli tomu, že na této stránce máte k dispozici celou řadu možností, zde jsou dva další způsoby:

1: oktáva

  • GNU Octave je interpretovaný jazyk na vysoké úrovni, určený především pro numerické výpočty. Poskytuje schopnosti pro numerické řešení lineárních a nelineárních problémů a pro provádění dalších numerických experimentů.

Zde je rychlý příklad oktávy.

octave -q --eval 'A=1:10;
  printf ("# %f\t%f\t%f\t%f\n", min(A), max(A), median(A), mean(A));'  
# 1.000000        10.000000       5.500000        5.500000

2: bash + jednoúčelové nástroje .

Pro bash pro zpracování čísel s pohyblivou řádovou čárkou používá tento skript numprocess a numaverage z balíčku num-utils.

PS. Také jsem se přiměřeně podíval na bc, ale pro tuto konkrétní práci nenabízí nic jiného než to, co awk dělá. Je to (jak uvádí 'c' ve stavu 'bc') kalkulačka - kalkulačka, která vyžaduje hodně programování jako awk a tento bash skript ...


arr=($(sort -n "LIST" |tee >(numaverage 2>/dev/null >stats.avg) ))
cnt=${#arr[@]}; ((cnt==0)) && { echo -e "0\t0\t0\t0\t0"; exit; }
mid=$((cnt/2)); 
if [[ ${cnt#${cnt%?}} == [02468] ]] 
   then med=$( echo -n "${arr[mid-1]}" |numprocess /+${arr[mid]},%2/ )
   else med=${arr[mid]}; 
fi     #  count   min       max           median        average
echo -ne "$cnt\t${arr[0]}\t${arr[cnt-1]}\t$med\t"; cat stats.avg 
5
Peter.O

Za druhé Lesmanova volba R a nabídnu svůj první R program. Na standardní vstup přečte jedno číslo na řádku a zapíše čtyři čísla (min, max, průměr, medián) oddělená mezerami na standardní výstup.

#!/usr/bin/env Rscript
a <- scan(file("stdin"), c(0), quiet=TRUE);
cat(min(a), max(a), mean(a), median(a), "\n");

num je maličký awk wrapper, který to přesně dělá a další, např.

$ echo "1 2 3 4 5 6 7 8 9" | num max
9
$ echo "1 2 3 4 5 6 7 8 9" | num min max median mean
..and so on

to vám ušetří z objevování kola v ultra-přenosném awk. Dokumenty jsou uvedeny výše a přímý odkaz zde (zkontrolujte také stránka GitHub ).

3
coderofsalvation

Níže uvedený tandem sort/awk:

sort -n | awk '{a[i++]=$0;s+=$0}END{print a[0],a[i-1],(a[int(i/2)]+a[int((i-1)/2)])/2,s/i}'

(vypočítá se střední hodnota ze dvou středních hodnot, je-li počet hodnot sudý)

2
mik

Vezmeme-li narážky z kódu Bruce, je zde efektivnější implementace, která neuchovává všechna data v paměti. Jak je uvedeno v otázce, předpokládá se, že vstupní soubor má (maximálně) jedno číslo na řádek. Počítá řádky ve vstupním souboru, které obsahují kvalifikační číslo, a předá počet příkazu awk spolu s (před) seřazenými daty. Například pokud soubor obsahuje

6.0
4.2
8.3
9.5
1.7

pak vstup do awk je ve skutečnosti

5
1.7
4.2
6.0
8.3
9.5

Skript awk pak zaznamená počet dat v NR==1 blok kódu a uloží střední hodnotu (nebo dvě střední hodnoty, které jsou zprůměrovány pro získání mediánu), když je uvidí.

FILENAME="Salaries.csv"

(awk 'BEGIN {c=0} $1 ~ /^[-0-9]*(\.[0-9]*)?$/ {c=c+1;} END {print c;}' "$FILENAME"; \
        sort -n "$FILENAME") | awk '
  BEGIN {
    c = 0
    sum = 0
    med1_loc = 0
    med2_loc = 0
    med1_val = 0
    med2_val = 0
    min = 0
    max = 0
  }

  NR==1 {
    LINES = $1
    # We check whether numlines is even or odd so that we keep only
    # the locations in the array where the median might be.
    if (LINES%2==0) {med1_loc = LINES/2-1; med2_loc = med1_loc+1;}
    if (LINES%2!=0) {med1_loc = med2_loc = (LINES-1)/2;}
  }

  $1 ~ /^[-0-9]*(\.[0-9]*)?$/  &&  NR!=1 {
    # setting min value
    if (c==0) {min = $1;}
    # middle two values in array
    if (c==med1_loc) {med1_val = $1;}
    if (c==med2_loc) {med2_val = $1;}
    c++
    sum += $1
    max = $1
  }
  END {
    ave = sum / c
    median = (med1_val + med2_val ) / 2
    print "sum:" sum
    print "count:" c
    print "mean:" ave
    print "median:" median
    print "min:" min
    print "max:" max
  }
'
2
Rahul Agarwal

S Perl:

$ printf '%s\n' 1 2 4 |
   Perl -MList::Util=min,max -MStatistics::Basic=mean,median -w -le '
     chomp(@l = <>); print for min(@l), max(@l), mean(@l), median(@l)'
1
4
2.33
2
2

cat/python jediné řešení - není důkaz o prázdném vstupu!

cat data |  python3 -c "import fileinput as FI,statistics as STAT; i = [int(l) for l in FI.input()]; print('min:', min(i), ' max: ', max(i), ' avg: ', STAT.mean(i), ' median: ', STAT.median(i))"
1
ravwojdyla
function median()
{
    declare -a nums=($(cat))
    printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}  
0
David McLaughlin

Pokud se více zajímáte o obslužný program než o chladný nebo chytrý, pak Perl je snadnější volbou než awk. Celkově to bude na každé * nix s důsledným chováním a instalace na Windows je snadná a bezplatná. Myslím, že je to také méně záhadné než awk, a budou existovat některé statistické moduly, které byste mohli použít, kdybyste chtěli dům na půl cesty mezi jeho napsáním a něčím jako R. Můj docela netestovaný (ve skutečnosti vím, že má chyby ale funguje to pro mé účely) Skript Perl trval asi minutu, a myslím, že jedinou kryptickou částí by byla while(<>), což je velmi užitečná zkratka, což znamená vzít soubor (y) předané jako argumenty příkazového řádku, přečtěte řádek najednou a vložte tento řádek do speciální proměnné $_. Můžete to tedy vložit do souboru s názvem count.pl a spustit jako Perl count.pl myfile. Kromě toho by mělo být bolestně zřejmé, co se děje.

$max = 0;
while (<>) {
 $sum = $sum + $_;
 $max = $_ if ($_ > $max);
 $count++;
}
$avg=$sum/$count;
print "$count numbers total=$sum max=$max mean=$avg\n";
0
iain