การวาดฮิสโตแกรมจากเอาต์พุตคำสั่ง bash


31

ฉันมีผลลัพธ์ต่อไปนี้:

2015/1/7    8
2015/1/8    49
2015/1/9    40
2015/1/10   337
2015/1/11   11
2015/1/12   3
2015/1/13   9
2015/1/14   102
2015/1/15   62
2015/1/16   10
2015/1/17   30
2015/1/18   30
2015/1/19   1
2015/1/20   3
2015/1/21   23
2015/1/22   12
2015/1/24   6
2015/1/25   3
2015/1/27   2
2015/1/28   16
2015/1/29   1
2015/2/1    12
2015/2/2    2
2015/2/3    1
2015/2/4    10
2015/2/5    13
2015/2/6    2
2015/2/9    2
2015/2/10   25
2015/2/11   1
2015/2/12   6
2015/2/13   12
2015/2/14   2
2015/2/16   8
2015/2/17   8
2015/2/20   1
2015/2/23   1
2015/2/27   1
2015/3/2    3
2015/3/3    2

และฉันต้องการวาดฮิสโตแกรม

2015/1/7  ===
2015/1/8  ===========
2015/1/9  ==========
2015/1/10 ====================================================================
2015/1/11 ===
2015/1/11 =
...

คุณรู้หรือไม่ว่ามีคำสั่ง bash ที่จะให้ฉันทำอย่างนั้น?


1
bashplotlibเป็นวิธีการแก้ที่ดี
ไมเคิล Mior

นั่นเป็นหนึ่งในความเสี่ยงของการให้ลิงค์แทนคำตอบในตัวเอง หากคำตอบ SO ที่ถูกลบมีประโยชน์โปรดโพสต์เป็นคำตอบที่นี่
Jeff Schaller

คำตอบ:


12

ลองนี้ใน :

perl -lane 'print $F[0], "\t", "=" x ($F[1] / 5)' file

คำอธิบาย:

  • -aชัดเจนsplit()ใน@Fอาร์เรย์เราได้รับค่าด้วย$F[n]
  • x คือการบอก Perl เพื่อพิมพ์ตัวอักษร N ครั้ง
  • ($F[1] / 5) : ที่นี่เราได้ตัวเลขและหารด้วย 5 เพื่อผลลัพธ์งานพิมพ์ที่น่ารัก

1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'มันดูดีจริงๆ :) ขอบคุณ
Natim

12

ในperl:

perl -pe 's/ (\d+)$/"="x$1/e' file
  • eทำให้การแสดงออกที่ได้รับการประเมินดังนั้นฉันได้รับ=ซ้ำโดยใช้ค่าของ$1(จำนวนที่จับคู่ด้วย(\d+))
  • คุณสามารถทำได้"="x($1\/3)แทนที่จะ"="x$1ได้เส้นที่สั้นกว่า (การ/หลบหนีเนื่องจากเราอยู่กลางคำสั่งการแทนที่)

ในbash(ได้รับแรงบันดาลใจจากคำตอบ SO นี้ ):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printfแผ่นสายที่สองโดยใช้ช่องว่างเพื่อให้ได้ความกว้างของ$n ( %${n}s) =และฉันแทนที่ช่องว่างด้วย
  • คอลัมน์จะถูกคั่นด้วยการใช้แท็บ ( \t) column -ts'\t'แต่คุณสามารถทำให้มันสวยโดยท่อไป
  • คุณสามารถใช้$((n/3))แทน${n}เพื่อให้ได้บรรทัดที่สั้นกว่า

รุ่นอื่น:

unset IFS; printf "%s\t%*s\n" $(sed 's/$/ =/' test.txt) | tr ' ' =

ข้อเสียเปรียบเพียงอย่างเดียวที่ฉันเห็นคือคุณจะต้องsedเอาท์พุทของท่อไปยังบางสิ่งหากคุณต้องการลดขนาดลงมิฉะนั้นนี่เป็นตัวเลือกที่สะอาดที่สุด หากมีโอกาสที่จะใส่ไฟล์ของคุณที่มีหนึ่งของ[?*คุณควรจะนำไปสู่คำสั่งปรับ W set -f;/


2
ไชโยสำหรับแสดงวิธีแก้ปัญหาเปลือกด้วย โซลูชัน Perl ของคุณสะอาดมากเช่นกัน
ลูกไก่

@mikeserv ยอดเยี่ยม! ฉันมักจะลืม%*sแม้ว่ามันจะเป็นprintfเคล็ดลับแรกที่ฉันเรียนรู้ในการเขียนโปรแกรม C
muru

printf(sed) | trรุ่นไม่ได้ทำงานที่นี่เท่าที่ผมสามารถบอกได้
Natim

@Natim นี่อยู่ที่ไหน
muru

@ ข้อ จำกัด mikeserv ในความยาวอาร์กิวเมนต์อาจจะ?
muru

6

ง่ายด้วย awk

awk '{$2=sprintf("%-*s", $2, ""); gsub(" ", "=", $2); printf("%-10s%s\n", $1, $2)}' file

2015/1/7 ========
2015/1/8 =================================================
2015/1/9 ========================================
..
..

หรือด้วยภาษาโปรแกรมที่ฉันโปรดปราน

python3 -c 'import sys
for line in sys.stdin:
  data, width = line.split()
  print("{:<10}{:=<{width}}".format(data, "", width=width))' <file

3

เกี่ยวกับ:

#! /bin/bash
histo="======================================================================+"

read datewd value

while [ -n "$datewd" ] ; do
   # Use a default width of 70 for the histogram
   echo -n "$datewd      "
   echo ${histo:0:$value}

   read datewd value
done

ซึ่งผลิต:

~/bash $./histogram.sh < histdata.txt
2015/1/7    ========
2015/1/8    =================================================
2015/1/9    ========================================
2015/1/10   ======================================================================+
2015/1/11   ===========
2015/1/12   ===
2015/1/13   =========
2015/1/14   ======================================================================+
2015/1/15   ==============================================================
2015/1/16   ==========
2015/1/17   ==============================
2015/1/18   ==============================
2015/1/19   =
2015/1/20   ===
2015/1/21   =======================
2015/1/22   ============
2015/1/24   ======
2015/1/25   ===
2015/1/27   ==
2015/1/28   ================
2015/1/29   =
2015/2/1    ============
2015/2/2    ==
2015/2/3    =
2015/2/4    ==========
2015/2/5    =============
2015/2/6    ==
2015/2/9    ==
2015/2/10   =========================
2015/2/11   =
2015/2/12   ======
2015/2/13   ============
2015/2/14   ==
2015/2/16   ========
2015/2/17   ========
2015/2/20   =
2015/2/23   =
2015/2/27   =
2015/3/2    ===
2015/3/3    ==
~/bash $

1

สิ่งนี้ทำให้ฉันเป็นปัญหาเกี่ยวกับบรรทัดคำสั่งแบบดั้งเดิม นี่คือbashโซลูชันสคริปต์ของฉัน:

awk '{if (count[$1]){count[$1] += $2} else {count[$1] = $2}} \
        END{for (year in count) {print year, count[year];}}' data |
sed -e 's/\// /g' | sort -k1,1n -k2,2n -k3,3n |
awk '{printf("%d/%d/%d\t", $1,$2,$3); for (i=0;i<$4;++i) {printf("=")}; printf("\n");}'

สคริปต์เล็กน้อยด้านบนถือว่าข้อมูลอยู่ในไฟล์ชื่อ "data" ในจินตนาการ

ฉันไม่มีความสุขเกินไปกับการ "เรียกใช้ผ่านสายและเรียงลำดับ" - มันจะไม่จำเป็นถ้าเดือนและวันของเดือนมี 2 หลักเสมอ แต่นั่นคือชีวิต

นอกจากนี้ในฐานะที่เป็นบันทึกประวัติศาสตร์ Unixes แบบดั้งเดิมเคยใช้มาพร้อมกับยูทิลิตีการวางแผนบรรทัดคำสั่งที่สามารถสร้างกราฟและแปลง ASCII ที่น่าเกลียดได้ ฉันจำชื่อไม่ได้ แต่ดูเหมือนว่าGNU plotutils จะมาแทนที่ยูทิลิตี้ดั้งเดิม


ไม่ควรที่จะเป็นif ($1 in count) ...อย่างไร
muru

1
@muru - ดูเหมือนจะทำงานอย่างใดอย่างหนึ่ง อย่างไรก็ตามฉันพบข้อผิดพลาดในประโยค "else" ขอบคุณ
Bruce Ediger

1

ออกกำลังกายที่นี่ ฉันทิ้งข้อมูลในไฟล์ชื่อ "data" เพราะฉันจินตนาการมาก

ทีนี้คุณถามมันด้วยการทุบตี ... นี่มันเป็นการทุบตีบริสุทธิ์

cat data | while read date i; do printf "%-10s " $date; for x in $(seq 1 $i); do echo -n "="; done; echo; done

awk เป็นตัวเลือกที่ดีกว่า

awk '{ s=" ";while ($2-->0) s=s"=";printf "%-10s %s\n",$1,s }' data

คุณสามารถไพพ์ข้อมูลผ่าน awk แทนที่จะใช้ไฟล์ได้หรือไม่?
Natim

ใช่มันเป็นวิธีเดียวกัน เพียงเพิ่ม "ข้อมูลแมว |" ที่จุดเริ่มต้นเช่นฉันมีสำหรับบิตทุบตีหรือ "<data" ในตอนท้าย หรือคุณสามารถมีส่วน awk โดยไม่ระบุไฟล์วางข้อมูลและกด ctrl-D ที่ส่วนท้าย การระบุไฟล์จะถือว่าไฟล์นั้นเป็น stdin และฉันไม่ต้องการคัดลอกและวาง datafile ต่อไปเพราะฉันขี้เกียจ
Falsenames

1
จริงๆแล้วฉันเพิ่งอ่านคำถามอีกครั้งในขณะที่เชื่อมโยงสิ่งนี้กับผู้ร่วมงาน ... คุณบอกว่าคุณมี "เอาท์พุท" ไม่ใช่ไฟล์ข้อมูล ดังนั้นคุณสามารถเรียกใช้สิ่งที่กำลังสร้างรายงานนั้นจากนั้นไพพ์ไปที่ awk และเสร็จแล้ว ไพพ์เพียงส่งเอาต์พุตโดยตรงของคำสั่งสุดท้ายเป็นแหล่งอินพุตสำหรับคำสั่งถัดไป
Falsenames

0

ลองสิ่งนี้:

while read value count; do
    printf '%s:\t%s\n' "${value}" "$(printf "%${count}s" | tr ' ' '=')"
done <path/to/my-output

ส่วนที่ยุ่งยากเพียงอย่างเดียวคือโครงสร้างของบาร์ ฉันทำที่นี่โดยมอบหมายprintfและtrชอบคำตอบดังนั้นนี้

เป็นโบนัสมันเป็น POSIX- ที่shสอดคล้อง

อ้างอิง:

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.