表形式のデータを扱っている時に、「列Aのデータと別Bのデータに◯◯処理した値を使ってグラフ作りたい」 みたいなシチュエーションが、稀によくあります。そういう時、PandasDataFrameを使うと表形式の データを柔軟に扱えて何かと便利です。

この記事では、DataFrameを使った列同士の演算処理など、毎回「どうやるんだっけ?💦」ってなりがちな内容を メモして、必要な時にすぐ参照できるようにしたいと思います。

📄 サンプルデータを準備

何かサンプルデータがあった方が処理内容を理解しやすそうなので、気象庁のHPから気象データをダウンロードして 使おうと思います。CSV形式でダウンロードします。

今回の例では「項目を選ぶ」のページで、以下の項目を選択しました。

  • 気温 - 日平均気温
  • 湿度 - 日平均相対湿度

地域は「大阪府 - 大阪市」と「静岡県 - 静岡市」を、それぞれ別々のCSVとしてダウンロードしました。

📖 データを読み込んでDataFrameを作成

データの前処理

では読み込んで行きましょう・・と言いたいところですが、ダウンロードしたCSVファイルは純粋な表形式ではなく、 そのままでは意図したように処理できません。 下図のように「メタ情報が冒頭に書かれている」「列ヘッダが複数行に分かれて記述されている」といった問題があります。

気象庁のCSVサンプル

不要な行を削除しつつ、「品質情報」や「均質情報」の列は今回の例では使わないので削除して整えます。 その際、VSCodeならEdit CSVを使うとExcelNumbersライクな表形式画面で視覚的に行と列の操作ができて安心です。

気象庁のCSVを調整したの

pandasで読み込み

文字コードがShift-JISなCSVファイルなので、encodingパラメータも指定しながら読み込みます。

import pandas as pd

osaka_df = pd.read_csv('osaka.csv', encoding='shift_jis')
osaka_df.head()

📊 データの活用

特定列の値を処理して別の新しい列を作る

温度列の値は単位が摂氏のデータなので、これに演算処理を施して華氏の温度列を作ります。なお、round関数を使って少数点以下が一桁の数値にしています。

華氏 = (摂氏x9÷5) + 32

osaka_df['平均気温(°F)'] = round( (osaka_df['平均気温(℃)']*9/5) + 32, 1)

複数列の値を使って別の新しい列を作る

温度列と湿度列の値を使って不快指数列を作りたいと思います。計算式は wiki 参照。

osaka_df['不快指数'] = round( (0.81*osaka_df['平均気温(℃)'] + 0.01*osaka_df['平均湿度(%)'] * ( 0.99*osaka_df['平均気温(℃)'] - 14.3) ) + 46.3, 1)

複数の表をマージする

「大阪府-大阪市」のデータと「静岡県-静岡市」の表データを結合して一つのデータにします。(この後のピボットテーブルの例のための伏線です。)

from datetime import datetime

# 「静岡県-静岡市」のデータにも、ここまでの例と同様の処理を行う。
shizuoka_df = pd.read_csv('shizuoka.csv', encoding='shift_jis')
shizuoka_df.insert(1, '地域', '静岡県-静岡市')
shizuoka_df['平均気温(°F)'] = round( (shizuoka_df['平均気温(℃)'] * 9 / 5) + 32, 1)
shizuoka_df['不快指数'] = round( (0.81*shizuoka_df['平均気温(℃)'] + 0.01*shizuoka_df['平均湿度(%)'] * ( 0.99*shizuoka_df['平均気温(℃)'] - 14.3) ) + 46.3, 1)

cat_df = pd.concat( [osaka_df, shizuoka_df])

# Seriesの各値に複雑な処理をしたい場合は、map関数を使うとうまくいく。
cat_df['年月日'] = cat_df['年月日'].map(lambda x: datetime.strptime(x, "%Y/%m/%d"))

ピボットテーブルを使って特定の値に着目した表にする

「不快指数」に着目して、地域と年月日で整理した表を作ります。

pd.pivot_table(cat_df, values="不快指数", index=["年月日"], columns=["地域"])

下図のような表が得られる。

ピボットテーブル

seabornを使ってグラフにする

不快指数の推移を地域ごとにグラフ化してみます。きれいにしたいので seaborn を使います。

import seaborn as sns
from matplotlib import rcParams
import matplotlib.dates as mdates

sns.set_theme(style="whitegrid")
# 凡例などの日本語が表示されるように、日本語フォントを指定しましょう。
rcParams['font.family'] = 'HackGen35Nerd'

ax = sns.lineplot(
    data=cat_df,
    x="年月日", y="不快指数", hue="地域"
)

ax.set_title("2025年夏の大阪市と静岡市における不快指数の推移")
ax.xaxis.label.set_text('日付')

# 日付情報は適当に間引かないと潰れて見えなくなるので、X軸のメモリ表示を調整する。
locator = mdates.AutoDateLocator(minticks=5, maxticks=7)
ax.xaxis.set_major_locator(mdates.DayLocator(bymonthday=None, interval=7, tz=None))

# 2025年のデータであることをタイトルなどで示して「◯月□日」という日付にする。
# ゼロ埋めじゃない数字にするため書式をいじってる。Windowsではハイフンをシャープにするといいらしい。
formatter = mdates.DateFormatter("%-m月%-d日")
ax.xaxis.set_major_formatter(formatter)

sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))

なお、グラフを日本語化する方法として「japanize-matplotlibをインポートしよう」という記事が多いのですが、私の環境ではうまく動作せず、試行錯誤の末に「seabornのテーマ適用後に日本語対応フォントを指定する」という方法を採用しました。ここでは、超お気に入りで数年使い続けているHackGenフォントのNerd版を指定しました👍️

このグラフではX軸にdatetime型のデータを指定したのですが、適度に間引かないと文字が重なった読めなくなってしまってました。そこで、Locatorの仕組みを使って「7日間ごとにX軸の目盛りの数値を描画する」というように調整しています。また、Formatterの仕組みで「◯月□日」というフォーマットに変更しています。このあたりの仕組みは、以下の記事を参考にしました。

出力結果のグラフは下図のようになります。

グラフ

🎁 おまけ

この記事で実践した内容を記したJupiter NotebookGistに貼り付けましたので、手元で確認するなど活用ください。