【Matplotlibでグラフ作成】日本語で軸ラベルが表示できずgoogletransで強行突破した話

# ■概要
- [法人企業統計調査(財務省財務総合政策研究所)](https://www.mof.go.jp/pri/reference/ssc/index.htm)の結果を可視化しようと、[Matplotlib](https://matplotlib.org/)を使ってみた時の話。
- Matplotlibでグラフ表示時に日本語が文字化けし、無理やり英訳してグラフ出力してみたので、そのロジックを全文公開。
- ご覧いただいた方が、何かしらのヒントを得られることを願っております。

#■環境
- OS: Windows10
- Python 3.9.7
- 実行環境: Anaconda Prompt (anaconda3)
- 使用したライブラリ: glob(ファイルパス取得), pandas(基礎処理), googletrans(翻訳), matplotlib.pyplot(可視化)
- なお、翻訳には、***googletransのVersion 4.0.0rc1***を使いました。<BR>Version 3.0.0だとエラーが出たので、PRE-RELEASE版を使ってます。(詳細: [こちらのサイトを参照](https://sig9.hatenablog.com/entry/2021/08/26/000000))

# ■機能要件
- 1. ***ローカル環境に格納した法人企業統計調査のCSVファイル(6ファイル)を読み込み、コマンドラインにカラム名を入力すれば、
6つの資本金規模別に2001年~2020年の時系列データが折れ線グラフ(line plot)で表示されること***。
- 2. ***表示する各グラフのタイトルを業種名 / 資本金規模とし、CSVファイルのレコードに応じてタイトルが変わること。***
- 3. 実装するツールは個人利用のため、とりあえず実装要件1,2が満たされていればOKとしました。


<details><summary>【参考: 今回用いた法人企業統計調査のデータの取得手順】</summary>1. [政府統計の総合窓口(e-Stat)](https://www.e-stat.go.jp/stat-search/files?page=1&layout=datalist&toukei=00350600&tstat=000001047744&cycle=8&tclass1=000001049372&tclass2val=0)から法人企業統計調査の時系列データのページに移動。<BR>今回は、2020年度の結果を使用。(下図: e-statのキャプチャをもとに作成。赤枠部を選択。)<BR><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/317e0327-9f2b-7af9-6658-804e6b76d5a2.png" width=600>
2.「統計表・グラフ表示」画面まで進み、業種(赤枠部), 規模(青枠部)を選択。<BR>「ダウンロード」を押下し、画面遷移。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/e32bc14e-d2da-330d-ab8d-de00c204978d.png" width=600><BR>
3.「ダウンロード設定」画面で出力するファイルの条件を選択。<BR>なお、選択する項目によって出力される内容(構成・データ)が異なるため、後述のソースコードを引用する際はご注意ください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/35a2d703-2bca-ed3e-4053-0985411b2503.png" width=600><BR>
なお、取得したCSVファイルをExcelで開くとこのようになっています。<BR><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/cc1e0d00-0b9d-5b5f-db73-2fc80303faee.png" width=600>
</details>

# ■ソースコード(全文公開)
```python
import glob
import pandas as pd
import matplotlib.pyplot as plt
from googletrans import Translator


'''
1. CSVファイルを格納しているディレクトリのパスから、各CSVファイルのパスを取得/getFilePath
引数: グラフ化するCSVファイルを格納しているディレクトリのパス/dirPath
戻り値: CSVファイルの絶対パスリスト/absPathList
'''
def getFilePath(dirPath):

    #ディレクトリ内のファイルパスを取得
    absPathList = glob.glob((dirPath + '\\*'))
    
    return absPathList

'''
連番生成/getNums
引数: 最初の数字, 最後の数字
戻り値: 連番リスト/numsList
'''
def getNums(fnum, lnum):
    
    #連番リスト
    numList = [fnum]
    
    while (numList[(len(numList)-1)] != lnum):
    
        num_latest = len(numList) - 1
        nextNum = numList[num_latest] + 1
        numList.append(nextNum)
    
    return numList
        

'''
2. CSVファイルの読み込み,DataFrameの整形/getDF
引数: csvファイルパス/fpath
戻り値: df
'''
def getDF(fpath):
        
    #CSV→DataFrame(文字コード:SHIFT-JISとして読み込み)
    df_read = pd.read_csv(fpath, header=None, encoding='cp932')
    
    '''
    ファイルごとにレコード数が異なるため、年期カラムの年数を指定して、2001~2020年のデータを取得する。
    '''
    #文字列「年  期」がある行のインデックス(何行目にあるか)を取得(どのCSVファイルも5列目に検索する値があると仮定)
    rowIndex = df_read.index[df_read[2] == '年  期'].tolist()
    
    #取得したindexにある値をheaderとして指定
    headerList = df_read.iloc[rowIndex[0]]
    df_read.columns = headerList 
    
    '''不要な列の削除'''
    #不要な行・列番号を指定し、リストで取得
    rowsList = getNums(0,rowIndex[0])
    colsList = [3]
    
    #読み込んだDFから不要なrowsを削除
    df = df_read.drop(index=df_read.index[rowsList], columns=df_read.columns[colsList])
    
    return df


'''
3. 先頭行をheaderとして、入力したカラム名に一致するカラム名とその列のデータを取得/extractCol
引数: 手順2で整形したDataFrame/df
戻り値: グラフ化の対象となるデータ/data
'''
def extractCol(df):
    
    #年期カラムの値から数字のみ取得・置換
    for y in df['年  期']:
        df = df.replace(y, y[0:4])
    
    #年期カラムの値が2001~2020年の行のインデックス(list)を取得
    f_num = [(df.index[df['年  期'] == '2001'].tolist()[0] - 1)]
    l_num = df.index[df['年  期'] == '2020'].tolist()
    
    #グラフ化するレコードを取得
    data = df.iloc[f_num[0]:l_num[0],:]
    
    return data


'''
Googletransを用いた日英翻訳/translateJP2EN
引数: 日本語のテキスト/JPtext
戻り値: 英語のテキスト/ENtext
'''
def translateJP2EN(JPtext):
    
    translator = Translator()
    
    ENtext = translator.translate(JPtext, src='ja', dest='en').text
    
    return ENtext
    

'''
4. 取得したいデータを指定し、グラフ化/visualizeData
引数: データセット/data, グラフの配置位置(list型)/lay_val
戻り値: グラフ
'''
def visualizeData(fig, axs, input_col, data, lay_val):
    
    #業種を取得(for title)
    industry = data.iloc[0]['業種 (金融業、保険業以外の業種)']
    industryEN = translateJP2EN(industry)
    
    #資本金規模を取得(for title)
    capital = data.iloc[0]['規模 (金融業、保険業以外の業種)']
    capitalEN = translateJP2EN(capital)
    
    #カラム名をリストで取得
    colNames = list(data.columns)
    
    if (input_col not in colNames):
        print('入力した値が見つかりませんでした。')
        print('抽出したDataFrameのみ表示します')
        print(data)
        exit()

    else:
        print('検索した値が見つかりました。')
    
    '''入力値をカラム名とする列の値を取得'''
    obj_data = data[input_col]
    
    #データ型を調整
    obj_data = obj_data.astype(float) 
    
    '''取得した値をline plotでグラフ化・表示'''   
    #x軸(横軸), y軸(縦軸)に割り当てる値を指定
    x = data['年  期']
    y = obj_data
    
    #資本金規模を取得
    capital = data.iloc[0]['規模 (金融業、保険業以外の業種)']
    cap_EN = translateJP2EN(capital)
    
    '''プロット'''
    #グラフの表示位置
    ax = axs[lay_val[0], lay_val[1]]
    
    #グラフで表示する値の指定
    ax.plot(x, y)
    
    #y軸のスケールの種類
    ax.set_yscale('linear')
    
    #y軸のタイトル(必要に応じてアクティベート)
    #ax.set_ylabel(labelEN)
    
    #x軸のタイトル(必要に応じてアクティベート)
    #ax.set_xlabel('Fiscal Year')
    
    #タイトルの指定
    title_str = industryEN + ' /  ' + capitalEN
    ax.set_title(title_str)
    
    #グリッドの有無
    ax.grid(axis='y')
    

def controller():   

    #CSVファイルを格納しているディレクトリのパス
    dirPath = r'(省略)'
    
    #絶対パスの取得
    absPathList = getFilePath(dirPath)
    
    #取得したいデータ名を入力
    input_col = input('グラフ化したい列のカラム名を入力してください:')
    
    #出力する6つのファイルの表示位置を指定
    layoutLists = [[0,0], [0,1], [1,0], [1,1], [2,0], [2,1]]
    
    #表示するグラフの数を指定
    fig, axs = plt.subplots(3, 2, figsize=(3, 4),constrained_layout=True)
    
    #各CSVファイルの読み込み~グラフ化
    for num in range(len(absPathList)):
        
        print('【',(num + 1), '/', len(absPathList), '】 読み込み中:\r\n')
        print(absPathList[num])
        
        #生データの読み込み
        df = getDF(absPathList[num])
        
        #dataframeの整形(意図したとおりに作成できているか確認)
        data = extractCol(df)
        
        #グラフ化
        visualizeData(fig, axs, input_col, data, layoutLists[num])
    
    #全体のタイトル(コマンドラインで入力したカラム名)を英訳
    labelEN = translateJP2EN(input_col)
    
    #全体のタイトルをつける
    title = str(labelEN + ' , FY 2001-2020')
    
    #subplotのタイトルをつける
    fig.suptitle(title, fontsize=16)
    
    plt.show()


'''
実行
'''
controller()

```
# ■ 実行結果
実際に、e-statにおいて以下の条件でデータを取得し、上述のソースコードを実行した結果は以下の通りです。

<details><summary>【詳細】選択した業種と規模</summary>```業種:「情報通信業」<BR>
資本金規模: 「10億円以上」, 「1億円以上10億円未満」, 「5千万円以上1億円未満」, 「2千万円以上5千万円未満」, 「1千万円以上2千万円未満」, 「1千万円未満」の計6種類```</details>

入力値: 母集団(当期末)【社】
出力結果: 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/62f4dd03-8226-f516-e880-d410d0174ab5.png" width=600>

入力値: 自己資本経常利益率(当期末)【％】
出力結果:
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/3c1ce606-7a2b-3166-36e4-af50023f8359.png" width=600>

# ■実装中に困ったこと
タイトルにもありますが、日本語で軸ラベル・タイトルを出力しようとすると以下のように、文字化けしました、、、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023907/752516df-4e66-3bbb-56fe-fce5e2f5d49c.png" width=400>
解決方法がわからず時間だけが過ぎていくので、「アルファベットなら大丈夫か」と思い、
入力したカラム名、CSVファイルから取得した業種と規模を処理中に英訳し、力業ですが何とかタイトルを表示できました。

# ■所感
- googletransを使うという選択肢は我ながら柔軟な対応ができたと自負していましたが、いざグラフを出力してタイトルを見てみると誤訳が多く、出力したグラフを人に見せるにはまだまだ課題残る形となりました。
- ただ、CSVファイルのコンテンツに応じて、業種や規模の情報をグラフに反映できるため、軸ラベル・タイトルの指定、グラフの配置にかかる時間を考慮すれば、手作業でExcelでグラフ作成するよりもある程度時間は削減できると予想。(マクロが書けさえすれば、、)
- なお、今回はpandas.DataframeやMatplotlibの練習の一環での実装でしたが、
***PythonでExcel操作を可能にするライブラリ「[openpyxl](https://openpyxl.readthedocs.io/en/stable/)」であれば日本語対応していたはず***(どこかのサイトで見た記憶)なので、e-Statからファイルをダウンロードする際、XLSX形式を選択すれば、表題のような奇行に走らなくても済むのかもしれません。

# さいごに
最後までご覧いただきありがとうございました。
***「記載したソースコードの通り実行できない」、「書いている意味が分からない」、「もっと改善の余地がある」***など修正点・改善点等ございましたら、お気軽にお申し付けくださいませ。
できる範囲でお答え致します。
それでは、どうぞよろしくお願い致します。