【Python】pandas 再利用しそうなコード 


## インポート

<details><summary>インポート</summary><div>　

~~~
import pandas as pd
~~~

ついでに、下記もインポートしておく
（データフレームを表示する際にprintを使うよりdisplayを使うほうがきれい）

~~~
from IPython.display import display
~~~

</div></details>

## データ確認

<details><summary>データ確認</summary><div>　

~~~
# 先頭の5行を表示
print(df.head(5))
# 最後尾の5行を表示
print(df.tail(5))
# dataframe形式の表のレイアウトで表示
display(df)
~~~

</div></details>

## DataFrame

<details><summary>DataFrame</summary><div>　

### 空のデータフレーム
~~~
# 空のデータフレーム
df = pd.DataFrame(index=[], columns=[])
df = pd.DataFrame(index=[], columns=range(15))
df = pd.DataFrame(0,index=range(4), columns=range(15))  # ０で埋める

# 既存データフレームから空のデータフレームを作成
df_blank = df.iloc[0:0]
~~~

### 列（Columns）名置き換え
~~~
# カラム名を連番に置き換え           （forで加工しやすいので）
df.columns=list(range(len(df.columns)))
# カラム名をアルファベットに置き換え  (Excelと整合しやすいので)
df.columns=([chr(ord('A') + i) for i in range(26)]+["AA"]+["AB"])
~~~

### 行（Index）名振り直し
~~~
# indexを0から振り直す
df=df.reset_index()
df=df.reset_index(drop=True)

# indexを1から振り直す                (Excelと整合しやすいので)
df.index = df.index + 1

# indexとは別の行に連番を振る
serial_num = pd.RangeIndex(start=0, stop=len(df.index) , step=1)
df['No'] = serial_num
~~~

</div></details>

## 列（Columns）加工

<details><summary>列加工</summary><div>　

### 列抽出
~~~
df['A']
df=df[["F","Z"]]
# 任意の列だけ表示
df[['列名']]
# 1,2,4 行目
df.iloc[[1,2,4]]
# 0-2 列目
df.iloc[[0,2]]
~~~

### 列追加
~~~
# ない列を指定すると列が追加される
df['job'] = ["Engineer"]
df['new_column'] = 0  　　　　　　　#  初期値を0で埋めて追加
~~~

### 条件抽出
~~~
df=df[df['A'] > 450]
df=df[df['A']=='P1'] 
# 複数条件指定
【注】（and、or、not)の替わりに（&、|、~）を使わないとエラーとなる
【注】比較演算子を使うときは条件ごとに括弧で囲まないとエラーとなる
df=df[(df['D']==0.0) | (df['D']==0.1)]
~~~

### 条件代入
~~~
# x=0 → y  x<>0 →x 
dfsum["z"]=dfsum["y"].where(dfsum["x"] ==0, dfsum["x"])
# 代入
gf2["打率"]=gf2['安打']/gf2['打数']
~~~

### 列名変更
~~~
# y を sales に変換
df.rename(columns={'y': 'sales'}, inplace=True)
df.columns = ['year_orig', 'pop', 'rate', '%']
~~~

### 列削除

~~~
# 列の場合はaxis=1が必要（デフォルトはaxis=0を行指定）
df.drop(['remarks'], axis=1, inplace=True)
# 列の内容の重複を排除
df=set(df2.A列)
~~~

### 列の値代入・更新
~~~
df['A'].apply(lambda x: x * 2)   # =A1*2
df['A'].apply(lambda x: 1 if x>0 else 0)  # IF(A1>0,1,0)的
~~~

### スライス
~~~
# expand=Trueがあると、2つに分割してくれるが、ないとリスト形式内で分割される
print(df['a'].str[:2],expand=True)
~~~

### タイプ変更
~~~
# 最初はobject型になっている
df=df.astype(float)
df1["G"]=df1["G"].astype(int)
~~~
|オプション|説明|
|---|---|
|inplace=True|破壊的変更となり、元データも変更になる|

</div></details>

## 行（Index）加工

<details><summary>行加工</summary><div>　

【注】0から始まるので、Excel行-1となる

### 行抽出
~~~
# 特定の区間の行を抽出する
df[1:3]
#  100の行だけ抽出
df.loc[100]
#  100行目から105行目まで抽出
df[100:106]
# 最初の10行だけ抽出
df[:10]
~~~

### 行追加
~~~
ない行を指定すると行が追加される
df.loc['ONE'] = 0
# 行ごとに値追加
df.loc['FIVE'] = ['A5', 'B5', 'C5']

record = pd.Series([VD0,VD1], index=df2.columns)
df2 = df2.append(record, ignore_index=True)
~~~

### 行削除
~~~
df=df.drop(range(0,3))
df.drop(range(0,3),inplace=True )
~~~

</div></details>

## 置換

<details><summary>置換</summary><div>　

~~~
df['col1'] = df['col1'].str.replace('x', 'z')

# 複数一括
df['col1']= df['col1'].replace({0:-1,1:-2,2:-3})
~~~

</div></details>

## 結合

<details><summary>結合</summary><div>　

~~~
# 縦結合
df=pd.concat([df, df1])
# 横結合
df=pd.concat([df, df1], axis=1)
~~~
【注】Indexが異なると、真横に結合されないので、真横につけたい時はIndexを振り直す

</div></details>

## マージ

<details><summary>マージ</summary><div>　

~~~
dfsum=pd.merge(dfsum, gf2,on='キー',how="outer")
~~~
・「on」--- 明示的に結合するキーを指定
  複数指定可能　on=['keyA', 'keyB']
・how」-- 結合方法を指定
 inner -- 両方のデータフレームのキーがあるもののみ
 left  -- 左のデータフレームに。右のデータフレームの情報が付加され、不足はNaN
 right -- 右のデータフレームに。左のデータフレームの情報が付加され、不足はNaN
 outer -- 両方のデータフレームのすべてのキーを選択。不足情報はNaN

</div></details>

## グループ

<details><summary>グループ</summary><div>　

~~~
gf = df3.groupby('選手名')
gf1=gf.agg({'本塁': max,'盗塁':'max','打率':'max'})
~~~

</div></details>

## セル

<details><summary>セル</summary><div>　

~~~
# 指定 (atの方が早いらしい)
df.loc[333,'bbb']    #df.loc['行名','列名']
df.iloc[2,1]　　　　　#df.iloc['行番号','列番号']
df.at[333,'bbb']　　  #df.at['行名','列名']
df.iat[2,1]          # df.at['行番号','列番号']
~~~

~~~
# 代入
df.loc[99,0] ='▲'
df.loc[:, 2:8] = 0
~~~
【注】df.loc[99][0]の書き方（chained indexing）だと、SettingWithCopyWarningが発生したり、
値がかわらなかったりする

~~~
# 値の条件抽出
VD0=df1.loc[((df1["D"]==0) & (df1["G"]==6)),"E"] 
VD0=VD0.values.tolist()[0]    
~~~

</div></details>

## ソート

<details><summary>ソート</summary><div>　

~~~
df.sort_values(['sales', 'temperature'], ascending=False)
sorted(df)
~~~
|オプション|説明|
|---|---|
|ascending=True|降順|
|ascending=False|昇順|

</div></details>

## 複製

<details><summary>複製</summary><div>　

~~~
df1 = df.copy() 
~~~

【注】df1 = dfにすると、参照渡しになるので、df1を変更するとdfも変わってしまう

</div></details>

## 変換

<details><summary>変換</summary><div>　

~~~
# リスト　→　DataFrame
pd.DataFrame(data=list)

# データ部分　→　リスト
s = pd.Series([0, 1, 2])
print(s)
#  0    0
#  1    1
#  2    2
#  dtype: int64
list= s.values.tolist()　　　　# リストになる
V0=s.values.tolist()[0] 　  # 値取出し
~~~

</div></details>

## 表示

<details><summary>表示</summary><div>　

~~~
# カラム、Indexを表示しない
print(df.to_string(index=False, header=False))
~~~

</div></details>

## 既存のデータフレームを新しいデータフレーム分を更新する

<details><summary>既存のデータフレームを更新
</summary><div>　

（条件）
・既存データフレーム
　NaNがある
　新データフレームにない列がある
　新データフレームにない行がある
・新しいデータフレーム
　NaNがある
　既存データフレームにない列がある
　既存データフレームにない行がある
・更新条件
　数字は大きい方を採用
　文字は新しい方を採用
（結果）
いい方法が、見つからず。
下記のように、しましたが、いい方法がありましたら、御教授をお願いします
（手順）
・マージした後、ソートする
　→これで行数、列数は自動調整される。但し、共通の列は2列にわかれたまま
　df=pd.merge(dfA, dfB,on='A列',how="outer")
　df=df.sort_values(['A列'], ascending=True)
・NaNを一旦、使用していない0に変換する
　df1=df.fillna(0)
・数字のB列は、大きい値を採用
　df1['B列'] = np.where(df1['B列_x']>df1['B列_y'], df1['B列_x'], df1['B列_y'])
・文字列のC列は新しい方を採用する
　単純に上書きすると、既にある値をNaNにしてしまう可能性があるので、NaN=0でない場合のみ書き換える
　df1['C列'] = np.where(df1['C列_y']!=0, df1['C列_y'], df1['C列_x'])
・列を元に戻す
　df2=df1.reindex(columns=['A列', 'B列', 'C列', 'D列', 'E列'])
・0をNaNに戻す
　df2 = df2.replace([0], np.nan)

別ファイルの列同士では、キーが合っていないので、一括では比較できない、そこで最初にマージして
キーを合わすことで、比較できようにした

</div></details>

## ファイル読み込み・書き出し

<details><summary>ファイル読み込み・書き出し</summary><div>　

### Excelファイル読み込み

~~~
df = pd.read_excel('sample.xlsx',sheet_name='sheet1')
~~~
|オプション|説明|
|---|---|
|sheet_name=""|シート名を指定|
|header=None|指定するとヘッダなしで読み込む|
||指定しないときは、1行目が自動的にカラム名として挿入される|
|header=2|指定行をヘッダとして読み込み。それ以前の行は無視される。これ以降から0行が始まる|

### Excelファイル書き出し
~~~
df.to_excel('sample.xlsx',sheet_name='sheet1', encoding='cp932',index=False, header=False)
~~~

|オプション|説明|
|---|---|
|sheet_name=""|シート名を指定|
|index=False|行名を削除して書き出し|
|header=False|列名を削除して書き出し|


### csvファイル読み込み
~~~
df=pd.read_csv("sample.csv",encoding="cp932",header=None)
df=pd.read_csv("sample.csv",encoding="shift_jis",header=False)

df1=pd.read_csv(sample.csv,encoding='cp932',names=col_names,low_memory=False )

~~~

【注】windows拡張文字列が混ざっている場合は、文字コードをcp932としてやる必要がある
【注】重たいファイル読み込む時はlow_memory=Falseを記載する

~~~
df = pd.DataFrame(index=[], columns=range(21))
col_names = ['c{0:02d}'.format(i) for i in range(21)]
df=pd.read_csv("myfile.csv",encoding="cp932", names = col_names)
~~~
【注】途中で列数が変わる場合、エラーになるので、事前に列数と仮の名前を設定しておく


### csvファイル書き出し
~~~
df.to_csv('sample.csv',encoding='cp932',header=False)
~~~

### TAB区切りファイル読み込み
~~~
df=pd.read_csv("sample.DAT",encoding='cp932', sep='\t',names=col_names)
~~~

### テキストファイル読み込み
~~~
df=pd.read_table("sample.DAT",encoding='cp932',header=None)
~~~

### テキストファイル書き込み
~~~
df.to_csv("employee.csv", sep=",",header=False)
~~~

</div></details>

## 欠如データ

<details><summary>欠如データ</summary><div>　

~~~
# A列にあるNaNを'0.0'に置き換える
df.fillna(value={'A': 0.0}, inplace=True)
# A列にNaNがある行を削除する
df.dropna(subset=[A], axis=0, inplace=True)
# A列の '--' を 0に置き換える
df['A'] = df['A'].replace('--', 0).astype(float)

# NaNを上セルの値で埋める
df=df.fillna(method='ffill')

# 0をNaNに戻す
df = df.replace([0], np.nan)
~~~

</div></details>

## 可視化

<details><summary>可視化</summary><div>　

~~~
import matplotlib.pyplot as plt
~~~
以下を実行するとプロットスタイルが変更されていい感じになる

~~~
plt.style.use('ggplot') 
~~~

| 記法              | 操作の内容       |
|-------------------|------------------|
| plt.title('グラフタイトル') | タイトルを設定   |
| plt.xlabel('X軸ラベル')| x軸の名前を設定  |
| plt.ylabel('Y軸ラベル')| y軸の名前を設定  |
| plt.xlim(Xの最小値, Xの最大値)  | x軸の範囲を指定  |
| plt.ylim(Yの最小値, Yの最大値)  | y軸の範囲を指定  |
| plt.legend() | 凡例を表示       |
| plt.grid() | グリッド線を表示 |

~~~
import random
import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame(index=[], columns=range(2))
df.columns=list(range(len(df.columns)))

for i in range(4):
    df.loc[i] = [i ,2*random.random()]

print(df)
plt.plot(df[0],df[1])
plt.show()
~~~
</div></details>

## メモリ節約

<details><summary>メモリ節約</summary><div>

### 不要な変数を削除して、メモリ解放する

delで不要な変数を削除する
~~~
del 変数
~~~

使用していないメモリを開放する
~~~
gc.collect()
~~~

不要なカラムを消す
~~~
del df['column1']
# または
df = df.drop(columns=['column1'], axis=1)
~~~

read_csvする場合は、必要なカラムだけを読み込む
~~~
df = pd.read_csv('file.csv', usecols=['column1', 'column2'])
~~~

必要レコードだけ抽出する
~~~
df = df.loc[df["id"] >= 100000]
~~~

### copyの多用しない

~~~
df1 = df.copy() 　# copyした変数分のメモリが新規に消費されてしまう
df2 = df 　　　　　# 参照渡ししても問題ない場合はcopy()を使わない
~~~

### 破壊的代入する
既存の変数を変更する際、変数名を変えてしまうと、新しい変数ができてしまうので、
極力同じ変数名を使うようにします。

~~~
# 非破壊的な例
df3 = df1.loc[df1['id'] >= 10000]
df4 = pd.concat([df3, df2])

# 破壊的な例
df1 = df1.loc[df1['id'] >= 10000]
df1 = pd.concat([df1, df2])
~~~

### mergeする際は、copy=Falseにする。
デフォルトだとTrueになっており、copyが生成され、メモリを必要以上に使用してしまう

~~~
df1 = df1.merge(df2, on='column1', how='left', copy=False)
~~~

### カラムの型を指定
指定しないと自動でint64またはfloat64となってしまうので、必要分だけを指定する
~~~
df = df.astype(
            {
                "id": np.int32,
                "status": np.int8,
                "price": np.int32,
                "weight": np.float32
            }
        )
~~~

read_csvする場合は、読み込み時点で型指定しておく

~~~
df = pd.read_csv(
	"file.csv", 
	dtype={
                "id": np.int32,
                "status": np.int8,
                "price": np.int32,
                "weight": np.float32
            }
    )
~~~

### メモリ使用量の確認方法

~~~
print(f"df memory usage: {df.memory_usage(deep=True).sum() / 1024 ** 2} MB")
~~~

</div></details>