pandasの列操作まとめ|列の結合・分割・applyとインデックスの整形
/ 8 min read
Table of Contents
はじめに
会計システムから出したデータは、そのままの形で使えることがまずありません。住所と町域名がひとつのセルに詰め込まれていたり、ヘッダーが1行目のデータとして入っていたり、集計したらキーがインデックスに移動してしまったり。
この記事では、そうした「形を整える」操作をまとめました。コードはすべて pandas 3.0 で実際に動かして確認しています。
複数の列を1つに結合する
各行の値をカンマでつないで新しい列を作ります。
df["new_col"] = df.astype(str).apply(",".join, axis=1)| A | B | new_col |
|---|---|---|
| a | b | a,b |
| c | d | c,d |
特定の列だけを結合したいときは、対象を絞ってから渡します。
df["キー"] = df[["部門コード", "勘定科目コード"]].astype(str).apply("-".join, axis=1)区切り文字が不要なら str.cat() のほうが簡潔です。
df["キー"] = df["部門コード"].str.cat(df["勘定科目コード"], sep="-")1つの列を複数の列に分解する
リストが入った列を、要素ごとの列に展開します。
df_s = df["lst"].apply(pd.Series)df_s.columns = ["name", "x", "y"]| lst | name | x | y | |
|---|---|---|---|---|
| [hoge, 100, 200] | → | hoge | 100 | 200 |
文字列を区切り文字で分解する場合は、str.split() に expand=True を付けるほうが速くて簡潔です。
# 「東京都-千代田区-1-1」を3列に分けるdf[["都道府県", "市区町村", "番地"]] = df["住所"].str.split("-", n=2, expand=True)n=2 は「最初の2回だけ分割する」という指定です。番地にハイフンが含まれていても、余計な列が増えずに済みます。
1行を複数行に展開する
1つのセルに複数の値が詰め込まれている場合、explode() で行に展開できます。
df = df.assign(町域名=df["町域名"].str.split("、")).explode("町域名")| 郵便番号 | 町域名 | 郵便番号 | 町域名 | |
|---|---|---|---|---|
| 11111111 | 甲、乙 | → | 11111111 | 甲 |
| 11111111 | 乙 |
assign() で列をリストに変えてから、explode() でそのリストを行に開く、という2段構えです。explode() は assign() の外側に置きます。
展開後はインデックスが重複するので、振り直しておくと後が楽です。
df = df.assign(町域名=df["町域名"].str.split("、")).explode("町域名").reset_index(drop=True)区切り文字を残したまま分割する
正規表現の後読み・先読みを使うと、区切り文字を消さずに分割できます。
# 句点「。」の直後で区切る(句点は残る)df.assign(文=df["文"].str.split(r"(?<=。)(?=.)")).explode("文")(?<=。) が「直前が句点」、(?=.) が「直後に何か文字がある」という条件です。末尾の句点で空文字が生まれるのを防いでいます。
applyを複数列で使う
行方向に処理するので axis=1 を指定します。
df["new"] = df.apply(lambda x: x["ColA"] + x["ColB"], axis=1)ただし、単純な四則演算に apply は不要です。列同士の演算はそのまま書けて、しかもはるかに高速です。
# apply を使わずに書けるdf["new"] = df["ColA"] + df["ColB"]
# 条件分岐も np.where で書けるimport numpy as npdf["区分"] = np.where(df["金額"] >= 0, "借方", "貸方")apply は「1行ずつPythonの関数を呼ぶ」処理なので、数万行を超えると目に見えて遅くなります。ベクトル演算で書けないか一度考えてから使ってください。
1から始まる連番の列を追加する
伝票番号や通し番号を振るときに使います。
df["No"] = range(1, len(df) + 1)pd.RangeIndex を使っても同じ結果になります。
df["No"] = pd.RangeIndex(start=1, stop=len(df) + 1, step=1)groupby したあとインデックスに移った列を戻す
groupby() すると、キーにした列がインデックスになります。
df = df.groupby("部門").sum()# 「部門」が列ではなくインデックスになっているas_index=False を付ければ、最初から列のまま残ります。
df = df.groupby("部門", as_index=False)["金額"].sum()すでにインデックスになってしまった場合は reset_index() で戻します。
df = df.groupby(["部門", "勘定科目"]).sum().reset_index()特定の行をヘッダーにする
Excelから読み込むと、1行目がヘッダーではなくデータとして入ってしまうことがあります。
# 1行目をヘッダーにして、その行自体は削除するdf.columns = df.iloc[0]df = df[1:].reset_index(drop=True)読み込みの時点で分かっているなら、header で指定するほうが確実です。
# 3行目(0始まりで2)をヘッダーとして読むdf = pd.read_excel("shisan.xlsx", header=2)特定の行を横にずらす
ヘッダーの位置がずれている表を直すときに使います。
# 1行目を右に2つずらすdf.iloc[0] = df.iloc[0].shift(2, fill_value="na")おわりに
形を整える操作は、一度書いてしまえば毎月そのまま使い回せます。逆に言えば、ここを手作業でやっている限り毎月同じ時間がかかり続けるということでもあります。
apply は万能に見えますが、多くの場合はベクトル演算で書き換えられます。処理が遅いと感じたら、まず apply を疑ってみてください。
