Menggambar histogram dari output perintah bash

31

Saya memiliki output sebagai berikut:

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

Dan saya ingin menggambar histogram

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

Apakah Anda tahu jika ada perintah bash yang akan membiarkan saya melakukan itu?

Natim
sumber
1
bashplotlib adalah solusi yang bagus
Michael Mior
Itu memang salah satu risiko menyediakan tautan, bukan jawaban yang lengkap. Jika jawaban SO yang dihapus berguna, kirimkan sebagai jawaban di sini.
Jeff Schaller

Jawaban:

12

Coba ini di :

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

PENJELASAN:

  • -aadalah eksplisit split()dalam @Farray, kita mendapatkan nilai dengan$F[n]
  • x adalah untuk memberitahu perl untuk mencetak karakter N kali
  • ($F[1] / 5) : di sini kita mendapatkan nomor dan membaginya dengan 5 untuk hasil cetak cantik
Gilles Quenot
sumber
1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'Ini terlihat sangat bagus :) terima kasih
Natim
12

Di perl:

perl -pe 's/ (\d+)$/"="x$1/e' file
  • emenyebabkan ekspresi dievaluasi, jadi saya =diulang menggunakan nilai $1(angka yang cocok dengan (\d+)).
  • Anda bisa melakukannya "="x($1\/3)daripada "="x$1mendapatkan garis yang lebih pendek. (Itu /lolos karena kita berada di tengah-tengah perintah substitusi.)

In bash(terinspirasi dari jawaban SO ini ):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printfbantalan string kedua menggunakan spasi untuk mendapatkan lebar $n ( %${n}s), dan saya mengganti spasi dengan =.
  • Kolom dibatasi menggunakan tab ( \t), tetapi Anda dapat membuatnya lebih cantik dengan mem-piping ke column -ts'\t'.
  • Anda bisa menggunakan $((n/3))alih-alih ${n}mendapatkan garis yang lebih pendek.

Versi lain:

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

Satu-satunya kekurangan yang bisa saya lihat adalah bahwa Anda harus menyalurkan sedoutput ke sesuatu jika Anda ingin mengurangi, jika tidak, ini adalah pilihan terbersih. Jika ada kemungkinan file input Anda berisi salah satu dari [?*Anda harus memimpin perintah w / set -f;.

muru
sumber
2
Bravo untuk menunjukkan solusi shell juga. Solusi Perl Anda juga sangat bersih.
anak ayam
@ mikeserv Hebat! Saya selalu lupa %*smeskipun itu adalah printftrik terkait pertama yang saya pelajari dalam pemrograman C.
muru
The printf(sed) | trVersi tidak bekerja di sini sejauh yang saya tahu.
Natim
@Natim ada di mana?
muru
@mikeserv mungkin membatasi panjang argumen?
muru
6

Mudah dengan awk

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

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

Atau dengan bahasa pemrograman favorit saya

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

Bagaimana tentang:

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

Yang menghasilkan:

~/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 $
Robert Nix
sumber
1

Ini menurut saya sebagai masalah garis perintah tradisional yang menyenangkan. Inilah bashsolusi skrip saya :

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");}'

Skrip kecil di atas menganggap data ada dalam file yang secara imajinatif dinamai "data".

Saya tidak terlalu senang dengan garis "jalankan melalui sed dan sortir" - itu tidak perlu jika bulan dan hari-bulan Anda selalu memiliki 2 digit, tapi itulah kehidupan.

Juga, sebagai catatan sejarah, Unix tradisional biasanya datang dengan utilitas plot perintah yang bisa membuat grafik dan plot ASCII yang jelek. Saya tidak ingat namanya, tetapi sepertinya GNU plotutils menggantikan utilitas tradisional yang lama.

Bruce Ediger
sumber
Bukankah seharusnya begitu if ($1 in count) ...?
muru
1
@uru - tampaknya bekerja dengan baik. Namun, saya menemukan kesalahan ketik pada klausa "lain". Terima kasih.
Bruce Ediger
1

Latihan yang bagus di sini. Saya membuang data dalam file yang disebut "data" karena saya sangat imajinatif.

Nah, Anda memintanya dalam bash ... ini murni bash.

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

awk adalah pilihan yang lebih baik.

awk '{ s=" ";while ($2-->0) s=s"=";printf "%-10s %s\n",$1,s }' data
Nama palsu
sumber
Bisakah Anda menyalurkan data melalui awk alih-alih menggunakan file?
Natim
Ya, sama saja. Cukup tambahkan "data kucing |" di awal seperti yang saya miliki untuk bit bash, atau "data" di akhir. Atau Anda bahkan dapat memiliki bagian awk tanpa file yang ditentukan, menempelkan data dan tekan ctrl-D di akhir. Menentukan file hanya memperlakukan file itu sebagai stdin, dan saya tidak ingin terus menyalin dan menempel datafile karena saya malas.
Nama samaran
1
Sebenarnya, saya hanya membaca ulang pertanyaan sambil menautkan ini ke rekan kerja ... Anda mengatakan Anda memiliki "output", bukan file data. Jadi, Anda bisa menjalankan apa pun yang membuat laporan itu, lalu mengirimnya ke awk, dan Anda selesai. Pipa hanya mengarahkan output dari perintah terakhir sebagai sumber input untuk perintah berikutnya.
Nama samaran
0

Coba ini:

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

Satu-satunya bagian yang sulit adalah pembangunan bar. Saya melakukannya di sini dengan mendelegasikan ke printfdan trmenyukai jawaban SO ini .

Sebagai bonus, POSIX- sh-compliant.

Referensi:

rubik
sumber