Pythonでバブルチャートを書いてみる

# はじめに
前回は仮のデータでバブルチャートを書きましたので、今回は実際の特許データを使用してバブルチャートを作りたいと思います。分析に使用する特許データは、前々回クロス集計に使用したデータと同じです。


# プログラム
## データの前処理
データの前処理部分については、前々回のクロス集計と同じです。処理としましては、特許データから、出願人とＦＩのtop10のデータフレームを作成しています。

しかし、自分で書いたコードではあるものの、時間がたちますと、自分でもどういう内容か忘れています。やはり、コード中にこまめに説明を入れておくことが初心者には重要と思いました。

```Python
! pip install japanize-matplotlib
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
import japanize_matplotlib
# googleドライブの利用
from google.colab import drive
drive.mount('/content/drive')
# データの前処理
# データの読み込み(google ドライブから)
df_drone_data_1 = pd.read_csv("/content/drive/MyDrive/pat_data_drone.20181231.csv", encoding = "shift-jis")
df_drone_data_2 = pd.read_csv("/content/drive/MyDrive/pat_data_drone.20190101.csv", encoding = "shift-jis")
# データフレームの結合（縦方向）
df_drone_data = pd.concat([df_drone_data_1, df_drone_data_2], ignore_index = True)
#必要な列のみ抽出
df_drone_data = df_drone_data[['出願人/権利者', 'FI']]
#不要語の削除（出願人/権利者）
df_drone_data['出願人/権利者'] = df_drone_data['出願人/権利者'].str.replace('▲', '')
df_drone_data['出願人/権利者'] = df_drone_data['出願人/権利者'].str.replace('▼', '')
# 欠落データを削除
df_drone_data = df_drone_data.dropna()
# 'FI' を分割
df_drone_data['FI'] = df_drone_data['FI'].str.split(',')
# '出願人/権利者'を分割
df_drone_data['出願人/権利者'] = df_drone_data['出願人/権利者'].str.split(',')
# エクスプロード
df_drone_data = df_drone_data.explode('FI').explode('出願人/権利者')
# インデックスのリセット
df_drone_data = df_drone_data.reset_index(drop=True)
# FIのカウント
fi_counts = df_drone_data['FI'].value_counts()
# FIをtop10で並べ替え
top_fi = fi_counts.sort_values(ascending=False).index[:10].tolist()
# 出願人/権利者のカウント
person_counts = df_drone_data['出願人/権利者'].value_counts()
# 出願人/権利者をtop10で並べ替え
top_person = person_counts.sort_values(ascending=False).index[:10].tolist()
# データフレームdf_drone_dataから「出願人/権利者」列と「FI」列をtop10のみ含むようにフィルタリング
df_drone_data['出願人/権利者'] =df_drone_data [df_drone_data['出願人/権利者'].isin(top_person)]['出願人/権利者']
df_drone_data['FI'] =df_drone_data[df_drone_data['FI'].isin(top_fi)]['FI']
```
## groupby処理
次に、「出願人/権利者」と「FI」の列でグループ化し、グループ内の要素数を数えます。
```Python
# 「出願人/権利者」と「FI」の列でグループ化し、グループ内の要素数を数える
result = df_drone_data.groupby(['出願人/権利者', 'FI']).size().reset_index(name='Counts')
# 処理の確認
print(result)
```
出力を確認します。出願人とFIのグループの数が想定通りにカウントされていることがわかります。
```
       出願人/権利者           FI  Counts
0     キヤノン株式会社  B64C13/20@Z       2
1     キヤノン株式会社    B64C27/08       2
2     キヤノン株式会社    B64C39/02       4
3     キヤノン株式会社    B64D47/08       2
4   ソフトバンク株式会社  B64C13/18@Z       1
..         ...          ...     ...
56    西武建設株式会社    B64C39/02       9
57    西武建設株式会社    B64D27/24       3
58    西武建設株式会社    B64D47/08       2
59    西武建設株式会社     B64F1/12       1
60    西武建設株式会社     B64F1/36       2
```
## 散布図作成
散布図をmatplotlibで描画します。バブルの大きさのみでは差異がわかりにくいと思いましたので、バブルごとにカウント数を表示する処理も行いました。
```Python
# 「出願人/権利者」と「FI」の列からなる値をリスト形式に変換
x = result['FI'].tolist()
y = result['出願人/権利者'].tolist()
# 「Counts」列からなる値を元に、散布図のすべての点の大きさを示すリストを作成
sizes = [size * 20 for size in result['Counts'].tolist()]
# 「Counts」列からなる値を元に、散布図のすべての点のカウント数を示すリストを作成
nums = [num for num in result['Counts'].tolist()]
# 「出願人/権利者」列からなる値を元に、カテゴリーのリストを作成
categories = list(set(y))
# 色マップを作成
colors = cm.rainbow(np.linspace(0, 1, len(categories)))
colors = {categories[i]: colors[i] for i in range(len(categories))}
colors = [colors[item] for item in y]
# 散布図を作成
fig, ax = plt.subplots()
ax.scatter(x, y, s=sizes, c=colors)
# 散布図にカウント数を表示
for i, txt in enumerate(nums):
    ax.annotate(txt, (x[i], y[i]), textcoords="offset points", xytext=(1,1), ha='left', va='bottom')
# X軸項目を60°傾ける
plt.xticks(rotation=60)
# 図を表示
plt.show()
```
## バブルチャート
完成したバブルチャートは以下となります。確かに見やすくはあります。ただし、色使いや図の大きさ等の見た目に関しては作成者のセンスがものをいいますので、今後改良が必要と思いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3110569/f3c525aa-5800-27db-7a52-f1dc9bcfc4fe.png)
# 感想
今回Pythonでバブルチャートを描画してみましたが、Excelで作成するよりも、楽なような気もしますが、どちらもあまり楽ではない気もします。

数値だけ必要な場合にはクロス集計で十分ですので、プレゼンなどに使用するのであれば、バブルチャートは有用ですが、そうでなければわざわざ作成する必要もないかもしれません。

しかし、groupbyの使い方などは、大いに勉強になりました。今後もできるだけ、バブルチャートを活用してゆきたいと思います。