Pythonで大量のデータを扱うなら、Pandasの習得は必須です。
- 少ないコードで済む
- 複雑な処理が簡単にできる
DataFrameという独自のオブジェクトを扱うので慣れるまでは取り扱いに苦労しますが、一度なじんでしまえばそこは天国です。(大げさではない)
ぜひマスターしましょう。
本記事では僕が実務でよく使うものをまとめましたので、辞書的にお使いいただけると幸いです!
Pandasを使う準備
pip install
pip install pandas openpyxl
一般的なライブラリと同様、pip installでインストール可能。
openpyxlはread_excelやto_excelなどを動かすのに必須なので、ここでインストールしておくと良いでしょう。
import
import pandas as pd
Pandasのインポートではas pdとすることが一般的です。
公式ドキュメントでもこの書き方を推奨していますし、ウェブ上の情報もまずこの書き方をしているのでこれに倣うべきです。
DataFlameとして読み込む
PandasはDataFrame型しか扱えません。
そのため、まずは各種データをDataFrame型として読み込む必要があります。
CSV|基本
# Excel → DataFrame
df = pd.read_csv(csv_path)
最もシンプルなCSV読み込み方法です。
CSVはエラーや文字化けが起こりやすいです。もしこれで失敗する場合には、次以降に紹介した方法をお試しください。
CSV|ファイル名に日本語を含む
# Excel → DataFrame(ファイル名に日本語を含む場合)
df = pd.read_csv(csv_path, engine='python')
CSVのファイル名に日本語を含む場合、基本の方法だとエラーになります。
engineをpythonにすることで大抵のエラーは回避可能です。
CSV|文字化けする場合
# Excel → DataFrame(文字化けする場合1)
df = pd.read_csv(csv_path, encoding='utf-8_sig')
# Excel → DataFrame(文字化けする場合2)
df = pd.read_csv(csv_path, encoding='cp932')
文字化けしてしまう場合はエンコードをutf-8_sigやcp932にすると解消されることがあります。
utf-8_sigはbom付きutf-8と呼ばれ、Excelなどで採用されているエンコード方式です。
CSV|最終手段
import codec
# Excel → DataFrame(最終手段)
with codecs open(csv_path, mode='r', encoding="utf-8_sig", errors='ignore') as f:
df = pd.read_csv(f)
以上の方法でも読み込めない場合はこちらが最終手段になります。
もともとのCSVファイルのエンコードを確認し、それをencodingの引数に渡すと成功する確率が高いです。
CSV|オプション
# インデックス列を指定
df = pd.read_csv(csv_path, index_col=0)
Excel|基本
# Excel → DataFrame
df = pd.read_excel(excel_path, sheet_name=sheet_name)
read_excelを使うにはopenpyxlが必要なので、忘れずにpip installしておきましょう。
Excel|オプション
# 2行目をカラム名に設定する
df = pd.read_excel(excel_path, sheet_name=sheet_name, header=2)
# 1シート目を読み込む
df = pd.read_excel(excel_path, sheet_name=0)
# 型を指定して読み込む
df = pd.read_excel(excel_path, sheet_name=0, dtype={'列名1': str, '列名2: object'})
辞書
# dict → DataFrame
array = []
array.append({
'列名1': 'value1',
'列名2': 'value2',
'列名3': 'value3',
})
df = pd.DataFrame.from_dict(array, dtype=object)
辞書に格納すると可読性が上がるので、僕は好んで使っています。
リスト
# Array → DataFrame
df = pd.DataFrame(array, columns=["1", "2", "3"], index=["1", "2", "3"])
カラム名、インデックス名を渡さない場合はPandasが勝手に命名します。
DataFrameから書き出す方法
最終的にできあがったDataFrameは、他の形式で書き出すことができます。
CSV|基本
# CSVとして出力
df.to_csv(csv_path, encoding='utf-8_sig')
CSV|インデックスなし
# CSVとして出力
df.to_csv(csv_path, index=False)
Excelとして書き出す
# DataFrame → Excel
df.to_excel(output_path, sheet_name=sheet_name)
第二引数sheet_nameを省略すると”Sheet1″というシート名になります。
Excelとして書き出す|追記モード
# DataFrame → Excel(追記モード)
with pd.ExcelWriter(output_path, mode="a") as writer:
df.to_excel(writer, sheet_name=sheet_name)
ExcelWriterを使います。
# シート追記モードでExcelに書き込む
def pd_add_sheet(df, book_path, sheet_name):
with pd.ExcelWriter(book_path, mode="a") as writer:
df.to_excel(writer, sheet_name=sheet_name)
こちらのコードはよく使うので、関数化しておくと便利です。
型変換
基本的にはastypeで型変換ができます。
ただし日付系の型はやり方が少し異なるので注意しましょう。
int型に変換
# int型に変換
df["列名"] = df["列名"].astype(int)
str型に変換
# int型に変換
df["列名"] = df["列名"].astype(str)
float型に変換
# float型に変換
df["列名"] = df["列名"].astype(float)
timedelta型に変換
# str型 → timedelta型
df["時間"] = pd.to_timedelta(df["時間"])
前処理としてstr型で00:00:00【時:分:秒】の形式にしておく必要があります。
# time型 → str型
df["時間"] = df["時間"].astype(str)
# str型 → timedelta型
df["時間"] = pd.to_timedelta(df["時間"])
元のデータがdatetime.time型の場合は直接timedelta型に変換できません。
その場合はいったんstr型に変換してからtimedelta型に変換します。
# 00:00:00の形式にする
df["時間"] = pd.to_timedelta(df["時間"] + ":0")
# str型 → timedelta型
df["時間"] = pd.to_timedelta(df["時間"])
もともとの文字列が00:00の形であれば、上記のように秒:00を付け加えます。
str型 → datetime型
# str型 → datetime型
df['年月日'] = pd.to_datetime(df['年月日'])
# 要素に置き換え
df['年月日'] = df['年月日'].dt.year
df['年月日'] = df['年月日'].dt.month
df['年月日'] = df['年月日'].dt.day
年月がstr型で取り込まれてしまった場合などには、datetime型に変換すると便利です。
サンプルコードのように月だけ抽出したり、年だけに置き換えたりなどが簡単になります。
datetime型 → str型
# datetime型 → str型
df['年月日'] = df['年月日']).dt.strftime('%Y%m%d')
スラッシュ区切りの場合は‘%Y/%m/%d’とします。
データの絞り込み
Excelでいうところのフィルター機能です。
条件で絞り込む|dfの入れ子
# 身長180以上に絞り込む
df = df[df["身長"] >= 180]
# 身長180ピッタリのデータに絞り込む
df = df[df["体重"] == 180]
それぞれのdfの役割は以下の通りです。
- 内側:条件に合致した行をTrue、そうでないものをFalseにする
- 外側:Trueのデータだけを抽出
パッと見は複雑ですが、そこまで難しいことはありません。
条件で絞り込む|locを使った方法
# 複数条件で絞り込む
df = df.loc[df['種類'] == 'バルク', '正規品']
条件で絞り込む|query
# 文字列で絞り込み
df = df.query("列名 == '絞り込みたい文字列'")
# 変数(文字列)で絞り込み
var = '春'
df = df.query(f"列名 == '{var}'")
# 数値で絞り込み
df = df.query("列名 == 絞り込みたい数値")
クオテーションの使い方がやや面倒なので、dfの入れ子を使う方法がおすすめです。
ある文字列を含むものに絞り込む
# リストを作成
name_list = ["Mary", "John"]
# 「身長」列をリストに含まれるものだけに絞り込み
df = df[df["name"].isin(name_list)]
列を抽出
# 列を抽出(列名)
df = df.loc[:, ["列名1", "列名3", "列名5"]]
# 列を抽出(列番号)
df = df.iloc[:, [1, 3, 5]]
列名をキーに抽出する場合はlocを、列番号をキーに抽出する場合はilocを使います。
ユニークな値を取得
# name列のユニークな値を取得
df = list(df["name"].unique())
unique関数はDataFrameに対応していないので、Seriesにしてから使います。
ある値が出現したら、その行以降を取得する
#1 社員番号'12345'がある行番号を取得
index_num = df['社員番号'].index.get_loc('12345')
#2 スライスで絞り込み
df = df[index_num+1:]
データクレンジング
インデックス列を指定
# 特定の列をインデックス名にする(元々の列を残す場合はdrop=False)
df = pd.set_index(csv_path, index_col=0)
デフォルトでは0始まりの番号ですが、特定の列の値をインデックス名にできます。
カラム名・インデックス名の変更
# カラム名を変更
columns = {"1": "col1", "2": "col2"}
index = {"1": "row1", "2": "row2"}
df = df.rename(columns=columns, index=row)
変更前・変更後の名前を辞書で渡します。
インデックス番号をリセット
# インデックスをリセット
df = df.reset_index(drop=True)
絞り込みした後などはインデックスが歯抜けになるので、リセットすると0から始まるキレイなインデックスがふられます。
drop=Falseとした場合には元々のインデックス番号の列が残ります。
DataFrame同士を結合
# DataFrame同士を結合(縦方向)
df = pd.concat([df_1, df_2])
# DataFrame同士を結合(横方向)
df = pd.concat([df_1, df_2], axis=1)
# DataFrame同士を結合(共通の列だけ残す)
df = pd.concat([df_1, df_2], join='inner')
行・列の削除
# 列の削除
df = df.drop(df.columns[[1, 3, 7]], axis='columns')
# 行の削除
df = df.drop(df.columns[2, 4, 8])
列の追加
# 15列目にarray(配列)を要素とする「名前」列を追加
df = df.insert(15, '名前', array)
欠損値|置き換え
# 欠損値を空白に置き換え
df = df.fillna("")
# 特定の列のみ欠損値を0に置き換え
var = {"col1": 0}
df = df.fillna(var)
欠損値の置き換えはデータ型に気を付けましょう。
例えばint型の列なら空白ではなく0などとに置換しないと集計に失敗します。
欠損値|欠損値のみの行を削除
# 欠損値のみの行を削除
df = df.dropna(how="all")
ある行の値がすべてNanの場合には、その行を削除します。
並び替え
# 普通の並び替え(降順)
df = df.sort_values('列名', ascending=False)
# 複数条件
df = df.sort_values(by=["列名1", "列名2"])
デフォルトは昇順なので、降順にする際にはascendingをFalseにします。
複数条件の場合には、byに渡したリストの左側ほど並び替えの優先度が高くなります。
数値以外なら行削除
# 数値以外なら行削除(方法1)
df = df[df['ID'].astype(str).str.isdecimal]
# 数値以外なら行削除(方法2)
df = df[~pd.to_numeric(df['ID'].errors='coerce').isnull()]
文字列以外をNaNに置き換えた後、NaN以外の行を取得するコードです。
「列をint型に変換したい!でも文字列が混じっている…」という場合にどうぞ。
errors='coerce'
とすることで数値にできなかったものをNaNにしてくれます。
引数をraise
にすると例外が、ignore
にすると無視してそのまま放置します。
数値以外だった場合に別の値に置き換え
# 数値以外をNaNにする
df = pd.to_numeric(df['ID'].errors='coerce')
# NaNを別の文字列に置き換え
df['ID'].fillna(0)
考え方は「数値以外なら行削除」と同じです。
数値以外をNaNにして、fillnaする流れですね。
集計
列の合計値を取得
# cost列の合計値
total_cost = df["cost"].sum()
値を取得する
# 行・列名から値を取得
var = df.at['行ラベル']['列ラベル']
# 行・列番号から値を取得
var = df.iat['行番号', '列番号']
右辺と左辺を逆にすれば値の代入ができます。
まとめ
PandasはDataFrameの扱い方にクセがあるものの、使えるようになるとコードを書くのが格段にラクになります。
マスターを急ぐと心が折れるので、少しずつできることを増やしていきましょう!
なお、to_excelしたデータを細かく編集するにはwin32comによるExcel操作が便利です。
別記事で詳しく解説しましたのでこちらも参考にしてみてください。

コメント