第11回:データの集計とグラフ描画¶
前回は、データをファイルに保存・読み込みできるようになった。今回はそれを一歩進めて、ファイルのデータを読んで集計し、グラフにする。
数字の羅列は読めない¶
たとえばサークル新歓の参加者数が、こう記録されているとする。
どのサークルが一番人気か、パッと分かるだろうか? 数字を眺めても分からない。でも棒グラフにすれば一目で分かる。
レポートのアンケート結果、卒論の実験データ、YouTube の再生数分析……「集計してグラフ」は今後ずっと使う。
外部ライブラリと pip¶
Python には標準で入っていない便利な道具が、ライブラリとして公開されている。グラフ描画には matplotlib(マットプロットリブ) を使う。
演習室にはインストール済み。自分の PC に入れるときは、コマンドプロンプトで次を実行する。
プログラムの先頭で次のように書くと、以降 plt.〇〇 で機能を呼び出せる(as plt は「短い別名」をつける書き方)。
まずは棒グラフを出してみる+日本語対策¶
plt.bar(ラベルのリスト, 値のリスト)で棒グラフを作り、plt.show()で表示する- 日本語が
□□□(豆腐)になるのを防ぐため、描画より前にフォントを設定する
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Yu Gothic' # ← 描画より前に書く(日本語対策)
plt.bar(['月', '火', '水'], [3, 7, 2])
plt.show()
プレ演習11-1¶
次のコードをそのまま打って実行し、棒グラフが表示されることを確認しよう。さらに、棒の高さ [3, 7, 2] を好きな数字に変えて、グラフが変わることも見てみよう。
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Yu Gothic'
plt.bar(['月', '火', '水'], [3, 7, 2])
plt.show()
ファイルのデータを集計する¶
実際のデータはファイルにあり、同じ項目が何度も登場する(サークル新歓なら、同じサークルが何日も出てくる)。やりたいのは「サークルごとに参加者数を合計する」こと。
そこで使うのが辞書(dictionary)。{ キー: 値 } で「名前 → 合計」を覚えておく。
集計の型(辞書で足していく)¶
「初めて見る名前なら新規登録、すでにあるなら足す」を if name in d: で振り分ける。
d[name] += 数は「いまの値に足す」d[name] = 数は「新規にセット」
d = {}
for line in f:
name, num = line.split(',')
if name in d:
d[name] += int(num) # すでにあれば足す
else:
d[name] = int(num) # 初めてなら新規登録
1 行に項目が 2 つ(名前,個数)なら、name, num = line.split(',') のようにまとめて受け取れる。
多い順に並べる(sorted)¶
辞書のままでは順番がバラバラ。値の大きい順に並べたい。
d.items()で(キー, 値)のペアを取り出し、sortedで並べ替えるkey=lambda x: x[1]は「各ペアの 2 番目(値)を基準に並べる」という指定(第5回のラムダ式)reverse=Trueを付けると大きい順(降順)になる
ranking = sorted(d.items(), key=lambda x: x[1], reverse=True)
for name, total in ranking:
print(name, total)
x[1] を x[0] にすると「名前順」、reverse=True を外すと「小さい順」になる。
プレ演習11-2¶
下のプログラムを実行する前に、画面に何が出るかを紙やメモ帳に予測して書こう。そのあと実行して、予測と合っているか確認しよう。
kudamono.csv は上のリンクからダウンロードし、プログラムと同じフォルダに置く(中身は次の通り)。
d = {}
with open('kudamono.csv', 'r', encoding='UTF-8') as f:
for line in f:
name, num = line.split(',')
if name in d:
d[name] += int(num)
else:
d[name] = int(num)
ranking = sorted(d.items(), key=lambda x: x[1], reverse=True)
for item in ranking:
print(item)
ポイント
- 同じ果物が 2 回ずつ出てくる。
+=で合計される(頭の中で足し算してみよう)。 - ファイルには合計は書かれていない。プログラムが集計してから並べ替えている。
- 並べ替え後の順は、ファイルの登場順とは違う(値の大きい順になる)。
print(item)はペアをそのまま出すので、('名前', 合計)の形で表示される。
プレ演習11-3¶
下は kudamono.csv を読んで「種類ごとの合計を多い順に表示」したいが、3 か所まちがいがある。それぞれ見つけて、正しく動くように直そう(kudamono.csv は 11-2 と同じ)。
d = {}
with open('kudamono.csv', 'r', encoding='UTF-8') as f:
for line in f:
name, num = line.split(',')
if name in d:
d[name] = int(num)
else:
d[name] = int(num)
ranking = sorted(d.items(), key=lambda x: x[0])
for name, total in ranking:
print(name, total)
ヒント
- 1 か所目:
if name in d:(すでにある)のときの処理を見比べよう。足したいのに、elseと同じ「新規にセット」になっていないか? 本文「集計の型」の+=を見直す。 - 2 か所目:
key=lambda x: x[0]は何順になる? 値で並べるにはx[0]を何にする? - 3 か所目:このままだと小さい順になる。大きい順にするオプションが抜けていないか?
もっと知りたい人へ(発展)¶
- 折れ線グラフ:
plt.plot(値のリスト)(時間変化の表示に向く。授業内演習で使う) - 散布図:
plt.scatter(xのリスト, yのリスト)(2 つの量の関係を見る) - 画像として保存:
plt.savefig('graph.png')(plt.show()の代わりに使うとファイルに保存される) - 見た目を整える:
plt.xlabel(...)/plt.legend()(凡例)/plt.axhline(y=平均, color='red', linestyle='--')(平均の横線。応用課題で使う)
授業内演習¶
1 〜 100 の整数の乱数を 50 個つくり、それらを折れ線グラフで表示するプログラムを作成しなさい。
実行例(イメージ):
ヒント
- 乱数は
randomモジュールを使う。import randomしてからrandom.randint(1, 100)で1〜100の整数が 1 つ得られる。 forループで 50 回くり返し、リストにappendで貯める。- 折れ線は
plt.plot(リスト)→plt.show()。
基本課題¶
キーボードからファイル名を入力し、そのファイル(駅名,今年度乗車人数,前年度乗車人数)を読み込んで、合計人数が一番多い駅名を表示し、さらに各駅の合計人数の棒グラフを多い順に表示するプログラムを作成しなさい。
data_tokyu.txt は上のリンクからダウンロードし、プログラムと同じフォルダに置くこと。
実行例:
(さらに、各駅の合計人数を多い順に並べた棒グラフが表示される)
約束ごと:
- ファイル名はキーボードから入力すること
- 一番多い駅の表示と、棒グラフの両方を出すこと
- 棒グラフは多い順に並べること
データ出典
data_tokyu.txt は 2023 年度の東横線のデータから一部抜粋(東急 乗降人員データ)。
応用課題¶
基本課題と同じファイル(駅名,今年度乗車人数,前年度乗車人数)を読み込み、各駅の増加率を棒グラフで表示しなさい。増加率は次で計算する。
約束ごと:
- x 軸は今年度乗車人数の多い順に並べること
- 平均増加率を赤い破線で重ねること(発展の
plt.axhlineが使える) - 増加率がマイナスの駅は、棒が下向きになる(そのままで OK)
実行例: