Чи є спосіб отримати мінімум, макс, медіану та середнє число списку чисел в одній команді?


93

У мене є список номерів у файлі, по одному на рядок. Як я можу отримати мінімальні, максимальні, середні та середні значення? Я хочу використовувати результати в баш-скрипті.

Хоча моя безпосередня ситуація стосується цілих чисел, рішення для чисел з плаваючою комою було б корисним вниз за рядком, але простий цілий метод є чудовим.


Відповіді:


50

Ви можете використовувати мову програмування R .

Ось швидкий і брудний сценарій R:

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

Зверніть увагу на те, "stdin"у scanякому є спеціальне ім'я файлу, яке слід читати зі стандартного вводу (це означає, що з труб чи переадресацій).

Тепер ви можете перенаправити свої дані через stdin до сценарію R:

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

Також працює для плаваючих точок:

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

Якщо ви не хочете писати файл сценарію R, ви можете викликати справжній однокласник (з рядком тільки для читання) у командному рядку, використовуючи 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

Прочитайте чудові посібники з R на веб- сторінці http://cran.r-project.org/manuals.html .

На жаль, повна посилання доступна лише у форматі PDF. Ще один спосіб прочитати посилання - це введення ?topicnameв підказку інтерактивного R-сеансу.


Для повноти: є команда R, яка виводить усі необхідні значення та інше. На жаль, у доброзичливому для людини форматі, який важко програматизувати.

> 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 

1
Це виглядає цікаво .. Я буду детальніше ознайомитись із ним завтра .. На підставі сторінки Вікіпедії "R став фактичним стандартом серед статистиків" ... ну це важливе визнання ... Я спробував перевантажити його на днях (я постійно бачив це згадане), але я не зміг його знайти в
репортажі

10
у репортах ubuntu (і debian?) репо названий пакунок r-base.
lesmana

дякую, мені потрібна ця посилання на ім'я :) Я не думав про r - у синаптичному полі пошуку, і він не діє на самотнього персонажа ... Я спробував це зараз, і це виглядає ідеально. Rмова, очевидно, найкраща для моєї вимоги в цій ситуації. Відповідно до відповіді Гілла, Rscriptінтерфейс до файлів скриптів є найбільш відповідним (проти R, який є інтерактивним інтерфейсом) ... і R в терміналі створює зручний калькулятор , або тестове середовище (як python :)
Peter.O

(+1) Я люблю Р. Я не можу його досить рекомендувати.
Дасон

6
або простоcat datafile | Rscript -e 'print(summary(scan("stdin")));'
shabbychef

52

Я фактично зберігаю невелику програму awk, щоб дати суму, кількість даних, мінімальну дату, максимальну дату, середню та медіану одного стовпця числових даних (включаючи від’ємні числа):

#!/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];
  }
'

Наведений вище сценарій читає зі stdin та друкує розділені стовпчиками стовпці виводу в одному рядку.


1
Ага! це очевидно (тепер, коли я бачив ваш awk-скрипт :) ... Немає необхідності постійно перевіряти min та max, коли масив сортується :), а це означає, що NR==1може перейти (непотрібне використання- якщо) разом із перевірками min / max, тож усі ініціалізації можуть бути розміщені в розділі ПОЧАТОК (добре!) ... Дозволити коментарі теж приємний штрих .. Дякую, +1 ...
Peter.O

Просто думка .. можливо, дозволяти лише цифри краще, ніж забороняти коментарі (але це залежить від ваших вимог) ..
Peter.O

1
Технічно awkприпустимо, що "нові" змінні дорівнюють нулю, тому в цьому випадку BEGIN{}розділ непотрібний. Я виправив обгортку (також не потрібно уникати розривів рядків). Я також використовував OFS="\t"для очищення printлінії та реалізував другий коментар @ Peter.O. (Так, мій регулярний вираз дозволяє ., але як awkінтерпретує це як 0, це прийнятно.)
Адам Кац

1
@AdamKatz - це великі зміни, але, як це є, я не написав програму. awkЗараз мій сценарій істотно відрізняється. Я майже відчуваю, що ви повинні взяти кредит на вищезгадану програму, щоб дати кредит там, де належить кредит.
Брюс Едігер

1
Я написав сценарій perl під назвою avg, який робить це і багато іншого, до речі.
Адам Кац

47

З GNU datamash :

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

4
найпростіша відповідь на сьогоднішній день для bash, на запитання
rfabbri

3
brew install datamashнадає робочу версію для macOS, якщо у вас встановлений Hombrew.
Пер Лундберг

19

Мінімальну, максимальну та середню величини легко отримати за допомогою 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

Обчислення медіани трохи складніше, оскільки потрібно сортувати числа і зберігати їх у пам’яті на деякий час або читати їх двічі (перший раз порахувати їх, другий - щоб отримати середнє значення). Ось приклад, який зберігає всі номери в пам'яті:

% 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

Дякую ... твій приклад - це хороший вихід для пробудження, для мене .. Я трохи його переробив і збив їх разом (отримавши відчуття дивності) ... Я використовував awk's, asortа не трубопровідний sort, і, схоже, правильно сортувати цілі числа та десяткові знаки . Ось посилання на мою отриману версію paste.ubuntu.com/612674 ... (І примітка до Кіма: Я експериментую з awk вже пару годин . Робота з прикладом особистого інтересу для мене набагато краща) ... Загальна примітка для читачів: Мені все ж цікаво побачити інші методи. чим компактніше, тим краще. Почекаю трохи ...
Пітер.О


17

Мінімум:

jq -s min

Максимум:

jq -s max

Середня:

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

Середній:

jq -s add/length

В jqна -s( --slurpопції) створює масив для вхідних ліній після розбору кожного рядка , як JSON, або у вигляді числа в даному випадку.


3
Рішення jq варте особливої ​​згадки, оскільки воно є лаконічним та повторно призначає інструмент неочевидним чином.
jplindstrom

1
гарний! бажаю, щоб я міг дати +2
RASG

7
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]};

echo file.txtвиглядає не зовсім правильно, можливоcat
malat

6

І один (довгий) лайнер Perl, включаючи медіану:

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;'

Використовуються спеціальні параметри:

  • -0777 : читайте одразу весь файл замість рядка за рядком
  • -a : автосплит у масив @F

Більш читаною версією сценарію тієї ж речі буде:

#!/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;

Якщо ви хочете децималій, замініть %dщось на зразок %.2f.


6

Simple-r - це відповідь:

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

Він використовує середовище R для спрощення статистичного аналізу.


5

Ось лише для того, щоб на цій сторінці були представлені різноманітні варіанти, ось ще два способи:

1: октава

  • GNU Octave - мова для інтерпретації високого рівня, призначена в першу чергу для чисельних обчислень. Він надає можливості для чисельного вирішення лінійних і нелінійних задач, а також для виконання інших чисельних експериментів.

Ось короткий приклад октави.

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 + одноцільові інструменти .

Цей скрипт використовує bash для обробки чисел з плаваючою комою numprocessта numaverageз пакету num-utils.

PS. Я також розглядав розумно bc, але для цієї конкретної роботи він не пропонує нічого, крім того, що awkробить. Це (як "c" у "bc") калькулятор - калькулятор, який вимагає багато програмування, як awkі цей скрипт bash ...


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 

4

Я виберу другий лесман R і запропоную свою першу програму R. Він зчитує одне число на рядок на стандартному вході та записує чотири числа (хв, макс, середнє, медіану), розділених пробілами до стандартного виводу.

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

Дякую за "друге" (це заспокоює) ... ваш приклад виявився корисним, оскільки я не зрозумів прямолінійного, що Rце інтерактивний інтерфейс, і Rscriptзапускає сценарії файлів, які можна виконати відповідно до вашого прикладу хеш-чум , або викликається з баш-скрипту. Сценарії можуть обробляти аргументи командного рядка (наприклад, stackoverflow.com/questions/2045706/… ), так що це добре виглядає ... Також R-вирази можна використовувати в bash через -e..., але Мені цікаво, як Rпорівнюється з bc...
Пітер.О

2

Нижче 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}'

(він обчислює медіану як середнє значення двох центральних значень, якщо кількість значень парне)


2

Беручи підказки з коду Брюса, ось більш ефективна реалізація, яка не зберігає всі дані в пам'яті. Як зазначено в запитанні, передбачається, що вхідний файл має (максимум) одне число на рядок. Він підраховує рядки у вхідному файлі, які містять кваліфікуюче число, і передає цей рахунок awkкоманді разом з (перед) відсортованими даними. Так, наприклад, якщо файл містить

6.0
4.2
8.3
9.5
1.7

то вхід до awkнасправді

5
1.7
4.2
6.0
8.3
9.5

Потім awkсценарій фіксує кількість даних у NR==1блоці коду і зберігає середнє значення (або два середніх значення, які усереднюються для отримання медіани), коли він їх бачить.

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
  }
'

Ласкаво просимо до Unix & Linux! Гарна робота для першого повідомлення. (1) Хоча це може відповісти на питання, було б кращою відповіддю, якби ви могли пояснити, як / чому це робиться. За останні чотири роки стандарти сайту розвивалися; хоча відповіді, що стосуються лише коду, були прийнятні у 2011 році, ми надаємо перевагу вичерпним відповідям, які надають більше пояснень та контексту. Я не прошу пояснити весь сценарій; лише ті частини, які ви змінили (але якщо ви хочете пояснити весь сценарій, це теж добре). (BTW, я це прекрасно розумію; я прошу від імені наших менш досвідчених користувачів.)… (Продовжував)
G-Man

(Продовження)… Будь ласка, не відповідайте на коментарі; відредагуйте свою відповідь, щоб зробити її більш зрозумілою та повною. (2) Виправлення сценарію таким чином, що йому не потрібно зберігати весь масив в пам'яті, - це гарне вдосконалення, але я не впевнений, чи підходить сказати, що ваша версія "ефективніша", коли у вас є три непотрібні catкоманди; див. УУПЦ . … (Продовження)
G-Man

(Протяг)… (3) Ваш код безпечний, оскільки ви встановили FILENAMEі знаєте, для чого його встановили, але, як правило, завжди слід цитувати змінні оболонки, якщо у вас немає вагомих причин цього не робити, і ви впевнений, що ти знаєш, що робиш. (4) і ваша відповідь, і ігнорування Брюса негативні дані (тобто числа, що починаються з -); у питанні немає нічого, що б підказало, що це правильна чи бажана поведінка. Не відчувайте себе погано; минуло вже чотири роки, і, мабуть, я перша людина, яка помітила.
G-Man

Внесено зміни відповідно до пропозицій. Дідн, не знав про накладну команду котів. Завжди використовував його для передачі одиночних файлів. Дякуємо, що розповіли про УУПЦ .....
Рахул Агарвал

Добре. Я ліквідував третій catі додав пояснення.
G-Man

2

Це numкрихітна awkобгортка, яка саме робить це і багато іншого, наприклад

$ 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

це позбавляє вас від винаходити колесо у надзвичайно портативному awk. Документи наведені вище, і пряме посилання тут (перевірте також сторінку GitHub ).


Посилання на затемнений веб-код, який потрібно виконати на комп'ютері користувача, мені здається поганою ідеєю. Тут міститься код, що містить код

Де був цей "протестований" код, перш ніж його розміщувати на Github всі 4 місяці тому? Мені здається надзвичайно підозрілим те, що посилання на github повинно бути вилучено з команди завантаження curl. Набагато простіше дізнатися, як фінансово пожертвувати забудовника. Схоже, автор цього коду боїться, що люди можуть зайти до Github і подивитися на (майже неіснуючу) історію та статистику. Чи є причина взагалі називати цю битву випробуваною, окрім спроб зібрати гроші?
Антон

@BinaryZeba: оновлено
coderofsalvation

@Anthon ок, видалив 'битву' частину. Я не думаю, що це місце для змови FUD.
coderofsalvation

2

З 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

1

cat/pythonєдине рішення - не підтвердження порожнього вводу!

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))"

Ви не показували медіану
Peter.O

@ Peter.O виправлено.
ravwojdyla

Модуль статистики вимагає версії python> = 3.4
Peter.O

@ Peter.O ти маєш рацію - це проблема?
ravwojdyla

Це не є проблемою, якщо у вас немає відповідної версії python. Це просто робить його менш портативним.
Пітер.О

0

Якщо вас більше цікавить корисність, а не класна чи розумна, тоді perlце простіший вибір, ніж awk. За великим рахунком, він буде на кожному * nix з послідовною поведінкою, і його легко та безкоштовно встановити на Windows. Я думаю, що це також менш виразно, ніж awk, і буде кілька модулів статистики, які ви могли б використовувати, якби хотіли на півдорозі між тим, як писати його самостійно, і чимось на кшталт Р. Мій досить неперевірений (адже я знаю, що він має помилки, але він працює для моїх цілей ) На perlнаписання сценарію пішло близько хвилини, і я б здогадався, що єдиною криптовалютною частиною буде та while(<>), яка є дуже корисною стенограмою, тобто прийняти файли, передані як аргументи командного рядка, прочитати рядок за один раз і поставити цей рядок у спеціальній змінній$_. Таким чином, ви можете помістити це у файл під назвою count.pl і запустити його як perl count.pl myfile. Крім того, має бути болісно очевидно, що відбувається.

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

3
Ви не показували медіану
Peter.O

0
function median()
{
    declare -a nums=($(cat))
    printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}  

Ця відповідь була б корисною, якби було пояснення того, як вищевказаний код відповідає на питання, наприклад, ви повинні сказати, що shв якості інтерпретатора використовується Bash (not ). Існує також проблема з тим, як дані зчитуються в масив з файлу.
Ентоні Геоґеган
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.