Help us understand the problem. What is going on with this article?

ヒートマップの3軸目(色)をカスタムする方法(N番煎じ)

本記事はMicroAd Advent Calendar 2019の14日目の記事です。

はじめに

データの傾向を複数の観点から可視化するうえで、ヒートマップって便利ですよね。
matplotlibにもデータを流し込んだら、よしなにヒートマップを生成してくれるpcolorメソッドがあります。これはこれで、パパッと可視化したい時には便利なのですが、データの集計から可視化までがセットになってしまっているので、3軸目(色)に頻度以外の値や、自ら集計した値を利用したいという場合には利用できません。
こんな時、matplotlibのimshowを利用すると、下記のような3軸目(色)をカスタムしたヒートマップがかけてちょっと便利です。(下記はレンジごとの頻度ではなく、和を3軸目(色)にとった例です。)

image.png

実装

ここでは3つの連続変数のうち2つの連続変数をビンで区切り、各ビンごとに残った連続変数の統計量をヒートマップで表現する例を示します。
下記順序で説明します。
1. データセットの生成
2. データセットのビン切り
3. ヒートマップの生成

1.データセットの生成

まず、3連続変数のデータセットを正規分布からサンプリングして作ります。単峰分布だと可視化した時に映えなそうだったので、2峰分布にしてみました。2峰分布の母数はそれぞれ(平均、標準偏差)=(50, 10),(100, 10)とし、サンプル数を200,000件としています。

import pandas as pd
import numpy as np

def create_2peak_distribution(mean1, std1, mean2, std2, size):
    dist_1 = np.random.normal(mean1, std1, size)
    dist_2 = np.random.normal(mean2, std2, size)
    dist = np.concatenate([dist_1, dist_2]).reshape(size * 2, 1)
    return dist

#母数などの設定値
mean1 = 50
mean2 = 100
std1 = 10
std2 = 10
size = 100000

#各連続変数のデータ生成
cont_var1 = create_2peak_distribution(mean1, std1, mean2, std2, size)
cont_var2 = create_2peak_distribution(mean1, std1, mean2, std2, size)
cont_var3 = create_2peak_distribution(mean1, std1, mean2, std2, size)
#連続変数を1つのデータフレームにまとめる
arr = np.concatenate([cont_var1, cont_var2, cont_var3], axis=1)
df = pd.DataFrame(dat, columns=['cont_var1', 'cont_var2', 'cont_var3'], dtype=int)

2.データセットのビン切りとデータの集計

次に、1で生成したデータセットのうち2つの連続変数をビンに分割し、データを各ビンの粒度で集計します。今回の集計は凝ったことはせずに、和をとるだけにしていますが、いろいろできるはずです。興味のある方はぜひ試してみてください。
なお、ビンの数はスタージェスの公式で算出した値を参考にしています。

import math

def get_bin_count(size):
    return round(1 + math.log2(size))

def create_bins(df, col, bin_count):
    bin_width = df[col].max() // bin_count
    df[col] = (df[col] // bin_width) * bin_width
    return df

# 連続変数のビン分割
bin_count = get_bin_count(df.shape[0])
df = create_bins(df, 'cont_var1', bin_count)
df = create_bins(df, 'cont_var2', bin_count)
# ビンごとに残った連続変数の集計
heatmap_input = pd.pivot_table(df, index='cont_var1', columns='cont_var2', values='cont_var3', aggfunc=np.sum).fillna(0)

3.ヒートマップの生成

最後に、2の集計結果をimshowを使ってプロットします。
プロットする際には下記に注意すると混乱が少ないと思います。

  • imshowメソッドを呼ぶ際にはextent引数を設定すること
    これを設定しないと各軸のラベルにテーブルのインデックス番号とカラム番号が利用されてしまいます
  • invert_yaxis()を呼ぶこと
    デフォルトの原点はグラフの左上に設定されているため、これを設定しないとy軸に関して反転したグラフが描画されてしまいます
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)
hmap = ax.imshow(hmap_input,
                 extent=[hmap_input.columns.min(),
                         hmap_input.columns.max(),
                         hmap_input.index.max(),
                         hmap_input.index.min()],
                 cmap='viridis')
ax.invert_yaxis()
ax.set_title('SUM')
ax.set_xlabel('cont_var1')
ax.set_ylabel('cont_var2')
fig.colorbar(hmap, ax=ax)
plt.show()

これを実行すると冒頭で示したヒートマップが描画されます。

image.png

おわりに

今回は簡単に和を3軸目(色)にプロットしましたが、集計方法を変更することでいろいろ試せると思います。参考にしていただけると幸いです。

イメージのプロットによく利用するimshowメソッドを流用したこの手法を思いついた時は、「お、自分やるじゃん」と思ってしまいましたが、割とメジャーな方法だったということをあとで調べて知りました。前もって調べるの大事ですね。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした