【Python】mplfinance で複数グラフを描くとワーニング

## はじめに

株価データの分析のために，複数銘柄のチャートを描きたいと思った
例えば，TOPIX Core30 だと30銘柄ある
1銘柄ずつループで回していると，チャートは表示されるものの，ワーニングが表示される

```
/usr/local/lib/python3.9/site-packages/mplfinance/_mplwraps.py:60:
RuntimeWarning: More than 20 figures have been opened.
Figures created through the pyplot interface
(`matplotlib.pyplot.figure`) are retained until explicitly closed
and may consume too much memory. 
(To control this warning, see the rcParam `figure.max_open_warning`).
  f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs)
```
※本記事中，見やすさのため，出力結果の一部に改行をいれているところがあります

20個より多くのグラフを描くとワーニングになるようで，
このワーニングがでなくなる方法を備忘録として残しておく

## ワーニングが出たコード

[こちら](https://github.com/matplotlib/mplfinance/tree/master/examples/data)のデータ（SPY_20110701_20120630_Bollinger.csv）を使って同じチャートを30回描くコードだ

```python:warn_code.py
import pandas as pd
import mplfinance as mpf
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = mpf.figure(figsize=(3, 3),style='yahoo')
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)
```
グラフは30個表示されるが，
![mpl-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/1c69ed4a-7f9a-4579-f5a9-a4446c480541.png)
上述のワーニングが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/e4567e93-d7f5-0d8f-9573-1e0a6cfaf75f.png)

## 問題解決のため調査をはじめる

このワーニングの対処法を調査すると，matplotlib の解決策ばかりだった

https://qiita.com/code0327/items/c27472af808397bcd42b

https://qiita.com/Masahiro_T/items/bdd0482a8efd84cdd270

```python
plt.clf()
plo.close()
```

matplotlib では clf と close を呼べばいいらしい
しかし，mplfinance の場合は，closeやclfがない
試しに読んでみると

```
>>> mpl.close()
AttributeError: module 'mplfinance' has no attribute 'close'
```

```
>>> mpl.clf()
AttributeError: module 'mplfinance' has no attribute 'clf'
```

モジュール内の一覧を表示してみると

```python
>>> import mplfinance as mpl
>>> dir(mpl)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__',
'__package__', '__path__', '__spec__', '__version__', '_arg_validators', '_helpers',
'_mpf_warnings', '_mplwraps', '_panels', '_styledata', '_styles', '_utils', '_version',
'_widths', 'available_styles', 'figure', 'make_addplot', 'make_marketcolors',
'make_mpf_style', 'mplfinance', 'plot', 'plotting', 'show', 'write_style_file']
```
確かになさそうだ
fig を作った plt を得るしかなさそうである

## mplfinance のソースを見てみる

mplfinance.figure が定義されている，[_mplwraps.py](https://github.com/matplotlib/mplfinance/blob/master/src/mplfinance/_mplwraps.py) を見ると plt がいた

```python:_mplwraps.py
def figure(*args,**kwargs):

    style = _check_for_and_apply_style(kwargs)

    f = plt.figure(FigureClass=Mpf_Figure,*args,**kwargs)
    f.mpfstyle = style
    return f

class Mpf_Figure(mplfigure.Figure):
    …
```

`matplotlib.pyplot.figure()`関数を呼び出している

引数`FigureClass=Mpf_Figure`のMpf_Figureクラスは，_mplwraps.py 内で定義されているクラスだ

```
The wrapper function is the same as `matplotlib.pyplot.figure()`
except that it additionally accepts kwarg `style=` to set the mplfinance style.
```

_mplwraps.py のヘッダに書かれているが，`mplfinance.figure()`関数は基本 `matplotlib.pyplot.figure()`関数と同じだ
ただし，mplfinance の style を設定できるところだけが違う

```
style = _check_for_and_apply_style(kwargs)
```

`_check_for_and_apply_style()`関数で `matplotlib.pyplot.figure()`関数 に渡す kwargs から style を削除しているようだ

```
f.mpfstyle = style
```

そして，`matplotlib.pyplot.figure()`関数でできた mplfinance.figure に後からスタイルを設定している

## 解決案

matplotlib.pyplot と，mplfinance._mplwraps.Mpf_Figure をインポート

```python
import matplotlib.pyplot as plt
import mplfinance as mpf
from mplfinance._mplwraps import Mpf_Figure
```

figure の生成を，`mplfinance.figure()`関数から，`matplotlib.pyplot.figure()`関数に変更する
style は後から設定してあげる

```python
# もともとの figure 生成
fig = mpf.figure(figsize=(3, 3),style='yahoo')
# 新しい figure の生成方法
fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3))
fig.mpfstyle = 'yahoo'
```
plot が終わったら，clf と close を呼んであげる

```python
plt.show()
plt.clf()
plt.close()
```

まとめると以下のようだ

```python:ok_code.py
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
from mplfinance._mplwraps import Mpf_Figure
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = plt.figure(FigureClass=Mpf_Figure,figsize=(3, 3))
    fig.mpfstyle = 'yahoo'
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)
    plt.show()
    plt.clf()
    plt.close()
```
![mpl-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/385512/1c69ed4a-7f9a-4579-f5a9-a4446c480541.png)

めでたくワーニングが消えた

## おわりに

matplotlib でのワーニングを消す方法を参考に mplfinance で行ってみた

うまいやり方，良い方法かどうかわからない…

これは違う，こうしたほうが良いなどあれば，コメントいただければと思う

## 追記

単にこれでよかったのかもしれない

インポートについて理解していなかったが，
[Pythonのモジュールインポートのしくみ](http://python.matrix.jp/pages/tips/import.html)を読むと，
モジュールオブジェクトはシングルトンなため，
自分で import した plt（matplotlib.pyplot）を使って clr，close を呼ぶだけ

```python
# matplotlib.pyplot をインポート
import matplotlib.pyplot as plt

# plot，show の後に，pltのclf，closeを呼ぶ
plt.clf()
plt.close()
```

まとめると

```python:ok_code2.py
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
df = pd.read_csv('SPY_20110701_20120630_Bollinger.csv',index_col=0,parse_dates=True)
# 30回チャートを描く
for index in range(30):
    # チャート描画
    fig = mpf.figure(figsize=(3, 3),style='yahoo')
    ax1 = fig.add_subplot(1,1,1)
    mpf.plot(df, ax=ax1, style='yahoo', type='candle', xrotation=30)
    plt.show()
    plt.clf()
    plt.close()
```