コンテンツにスキップ

単元 8:重複・並べ替え

  • duplicated() で重複行を検出できる
  • drop_duplicates() で重複行を除去できる(保持する行を選べる)
  • sort_values() で 1 列・複数列で並べ替えられる
  • 昇順/降順、欠損値の扱い方を指定できる

重複と並べ替えは「データの整え」の仕上げ

Section titled “重複と並べ替えは「データの整え」の仕上げ”

前単元までで「欠損」「型」「列の編集」を整えました。集計や可視化に進む前の仕上げとして、重複の除去並べ替え を行います。

  • 重複 — 同じレコードが 2 回以上記録されていると、件数も平均もずれます。データ収集の不備や結合ミスで発生しがち
  • 並べ替え — 表を「最新順」「金額の高い順」など、人や次の処理が見やすい順序に並べることで、要約や上位抽出が簡単になります

どちらも 1 メソッドで完結する操作ですが、何を基準にするか の決定が重要です。

duplicated()「上から見て同じ行が出てきたら True」 を返します:

df.duplicated() # 各行が重複かどうかを Series で返す
df.duplicated().sum() # 重複行の数
df[df.duplicated()] # 重複行だけを抽出

デフォルトでは 全列が一致する行 を重複と見なします。「氏名と生年月日だけで判定したい」場合は subset を指定:

df.duplicated(subset=["氏名", "生年月日"])

drop_duplicates() は重複行を取り除きます。デフォルトでは 「最初に出てきた行を残す」 挙動です:

df.drop_duplicates()
df.drop_duplicates(subset=["氏名", "生年月日"])

「最後に出てきた行を残したい」場合は keep="last" を、「重複したらすべて落としたい」場合は keep=False を指定します:

df.drop_duplicates(keep="last") # 最後を残す
df.drop_duplicates(keep=False) # 重複したものは全部落とす

たとえば「同じ顧客の最新の購入記録だけ残したい」なら、日付で並べ替えてから keep="last"、というパターンになります。

sort_values() は指定列で並べ替えます:

df.sort_values("年齢") # 昇順(小さい順)
df.sort_values("年齢", ascending=False) # 降順(大きい順)

文字列の列でも使えますが、辞書順(A, B, C, … a, b, c, … 日本語は文字コード順)になる点に注意。意図した順序にならないことがあります。

「都市で並べた上で、同じ都市内では年齢が高い順」のような複合的な並べ替えはリストで指定:

df.sort_values(
by=["都市", "年齢"],
ascending=[True, False], # 都市は昇順、年齢は降順
)

ascending をリストで渡すと、列ごとに昇順/降順を変えられます。

並べ替えで欠損値(NaN)がどこに来るかは指定できます:

df.sort_values("年齢", na_position="last") # 既定:欠損を末尾に
df.sort_values("年齢", na_position="first") # 欠損を先頭に

意外と気付かないですが、欠損値の位置で上位抽出(head(10))の結果が変わります。

「値ではなくインデックスで並べる」場合は sort_index()

df.sort_index() # 行インデックスで並べ替え
df.sort_index(axis=1) # 列名で並べ替え

時系列データを「日付順」に戻したいときによく使います。

並べ替えと「上位抽出」のセット

Section titled “並べ替えと「上位抽出」のセット”

sort_values() の結果に head() を続ければ、「上位 N 件」を簡単に取り出せます:

df.sort_values("収入", ascending=False).head(10) # 収入が高い順 10 件
df.sort_values("年齢").head(5) # 年齢が低い順 5 件

このパターンは集計の前段としても、最終結果のレポートとしても頻出します。

  • 重複判定で subset を指定し忘れる — 全列一致を見るので、付随情報が少し違うだけで「重複」と認識されない
  • drop_duplicates() の戻り値を変数に代入し忘れる — pandas のメソッドは原則「新しい DataFrame を返す」。df.drop_duplicates() を呼んだだけでは df は変わらない。df = df.drop_duplicates() と代入し直す
  • 文字列の並べ替えが意図と異なる — 辞書順なので「10、2、3」が「10、2、3」のままになる(数値ソートではない)。事前に型を直すか、str.zfill() で桁数を揃える
  • 欠損値の位置を考慮せずに上位抽出na_position の指定で結果が変わる

users.csv を題材に、次を順に行いなさい。

  1. df.duplicated().sum() で重複の有無を確認する
  2. 年齢 列で降順に並べ替えて、上位 3 件を表示する
  3. 都市 昇順・年齢 降順の組み合わせで並べ替える
  4. 「同じ氏名は 1 行だけ残す」前提で drop_duplicates(subset=["名前"]) を実行し、行数の変化を確認する

drop_duplicates() の結果を df に代入し直すと、以降の操作にも反映されることが体感できます。

  • sort_valueskey 引数を使って、文字列を小文字化してから並べ替える
  • 時系列風のデータを作り、sort_index で日付順に戻してから head() で「最初の数日」を取り出す
  • duplicated()keep=False を指定し、「2 回以上現れる行を全部抽出」して可視化する