Matplotlibでy軸にグラデーション色付けしたbar graphをアニメーションで動かす

この記事はこちらの記事の続きになります。もしよかったらこちらも読んでいただけると嬉しいです。
**基本的なSortをmatplotlibでアニメーションにしてみた**

https://qiita.com/ZawaP/items/622188e5d364c5e78ddb

## tl;dr
* Matplotlibを使ってBarにgradation colormapを反映し、Animationを使って動かしています。gradationは各Barに対してColor imageをFillするというやり方です。
* defaultはX軸に対して、Gradation Colorとなるため、Barのheightに対してGradation colorにする場合は、各barのheightに対して、Color mapを対応させるようにしています。
    * Normalizeを無効化し、heightの最大値を基準としています。
* array-like という概念を初めて知りました。配列と何が違うのかは今後勉強します。

![animationWithoutNormalize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/c90aef76-bd10-cecd-b826-7c28c5b6e701.gif)


### step 1 Barへの色付け
---
![Figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/dde1ac3c-e452-5fd9-1d9f-075abe345533.png)

```python
import numpy as np
import matplotlib.pyplot as plt

def gradient_image(ax, extent, direction=0, cmap_range=(0, 1), **kwargs):
    phi = direction * np.pi / 2
    v = np.array([np.cos(phi), np.sin(phi)])
    X = np.array([[v @ [1, 0], v @ [1, 1]],
                [v @ [0, 0], v @ [0, 1]]])
    a, b = cmap_range
    X = a + (b - a) / X.max()*X
    im = ax.imshow(X, extent=extent, interpolation='bicubic', norm = plt.cm.colors.NoNorm(vmin = 0, vmax = 1), **kwargs)
    return im

def gradient_bar(ax, x, y, width=0.5, bottom=0):
    for left, top in zip(x, y):
        right = left + width
        gradient_image(ax, extent=(left, right, bottom, top), cmap=plt.cm.get_cmap('rainbow'), cmap_range=(0, top))

xmin, xmax = xlim = 0, 10
ymin, ymax = ylim = 0, 1

fig, ax = plt.subplots()
ax.set(xlim=xlim, ylim=ylim, autoscale_on=False)

x, y = [], []

ax.set_aspect('auto')

N = 10
x = np.arange(N) + 0.15
y = np.random.rand(N)
gradient_bar(ax, x, y, width=0.7)
ax.set_aspect('auto')
plt.show()
```
---
### step 2 Barへの色付け x Animation
![animationWithoutNormalize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/424dd99e-fbd3-69aa-e629-44fa9998754c.gif)

```python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation, writers
from sort_function_for_visualization import insert_sort, bubble_sort, quick_sort, merge_sort
from functools import partial

def gradient_image(ax, extent, direction=0, cmap_range=(0, 1), **kwargs):
    phi = direction * np.pi / 2
    v = np.array([np.cos(phi), np.sin(phi)])
    X = np.array([[v @ [1, 0], v @ [1, 1]],
                [v @ [0, 0], v @ [0, 1]]])
    a, b = cmap_range
    X = a + (b - a) / X.max()*X
    im = ax.imshow(X, extent=extent, interpolation='bicubic', norm = plt.cm.colors.NoNorm(vmin = 0, vmax = 1), **kwargs)
    return im

def gradient_bar(ax, x, y, width=0.5, bottom=0):
    n = len(x)
    for left, top in zip(x, y):
        right = left + width
        gradient_image(ax, extent=(left, right, bottom, top), cmap=plt.cm.get_cmap('rainbow'), cmap_range=(0, top/n))


n = 10
# 重複データが出ないように変更
data = [i for i in range(1,n+1)]
np.random.shuffle(data)

generator = bubble_sort(data)
sort_name = "bubble_sort"

fig, ax = plt.subplots()
    
font = {'family':'serif','color':'blue','size':20}
plt.title(sort_name, fontdict = font)

operation = [0]

xmin, xmax = xlim = 0, 10
ymin, ymax = ylim = 0, 10

ax.set(xlim=xlim, ylim=ylim, autoscale_on=False)

def update(frame):
    ax.cla() 
    ax.set(xlim=xlim, ylim=ylim, autoscale_on=False)
    x = range(len(data))
    y = data
    ax.text(0.01, 0.95, "", transform = ax.transAxes).set_text("operations : {}".format(operation[0]))
    gradient_bar(ax, x, y, width=0.7)
    operation[0] += 1


anim = FuncAnimation(fig, partial(update), frames=generator, interval=300, repeat=False, save_count=100)

#anim.save('animationWithoutNormalize.gif') 
plt.show()
```
---
### 別Ver。こちらは単色のbarです。Heightによって色を変えています。
![SortType.MERGE.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/a0be20fd-d552-ad83-3c87-787fdd3e07b1.gif)

![SortType.QUICK.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/6d1f5244-e309-ceb6-67d5-757a2964348b.gif)


---
### 参考

* matplotlib Bar chart with gradients
https://matplotlib.org/stable/gallery/lines_bars_and_markers/gradient_bar.html

* matplotlib.pyplot.bar
https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.bar.html#matplotlib-pyplot-bar

* matplotlib.animation
https://matplotlib.org/stable/api/animation_api.html

* Colormap normalization
https://matplotlib.org/stable/tutorials/colors/colormapnorms.html