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.
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
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.
S GNU datamash :
$ printf '1\n2\n4\n' | datamash max 1 min 1 mean 1 median 1
4 1 2.3333333333333 2
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
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.
pythonpy funguje dobře pro tuto věc:
cat file.txt | py --ji -l 'min(l), max(l), numpy.median(l), numpy.mean(l)'
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
.
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]};
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.
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
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
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 ).
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ý)
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
}
'
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
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))"
function median()
{
declare -a nums=($(cat))
printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}
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";