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?


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

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


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

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 $

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.


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

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:

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.