質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.69%

python ヒストグラムのアニメーション

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 963

kouheichild

score 11

前提・実現したいこと

pythonでヒストグラムが動くアニメーションを作りたいです.
具体的には,平均15,標準偏差2(固定)で平均が15から0に移動するアニメーションを作りたいです.
下記のコードを実行したのですが、静止画になってしまい、うまくアニメーションが作成できません.

https://qiita.com/yubais/items/c95ba9ff1b23dd33fde2
また記載したコードは、上記のページに記載されたコードを参考にしました。このページに記載されたコードを流した場合はアニメーションが作動します。

大変恐縮ですが,ご教授お願いいたします.

該当のソースコード

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from scipy import stats

fig = plt.figure()
ims=[]
a =np.linspace(start=15,stop=0,num=16)
for i in a:
    np.random.seed(1)
    rand = stats.norm.rvs(i,2,7321)
    im = plt.hist(rand)             
    ims.append(im)                  

# 16枚のプロットを 1000ms ごとに表示
ani = animation.ArtistAnimation(fig, ims, interval=1000)
plt.show()
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • firedfly

    2019/01/30 22:52

    こんにちは。
    「うまくアニメーションが作成できません」というだけでは伝わりません。
    エラーが出るのか、エラーは出ないが望む動作にならないのか。自分で試したことはなにか。
    よい回答を望むなら、出せる情報はすべた出してよい質問を心がけましょう。

    キャンセル

  • kouheichild

    2019/01/30 23:17

    firedfly様
    ご回答ありがとうございます。
    不十分な質問で申し訳ございません。
    ご指摘ありがとうございます。

    この場をお借りして補足させていただきますと、
    記載したコードを実行すると、静止画になってしまいます。アニメーションがうまく機能しないことが問題です。

    https://qiita.com/yubais/items/c95ba9ff1b23dd33fde2
    また記載したコードは、上記のページに記載されたコードを参考にしました。このページに記載されたコードを流した場合はアニメーションが作動します。

    キャンセル

  • firedfly

    2019/01/30 23:21

    素晴らしい補足をありがとうございます。
    時間に余裕のある時でよいので、質問本文にもぜひご追記ください。
    そうすれば私以外の回答者が来たときに見やすいですので。

    キャンセル

回答 2

checkベストアンサー

+3

原因

自分も原因がわからなかったので少し実験してみました。
(A) im = plt.hist(rand)
質問者さんのコードでは(A)となっていますが、この状態で実行すると「ウィンドウに静止画が表示される」というよりは「matplotlibでエラーが発生している」ことに気づきます。下記はPyCharmで実行してみたときのコンソールの情報です。

C:\Users\ksoh\PycharmProjects\teratail\venv\Scripts\python.exe C:/Users/ksoh/PycharmProjects/teratail/t05_animation.py
Traceback (most recent call last):
  File "C:\Users\ksoh\AppData\Roaming\Python\Python37\site-packages\matplotlib\cbook\__init__.py", line 215, in process
    func(*args, **kwargs)
  File "C:\Users\ksoh\AppData\Roaming\Python\Python37\site-packages\matplotlib\animation.py", line 999, in _start
    self._init_draw()
  File "C:\Users\ksoh\AppData\Roaming\Python\Python37\site-packages\matplotlib\animation.py", line 1520, in _init_draw
    artist.set_visible(False)
AttributeError: 'numpy.ndarray' object has no attribute 'set_visible'

一方、リンク先では
(B) im = plt.plot(rand)
のようにplot関数の結果を蓄積してアニメーションしていますがそちらだと上記のようなエラーは出ずにアニメーションが表示されますね。

つまりplt.plotの結果のリストはアニメーションできて、plt.histの結果のリストはアニメーションできないということのように思えました。

もう一度先のエラーメッセージを見ると・・・

matplotlibの中でartistという変数にnumpy.ndarrayが格納されていたため「numpy.ndarrayオブジェクトにはset_visibleなんてメソッドないです」と言われてます。確かにnumpyの配列にそんなメソッドはないです。ArtistAnimationのリファレンスを見ると
https://matplotlib.org/api/_as_gen/matplotlib.animation.ArtistAnimation.html

artists : list
   Each list entry a collection of artists that represent what needs to be enabled on each frame. These will be disabled for other frames.

とあり、第二引数に渡すのはmatplotlibのartist(多分グラフを構成するオブジェクト群)のリストらしいことがわかります。そのartistには必ずset_visibleというメソッドがあるはずだということなのでしょう。

さてplotのリファレンスを見ると
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html

Returns:
  lines
  A list of Line2D objects representing the plotted data.

さらにhistのリファレンスを見ると
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist.html

Returns: 
  n : array or list of arrays
    The values of the histogram bins. ...
  bins : array
    The edges of the bins. ...
  patches : list or list of lists
    Silent list of individual patches used to create the histogram or list of such list if multiple input datasets.

とあり、plotの方はグラフを構成するLine2Dのリストを返すのに対してhistはn, bins, patchesの3つの要素を持つtupleが返されグラフを構成するオブジェクトは3つ目のpatchesに当たるように見えました。

対処(推測)

以上のことから「多分histが返す3つ目の要素をリストにすればアニメーションできるんじゃないか」と推測しました。そのとおりやってみると実行時例外はおきなくなりアニメーションが表示されました。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from scipy import stats

fig = plt.figure()
artists = []
a = np.linspace(start=15, stop=0, num=16)
for i in a:
    np.random.seed(1)
    rand = stats.norm.rvs(i,2,7321)
    n, bins, artist = plt.hist(rand)             
    artists.append(artist)                  

# 16枚のプロットを 1000ms ごとに表示
ani = animation.ArtistAnimation(fig, artists, interval=1000)
plt.show()

余談:
例えばPyCharmのようなコードの誤りを事前に警告してくれるような親切なIDEを用いて、次の図のように警告を発してくれればまだ気づきやすかったと思います。

fig.1

しかし残念ながらPythonの超有名ライブラリーであるnumpyもmatplotlibもPython3.5でサポートされた型ヒントをサポートしていません。(Python3.5より前のバージョンを利用しているユーザーがまだまだ多いということを考えればサポートできないのも当然といえましょう。なお、上の図はわざわざ自分で型ヒント付きの定義をしてPyCharmに誤りを警告させてみたものです)

それゆえ引数の型があっているかについてIDEのサポートは期待できず、当然自分が使う関数の仕様をドキュメントにより調べようとするわけですが、Pythonではコード上に型の宣言がないことが多いですし、そもそも動的型付け言語であることを最大限生かした関数、すなわち「スカラーの数値でも、strでも、tuple/list/ndarrayでもいいよー」的な、良く言えば柔軟、悪く言えばチャランポラン(?)な関数も多く、ドキュメントを見てさえすぐに間違いに気づけるとは限りません。

逆に言えばリファレンスをよく見ずになんとかなるものではないといえましょう。単にサンプルを見てそれを応用しようとしても本件のような落とし穴に落ちやすいと思います。

質問者さんがリファレンスをどのくらい見ておられるか、どのくらいプログラムの実行結果に関する情報に注意を向けているか不明ですが

  • ライブラリーが発する例外/警告メッセージを注意深く見て、その意味を想像する
  • リファレンスを見て仕様を把握しようとする
  • ときに実験によって原因究明に努める

そういう努力をしないと解決に近づくことはおぼつかないと思います。今回の回答があってるかどうか完全な自身はないですが、少なくとも前述のことをせずには原因に近づける気がしません。「どういうコードを書けばよいか」が知りたい情報だとは思いますが、「それをどのようにして得るか」の方が本質的にはもっと重要と考えます。質問する際にも「どういうところまで調べたか」を言っていただけると閲覧者のみなさんに有用な質問になると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/31 08:00

    高評価をひとりで5個つけて永久保存して都度参照したいくらいの良回答です。
    「回答者が望む情報はなにか」や「回答を得るためにどう試行錯誤できるのか」など共通する方法論は、teratail Wikiを作って書いて都度リンクを貼りたいですね。

    キャンセル

  • 2019/01/31 14:19

    KSwordOfHastem 様

    ご回答ありがとうございます.
    質問の答えだけでなく,その原因・それを見つけるまでの経緯・考え方までご教示ありがとうございます.
    当方,リファレンスは実は一度も見たことがなく,ネットに転がっているコードを応用させることばかりをしてしまっておりました.
    KSwordOfHastem 様 のお言葉を受け,今後は自分で原因を探る努力をしたいと思います.
    ご多忙の中,ご回答いただきありがとうございました.
    今後teratailを利用させていただく際にも,ご縁がありましたら再度ご教示いただけたら幸いです.
    何卒よろしくお願いいたします.

    キャンセル

  • 2019/01/31 21:39

    自分もArtistAnimationについてもやもやしていたので本質問をきっかけに調べた結果たまたまなにがしかつかめた気がしたので調べたことを書いてみた次第です。それがなんらかのヒントになってくれれば幸いです。
    正直なところ本質問がきっかけである程度スッキリしたので自分自身もこの質問に助けられました。
    コメントおよせくださいましてありがとうございました。>質問者さん、firedflyさん

    キャンセル

+2

FuncAnimation を使ったらできました。

matplotlib のデフォルトのヒストグラムは少し味気ないので、サンプルコードでは seaborn を併用しています。

import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from matplotlib.animation import FuncAnimation
from scipy import stats
# jupyter notebook の場合、コメントアウト
# %matplotlib nbagg

sns.set(style='ticks')
np.random.seed(42)

locs = np.linspace(15, 0, 16)  # 平均
fig, ax = plt.subplots(figsize=(4, 4))

def plot(i, locs):
    data = stats.norm.rvs(loc=locs[i], scale=2, size=7321)

    ax.clear()  # 図をクリア
    # kde=False 密度関数描画しない、bins=50 ビンの数、norm_hist ヒストグラム正規化
    sns.distplot(data, bins=50, ax=ax, kde=False, norm_hist=True)
    # ax.hist(data) setborn 使わない場合はこっち
    ax.set_ylim(0, 0.3)  # y 軸範囲固定


# 16枚のプロットを 1000ms ごとに表示
anim = FuncAnimation(fig, plot, frames=len(locs), fargs=(locs,), repeat=True)
anim.save('animation.gif', writer='imagemagick')

plt.show()

イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/31 14:23

    tiitoi様

    ご回答ありがとうございます.
    大変丁寧なご回答ありがとうございます.
    今後ともどうかよろしくお願いいたします.

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.69%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる