ฉันมีรายการตัวเลขในไฟล์หนึ่งรายการต่อบรรทัด ฉันจะรับค่าต่ำสุดสูงสุดค่ามัธยฐานและค่าเฉลี่ยได้อย่างไร ฉันต้องการใช้ผลลัพธ์ในสคริปต์ทุบตี
แม้ว่าสถานการณ์ปัจจุบันของฉันจะเป็นจำนวนเต็ม แต่วิธีแก้ปัญหาสำหรับเลขทศนิยมจะเป็นประโยชน์ในบรรทัด
ฉันมีรายการตัวเลขในไฟล์หนึ่งรายการต่อบรรทัด ฉันจะรับค่าต่ำสุดสูงสุดค่ามัธยฐานและค่าเฉลี่ยได้อย่างไร ฉันต้องการใช้ผลลัพธ์ในสคริปต์ทุบตี
แม้ว่าสถานการณ์ปัจจุบันของฉันจะเป็นจำนวนเต็ม แต่วิธีแก้ปัญหาสำหรับเลขทศนิยมจะเป็นประโยชน์ในบรรทัด
คำตอบ:
คุณสามารถใช้ภาษาการเขียนโปรแกรม 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
r-base
แพคเกจการตั้งชื่อ
R
ภาษาเป็นสิ่งที่ดีที่สุดสำหรับความต้องการของฉันในสถานการณ์นี้ .. ตามคำตอบของ Gilles Rscript
อินเตอร์เฟซไปยังไฟล์สคริปต์นั้นเหมาะสมที่สุด (เทียบกับR
ซึ่งเป็นส่วนต่อประสานแบบโต้ตอบ) ... และ R ในเครื่องเทอร์มินัล หรือสภาพแวดล้อมการทดสอบ (เช่นงูหลาม :)
cat datafile | Rscript -e 'print(summary(scan("stdin")));'
จริง ๆ แล้วฉันเก็บโปรแกรม 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 และพิมพ์คอลัมน์ที่คั่นด้วยแท็บในบรรทัดเดียว
NR==1
สามารถไปได้ (การใช้ที่ไร้ประโยชน์ของ - ถ้า) พร้อมกับการตรวจสอบขั้นต่ำ / สูงสุดดังนั้นการเริ่มต้นทั้งหมดจะอยู่ในส่วน BEGIN (ดี!) ... การอนุญาตให้แสดงความคิดเห็นก็เป็นสิ่งที่ดีเช่นกันขอบคุณ ... +1 +1
awk
จะถือว่าตัวแปร "ใหม่" เป็นศูนย์ดังนั้นในกรณีนี้BEGIN{}
ส่วนที่ไม่จำเป็น ฉันได้แก้ไขการตัด (ไม่จำเป็นต้องยกเว้นการแบ่งบรรทัด) ฉันเคยOFS="\t"
ทำความสะอาดprint
สายและใช้ความคิดเห็นที่สองของ @ Peter.O (ใช่ regex ของฉันช่วย.
แต่awk
ตีความว่าเป็น0
ที่ยอมรับ.)
awk
ตอนนี้สคริปต์ของฉันแตกต่างอย่างมาก ฉันเกือบรู้สึกว่าคุณควรใช้เครดิตสำหรับโปรแกรมด้านบนเพื่อให้เครดิตเมื่อถึงกำหนดชำระเครดิต
ด้วยdatamash ของ GNU :
$ printf '1\n2\n4\n' | datamash max 1 min 1 mean 1 median 1
4 1 2.3333333333333 2
brew install datamash
ให้รุ่นที่ใช้งานได้สำหรับ macOS หากคุณติดตั้ง Hombrew
ต่ำสุด, สูงสุดและปานกลางนั้นง่ายต่อการรับด้วย 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
asort
มากกว่า piped sort
และดูเหมือนว่าจะเรียงลำดับจำนวนเต็มและทศนิยมอย่างถูกต้อง .. นี่คือลิงค์ไปยังรุ่นที่ได้ผลลัพธ์ของฉันpaste.ubuntu.com/612674 ... (และหมายเหตุถึง Kim: ฉันได้ทดลองใช้ awk สองสามชั่วโมงแล้ว . การทำงานกับตัวอย่างผลประโยชน์ส่วนตัวดีกว่าสำหรับฉัน) ... หมายเหตุทั่วไปสำหรับผู้อ่าน: ฉันยังคงสนใจที่จะเห็นวิธีการอื่น มากขึ้นขนาดกะทัดรัดที่ดีกว่า ฉันจะรอสักครู่ ...
pythonpyทำงานได้ดีสำหรับสิ่งนี้:
cat file.txt | py --ji -l 'min(l), max(l), numpy.median(l), numpy.mean(l)'
จำนวนขั้นต่ำ:
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
ใน( ) ตัวเลือกสร้างอาร์เรย์สำหรับเส้นที่นำเข้าที่หลังจากแยกแต่ละบรรทัดเป็น JSON หรือเป็นจำนวนในกรณีนี้jq
-s
--slurp
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
และ 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
: autosplit ในอาร์เรย์ @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
Simple-rคือคำตอบ:
r summary file.txt
r -e 'min(d); max(d); median(d); mean(d)' file.txt
มันใช้สภาพแวดล้อม R เพื่อลดความซับซ้อนของการวิเคราะห์ทางสถิติ
เพียงเพื่อให้มีตัวเลือกที่หลากหลายในหน้านี้ต่อไปนี้เป็นสองวิธีเพิ่มเติม:
นี่คือตัวอย่างอ็อกเทฟด่วน
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: ทุบตี + วัตถุประสงค์เดียวเครื่องมือ
สำหรับทุบตีที่จะจัดการกับจำนวนจุดลอยตัวสคริปต์นี้ใช้numprocess
และจากแพคเกจnumaverage
num-utils
PS ฉันก็มีลักษณะที่สมเหตุสมผลbc
แต่สำหรับงานนี้โดยเฉพาะมันไม่ได้ให้อะไรนอกเหนือไปจากสิ่งที่awk
ทำ มันคือ (เป็น 'c' ใน 'bc' ฯ ) เครื่องคิดเลข - เครื่องคิดเลขที่ต้องมีการเขียนโปรแกรมมากawk
และสคริปต์ทุบตีนี้ ...
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
ฉันจะเลือก R ของ Lesmana เป็นครั้งที่สองและเสนอโปรแกรม R แรกของฉัน มันอ่านหนึ่งหมายเลขต่อบรรทัดในอินพุตมาตรฐานและเขียนตัวเลขสี่ตัว (min, max, average, median) คั่นด้วยช่องว่างไปยังเอาต์พุตมาตรฐาน
#!/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 ในการทุบตีผ่าน-e
... แต่ ฉันสงสัยว่าR
จะเปรียบเทียบกับbc
...
ด้านล่าง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}'
(มันคำนวณค่ามัธยฐานเป็นค่าเฉลี่ยของสองค่ากลางถ้านับค่าเป็นคู่)
การชี้นำจากโค้ดของ Bruce นี่คือการติดตั้งที่มีประสิทธิภาพมากกว่าซึ่งไม่ได้เก็บข้อมูลทั้งหมดไว้ในหน่วยความจำ ตามที่ระบุในคำถามจะถือว่าไฟล์อินพุตมีอย่างน้อยหนึ่งหมายเลขต่อบรรทัด มันจะนับบรรทัดในไฟล์อินพุตที่มีหมายเลขที่มีคุณสมบัติและผ่านการนับไปยัง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
}
'
cat
คำสั่งที่ไม่จำเป็น ดูUUOC … (ต่อ)
FILENAME
และคุณรู้ว่าสิ่งที่คุณตั้งไว้ แต่โดยทั่วไปคุณควรอ้างอิงตัวแปรเชลล์เสมอเว้นแต่คุณจะมีเหตุผลที่ดีที่จะไม่และคุณแน่ใจว่าคุณรู้ว่าคุณกำลังทำอะไร (4) ทั้งคำตอบของคุณและบรูซไม่สนใจการป้อนข้อมูลเชิงลบ (เช่นตัวเลขที่ขึ้นต้นด้วย-
); ไม่มีอะไรในคำถามที่จะแนะนำว่านี่เป็นพฤติกรรมที่ถูกต้องหรือต้องการ อย่ารู้สึกแย่ เป็นเวลากว่าสี่ปีแล้วและเห็นได้ชัดว่าฉันเป็นคนแรกที่สังเกตเห็น
cat
และเพิ่มไปยังคำอธิบาย
The wrapper 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
มันช่วยให้คุณประหยัดเวลาไม่ต้องคิดค้นล้อใหม่ใน ultra-portable awk เอกสารดังกล่าวข้างต้นและลิงค์โดยตรงที่นี่ (ตรวจสอบที่หน้า GitHub ด้วย )
ด้วย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
ทางออกเดียว - ไม่ใช่หลักฐานอินพุตว่าง!
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))"
หากคุณสนใจในยูทิลิตี้แทนที่จะเป็นเย็นหรือฉลาดแล้วเป็นทางเลือกที่ง่ายกว่าperl
awk
โดยขนาดใหญ่มันจะอยู่ใน * ระวังด้วยพฤติกรรมที่สอดคล้องกันและเป็นเรื่องง่ายและอิสระในการติดตั้งบน 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";
function median()
{
declare -a nums=($(cat))
printf '%s\n' "${nums[@]}" | sort -n | tail -n $((${#nums[@]} / 2 + 1)) | head -n 1
}
sh
) เป็นล่าม นอกจากนี้ยังมีปัญหาในการอ่านข้อมูลลงในอาเรย์จากไฟล์