在宅リーマン勉強日誌

忘れがちな毎日のメモ。

【6日目】KaggleでPythonの勉強してみた【独学】

KaggleのコースでPythonの勉強をしています。 前回までは毎日少しずつ進めていましたが、とうとう止まってしまいました。 遅れを取り戻すべく、できるだけ進めていきたいです。

勉強内容

Lesson:Loops and List Comprehensions (Exercise)

勉強時間:60分

TutorialはCourseの和訳(概略)、Tutorialは試行とどう考えていたかを記しています。

Lesson:Loops and List Comprehensions (Exercise)

Q1は一昨日終わらせたので、続きから。

Q2-a:[1, 2, 3, 4] > 2がどのように実行されるかを考えよ

Q2-b:RとPythonは二つのリストを比べ、正誤を返すライブラリと同じ挙動の関数を作成せよ

任意の数nよりも大きいエレメントかどうか(正誤)を返す関数を作りなさい。 Q2-aはエラーを出してしまうので、これができる関数をQ2-bで作りなさい、問題です。 最初思いついたのは、閾値より大きいと書き出しをするというもの

def elementwise_greater_than(L, thresh):
    """Return a list with the same length as L, where the value at index i is 
    True if L[i] is greater than thresh, and False otherwise.
    
    >>> elementwise_greater_than([1, 2, 3, 4], 2)
    [False, False, True, True]
    """
    return [ans for ans in L if ans>thresh]
print(elementwise_greater_than([1, 2, 3, 4], 2))

ただこれでは、[3,4]としか印字されないです。 ans>threshの時はTrueを返して、としたいけど、ifの中で返すのでは最終的なアウトプットが出せないのは前の問題の段階で自明。

挙動を確認するために、やはりいきなり1行で書くのはよくないですね。 複数回のエラーを経て、下記コードにたどり着きました。

def elementwise_greater_than(L, thresh):
    """Return a list with the same length as L, where the value at index i is 
    True if L[i] is greater than thresh, and False otherwise.
    
    >>> elementwise_greater_than([1, 2, 3, 4], 2)
    [False, False, True, True]
    """
    ans  =[]
    for judge in L:
        if judge >thresh:
            ans.append(True)
        else:
            ans.append(False)
    return ans

やはりまだPythonのリスト内条件文に慣れない…。 正答例と方向性は同じだけど、圧倒的にスマートでないことがばれるコードを書いているので、正答例をまねするように書くようにせねば。 瞬時にこれが思いつくようになるのにあとどれくらいかかるだろうか。

Q3:関数を完成させよ。

ある期間に提供された食事リストが与えられる。2日連続で提供された食事であればTrueを、それ以外はFalseを返す

この食事リストというものがわからないので、エラーを吐かせるためにいくつかコードを入れてみると、テストでは以下のリストが入ってくるみたい。

Falseの例:meals=['Egg', 'Spam']

Trueの例:meals=['Spam', 'Eggs', 'Spam', 'Spam', 'Bacon', 'Spam']

配列を返す、とかではなく、純粋にTrue, Falseを返せばいいからQ1が少し使えるかなと思いました。 ただ、一つ前の配列をどう入れるかがわからない。(Cとかだとi-1とか使うけど、ださいかなと思って却下。) return any([serve == meals for serve in meals]) この一行のmealsの部分を一つ前の内容が入るようにすればいいのですが…。

def menu_is_boring(meals):
    """Given a list of meals served over some period of time, return True if the
    same meal has ever been served two days in a row, and False otherwise.
    """
    past = ""
    for serve in meals: 
        if serve == past:
            return True
        past = serve
    return False  

なんかかっこよくしたかったけど…と思ったけど、正答例もあまり変わらず。 ダサいと思って却下したものが正答例だったのでこれはおっけー! ちょっとだけよくできました。(という気分)

Lesson:Loops and List Comprehensions (Exercise)

3日にわたって続けた

Q4:スロットマシンチャレンジestimate_average_slot_payout(難問)

スロット関数を動かすと、何ドル勝ったかが出ます。基本的には0が返ってきますが、時折多額が返ってきます。平均的にいくらの原因があるかはカジノで秘密とされていますが、モンテカルロ法という方法を使うことで、期待値=平均的な結果を出すことができます。以下の関数を完成させてスロットマシンの1プレイ当たりの期待値を計算してください。

これ、各セクションの難問でカジノ関係はいつぞやからか、Python Challenge Casinoって呼ばれてるみたい笑

あと聞きなれない「モンテカルロ法*1という言葉。(情報系の学生だと散々聞いてるかもしれないですが、近似の方ではなくギャンブルな方のやつです)

でもこれ読み進めていくと、モンテカルロ法使っているのかわからないんですよね~。なのでモンテカルロ法の詳細は脚注のページに任せるとして、関数自体は平均値ですね! シンプルにできますが、解が載っていないので、自分でやった例を書いておきます。

def estimate_average_slot_payout(n_runs):
    """Run the slot machine n_runs times and return the average net profit per run.
    Example calls (note that return value is nondeterministic!):
    >>> estimate_average_slot_payout(1)
    -1
    >>> estimate_average_slot_payout(1)
    0.5
    """
    sum_n = 0
    for i in range(n_runs):
        sum_n += play_slot_machine()
    return (sum_n-n_runs)/n_runs

他の回答例はリンク先へ。 著者のColin Morrisもコメントしていて、激熱ですね。 今回の問題はリスト内でやる方法ももちろんありますが、かなりの試行数を行うことでやっと平均的な0.025の値に近づいていくので、リストでメモリを食うよりもループの中で都度計算した方がよいとのこと。 www.kaggle.com

同じような回答だけど、解が全然違うという場合も多いと思います。 試行回数が足りない可能性があります。

ではどのくらい必要なんでしょうか。 f:id:KashiwaMOCHI:20200501091420p:plain

あくまで乱数の平均なので、試行回数が低い方は本当にあてにならない数値ではあるんですが、0.025に収束し始めるのが、試行回数が10の7乗を越したあたりに見えますね。100万回やれば、ようやく確実にプラスになるくらいです。全然勝てないですね。

しかも勝てたとしても、3円くらい笑 パチンコ業界が儲かるわけだ。

*1:モンテカルロ法とは、昔モナコモンテカルロのカジノで、この方法を使ってカジノをつぶしたといわれている方法です。(本当かな) 有効なゲームは、ブラックジャックバカラ・ルーレット、なので今回のスロットも一応近いっちゃ近いのかなー。スロットが有効なのかはわからん。www.xn--lck0a5auxk.jp