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は素晴らしいソリューションです
Michael

それは確かに、自己完結型の回答ではなくリンクを提供するリスクの1つです。削除されたSO回答が役立つ場合、回答としてここに投稿してください。
ジェフシャラー

回答:


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)'それは本当に素晴らしく見える:)おかげで
ナティム

12

perl

perl -pe 's/ (\d+)$/"="x$1/e' file
  • e式が評価されるため、(で一致した数)の=値を使用して繰り返さ$1(\d+)ます。
  • "="x($1\/3)代わりに"="x$1短い行を取得することもできます。(/置換コマンドの途中なので、エスケープされます。)

In bashこのSOの回答からヒントを得た):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printfスペースを使用して2番目の文字列をパディングして$n%${n}s)の幅を取得し、スペースをに置き換えます=
  • 列はタブ(\t)を使用して区切られていますが、にパイピングすることで列をきれいにすることができますcolumn -ts'\t'
  • $((n/3))代わりに${n}を使用して、短い行を取得できます。

別のバージョン:

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

私が見ることができる唯一の欠点は、sed縮小したい場合に何かの出力をパイプする必要があるということです。そうでなければ、これは最もクリーンなオプションです。入力ファイルの1つが含まれている可能性がある場合は[?*、コマンドw /をリードする必要がありますset -f;


2
シェルソリューションも示してくれたBravo。Perlソリューションも非常にきれいです。

@mikeserv素晴らしい!Cプログラミングで学んだ%*s最初のprintf関連するトリックであったにもかかわらず、私はいつも忘れています。
ムル

printf(sed) | tr私が知る限り、このバージョンはここでは機能しません。
ナティム

@Natimここはどこですか?
ムル

おそらく引数の長さの@mikeserv制限?
ムル

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」という名前のファイルにあると想定しています。

私は「sed and sortを実行する」行にあまり満足していません-あなたの月と日が常に2桁であれば、それは不要でしょうが、それは人生です。

また、歴史的なメモとして、従来のUnixにはコマンドラインプロットユーティリティが付属しており、かなりutilityいASCIIグラフとプロットを実行できました。名前は思い出せませんが、GNU plotutilsが古い伝統的なユーティリティを置き換えているようです。


そうじゃないif ($1 in count) ...
ムル

1
@muru-どちらの方法でも動作するようです。ただし、「else」句にタイプミスが見つかりました。ありがとう。
ブルースエディガー

1

ここでいい運動を。私は非常に想像力に富んでいるため、「data」というファイルにデータをダンプしました。

まあ、あなたはそれをbashで要求しました...ここでは純粋なbashです。

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を介してデータをパイプできますか?
ナティム

はい、どちらの場合も同じです。「猫データ|」を追加するだけです 最初はbashビットの場合と同じように、最後に「<data」がありました。または、ファイルを指定せずにawk部分だけを持ち、データを貼り付けて、最後にctrl-Dを押すこともできます。ファイルを指定すると、そのファイルは標準入力として扱われますが、怠けているので、データファイルのコピーと貼り付けを続けたくありませんでした。
偽名

1
実際、これを同僚にリンクしながら質問を読み直しただけです。データファイルではなく「出力」があったと言いました。したがって、そのレポートを作成しているものは何でも実行し、それをawkにパイプするだけで完了です。パイプは、最後のコマンドの出力を次のコマンドの入力ソースとして直接指示します。
偽名

0

これを試して:

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

唯一のトリッキーな部分は、バーの構造です。私はに委譲することで、ここでそれを行うprintftr同様に、このSOの答え

おまけとして、POSIX sh準拠です。

参照:

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.