ヒストグラムと確率密度曲線を重ねて描くとき

概要

ある確率分布に従っていると思われるデータのヒストグラムと、その確率分布の確率密度曲線を重ねて描きたい場合がある。

RやPythonのmatplotlibではヒストグラムを描く機能が提供されていて、縦軸に度数をとったり、頻度(確率)をとったり選択できる。

縦軸に頻度を選んだ場合は、そのまま確率密度の曲線を重ねて描けばよいが、度数の場合に確率密度を重ねる手順でちょっと戸惑ったので、その手順を記録。

一様分布の例

例として、0 ≤ x <1の値が一様な確率で現れる確率分布を考える。その確率密度曲線は以下のようになる。

histogram_and_probability_density_1

(1)    \begin{equation*} \int_0^\infty u(x) dx = \int_0^\infty 1 dx = \left[ x \right]_0^\infty = 1 \end{equation*}

この一様乱数を1000個発生させて、その度数をヒストグラムに表した場合を考える。

仮に各値が実際に全く一様に表れたとすると、グラフは以下のようになり、階級幅によって高さの値が変わってくる。

histogram_and_probability_density_2

ヒストグラムの定義を、「ある階級に含まれる度数が階級幅と高さの積」と定義すれば、上の縦軸の値は1000となり、階級幅を変えても高さは変わらないが、縦軸の値を度数とすることで、階級幅によって縦軸の値が変化することになってしまう。

一般論

次に、一般の確率密度関数を考える。

histogram_and_probability_density_3

確率密度関数をf(x)として、区間[x, x + dx)の間の確率P(x)は次式のようになる。

(2)    \begin{equation*} P(x) = f(x) dx \end{equation*}

両辺に全度数Nをかけると、その値は値xの階級におけるヒストグラムの高さに相当する。

(3)    \begin{equation*} N P(x) = N f(x) dx \end{equation*}

階級iの高さをhi、階級幅をwiとすると、次式は以下のように書ける。

(4)    \begin{equation*} h_i = N w_i f(x) \end{equation*}

したがって、縦軸が度数表示のヒストグラムに確率密度曲線を重ねて描く場合、階級幅wが一定なら、確率密度関数に総度数Nと階級幅wを乗じて描けばよい。

 

Python3 – 文字列

内容の取得

文字列長

文字列長はlen()関数で得られる。

一文字取得

配列のように文字列中の位置を指定して、一文字取得する。開始位置は0。-1で最後の文字を指定でき、そこから順に-2、-3と先頭に向けて遡る。

イテレータ、リストによる連続取得

文字列はイテレータ。

list()関数で一文字ずつのリストが得られる。

文字の出現回数

count(sub[, start[, end]])は元の文字列の中の部分文字列subの出現回数を返す。startendはオプションで指定可能。

部分文字列の取得(スライス)

以下の記法で部分文字列を取り出せる。ただし取り出される最後の文字列は終了位置-1番目の文字。

具体例は以下の通り。

正のステップ値の場合は検索方向が左→右なので以下の関係でなければならない。

負のステップ値を使うと、最後尾から先頭へ向かって文字を取り出す。特にステップ値を-1とすると、1文字ずつ前へ向かって取り出すので、文字列の反転に便利。ただし開始位置と終了位置に注意が必要。

負のステップ値の場合には検索方向が右→左になるので、以下の関係でなければならない。

n文字目からm文字取得したい時。

スライスとfinde/rfindでは開始位置・終了位置の指定の考え方が違ってくる

特定文字での分割

split()メソッドは、指定した文字列で元の文字列を分解し、リストで返す。

partition()メソッドは指定文字の最初の出現位置で文字列を分割し、タプルで結果を返す。

内容の判定

stringオブジェクトの以下のメソッドは、それぞれの条件に合致した場合にTrueを返す。空文字列に対しては全てFalseを返す。

isalpha()
全ての文字がアルファベットの場合にTrue。
isdigit()
全ての文字が数字の場合にTrue。
isalnum()
全ての文字がアルファベットか数字の場合にTrue。
islower()
文字列中のアルファベットが全て小文字の場合にTrue。アルファベット以外の数字や記号が含まれていても判定対象外で無視されるが、アルファベットがまったく含まれていないとFalse。
isupper()
文字列中のアルファベットが全て大文字の場合にTrue。アルファベット以外の数字や記号が含まれていても判定対象外。アルファベット以外の数字や記号が含まれていても判定対象外で無視されるが、アルファベットがまったく含まれていないとFalse。
isspace()
全ての文字がスペースの場合にTrue。
istitle()
文字列中の区切られた部分文字列がタイトルケースの場合にTrue。

検索

find/rfind

find()は指定した文字列を検索し、そのインデックスを返す。存在しない場合は-1が返される。検索範囲の指定はスライスと同じで[開始位置, 終了位置)。

index()も同じ使い方ができるが、存在しない場合にValueError: substring not foundが返される。

rfind()は文字列の後方から検索する。開始位置と終了位置の意味はスライスと同じだが、開始位置だけ指定すると、そこから文字列の後方が検索範囲となってしまう点に注意。

find/rfindとスライスでの範囲指定が違ってくる点に注意

内容の変更

連結

+演算子で文字列を連結できる。

join()メソッドで、元の文字列オブジェクトを区切り文字にして、引数リストの各文字列要素を連結できる。

数値と文字列を連結する場合、Pythonでは自動変換されない。str()関数で明示的に文字列に変換する必要がある。

join()の方が推奨されているらしい。

繰り返し

*演算子で同じ文字列を複数回繰り返した文字列を得られる。

置き換え

replace(old, new[, count)メソッドは、部分文字列oldnewで置き換える。countが指定されると、先頭からその個数分だけ置き換える。

置き換え前後の部分文字列の長さが違ってもよい。置き換え後に空文字列を指定すると、文字列の削除に使える。

replace()のほか、sub()関数(reパッケージ)、str.translate()関数も使える。

なお、部分文字列をスライスで取得した書式を使って文字列を代入することはできず、エラーとなる。

書式・整形

センタリング・左寄せ・右寄せ

center()ljust()rjust()の各メソッドで、元の文字列を指定した幅の中でセンタリング・左寄せ・右寄せできる。デフォルトでは空いた場所がスペース(‘ ‘)で埋められるが、その文字を指定することが可能。

余白などの切り落とし

strip()lstrip()rstrip()各メソッドは、文字列の両端の指定文字を削除する。デフォルトでは空白が削除されるが、複数の文字を切り落とす対象として指定できる。

ただし、切り落とされるのは最も外側の文字列群だけであることに注意。

 

Python3 – map()関数

map()関数は以下の引数をとり、第1引数の関数を第2引数のリストに適用する。

たとえば数値要素のリストから文字列要素のリストを得るには以下のようにする。

注意点の一つとして、map()関数の結果はリストにはならずイテレータを返すだけなので、list()関数でリスト化する必要がある。

もう一つの注意点として、mapオブジェクトが一度リスト化されると、その内容は空になってしまう。

map()関数の第1引数は、ユーザ定義案数やlambda式でもok。

 

Python3 – スライスとfindでの位置指定の違い

文字列のスライスとfind/rfindで検索範囲の指定が違っていて混乱したので整理する。

文字列のスライスでは、次のように指定する。

このとき、stepが正なら前方から後方へ、負なら後方から前方へ向かって文字列が取り出されるが、そのときのstartとendの指定は、stepが正か負かで次のように違ってくる。

つまりstepが正の時は、前方から後方へ文字列が取り出されるのでstart<stop、stepが負の時は逆にstart>stopでなければならない。

一方、find/rfindについてはスライスと違ってくる。

(string, start, stop)の指定で、検索範囲の指定は常にstart < stopでなければならない。

 

 

 

 

Python3 – モジュールとパッケージ

概要

モジュールは共通して利用したい実行ファイルをインポートできるように配置したもの。パッケージはパッケージやモジュールをひとまとめにするもの。

  • ローカルの実行環境では、ディレクトリがパッケージになる
    • パッケージ内の__init__.pyファイルは特別なモジュールで、パッケージをインポートするだけでモジュール内の関数やクラスが使える
    • パッケージ下にモジュールファイルを置くと、[パッケージ名].[モジュール名]でモジュール内の関数やクラスが使える
  • パッケージのセットアップとインストール→今後

カレントディレクトリ下での使用

モジュールは普通のPython実行ファイルで作成。

たとえばカレントディレクトリに以下のようなファイルをtestmodule.pyとして置く。このモジュールには関数が1つとクラスが1つ定義されている。

そして同じカレントディレクトリにあるtest.pyを以下のように書く。

この時点でファイル構成は以下のようになっている。

ここでtest.pyを実行すると次のように表示される。無事インポートできて、モジュールで定義した関数、クラスとも使える。

実行後には新しいディレクトリが追加されて、以下のようになった。

カレントディレクトリ下でのモジュール管理のまとめ

  • 関数・クラスなどを含むモジュールをPythonファイルとして作成し、カレントファイルに置く
  • 利用側で、モジュール名(モジュールのファイル名から拡張子”.py”を除いた名前)を指定してインポート
  • [モジュール名].[関数名/クラス名]として利用

サブディレクトリ管理でのエラー

モジュールをカレントディレクトリではなくサブディレクトリに置いて管理したいので、以下のように置いてみる。

そして以下のように呼び出そうとしたが残念ながらエラー。

ただしこの実行後、サブディレクトリの下に新しいディレクトリがつくられていた。

これはimport文を読んだPythonがsubdirというパッケージを探しに行ったが見つからなかったためで、モジュールを置いたディレクトリをパッケージとしてPythonに認識させる必要がある。

パッケージ

パッケージ直下のモジュール

ディレクトリをパッケージとしてPythonに認識させるためには__init__.pyというファイルが必要になる。

たとえば以下のようなディレクトリ・ファイル構造として

__init__.pyファイル内容を前のtestmodule.pyと同じとする。

カレントディレクトリ下のtest.pyでは前は[モジュール名].[関数/クラス]としていたが、今回は以下のようにパッケージから呼び出すように変更。__init__.pyファイルに書いたモジュールは、パッケージ直下から呼び出される。

出力は以下の通りとなって成功。

またここでもmypackageディレクトリの下に新しいディレクトリが作成された。

パッケージ内のモジュール

testpackageディレクトリ下にothermodule.pyファイルを置く。

test.pyを以下のようにして実行。

以下のように実行される。

一度実行されると、pycacheディレクトリに新たなファイルが追加される。

 

 

待ち行列(M/M/1)の再現 – 時刻制御

考え方

時刻制御(time driven)による待ち行列の計算の考え方は次の通り。

  1. 時刻t0から始めて、以下時間間隔Δtずつ増やしながら計算を進めていく
  2. ある時刻で一様乱数の値がλΔtより小さければ到着が発生
    • 新たにトランザクションを生成し、到着時刻を記録し、システムに投入する
  3. ある時刻で到着が発生せず、システムにトランザクションがある場合、一様乱数の値がμΔtより小さければサービスが終了
    • サービス中のトランザクションのサービスを終了させ、終了時刻を記録する
    • このとき待ち行列にトランザクションがあれば、先頭のトランザクションのサービスを開始し、サービス開始時刻を記録する
  4. 予め設定していた時刻(またはトランザクション数)に達したら終了

queue-mm1-time-driven-fig1

たとえば上の図でトランザクション②に注目すると、

  1. 時刻t4でトランザクションが発生
  2. t7でトランザクション①のサービスが終了し、②のサービスが開始
  3. t8でサービス終了

イベント制御のケースではRを用いたが、今回はPythonを使う。なおM/M/1待ち行列の解析的アプローチはこちら

待ち行列システムのクラスをつくり、システムの外からはトランザクションの到着、サービス終了の操作を行うだけで、内部的に処理が進むようにする。

クラス構成

待ち行列システムを表すQueueSystemクラス、システムのサービス窓口を表すServiceクラス、システム中の1つのトランザクションを表すTransactionクラスから構成される。システム中の待ち行列はインスタンス変数のリストとして持つ。

M/M/1型の行列システムとして、QueueSystemクラスは1つのServiceオブジェクトと1つの待ち行列リストをメンバーに持つ。

トランザクションの到着の指示が出るとTransactionオブジェクトが生成され、システムに登録される。

その一方で実行中のサービス終了の指示が出るとサービスを受けているトランザクションが解放される。

各Transactionオブジェクトに対して、システムの到着、サービスの開始、サービスの終了の時刻が自動的に登録される。

Transactionクラス

  • 識別子としての整数値のindexのほか、到着・サービス開始・サービス終了の時刻、到着時のシステム内トランザクション数と待ち行列長をメンバーとして持つ
    • これらのメンバーに対しては直接アクセスして参照・代入する
  • このクラスのオブジェクトの文字列表現を__str__メソッドで実装している

Serviceクラス

  • privateなメンバ_transactionを持ち、これはサービス中のトランザクションを保持する
    • サービス中のトランザクションがない場合はNone
  • サービス窓口が空いているか(vacant)、サービス中か(occupied)を判定するメソッドを持つ
  • サービス中のトランサクションへの参照を返すtransaction_in_service()メソッドを持つ
  • サービス開始時の処理を行うstart()メソッドを持つ
    • サービスを開始するTransactionオブジェクトとサービス開始時刻を指定する
  • サービス終了時の処理を行うend()メソッドを持つ
    • サービス終了時刻を指定し、サービス中のTransactionオブジェクトにその時刻を設定し、オブジェクトへの参照を返す

QueueSystemクラス

メンバとなるインスタンス変数は4つ

  • 待ち行列リスト_queue
  • サービス窓口オブジェクト_service
  • 到着済みのトランザクションを登録する_arrived_transaction_list
  • サービス完了済みのトランザクションを登録する_departed_transaction_list

getter系のメソッドは

  • システム中にあるトランザクション数を返すtransaction_number_in_system()
  • 待ち行列長を返すqueue_length()
  • システム中にあるトランザクションへの参照を返すtransaction_in_service()
  • 待ち行列中にあるトランザクションへの参照を返すtransaction_in_queue()
  • 到着済みのトランザクション数を返すarrived_transaction_number()
  • 完了済みのトランザクション数を返すdeparted_transaction_number()
  • 到着済みのトランザクションのn番目を返すarrived_transaction()
  • 完了済みのトランザクションのn番目を返すdeparted_transaction()

システムを操作するメソッドは

  • システムに到着したトランザクションを登録するarrive()
  • サービスが終了したトランザクションをシステムから取り出すdepart()

システムの状態を表示するメソッドとして以下の2つを実装している。

  • システムの現在の状態を表示するdisplay_current_status()
  • サービスを完了したトランザクション群の各パラメータを表示するdisplay_transaction_summary()

動作テスト

上記のクラス群を使って以下を実行。規則正しく刻まれた時刻に4つのトランザクションが到着し、その後規則正しくサービスが終了していく様子と、その結果を表示させている。

実行結果は以下の通りで、意図したようにトランザクションの到着に従って登録され、終了指示によって待ち行列・サービス状況が変化している。

時刻制御の実装

考え方

概略の流れは以下の通り。

  • 計算に必要なパラメータを設定する
    • 到着率 λ
    • サービス率 μ
    • 計算時間範囲 t_max
    • 計算時間間隔 dt
  • 時刻tが0からt_maxになるまでdtずつ増やしながら以下を実行(以下、randは[0, 1)の一様乱数値)
    • λdt < randならトランザクションが一つ到着して次の時間ステップへ
    • サービス中のトランザクションがあって、μdt < randならトランザクションが一つ終了して次の時間ステップへ
  • トランザクションのサマリーを表示

テスト実行

実行結果は以下の通り。

実行結果

到着時間間隔の分布の確認

およそ1000個のデータを発生させるため、観測時間を10000秒にして計算し、到着したトランザクションの到着時間間隔を確認した。確認部分のコードの未以下に示す。

実行結果は以下の通りで、指数分布の理論値とよく合っている。

matplotlibのhist関数はnormed=Trueとした場合に期待した動作をしないことがあるが、このケースではうまくいっている。

queue-mm1-time-driven-arrival-interval

平均トランザクション数・平均待ち行列長など

10000秒の観測時間で、システム内の平均トランザクション数、平均待ち行列長と、それらに対する平均応答時間、平均待ち時間について計算し、λ、μから計算される理論値と比較した。

計算結果は以下の通りで、かなり整合している。

各実行結果の傾向

およそ1000個のデータに相当する10000時間単位まで、時間ステップ0.1で計算を行い、理論値との違いを確認した。

ρ = 0.5のケース

λ = 1/10、μ = 1/5、ρ = 0.5のケース。平均トランザクション数と平均待ち行列長の理論値は、L = 1、Lq = 0.5で、これらに対する待ち時間はT = 10、Tw = 5。

トランザクション数に関する計算結果は理論値とかなりよく合っている。

L T 1/λ
1 1.19667319 11.32504892 9.46377759
2 1.14047151 10.43113949 9.14633938
3 1.01360544 9.81409135 9.68235860
4 1.10038241 10.86032505 9.86959165
5 1.21322160 11.32597765 9.33545664
6 0.91197544 9.24390993 10.13613912
7 0.92921236 9.52532403 10.25096570
8 0.92452830 9.96117180 10.77432870
9 0.87450199 9.12868526 10.43872440
10 0.93159923 9.19017341 9.86494311
AVG 1.02361714 10.08058469 9.89626249
STD 0.12142639 0.81382679 0.48579815

待ち行列長は若干ばらつきが大きくなっている。

Lq Tq 1/λ
1 0.67906067 6.27358121 9.23861664
2 0.63555992 5.54204322 8.71993819
3 0.50728863 4.87269193 9.60536397
4 0.57456979 5.73049713 9.97354408
5 0.63314711 6.07476723 9.59455888
6 0.42169908 4.29559877 10.18640773
7 0.46660020 4.73389831 10.14551282
8 0.44985104 5.01837140 11.15562921
9 0.38844622 4.27151394 10.99641011
10 0.44797688 4.41705202 9.85999996
AVG 0.52041995 5.12300151 9.94759816
STD 0.09722322 0.70052360 0.70110321

ρ = 0.9のケース

λ = 1/10、μ = 1/9、ρ = 0.9のケース。平均トランザクション数と平均待ち行列長の理論値は、L = 9、Lq = 8.1で、これらに対する待ち時間はT = 90、Tw = 81。

トランザクション数に関する計算結果は、理論値の周りに大きくばらついている。

L T 1/λ
1 7.59815951 76.92402863 10.12403445
2 7.72051010 86.82890542 11.24652443
3 7.96595331 80.47908560 10.10288191
4 5.22616633 54.55070994 10.43799728
5 5.11700000 52.88450000 10.33505961
6 8.65760322 89.70845921 10.36181226
7 6.19872476 65.22922423 10.52300703
8 10.88111888 110.54815185 10.15963092
9 8.41194645 86.59763131 10.29460088
10 18.80710660 193.82274112 10.30582456
AVG 8.65842892 89.75734373 10.38913733
STD 3.75303129 38.38222105 0.31302798

待ち行列長の計算結果もかなりばらついている。

Lq Tq 1/λ
1 6.71165644 67.97975460 10.12861061
2 6.82678002 77.21158342 11.31010274
3 7.02334630 71.38190661 10.16351801
4 4.35902637 45.64036511 10.47031177
5 4.22800000 44.04650000 10.41780984
6 7.72205438 80.19728097 10.38548513
7 5.31880978 56.14229543 10.55542457
8 9.95704296 101.38201798 10.18194040
9 7.49742533 77.17693100 10.29379122
10 17.87005076 184.15197970 10.30506193
AVG 7.75141923 80.53106148 10.42120562
STD 3.73662161 38.18242499 0.32416633

ρが高くなる(混雑してくる)と計算結果と理論値のかい離が大きくなるのは、(言語が異なるとはいえ)同じ傾向。時刻制御では計算時間間隔を0.1でも試してみたが、あまり傾向は変わらない。

 

 

Python3 – ジェネレータ

関数とyield文によるジェネレータ

イテレータ__next__メソッドでコレクションの要素を取り出すのに有用なのに対して、ジェネレータはyield文で任意の結果を順次返していく。関数内にyield文を書くと、その関数はジェネレータになる。

ジェネレータは以下のように構成する。

  • ジェネレータは関数として定義され、その関数への参照として取得する
  • forブロックのループのたびにジェネレータの中のyieldが順次1つずつ呼び出される
  • 呼び出せるyield文がなくなるか、forブロックで強制的に抜け出したときループが終了

先の例ではjoin文が4回だけ呼び出されるので、それが呼べなくなったときにループが終了する。これに対して次の例では、ジェネレータは無限に結果を生成し続け、forブロックで条件に合致したときにループを終了させている。

ジェネレータ式

内包表現と同じ表現を()内に書くと、要素を一つずつ生成するジェネレータオブジェクトを返す。

 

Python3でヒストグラムとplotの重ね合わせ

指数分布に従う乱数を生成するnumpy.random.exponential()関数の確認のため、同関数で発生させた乱数群のヒストグラムと、指数分布の確率密度関数のグラフを重ねて比較してみた。

コードは以下の通りで、ヒストグラムのrangeと関数計算のrangeを合わせている。

実行結果は以下の通り。

python-matplot-pyplot-exponential-random

 

Python3 – numpy.random – 乱数

ライブラリ

乱数を扱うには、numpy.randomライブラリが必要。

一様乱数 – rand()

引数なしのrand()関数は[0, 1)の一様分布に従う乱数を1つ発生させる(random()関数でも同じ)。

引数を1つ指定すると、その個数の乱数を要素に持つリストを返す(random()関数でも同じ)。

引数を2つ指定すると、その行数・列数の2次元配列の乱数を返す。引数がn1、n2、n3、・・・の場合、n1×n2×n3×・・・の多次元配列を返す。

一様整数乱数 – randint()/random_integers()

randint()

1~3個の引数をとり、終値未満の整数乱数を返す。終値を含まないため、配列からランダムに要素を取り出したい時に便利。

  • 引数が1つ(n)の場合、[0, n)の範囲の整数乱数を1つ返す
  • 引数が2つ(m, n)の場合、[m, n)の範囲の整数乱数を1つ返す
  • 引数が3つ(m, n, s)の場合[m, n)の範囲の整数乱数s個を要素とするリストを返す

サイズをリストで指定した場合、それに対応した次元・要素数の多次元配列で乱数列を返す。

random_integers()

引数の構成はrandint()と同じだが、

  • 上限値を含む、[start, end]の範囲の整数乱数
  • 引数の意味はrandint()と同じ

各種確率分布

exponential(lmd) – 指数分布
lmdは平均の逆数で、ポアソン過程でいう到着率、サービス率にあたる。

Python3 – matplotlib.pyplot

plot()

plot()に引数を与えて実行し、show()を実行するとGUIでグラフが表示される。

python-matplot-pyplot-yonly

表示結果で以下の点に留意。

  • x軸は0~3の範囲、y軸は1~4の範囲
    • plot()に引数リストを1つだけ与えると、それはy軸の値と解釈される→[1, 2, 3, 4]
    •  x軸の値は自動的に生成される
      • y軸と同じ幅でPythonのレンジの開始値0から始められる→[0, 1, 2, 3]
  • 直線が描かれている
    • オプション指定をしない場合、各点が直線で結ばれる

 

グラフの例

python-matplot-pyplot-cossin

棒グラフ

基本形

pyplot.bar()に2つの引数を与えるのが基本形。

  • 第1引数は横軸のリスト
  • 第2引数は横軸リストの要素に対応した値

python-matplot-pyplot-bar01

bar(x, y)の横軸xの要素はfloatでなければならないが、これを文字表記に変更する。

python-matplot-pyplot-bar02

ただし項目名が左寄せになっているので、これらをセンタリングする。

なおxticksを指定せず数値のままでセンタリングすると、xで与えた数値が中央に来るよう調整される。

python-matplot-pyplot-bar03

棒の位置や幅のコントロール

デフォルトでは棒の位置はxで与えた数値に左寄せされる。またwidth=で棒の幅を指定することができる。

以下のように指定することで、ヒストグラムのような図を描くこともできる。

python-matplot-pyplot-bar04

hist()関数の戻り値を使って、以下のように書けるが、時にグラフの両側に空きができてしまうので、さらに検討が必要。

 

ヒストグラム

pyplotで簡単にヒストグラムが描ける。デフォルトでは縦軸は度数。

python-matplot-pyplot-histgram-normed-false

縦軸を頻度にしたい場合は、normed=TRUEオプションをつける。

縦軸が小数になっていて、各階級の頻度が1/10bins→0.1程度になっていることに注意。

python-matplot-pyplot-histgram-normed-true

頻度分布について

上の例ではうまく頻度分布が表示されているが、以下のようにうまくいかない場合がある。

python-matplot-pyplot-histgram-normed-trouble

ヒストグラムとplotの重ね合わせ

ヒストグラムのrankの範囲とplotのx軸の範囲を同じにすれば、こちらの例のように重ねて描くことができる。

ヒストグラムの戻り値

hist()は3つの戻り値を返す。

bin_count
各ビンの度数。
bin_position
ビン数とrangeに応じたビンの位置。
patches
各ビンのオブジェクトの配列。ビンごとに色を変えるなどの操作ができる。