【小ネタ】uniqコマンドによる集計(カウント)結果を入力にグラフを描画するPythonスクリプト

【小ネタ】uniqコマンドによる集計(カウント)結果を入力にグラフを描画するPythonスクリプト

Clock Icon2024.10.04

uniq --count ( uniq -c ) コマンドの結果を標準入力にして、 それをグラフに描画するコマンドを作りたいです。 以下のようなイメージです。

sc_2024-10-04_15-05-12_29584

作ったもの

matplotlib を使って以下スクリプトを書きました。

bar_chart.py
import sys
import matplotlib.pyplot as plt

def read_data():
    """
    標準入力からデータを読み込み、(項目, 数値)のリストとして返す
    """
    data = []
    for line in sys.stdin:
        count, item = line.strip().split(None, 1)
        data.append((item, int(count)))
    return data

def create_horizontal_bar_chart(data):
    """
    データを入力に棒グラフ(横向き)を作成し表示する
    """
    items, counts = zip(*data)
    fig, ax = plt.subplots()

    ax.barh(items, counts) # 横向きの棒グラフを作成
    ax.invert_yaxis() # 上から順番に表示させる
    plt.tight_layout() # 余白調整
    plt.show()

if __name__ == "__main__":
    data = read_data()
    create_horizontal_bar_chart(data)

標準入力に uniq -c コマンドの結果を指定します。 その結果を基に棒グラフ(横向き)を作成し表示するスクリプトです。

実行には matplotlib が必要です。 無い場合は事前に pip install matplotlib を実行してください。

使ってみる

例1: サンプルファイル

冒頭の fruits.txt の例です。 テキストファイル内にあるフルーツをカウントして、棒グラフにします。

$ sort fruits.txt | uniq -c | sort -nr
   8 apple
   6 orange
   5 banana
   2 strawberry
   2 grape

$ sort fruits.txt | uniq -c | sort -nr | python3 bar_chart.py
# ↓以下画像↓

sc_2024-10-04_13-37-08_5637
bar_chart.py によるグラフ出力

例2: CloudWatch 名前空間ごとのメトリクス数

すべてのCloudWatchメトリクスについて、 「名前空間ごとにどれだけメトリクスがあるのか」を調べるには 以下コマンドを実行すれば分かります。

aws cloudwatch list-metrics --query "Metrics[].[Namespace]" --output text \
| sort | uniq -c | sort -nr
# 434 AWS/Usage
# 349 AWS/Config
#  42 AWS/Lambda
#  18 AWS/Events
#  16 AWS/S3
#  12 AWS/DynamoDB
#  11 AWS/Logs
#   8 AWS/States
#   8 AWS/SNS
#   8 AWS/Location
#   6 AWS/Rekognition
#   3 AWS/Glue
#   2 AWS/Scheduler
#   2 AWS/Firehose

これの結果をそのまま bar_chart.py で表示させてみます。

aws cloudwatch list-metrics --query "Metrics[].[Namespace]" --output text \
| sort | uniq -c | sort -nr \
| python3 bar_chart.py
# ↓以下画像↓

sc_2024-10-04_13-44-21_12630
bar_chart.py によるグラフ出力

[補足] 見栄えを良くする

以下色分けやカウント表示の拡張をいれたスクリプトも作成しました。 (生成AIの力を借りました。便利…!)

import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

def read_data():
    data = []
    for line in sys.stdin:
        count, item = line.strip().split(None, 1)
        data.append((item, int(count)))
    return data

def create_horizontal_bar_chart(data):
    items, counts = zip(*data)

    # カラーマップを作成(項目数に応じて色を生成)
    colors = plt.cm.Set3(np.linspace(0, 1, len(items)))

    fig, ax = plt.subplots(figsize=(12, 6))
    bars = ax.barh(range(len(items)), counts, color=colors)

    ax.set_xlabel('Count')
    ax.set_title('Item Distribution')
    ax.set_yticks(range(len(items)))
    ax.set_yticklabels(items)

    # バーの右側に数値を表示
    for i, (item, count) in enumerate(data):
        ax.text(count, i, f' {count}', va='center')

    # 凡例を追加
    for bar, item in zip(bars, items):
        bar.set_label(item)
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

    plt.gca().invert_yaxis()  # y軸を反転して、入力順を維持
    plt.tight_layout()
    plt.show()

if __name__ == "__main__":
    data = read_data()
    create_horizontal_bar_chart(data)

先ほど作成したものと使い方は同じですが、追加で numpy パッケージが必要です。

以下出力サンプルです。

sc_2024-10-04_13-45-58_27114

おわりに

uniq出力の可視化スクリプトでした。

uniqコマンドでサクッと集計した結果を、 サクッと可視化したいときに役に立つと思います。

以上、参考になれば幸いです。

参考

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.barh.html

https://matplotlib.org/stable/gallery/lines_bars_and_markers/barh.html#sphx-glr-gallery-lines-bars-and-markers-barh-py

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.