KaggleでPythonを学習しよう。

#はじめに

２０２１年の８月に[30 Days of ML](https://www.kaggle.com/thirty-days-of-ml)がKaggleで開かれました。

私もでしたが、この機会にKaggleを始めた方も多いのではないでしょうか？

３０日間の最初の１週目は[Pythonのコース](https://www.kaggle.com/learn/python)を終わらせよう、ということでした。

この時にDiscord(現在は招待リンクがうごいていません)でいろいろと回答とかしていましたので、Discordサーバが生きているうちにまとめておきます。

# [Python](https://www.kaggle.com/learn/python)
Learn the most important language for data science.
ということで

1. Hello,Python
1. Functions and Getting Help
1. Booleans and Conditionals
1. Lists
1. Loops and List Comprehensions
1. Strings and Dictionaries
1. Working with External Libraries 

の７項目がTutorialとExerciseの２つ準備されています。

Tutorialでその項の概要を学習したのち、Exerciseで実際に自分でコードを書いて確認していく流れになります。

日本語のページを作ってくれた方がいまして、Pythonが初めての方はこちらがいいでしょう。

[Kaggle’s 30 Days of ML Daily Assignments](https://docs.google.com/document/d/1qgoWaA8glKOBt-C7dBMUfX86Tt8tRsuKY1CUabafwok/edit)
[KeitoTanemura](https://twitter.com/KeitoTanemura)さん、ありがとうございます。

ぱっと見Jupyter Notebookなkaggle Notebookで作られているので
google colaboratoryから来た人は[Keyboard Shortcuts for Kaggle Kernels](https://www.kaggle.com/naushads/keyboard-shortcuts-for-kaggle-kernels)を参照してください。

##　Exercise実施上の注意点
Exerciseをやっていくと「１００％にならないよ〜」という声が多数Discord上に溢れていました。
その時のテンプレートの回答は「qX.check()をすべて終わらせてください」でした。

あとは「「qX.check()が動かないよ」というやつ。
これは「Click Run all」。　ほんと多かったですね :sweat: 


では次から各章の概説を紹介します。

## [Hello,Python](https://www.kaggle.com/colinmorris/hello-python)
Pythonの四則演算や文字列操作について紹介されています。
### [Exercise: Syntax, Variables, and Numbers](https://www.kaggle.com/kawamatatoshihisa/exercise-syntax-variables-and-numbers)
このExerciseはどちらかというと、このExercise自体に慣れてもらうことを目指しているみたいで、必須モジュールのインストールやヒントの見方をまずは練習しています。

でも、問題があれば解きたくなるもの、特にヒントなんかいらないぜ！

ということでQ0の「What is your favorite color? 」で「わからないよ〜」という声がDiscord上に結構でていました。

ここは素直に`q0.hint()`や`q0.solution()`を実行してください。
なお、`⌘ + /`でその行をコメントに切り替えられるので使うと便利です。

## [Functions and Getting Help](https://www.kaggle.com/colinmorris/functions-and-getting-help)
ヘルプの見方と関数について。

## [Exercise: Functions and Getting Help](https://www.kaggle.com/kawamatatoshihisa/exercise-functions-and-getting-help)

Q4は、バグがあるのを直すということで、エラーをしっかり見ることが必要でした。

`TypeError:abs() takes exactly one argument (2 given)`
`SyntaxError: 'return' outside function`
なので、`abs`は引数を一つに、`return`はインデントをしっかり作る必要があります。

エラーが何を意味しているのかをよく見れるようになると、それだけで初心者から一歩上に行ける気がしています。

## [Booleans and Conditionals](https://www.kaggle.com/colinmorris/booleans-and-conditionals)
真偽値(`True`と`False`)と条件判定について

初めてPythonする人だと問題ないんだろうけど、他の言語やっているとごっちゃになりそう。

## [Exercise: Booleans and Conditionals](https://www.kaggle.com/kawamatatoshihisa/exercise-booleans-and-conditionals)

Q1は引数がマイナスだと`-1`プラスだと`1`０だと`0`にする関数をつくれということ
kaggleの解法は素直に`if``elif``else`で条件わけしていたので、別解法

```python:q1_ans.py
def sign(num):
    return [-1,1][num > 0] if num !=0 else 0
```
３項演算子と`True == 1`で`False == 0`を利用してみました。
これって`numpy.sign`の動きそのものですね。

Q5cは[`^`がXOR](https://note.nkmk.me/python-bit-operation/)なので、こちらを使ってもいいです。

Q7の`should_hit`はディスカッションが別に立つくらいの問題なので、深入りしない方がいいです。
ディスカッションは[こちら](https://www.kaggle.com/getting-started/58735)

## [Lists](https://www.kaggle.com/colinmorris/lists)
ListとTupleの操作について。

結構レアなメソッドとか使っているので本家のサイトを適宜みたほうがいいと思います。
[5. データ構造](https://docs.python.org/ja/3/tutorial/datastructures.html)とか[整数型における追加のメソッド](https://docs.python.org/ja/3/library/stdtypes.html#int.bit_length)

## [Exercise: Lists](https://www.kaggle.com/kawamatatoshihisa/exercise-lists)

Q3は　

```python:q3_ans.py
r = ["Mario", "Bowser", "Luigi"]
r[0],r[-1] = r[-1], r[0]
r
```
を利用するとスッキリかけます。

Q5は公式回答の動作がよくわからないという人が結構いましたので[pythontutor.com](http://pythontutor.com/visualize.html#code=party_attendees%20%3D%20%5B%27Adela%27,%20%27Fleda%27,%20%27Owen%27,%20%27May%27,%20%27Mona%27,%20%27Gilbert%27,%20%27Ford%27%5D%0A%0Adef%20fashonably_late%28lst%3Alist,%20name%3Astr%29%20-%3E%20bool%3A%0A%20%20%20%20order%20%3D%20lst.index%28name%29%0A%20%20%20%20return%20order%20%3E%3D%20len%28lst%29%20/%202%20and%20order%20!%3D%20len%28lst%29%20-%201%0A%20%20%20%20%0Afashonably_late%28party_attendees,%20%27May%27%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)で一つ一つ追っていくといいと思います。

自分の回答だと

```python:fasionably_late.py
def fashionably_late(arrivals: list, name: str)-> bool:
    """Given an ordered list of arrivals to the party and a name, return whether the guest with that
    name was fashionably late.
    """
    fl = arrivals[-(-len(arrivals)//2):-1]
    
    return name in fl 
```

と[【Python】切り捨て除算演算子を使った切り上げ【算数】](https://qiita.com/ikanamazu/items/2b16f220fdaf6072793d)を利用して、`math.ceil`を使用しない感じでやってみました。

## [Loops and List Comprehensions](https://www.kaggle.com/colinmorris/loops-and-list-comprehensions)

ループと内包表記

[リスト内包表記の活用と悪用](https://qiita.com/KTakahiro1729/items/c9cb757473de50652374)がとても参考になると思います。

## Exercise: Loops and List Comprehensions

Q1の*has_lucky_number*は`[num % 7 == 0 for num in nums]`が`[True, False, False, True]`みたく展開されるので 
 [組み込み関数 any](https://docs.python.org/ja/3/library/functions.html#any)の解説どおりとなる。

Q4については

```python:estimate_average_slot_payout.py
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
    """
    r = [play_slot_machine() - 1 for _ in range(n_runs)]
    return (sum(r)) / n_runs

import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
import numpy as np
import pandas as pd

scale = np.linspace(100,1e6,100)
values = [estimate_average_slot_payout(int(i)) for i in scale]

fig, ax=plt.subplots(figsize=(10,5))
ax.plot(df)
ax.axhline(y=0.025, xmin=0.1, linestyle='--', color='r')
ax.annotate('0.025',xy=(0.95e6,-0.05),fontsize=16)
ax.set_ylim(-0.15, 0.15)

# 対数軸を数字表記にする。
formatter = ScalarFormatter()
formatter.set_scientific(False) 
ax.xaxis.set_major_formatter(formatter)
# plt.show()
# plt.savefig('graph.png')
```

![download-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130729/d9bd0ef6-76ee-b4de-7ef9-422b73a4c486.png)

と100から1_000_000までの期待値をグラフ化してみると分かり易いと思います。

なお、kaggleのgithubを[カンニング](https://github.com/Kaggle/learntools/blob/master/learntools/python/ex5.py)すると

```python:play_slot_machine.py
# Analytic solution for expected payout =
# .005 * 100 + (.05 - .005) * 5 + (.25 - .05) * 1.5
```

となっていて、`1.0250000000000001`が期待値となっています。

## Strings and Dictionaries

文字列と辞書の操作方法

しれっと[str.format](https://docs.python.org/ja/3/library/string.html#formatspec)が出てきたりしているので、リンク先をいったんみた方がいいと思います。

## Exercise: Strings and Dictionaries

Q2は貰い物ですが

```python:word_search.py
def word_search(doc_list, keyword):
    import re
    return [ idx for idx, doc in enumerate(doc_list)
        if re.search(rf'{keyword}\b', doc, re.IGNORECASE)]
```
といった解法もあります。`re.seach`を使うと二重ループしなくていいので、かっこいいと思いました。

Q3は素直にこれを使えば楽ですよね。

自分は以下みたく、一からやってました。まだまだです。

```python:multi_word_search.py
def multi_word_search(doc_list, keywords):
    """
    Takes list of documents (each document is a string) and a list of keywords.  
    Returns a dictionary where each key is a keyword, and the value is a list of indices
    (from doc_list) of the documents containing that keyword

    >>> doc_list = ["The Learn Python Challenge Casino.", "They bought a car and a casino", "Casinoville"]
    >>> keywords = ['casino', 'they']
    >>> multi_word_search(doc_list, keywords)
    {'casino': [0, 1], 'they': [1]}
    """
    d = {keyword: [] for keyword in keywords}

    for idx,doc in enumerate(doc_list):
        for wd in doc.split():
            for keyword in keywords:
                if wd.lower().rstrip(',.?') == keyword:
                    d[keyword].append(idx)
                    break

    return d
```
## Working with External Libraries

`import`と`dir``help`といったなにかと便利な機能の紹介。

TensorFlowのところは難しい。ので無視してしまった:sweat:

## Exercise: Working with External Libraries

Q1は`get_graph`の戻り値がmatplotlib.pyplotの[axesオブジェクト](https://matplotlib.org/stable/api/axes_api.html)を返しているので、いろいろとメソッドが使える。

[kaggle - Matplot Tutorial for Everyone](https://www.kaggle.com/saurav9786/matplot-tutorial-for-everyone)とかで別に勉強しないと、ここだけではかなり辛い。

```python:prettify_graph.py
def prettify_graph(graph):
    """Modify the given graph according to Jimmy's requests: add a title, make the y-axis
    start at 0, label the y-axis. (And, if you're feeling ambitious, format the tick marks
    as dollar amounts using the "$" symbol.)
    """
    graph.set_title("Results of 500 slot machine pulls")
    # Complete steps 2 and 3 here
    graph.set_ylim(bottom=0)
    graph.set_ylabel('Balance')
    
# https://matplotlib.org/stable/gallery/ticks_and_spines/tick-formatters.html 
# This is enough
    graph.yaxis.set_major_formatter('${x:0.0f}')

# https://stackoverflow.com/questions/63723514/userwarning-fixedformatter-should-only-be-used-together-with-fixedlocator
# https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_yticklabels.html#matplotlib.axes.Axes.set_yticklabels
#    yticks_list = graph.get_yticks().tolist()
#    print(yticks_list)
#    [0.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 350.0]
#     new_labels = [f'${x:.0f}' for x in yticks_list] 
#     graph.set_yticks(yticks_list)
#     graph.set_yticklabels(new_labels)

graph = jimmy_slots.get_graph()
prettify_graph(graph)
graph
```
この問題は、回答が古い使い方をしていてwarningがでます。
コメントアウトしているのが、修正したやり方。
でも、`set_major_formatter`を使うと一発で変換できます。

Q3は、まず貰い物

```python:bhg1()
def blackjack_hand_greater_than(hand_1, hand_2):
#     In case of draw, hand_2 wins anyway.
     return blackjack_hand_value(hand_1) > blackjack_hand_value(hand_2) 
 
def blackjack_hand_value(hand):
 #     Evaluate all cards, ignore aces for one and count them as 10s
     evaluate = lambda x: int(x) if x.isdigit() else 10
     ace_count = hand.count('A')
 #     We assume all aces count as 11 at the beginning
     value = sum([evaluate(c) for c in hand]) + ace_count
 #     Lower ace value if value exceeds 21 and it's possible
     while ace_count > 0 and value > 21:
         ace_count-=1
         value-=10
 #     If hand value exceeds 21 count it as 0 (or any other low value not possible in blackjack)
     return value if value <= 21 else 0
```


これは`evalute`のところが美しい。あと、ブタの判定を`blackjack_hand_value`側でやっているので、勝ち負け判定のところがシンプルになっているのがいいです。

自分のは

```python:bgh2()
def blackjack_hand_greater_than(hand_1, hand_2):
    """
    Return True if hand_1 beats hand_2, and False otherwise.
    
    In order for hand_1 to beat hand_2 the following must be true:
    - The total of hand_1 must not exceed 21
    - The total of hand_1 must exceed the total of hand_2 OR hand_2's total must exceed 21
    
    Hands are represented as a list of cards. Each card is represented by a string.
    
    When adding up a hand's total, cards with numbers count for that many points. Face
    cards ('J', 'Q', and 'K') are worth 10 points. 'A' can count for 1 or 11.
    
    When determining a hand's total, you should try to count aces in the way that 
    maximizes the hand's total without going over 21. e.g. the total of ['A', 'A', '9'] is 21,
    the total of ['A', 'A', '9', '3'] is 14.
    
    Examples:
    >>> blackjack_hand_greater_than(['K'], ['3', '4'])
    True
    >>> blackjack_hand_greater_than(['K'], ['10'])
    False
    >>> blackjack_hand_greater_than(['K', 'K', '2'], ['3'])
    False
    """
    
    def hands_total(hand):   
    
        hand = sorted(hand, key=lambda x: x.isdigit(), reverse=True)
        #  print(hand)
        #  sort order
        #  ['1', '10', '2', '3', '4', '5', '6', '7', '8', '9', 'J', 'K', 'Q', 'A']
        r = 0
        cards = ['10','J','Q','K']
        
        for h in hand:
            if h in cards:
                r += 10
            else:
                r += int(h,16) + (h == 'A')
                if r > 21 and h == 'A':
                    r -= 10
        return r

    r1, r2 = hands_total(hand_1), hands_total(hand_2)
    
    return r1 <= 21 and (r1 > r2 or r2 > 21)
```

_A_って16進数だと１０じゃん　で作ってみたコード。
`r += int(h,16) + (h == 'A')`のところが、`True`は`１`で`False`は`0`の応用になっています。

### まとめ

- kaggleのPython courseはPythonを使ったことがない人でもとりあえず使えるようになるいいコースだと思います。
- 当たり前だけど、これだけだとわかりづらいことも多数あるので本家[Python 標準ライブラリ](https://docs.python.org/ja/3/library/index.html)で調べたり[VISUALIZE CODE EXECUTION](https://pythontutor.com/)で実際のコードの動きを確認するといいと思います。
- この他にも機械学習についてもコースがいろいろあるので試してみるといいと思います。

pythonの話だけですね:sweat: