python pandas コード備忘録

## df.copy(deep = False)

 `deep = True`（デフォルト）：元のdfの行列・値を両方保持するためメモリを消費する。
 `deep = False`：元のdfの行列のみ保持するため、`deep = True`よりもメモリを消費しない。

### df.drop
`inplace = False`（デフォルト）：対象のオブジェクトを返すのみ。
`inplace = True`：削除結果を反映させる。
        
```py
    train_df_shallow =  train_df.copy(deep=False)
    train_df_shallow.drop("Sale",axis = 1,inplace= True)
```

## jupyter notebookにおいて、pandasで表示が省略されるのを防ぐ

```py
    pd.set_option('display.max_columns', 160) # 160番目の列まで全て強制表示
    pd.set_option('display.max_columns', None) # 全て強制表示
```

## get_dummiesの使い方

get_dummiesは、文字列のまま使用できるので、いったんlabel列を文字列に戻す。

```py
    df['label'] = le.inverse_transform(df['label'])
    df
```

| size | price | label  |
| ---- | ----- | ------ |
| 1    | 100   | cola   |
| 2    | 150   | tea    |
| 3    | 200   | coffee |

get_dummiesでlabel列をダミー変数化して、元のDataFrameに結合する。

```py
    df_dummy = pd.get_dummies(df['label'])
    df2 = pd.concat([df.drop(['label'],axis=1),df_dummy],axis=1)
    df2
```

| size | price | coffee | cola | tea |
| ---- | ----- | ------ | ---- | --- |
| 1    | 100   | 0      | 1    | 0   |
| 2    | 150   | 0      | 0    | 1   |
| 3    | 200   | 1      | 0    | 0   |

これでダミー変数化は完成した。

### 多重共線性

回帰分析などにダミー変数を使用したい場合は、変数同士の相関が高いと多重共線性という問題が発生するため、one-hotエンコーディングの配列から列の1つを削除して使用しなければならない。例えばCoffee列を削除しても、cola列とtea列がともに0であればcoffeeであることがわかるので、情報としての欠落はない。
get_dummieのdrop_firstパラメータにTrueを渡すと最初の列を削除できる。

```py
    df_dummy = pd.get_dummies(df['label'],drop_first = True)
    df3 = pd.concat([df.drop(['label'],axis=1),df_dummy],axis=1)
    df3
```

| size | price | cola | tea |
| ---- | ----- | ---- | --- |
| 1    | 100   | 1    | 0   |
| 2    | 150   | 0    | 1   |
| 3    | 200   | 0    | 0   |

重回帰分析などでは、カテゴリーの数がN個ある場合、N個のダミー変数を作ると予測できないため、どれか1個の変数を除外します。scikit-learnの重回帰分析 LinearRegression() では、この問題を回避する設計にしているようで、N個のダミー変数を作成しても動作します。


## enumerate関数（forループでインデックスを取得できる）

Pythonの`enumerate()`関数を使うと、forループの中でリスト（配列）などのイテラブルオブジェクトの要素と同時にインデックス番号（カウント、順番）を取得できる。

- enumerate関数を使ったforループ

引数にリストなどのイテラブルオブジェクトを指定する。
`インデックス番号, 要素`の順に取得できる。

0以外の数値から開始したい場合は、`enumerate()`関数の第二引数に任意の開始数値を指定する。

```py
    for i, name in enumerate(l, 1):
        print(i, name)
    # 1 Alice
    # 2 Bob
    # 3 Charlie
```

## カレントディレクトリの取得
```py
    import os
    os.getcwd()
```

## ファイル選択ダイアログの表示
```py
    root = tkinter.Tk()
    root.withdraw()
    fTyp = [("", "*.csv")]
    iDir = os.path.abspath(current_dir)
    tkinter.messagebox.showinfo('売上可視化プログラム', '処理したいCSVファイルを選択してください！')
    file = tkinter.filedialog.askopenfilename(filetypes = fTyp,initialdir = iDir)
    if file == "":
        print("CSV選択がキャンセルされました。終了します。")
        sys.exit(1)
```

## 文字判定
### 大文字かどうかを判定（isupper)

str.isupper()を使います。
文字列中の英字 全てが大文字、かつ１文字以上の場合はTrue、そうでない場合はFalseを返します。

```py
    # 全て英字大文字
    >>> 'ABCDE'.isupper()
    True
    
    # 全て英字小文字
    >>> 'abcde'.isupper()
    False
    
    # 英字　大文字・小文字混在
    >>> 'Abcde'.isupper()
    False
    
    # 英数字混在
    >>> 'ABC12'.isupper()
    True
    
    # 英数字以外も混在
    >>> 'Aa12\*'.isupper()
    False
```

### 小文字かどうかを判定（islower)

str.islower()を使います。
文字列中の英字（上記参照）全てが小文字で、かつそれが1文字以上ある場合はTrue、そうでない場合はFalseを返します。

```py
    # 全て英字大文字
    >>> 'ABCDE'.islower()
    False
    
    # 全て英字小文字
    >>> 'abcde'.islower()
    True
    
    # 英字　大文字・小文字混在
    >>> 'Abcde'.islower()
    False
    
    # 英数字混在
    >>> 'abc12'.islower()
    True
    
    # 英数字も混在
    >>> 'Aa12\*'.islower()
    False
```

### 英数字であることを判定（isalnum）

str.isalnum()を使います。
文字列中の全ての文字が英数字で、かつ1文字以上ある場合はTrue、そうでない場合はFalseを返します。

```py
    # 全て英字大文字
    >>> 'ABCDE'.isalnum()
    True
    
    # 全て英字小文字
    >>> 'abcde'.isalnum()
    True
    
    # 英字　大文字・小文字混在
    >>> 'Abcde'.isalnum()
    True
    
    # 英数字混在
    >>> 'abc12'.isalnum()
    True
    
    # 英数字以外混在
    >>> 'Aa12\*'.isalnum()
    False
```

### 英字かどうかを判定（isalpha）

str.isalpha()を使います。
文字列中の全ての文字が英字（上記参照）で、かつ1文字以上ある場合はTrue、そうでない場合はFalseを返します。

```py
    # 全て英字大文字
    >>> 'ABCDE'.isalpha()
    True
    
    # 全て英字小文字
    >>> 'abced'.isalpha()
    True
    
    # 英字　大文字・小文字混在
    >>> 'Abced'.isalpha()
    True
    
    # 英数字混在
    >>> 'abc12'.isalpha()
    False
    
    # 英数字以外混在
    >>> 'Aa12\*'.isalpha()
    False
```

### ASCII文字列かどうかを判定（isascii）

str.isascii()を使います。
文字列が空、または全ての文字が ASCII（ U+0000 〜 U+007F） である場合はTrue、それ以外の場合はFalseを返します。
Python3.7から追加されたメソッドなのでバージョンに注意。

```py
    # 全て英字大文字
    >>> 'ABCDE'.isascii()
    True
    
    # 全て英字小文字
    >>> 'abced'.isascii()
    True
    
    # 英字　大文字・小文字混在
    >>> 'abc12'.isascii()
    True
    
    # 英数字以外混在
    >>> 'Aa12\*'.isascii()
    True
    
    # ASCIIコード以外
    >>> 'あ'.isascii()
    False
```

### タイトルケース文字列かどうかを判定（istitle）

str.istitle()を使います。
タイトルケースとは、文の最初以外の単語についても先頭文字を大文字にすることです。
このメソッドは以下の２つを満たす場合にTrueを返します。

- 大文字は、英字以外の文字の後のみに置かれる
- 小文字は、英字の後にだけ続く

よって、以下の点に注意が必要です。

- アポストロフィー（’）の後の文字が小文字だとタイトルケースと判定されません（例：Tom’s →　Tom’S)
- 翻訳業界などでは4文字未満の前置詞や接続詞（on, theなど）はこれを適用しないルールがあるようですが、このメソッドはこのルールが適用されません

例
  
```py
    # 文章の最初の文字のみ大文字
    >>> "Tom's watch is on the table.".istitle()
    False
    
    # istitle()でTrueとなるタイトルケース
    >>> "Tom'S Watch Is On The Table.".istitle()
    True
    
    # アポストロフィー（'）の後の文字が小文字だとFalse
    >>> "Tom's Watch Is On The Table.".istitle()
    False
    
    # onやtheの先頭文字が小文字だとFalse
    >>> "Tom'S Watch Is on the Table.".istitle()
    False
```

## 文字コードと文字を相互変換
### 文字をUnicodeコードポイントに変換: ord()

`ord()`の引数に文字（1文字の文字列）を指定すると、その文字のUnicodeコードポイントが整数`int`で返される。

```py
    i = ord('A')
    print(i)
    # 65
    
    print(type(i))
    # <class 'int'>
```

- Unicodeコードポイントは16進数で表記されることが多い。整数を16進数表記の文字列に変換するには組み込み関数`hex()`を使う。

例

```py
    s = hex(i)
    print(s)
    # 0x41
    
    print(type(s))
    # <class 'str'>
```

- 組み込み関数`format()`を使うと、ゼロ埋めや`0x`の有無など、より細かい書式を指定できる。

例

```py
    print(format(i, '04x'))
    # 0041
    
    print(format(i, '#06x'))
    # 0x0041
```

- 特定の文字の16進数表記のUnicodeコードポイントを取得する処理を一気に書くと以下のようになる。

例

```py
    print(format(ord('X'), '#08x'))
    # 0x000058
    
    print(format(ord('💯'), '#08x'))
    # 0x01f4af
```

### Unicodeコードポイントを文字に変換: chr()

`chr()`の引数に整数を指定すると、その値がUnicodeコードポイントである文字が文字列`str`で返される。

```py
    print(chr(65))
    # A
    
    print(type(chr(65)))
    # <class 'str'>
```

Pythonでは、`0x`をつけると数値を16進数で記述できるので、16進数表記のUnicodeコードポイントが分かっていればそのまま`chr()`の引数として指定できる。ゼロ埋めされていても問題ない。

```py
    print(65 == 0x41)
    # True
    
    print(chr(0x41))
    # A
    
    print(chr(0x000041))
    # A
```

- Unicodeコードポイントを表す16進数表記の文字列からそのコードポイントの文字を取得したい場合、16進数表記の文字列を整数`int`に変換してから`chr()`に渡す。

16進数表記の文字列を整数`int`に変換するには`int()`を使う。第一引数に文字列、第二引数に基数`16`を指定する。

```py
    s = '0x0041'
    
    print(int(s, 16))
    # 65
    
    print(chr(int(s, 16)))
    # A
```

- 16進数表記の文字列を`int()`で整数に変換する場合、文字列に`0x`がついていれば第二引数は`0`でもOK。16進数の数値・文字列の扱いについての詳細は以下の記事を参照。

https://note.nkmk.me/python-bin-oct-hex-int-format/

- Unicodeコードポイントは`U+XXXX`の形で記述されることも多い。このような文字列をそのコードポイントの文字に変換するにはスライスで数値部分のみを選択すればよい。

https://note.nkmk.me/python-slice-usage/

```py
    s = 'U+0041'
    
    print(s[2:])
    # 0041
    
    print(chr(int(s[2:], 16)))
    # A
```

### 文字列をUnicodeコードポイントで記述: \x, \u, \U

文字列リテラルの中では`\x`, `\u`, `\U`に続けて16進数表記のUnicodeコードポイントを記述でき、その文字として扱われる。
`\xXX`, `\uXXXX`, `\UXXXXXXXX`のように、それぞれ2桁、4桁、8桁の16進数である必要がある。桁数が合っていないとエラー。

```py
    print('\x41')
    # A
    
    print('\u0041')
    # A
    
    print('\U00000041')
    # A
    
    print('\U0001f4af')
    # 💯
    
    # print('\u041')
    # SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-4: truncated \uXXXX escape
    
    # print('\U0000041')
    # SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-8: truncated \UXXXXXXXX escape
```

各コードが1文字として扱われる。文字数を返す組み込み関数`len()`で確認できる。

```py
    print('\u0041\u0042\u0043')
    # ABC
    
    print(len('\u0041\u0042\u0043'))
    # 3
```

エスケープシーケンスが無効化されるraw文字列ではそのままの文字の並びとして認識されるので注意。

https://note.nkmk.me/python-raw-string-escape/

```py
    print(r'\u0041\u0042\u0043')
    # \u0041\u0042\u0043
    
    print(len(r'\u0041\u0042\u0043'))
    # 18
```