Bir bash komut çıkışından histogram çizme


31

Aşağıdaki çıktı var:

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

Ve histogram çizmek istiyorum

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

Bunu yapmama izin verecek bir bash komutu olup olmadığını biliyor musunuz?


1
bashplotlib güzel bir çözümdür
Michael Mior

Bu gerçekten bağımsız cevaplar yerine bağlantılar sağlama risklerinden biridir. Eğer silinen SO cevap yararlıdır, burada bir cevap olarak gönderin.
Jeff Schaller

Yanıtlar:


12

Bunu deneyin :

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

AÇIKLAMALAR:

  • -aAçık bir olduğu split()yer @F, biz değerleri ile dizi olsun$F[n]
  • x perl'e N defa bir karakter yazdırmasını söyler.
  • ($F[1] / 5) : burada sayıyı alıyoruz ve güzel bir baskı çıktısı için 5'e bölüyoruz

1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'Gerçekten harika görünüyor :) teşekkürler
Natim

12

İçinde perl:

perl -pe 's/ (\d+)$/"="x$1/e' file
  • eifadenin değerlendirilmesine neden olur, bu yüzden (ile eşleşen sayı ) =değerini kullanarak tekrarlanırım .$1(\d+)
  • Kısa çizgiler almak "="x($1\/3)yerine bunu yapabilirsin "="x$1. ( /İkame komutunun ortasında bulunduğumuzdan beri kaçtı.)

İçinde bash( bu SO cevabından ilham alındı ):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printf$n ( %${n}s) genişliğini elde etmek için boşlukları kullanarak ikinci dizgiyi doldurur ve boşlukları ile değiştiririm =.
  • Sütunlar bir tab ( \t) kullanılarak sınırlandırılır , ancak bunu borulayarak daha da güzelleştirebilirsiniz column -ts'\t'.
  • Daha kısa çizgiler elde etmek $((n/3))yerine kullanabilirsiniz ${n}.

Başka bir sürüm:

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

Görebildiğim tek dezavantajı sed, ölçeklendirmek istediğinizde bir boruya verilmiş olan boruyu ihtiyacınız olması gerektiği , aksi takdirde bu en temiz seçenektir. Eğer bir tanesini içeren giriş dosyanızın bir şansı varsa [?*w / komutunu yönlendirmelisiniz set -f;.


2
Çok bir kabuk çözümü göstermek için Bravo. Perl çözümünüz de çok temiz.
piliçler

@mikeserv Harika! C programlamada öğrendiğim %*silk printfilgili numara olmasına rağmen her zaman unutuyorum .
muru

printf(sed) | trVersiyon değil bildiğim kadarıyla söyleyebilirim burada çalışıyor.
Natim

@Natim burada nerede?
muru

Belki tartışma uzunluğunda @mikeserv sınırlamaları?
muru

6

İle kolay awk

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

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

Veya en sevdiğim programlama diliyle

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

3

Peki ya:

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

Hangi üretir:

~/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

Bu beni eğlenceli, geleneksel bir komut satırı sorunu olarak etkiledi. İşte benim bashscript çözümü:

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

Yukarıdaki küçük betiğin, verinin yaratıcı bir şekilde "data" adında bir dosyada olduğunu varsayar.

"Baştan aşağıya diz ve sırala" dizisinden çok memnun değilim - ayınız ve ayın gününüzde her zaman 2 rakam olsaydı gereksiz olurdu, ama bu hayat.

Ayrıca, tarihsel bir not olarak, geleneksel Unix'ler oldukça çirkin ASCII grafikleri ve çizimleri yapabilecek bir komut satırı çizim aracıyla gelirdi. Adı hatırlayamıyorum, ancak eski geleneksel yardımcı programın yerine GNU plotutils gibi görünüyor .


Olmamalı mıydı if ($1 in count) ...?
muru

1
@muru - iki şekilde de çalışıyor gibi görünüyor. Ancak "else" cümlesinde bir yazım hatası buldum. Teşekkürler.
Bruce Ediger,

1

Burada güzel egzersiz. Verileri "data" adlı bir dosyaya attım, çünkü çok yaratıcıyım.

Eh, bash olarak istedin ... işte saf bash.

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

awk daha iyi bir seçenektir.

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

Bir dosya kullanmak yerine verileri awk aracılığıyla bağlayabilir misiniz?
Natim

Evet, her iki şekilde de aynı şey. Sadece bir "kedi verisi |" ekleyin başlangıçta bash bitleri için olduğu gibi, sonunda bir "<veri". Ya da bir dosya belirtilmeden awk kısmını bile alabilirsiniz, verileri yapıştırın ve sonunda ctrl-D tuşlarına basın. Dosyayı belirtmek sadece o dosyayı stdin olarak kabul eder ve tembel olduğum için veri dosyasını kopyalayıp yapıştırarak tutmak istemedim.
Falsenames,

1
Aslında, bunu bir iş arkadaşına bağlarken soruyu yeniden okudum ... bir veri dosyası değil, "çıktı" olduğunu söyledin. Böylece, o raporu yaratan her neyse onu çalıştırabilir, sonra onu awk'a aktarabilirsiniz. Borular sadece bir sonraki komutun girdi kaynağı olarak son komutun çıktısını doğrudan yönlendirir.
Falsenames,

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.