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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

統計

統計は、集団現象を数量で把握することです。また、調査で得られた性質や傾向を数量的に表したデータのことをいいます。

Q&A

解決済

2回答

1780閲覧

モンテカルロ法での最大値

Nyann

総合スコア12

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

統計

統計は、集団現象を数量で把握することです。また、調査で得られた性質や傾向を数量的に表したデータのことをいいます。

0グッド

0クリップ

投稿2020/05/04 09:04

編集2020/05/04 09:35

やりたいこと

Pythonを利用して、モンテカルロ法の計算を行っています。

考えている問題点

コードは下記に記した通り、各パラメータに分布を与えて数値を発生させたうえで計算する方式を考えてます。

当初はそれらしい結果が出てきたので上手くいっていると思っていたのですが、比較すべき統計値との乖離が大きく、結果を分析してみたところ現状の計算で出てくる最大値(out[6]:577256)の結果と各パラメータの最大値で無理やり計算した結果(Out[7]:625780)の乖離が原因では無いかと考えています。
現状のコードではリストに収納された数値同士の組み合わせでの計算なので、必ずしもすべてのパラメータが最大値のケースは発生しないことが原因なのではと思っています。

考えている対応策

対応策としては、1.無理やり全通り計算させるような方式に変更するか、2.乱数の生成数(現状10000)と実際の計算トライアルの数を異なるものとすれば(例:乱数の生成を1000通り、本計算の回数を10万通り)、改善されるのではと思っていますが、Python初心者でしてうまいコードの書き方が判りません。

アドバイスよろしくお願い致します。

###Python
コード
#Monte Carlo
In [1]:import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('darkgrid')

#Properties
In [2]:trials = 10000

A = np.random.uniform(0.1, 0.3, trials)
B = np.random.uniform(500, 700, trials)
C = np.random.triangular(10, 20, 30, trials)
D = 100
In [3]:x = A * B * C * D

x = x.astype(int)
count = np.bincount(x)
mode = np.argmax(count)
In [4]:fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.hist(x, bins=18, density=False, rwidth=0.6, Color="steelblue")
ax.set_title('Test')
ax.set_xlabel('x')
ax.set_ylabel('Frequency')

Out[4]:Text(0, 0.5, 'Frequency')
In [5]:print('P90: {:.0f} '.format(sorted(x)[int(trials * 0.1)]))
print('P50: {:.0f} '.format(sorted(x)[int(trials * 0.5)]))
print('P10: {:.0f} '.format(sorted(x)[int(trials * 0.9)]))
print('Mean: {:.0f} '.format(int(np.mean(x))))
print('Stdv: {:.0f} '.format(int(np.std(x))))
print('Mode: {:.0f} '.format(int(np.argmax(count))))

P90: 132027
P50: 229595
P10: 366067
Mean: 241177
Stdv: 90505
Mode: 199554

In [6]:max(x)
Out[6]:577256

In [7]:x2 = max(A) * max(B) * max(C) * D
x2
Out[7]:625780.9249227662

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ForestSeo

2020/05/04 09:07

コードは ```Python コード ``` このような形で投稿してください
Nyann

2020/05/04 09:36

ありがとうございます。ご指摘頂いた点修正させていただきました。 よろしくお願いします。
Nyann

2020/05/05 06:13

ありがとうございます。次回投稿する際にはこちらを確認したうえで投稿させていただきます。
m.ts10806

2020/05/05 06:16

いえ、次回では確実に忘れるので、今。
guest

回答2

0

コードの問題というよりも確率的起こりうるやむを得ないことかと思います。

統計量の計算対象であるxは、A×BとCによって生成されていると分解できます。(Dは定数なので、分布に影響しないので省略します)
このうちCは、使用している分布とパラメータから正規分布と似た特性(平均と最頻値が同じ・平均を中心に分布が左右対称)を持つのでモンテカルロシミュレーションの結果も平均に収束します。一方、A×Bは台形のような分布と取ります。質問に記載の1万件だとわかりにくいのですが、10万件で試行すると80~150が一様分布を示します。つまり、A×Bについては80~150の範囲については安定性に欠けるため、計算上の平均である120にうまく収束しないことが予測されます。そのため、A×B×Cもそれなりの誤差が生じてしまうのではないでしょうか。

対策としては、試行回数を増やすことが一番いいのではないでしょうか。つまり、A×Bの分布の一様分布が真に一様分布と同じようになれば、不安定性がなくなるので2400にうまく収束すると思います。

投稿2020/05/05 00:57

R.Shigemori

総合スコア3376

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nyann

2020/05/05 06:01

ありがとうございます。試行回数を増やしても1000回以上では大きな差は得られませんでした。Pythonコードに大きな問題が無いことが判ったので、比較対象にしているシミュレーション値の算出に問題がある可能性が高いと考えています。先方に確認中ですので判明しましたら情報更新させていただきます。
guest

0

ベストアンサー

現状の結果のどの部分に問題を感じているのか不明なのですが、現状の分布でシミュレートすると、99.9パーセンタイル値が 539000 あたりに、99.95パーセンタイル値でさえも 553000 あたりとなるので、たかだか 10000個のサンプルで最大値が 577256 となるのは妥当なのではないでしょうか。


【コメントを受けてソースコードの変更】

Pyhon

1 2# Monte Carlo 3import pandas as pd 4import numpy as np 5from scipy import stats 6import matplotlib.pyplot as plt 7import seaborn as sns 8import statistics 9sns.set_style('darkgrid') 10 11# Properties 12trials = 10000000 13A = np.random.uniform(0.1, 0.3, trials) 14B = np.random.uniform(500, 700, trials) 15C = np.random.triangular(10, 20, 30, trials) # 三角分布 16D = 100 17 18x = A * B * C * D 19 20x = x.astype(int) 21 22fig, ax = plt.subplots() 23ax.hist(x, bins=18, density=False, rwidth=0.6, Color="steelblue") 24ax.set_title('Test') 25ax.set_xlabel('x') 26ax.set_ylabel('Frequency') 27# plt.show() 28 29mean = np.mean(x) 30std = np.std(x) 31mode, _ = stats.mode(x) 32p0,p10,p50,p90,p100 = np.percentile(x, q=[0,10,50,90,100]) 33 34print(f'P90: {p90:.0f}') 35print(f'P50: {p50:.0f}') 36print(f'P10: {p10:.0f}') 37print(f'Mean: {mean:.0f}') 38print(f'Stdv: {std:.0f}') 39print(f'Mode: {mode[0]:.0f}') 40 41# 基本統計量だけならば下記のコードでも良い(MODEはないけど) 42print(pd.Series(x).describe()) 43# scipy.stats を使って基本統計量を出した場合は歪度と尖度も出る 44print(stats.describe(x))

投稿2020/05/04 12:21

編集2020/05/05 02:32
magichan

総合スコア15898

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nyann

2020/05/04 13:35

ありがとうございます。確かに統計的にはそうなると思います。 投稿に使用している数字・計算は模擬なのですが、実際のデータではMean、Median共に基準に対して9%程度の乖離が出ているので投稿致しました。 それほどの開きがあると手法(計算式等)自体に問題がある可能性も高いと思うのでそちらも検証するつもりではおりますが、まずは現状のPythonコードに問題無いか、また投稿させて頂いた対応策ではどのような結果が得られるかも確認したいと考えております。
magichan

2020/05/05 03:01 編集

ソースコードに関しては大きな問題は無いかと思いますが、モードやパーセンタイルの計算などにおいて時間がかかる処理を行っております(サンプル数を大きく取ると顕著になるかと思います。) パーセンタイルの計算はnumpy.percentile() を、モードの計算は scipy.stats.mode() を使うことをおすすめします。 また基本統計量は pandas.Series.describe() や scipy.stats.describe() などでも導けますので、こちらを使ってもよいかとおもいます。 *回答にサンプルを追記しておきました
magichan

2020/05/05 03:00

あと実際のデータとシミュレート上のデータの平均の乖離の件ですが、こちらは9%という値はあまり意味を持たないのかと思います。シミュレートにて論理値が得られるのであれば、統計的にその母集団の平均値との実際の標本平均との間で平均値の検定を行い、その差が有意かどうかで判断するべきではないでしょうか。
Nyann

2020/05/05 05:58

コードのアドバイスありがとうございました。確かにこちらの方がスマートなので活用させていただきます。 今回比較対象としている結果もシミュレーション値(おそらくCrystal Ball)でして、基本的には全く同じ計算を行っていることとなるため、その割には乖離が大きいと感じています。コードに問題が無いとすれば、適用している数式(若しくは入力値)に問題があるか、先方のミスの可能性があると思いますのでその点先方へ確認しているところです。 今回のようなケース、もしも各計算式に入力されている数値を出力することが出来れば検証しやすいと思うのですが、その場合どのようなコードを入力すればよいかアドバイス戴けますと大変助かります。よろしくお願いいたします。
magichan

2020/05/05 06:37

コードで1点気になる点があるとしたら x = x.astype(int) の部分でしょうか。この処理を入れている理由がわからないので何とも言えないのですが、計算で得られるデータは浮動小数なのに対し全てのデータを整数に丸めていますので、この処理が平均値などに影響を与えることは間違いありません。 > 各計算式に入力されている数値を出力することが出来れば検証しやすい 具体的に何の値をどのように表示したいのでしょうか?
Nyann

2020/05/05 08:13

x = x.astype(int)ですが、削除しました。以前のコードでModeを計算するために入れたコードでして、修正頂いたコードでは不要と判りました。 出力については変数の出力を意味しておりました。下記のコードの通り、変数のdfを作成しdf.to_csvで出力を試みているのですが、出力されません。コードの読み込みはうまくいきます。他のネットで出ているサンプルコードですと問題なく出力されます。 データ数の問題かと思い、trialを100に減らしてみましたが改善されませんでした。 -------- df = pd.DataFrame([A, B, C],index= ["A","B","C"]) df = df.T df.to_csv("XXXX.csv")
Nyann

2020/05/05 08:21

失礼しました。よく見たところJupyterのHomeからはcsvファイルが確認できるので出力はされているようです。確認不足で申し訳ありません。
magichan

2020/05/05 08:44

x = A * B * C * D の次の行にでも df = pd.DataFrame({'A':A, 'B':B, 'C':C, 'D':D, 'x':x}) df.to_csv('XXXX.csv') のようにしてみてください
Nyann

2020/05/05 11:03

ありがとうございます、見事に配列されました。transformを使わなくてもこれで出来てしまうんですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問