Pythonで円グラフを書いてみる（その２）

# はじめに
前回基本的なコードを作成しましたので、今回は実際の特許データを用いて円グラフを作成したいと思います。
# プログラム
## データフレーム作成
データフレームの作成には、以前の下記投稿で作成したコードを流用します。なお、出願人は５社に限定しています。基本的には何社でもOKですが、出願人の数が多い場合には円グラフが細かくなりすぎると思いましたので、ひとまず５社で書いてみることにしました。

https://qiita.com/ip_design/items/808086e82b7c3ce1ff60

```python
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
import japanize_matplotlib

#from matplotlib.patches import Circle
#from matplotlib.offsetbox import (OffsetImage, AnnotationBbox)

# googleドライブの利用
from google.colab import drive
drive.mount('/content/drive')
# データの前処理
# データの読み込み(google ドライブから)
df_fterm = pd.read_csv("/content/drive/MyDrive/吸収性物品3_2553.csv")
#必要な列のみ抽出
df_fterm = df_fterm[['Ｆターム(最新)','出願人・権利者(最新)']]
# 欠落データを削除
df_fterm = df_fterm.dropna()
# Fターム列をコピーして追加
df_fterm['課題'] = df_fterm['Ｆターム(最新)']
df_fterm['解決手段']= df_fterm['Ｆターム(最新)']
# ;で課題を分割
df_fterm['課題'] = df_fterm['課題'].str.split(';')
# ;で解決手段を分割
df_fterm['解決手段'] = df_fterm['解決手段'].str.split(';')
# ;で出願人・権利者(最新)を分割
df_fterm['出願人・権利者(最新)'] = df_fterm['出願人・権利者(最新)'].str.split(';')
# 課題と解決手段と出願人をエクスプロード
df_fterm = df_fterm.explode('課題').explode('解決手段').explode('出願人・権利者(最新)')

# 課題の限定処理
task_list = ["3B200 BA01", "3B200 BA02", "3B200 BA03", "3B200 BA04", "3B200 BA05", "3B200 BA06", "3B200 BA07", "3B200 BA08", "3B200 BA09", "3B200 BA10", "3B200 BA11", "3B200 BA12", "3B200 BA13", "3B200 BA14", "3B200 BA15", "3B200 BA16", "3B200 BA17", "3B200 BA18", "3B200 BA19"]
df_fterm = df_fterm[df_fterm['課題'].isin(task_list)]
# 解決手段の限定処理
solution_list = ["3B200 BB01", "3B200 BB02", "3B200 BB03", "3B200 BB04", "3B200 BB05", "3B200 BB06", "3B200 BB08", "3B200 BB09", "3B200 BB10", "3B200 BB11", "3B200 BB13", "3B200 BB14", "3B200 BB16", "3B200 BB17", "3B200 BB18", "3B200 BB20", "3B200 BB21", "3B200 BB22", "3B200 BB23", "3B200 BB24", "3B200 BB25", "3B200 BB26", "3B200 BB27"]
df_fterm = df_fterm[df_fterm['解決手段'].isin(solution_list)]
# 出願人の限定処理
applicant_list = ["ユニ・チャーム株式会社", "花王株式会社", "大王製紙株式会社", "王子ホールディングス株式会社", "日本製紙クレシア株式会社"]
df_fterm = df_fterm[df_fterm['出願人・権利者(最新)'].isin(applicant_list)]

# map置き換え用の辞書作成
replace_dict_1 = {
    '3B200 BA01': '吸水性',
    '3B200 BA02': '親水性',
    '3B200 BA03': '透水性',
    '3B200 BA04': '拡散性',
    '3B200 BA05': '撥水性',
    '3B200 BA06': '逆流防止',
    '3B200 BA07': '通気性',
    '3B200 BA08': '風合',
    '3B200 BA09': '嵩高',
    '3B200 BA10': '膨張性',
    '3B200 BA11': '伸張性',
    '3B200 BA12': '伸縮性',
    '3B200 BA13': '剛性',
    '3B200 BA14': '密度',
    '3B200 BA15': '光透過性',
    '3B200 BA16': '接着性',
    '3B200 BA17': '分解性',
    '3B200 BA18': '水分解性',
    '3B200 BA19': '保温性',
}
replace_dict_2 = {
    '3B200 BB01': '繊維材料',
    '3B200 BB02': '紙',
    '3B200 BB03': '不織布',
    '3B200 BB04': '合成繊維',
    '3B200 BB05': 'パルプ',
    '3B200 BB06': '織編物',
    '3B200 BB08': '合成樹脂',
    '3B200 BB09': 'シート状',
    '3B200 BB10': 'スポンジ状',
    '3B200 BB11': '弾性材料',
    '3B200 BB13': '表面処理',
    '3B200 BB14': '化学的処理',
    '3B200 BB16': '高吸収性材料',
    '3B200 BB17': 'ポリマー',
    '3B200 BB18': '処理変性',
    '3B200 BB20': '接着剤',
    '3B200 BB21': '薬剤',
    '3B200 BB22': '脱臭剤',
    '3B200 BB23': '活性炭',
    '3B200 BB24': '抗菌剤',
    '3B200 BB25': '香料',
    '3B200 BB26': '潤滑剤',
    '3B200 BB27': '凝血剤',
}
# mapメソッドを使用して分類を日本語に置き換える
df_fterm.loc[:, '課題'] = df_fterm['課題'].map(replace_dict_1)
df_fterm.loc[:, '解決手段'] = df_fterm['解決手段'].map(replace_dict_2)

def create_mapping_dict(categories):
    mapping_dict = {}
    for category, items in categories.items():
        mapping_dict.update({item: category for item in items})
    return mapping_dict

categories1 = {
    '水との関係性': ['吸水性', '親水性', '透水性', '撥水性', '逆流防止', '水分解性'],
    '物質の拡散・通過性': ['通気性', '拡散性', '光透過性'],
    '形状と構造の変化性': ['嵩高', '膨張性', '伸張性', '伸縮性'],
    '物質の強度・安定性': ['剛性', '密度', '接着性', '分解性'],
    '感触と保護性能': ['風合', '保温性']
}
input_dict1 = create_mapping_dict(categories1)

categories2 = {
    '素材のタイプ': ['繊維材料', '紙', '不織布', '合成繊維', 'パルプ', '織編物', '合成樹脂', 'ポリマー'],
    '形状・構造': ['シート状', 'スポンジ状', '弾性材料', '高吸収性材料'],
    '処理・改質': ['表面処理', '化学的処理', '処理変性'],
    '接着・固定化': ['接着剤', '薬剤', '凝血剤'],
    '機能性添加物': ['脱臭剤', '活性炭', '抗菌剤', '香料', '潤滑剤']
}
input_dict2 = create_mapping_dict(categories2)

# replaceメソッドを使用して分類をグループに置き換える
df_fterm['課題'] = df_fterm['課題'].replace(input_dict1)
df_fterm['解決手段'] = df_fterm['解決手段'].replace(input_dict2)

# データフレーム確認
print(df_fterm)
```
作成されたデータフレームは以下のようになります。出願人、課題、解決手段のセットができておりますので、前回作成したコードを、そのままくっつければ、円グラフが出力されるはずです。
```
                                               Ｆターム(最新)     出願人・権利者(最新)  \
0     3B200 AA03;3B200 BA01;3B200 BA03;3B200 BA16;3B...    日本製紙クレシア株式会社   
0     3B200 AA03;3B200 BA01;3B200 BA03;3B200 BA16;3B...    日本製紙クレシア株式会社   
0     3B200 AA03;3B200 BA01;3B200 BA03;3B200 BA16;3B...    日本製紙クレシア株式会社   
0     3B200 AA03;3B200 BA01;3B200 BA03;3B200 BA16;3B...    日本製紙クレシア株式会社   
0     3B200 AA03;3B200 BA01;3B200 BA03;3B200 BA16;3B...    日本製紙クレシア株式会社   
...                                                 ...             ...   
2169  3B200 AA01;3B200 AA03;3B200 BA01;3B200 BA04;3B...     ユニ・チャーム株式会社   
2174  3B200 AA01;3B200 AA03;3B200 BA07;3B200 BA13;3B...  王子ホールディングス株式会社   
2174  3B200 AA01;3B200 AA03;3B200 BA07;3B200 BA13;3B...  王子ホールディングス株式会社   
2174  3B200 AA01;3B200 AA03;3B200 BA07;3B200 BA13;3B...  王子ホールディングス株式会社   
2174  3B200 AA01;3B200 AA03;3B200 BA07;3B200 BA13;3B...  王子ホールディングス株式会社   

             課題    解決手段  
0        水との関係性  素材のタイプ  
0        水との関係性   形状・構造  
0        水との関係性  接着・固定化  
0        水との関係性  素材のタイプ  
0        水との関係性   形状・構造  
...         ...     ...  
2169  物質の拡散・通過性  素材のタイプ  
2174  物質の拡散・通過性  素材のタイプ  
2174  物質の拡散・通過性   形状・構造  
2174  物質の強度・安定性  素材のタイプ  
2174  物質の強度・安定性   形状・構造  
```
## 円グラフ化
円グラフ化のコードは前回の投稿で作成したものを使用します。

https://qiita.com/ip_design/items/6c4cb1d2a1ae3ad1fce8

```python
#課題と解決手段の組み合わせ毎の出願人の件数をカウント
grouped = df_fterm.groupby(['課題', '解決手段', '出願人・権利者(最新)']).size().reset_index(name='件数')

# 課題と解決手段の組み合わせ毎の出願人の構成比を計算
grouped['構成比'] = grouped.groupby(['課題', '解決手段'])['件数'].apply(lambda x: x / x.sum() * 100)

# 課題と解決手段の組み合わせ毎の構成比をピボットテーブルで整形
pivoted = grouped.pivot_table(values='構成比', index=['課題', '解決手段'], columns='出願人・権利者(最新)', fill_value=0).reset_index()

fig, ax = plt.subplots(figsize=(8.0, 8.0))

unique_resolution_methods = df_fterm['解決手段'].unique()
unique_issues = df_fterm['課題'].unique()
unique_applicants = pivoted.columns.tolist()[2:]

ax2 = ax.twinx()

for index, row in pivoted.iterrows():
    x = unique_resolution_methods.tolist().index(row['解決手段'])
    y = unique_issues.tolist().index(row['課題'])
    size = grouped.loc[grouped['課題'].eq(row['課題']) & grouped['解決手段'].eq(row['解決手段']), '件数'].sum() * 0.05
    ratio = [row[applicant] for applicant in unique_applicants]
    colors = cm.get_cmap('YlGn')(np.linspace(0, 1, len(unique_applicants)))
    wedges, _ = ax2.pie(ratio, radius=size / 100, center=(x, y), colors=colors, wedgeprops=dict(edgecolor='black'))

ax.set_xlabel('解決手段')
ax.set_ylabel('課題')

ax.set_xticks(range(len(unique_resolution_methods)))
ax.set_xticklabels(unique_resolution_methods)
ax.set_yticks(range(len(unique_issues)))
ax.set_yticklabels(unique_issues)

ax.set_xlim(-0.5, len(unique_resolution_methods) - 0.5)
ax.set_ylim(-0.5, len(unique_issues) - 0.5)

ax2.set_ylim(ax.get_ylim())  # ax2のy軸の範囲をaxと同じに設定

# ax2の四角を非表示にする
ax2.spines['top'].set_visible(False)
ax2.spines['bottom'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.get_yaxis().set_ticks([])

# 各辺の太さを0.5に設定
for spine in ax.spines.values():
    spine.set_linewidth(0.5)

ax.legend(wedges, unique_applicants, title="出願人", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))

plt.show()
```

## 出力
出力は以下となりました。イメージに近い（ただし、問題はある？）図を出力することができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3110569/e346cfa6-f5f8-398b-3e49-0c046ee94bab.png)
# 感想
バブルの大きさに大きな差がある場合には、小さなバブルの円グラフはつぶれてよくわからなくなりました。したがって、バブルの大きさに大きな差がある場合には、この手法を用いない方がよいと思いました。

また、円グラフは５分割としましたが、数が多いと視認性が低下するため、２社くらいに留めた方がよいと感じました。５社の比較をしたい場合には、各社ごとに個別のバブルチャートを作成する方がよいと思います。