YutaKaのPython教室

Python の文法やライブラリ、API、環境構築について画像・動画・ソースコード付きで徹底解説!

pandas 重複行の抽出と削除【図解で解決!】

pandasでは、DataFrameSeries内の重複行を簡単に抽出、削除することができます。

しかし、実際に重複処理をしようとしても、次のような問題に直面することも…。

  • そもそも重複行を抽出する方法は?
  • 重複行を削除することはできるの?
  • 特定の列が重複しているかを判定したい!

そこで、この記事ではpandasの重複処理について、データ抽出、削除、重複行に連番を振る方法をわかりやすく・図解付きで解説していきます!

pandas重複処理まとめ

DataFrameの主な重複行の処理として、まずは次の3つを抑えておきましょう。

処理内容 メソッド名 適用
重複行の判定 df.duplicated() データ列をもとに重複を判定
※インデックス列は調査対象にならない
重複行の削除 df.drop_duplicates() 同上、さらに対象行を削除
インデックス列の重複判定 df.index.duplicated() インデックス列をもとに重複判定

図にしてみると、次のような対応関係になっています。

DataFrame, Seriesのメソッドでは、次のように重複判定を行います。

  • データ列をもとに重複判定が行われる(インデックス列は重複判定に使用されない
  • 全てのデータ列が同じ場合のみ、重複行として判定される

インデックス列で重複判定する場合は、indexのメソッドを使用することに注意しましょう。

以下では、各重複処理について詳しく解説していきます。

重複列の判定・抽出|.duplicated()

重複列の判定には、.duplicated()メソッドを使用します。

デフォルトでは、次のような挙動になります。

  • 全てのデータ列が同じ値を持つ行がある場合に重複とみなされる
  • 2回目以降に現れた重複行がTrueとなるSeriesを返す

言葉ではわかりにくい部分もあるので、次のサンプルコードで挙動を確認してみましょう。

import pandas as pd
df = pd.DataFrame({"名前": ["田中", "田中", "田中", "伊藤", "田中"], 
                   "注文": ["カレー", "寿司", "カレー", "寿司", "カレー"],
                   "値段": [1000, 1200, 1000, 1200, 1000]})

#    名前   注文    値段
# 0  田中  カレー  1000  ← 0, 2, 4行目が重複
# 1  田中   寿司   1200
# 2  田中  カレー  1000  ← 0, 2, 4行目が重複
# 3  伊藤   寿司   1200
# 4  田中  カレー  1000  ← 0, 2, 4行目が重複

デフォルト設定のまま、df.duplicated()を実行してみます。

df.duplicated()

# 0    False   ← 0, 2, 4行目が重複
# 1    False
# 2     True   ← 0, 2, 4行目が重複
# 3    False   
# 4     True   ← 0, 2, 4行目が重複
# dtype: bool

重複している0, 2, 4行目のうち、2回目以降に現れる2, 4行目にのみTrueを持つSeriesが返されました。

DataFrameは真偽値のSereisを渡すと、Trueの行だけ抽出できるので、 df.duplicated()dfに渡せば重複行を抽出できますね。

df[df.duplicated()]

#    名前   注文    値段
# 2  田中  カレー  1000
# 4  田中  カレー  1000

逆にFalse行のみを抽出したい場合は、~で真偽を反転させればOKです。

df[~df.duplicated()]

#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200

重複行の削除は、後述のdf.drop_duplicated()で生成することもできます。

以下で、df.duplicated()の詳細設定について、解説していきます。

  • 【参考】基本的なデータ抽出方法については、次の記事を参考にしてください。
≫pandas DataFrameの行・列を抽出|loc, ilocなどわかりやすく解説!
pandasのDataFrameを使うと、行と列で構成されたデータを簡単に取り扱うことができます。この記事では、「DataFrameの特定の行、列のデータを抽出する方法」、「インデックス参照、.loc、.iloc、at、iat[]の使い方」をわかりやすい図解付きで解説しています。
www.yutaka-note.com/entry/pandas_access
 
≫ pandas 行名・列名からデータ抽出|条件を満たすラベル指定
データ分析では、特定の行・列のデータを抽出することがよくあります。しかし、pandasは抽出方法が柔軟すぎて、逆に抽出方法がわからなくなってしまうことも…。この記事では、①行名・列名を指定してデータを抽出する方法、②特定の文字を含んでいる行・列を抽出する方法、③正規表現で複雑な条件を設定する方法をわかりやすく解説します。ラベルからのデータ抽出ができるようになると、DataFrameの操作がぐっと楽になります!
www.yutaka-note.com/entry/pandas_filter
 

.duplicated()の引数

.duplicated()メソッドでは、引数を指定して次のような詳細設定を行うことができます。

設定内容 引数名 適用
重複判定する列の指定 subset="列名" or
              "列名のリスト"
デフォルト:全列が同じだと重複と判定
subsetで調査列を指定可能
Trueを返す重複行の指定 keep="first", or
           "last", or
            False
"first"(デフォルト):一個目以外の重複行でTrue
"last":最後以外の重複行でTrue
False:全ての重複行でTrue

以下で、サンプルデータを用いて挙動を確認してみます。

特定の列で重複判定|subset

.duplicated()では、基本的に全ての列が同じ値を持っていると重複判定されます。

df
#    名前   注文    値段
# 0  田中  カレー  1000  ← 0, 2, 4行目が重複
# 1  田中   寿司   1200
# 2  田中  カレー  1000  ← 0, 2, 4行目が重複
# 3  伊藤   寿司   1200
# 4  田中  カレー  1000  ← 0, 2, 4行目が重複

# subsetの指定なしで重複行を出力
df[df.duplicated(keep=False)]
#    名前   注文    値段
# 0  田中  カレー  1000
# 2  田中  カレー  1000
# 4  田中  カレー  1000

※ ここでは重複判定結果がわかりやすいように、keep=Falseで重複列全てを抽出しています(keepについては後述)。

全ての列ではなく、特定の列を指定して重複判定する場合には、引数subsetで列名を指定します。

  • 重複判定する列を指定:subset="列名" or ["列名"のリスト]

["列名"のリスト]を指定すれば、指定した列が全て同じデータの場合のみ重複とみなされます。

subset="名前"で、"名前"列が重複している行を判定、抽出してみます。

# .duplicateメソッドで真偽値を出力
df.duplicated(subset="名前", keep=False)
# 0    False
# 1     True
# 2    False
# 3     True
# 4     True
# dtype: bool

# 重複行の抽出
df[df.duplicated(subset="名前", keep=False)]
#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 2  田中  カレー  1000
# 4  田中  カレー  1000

"注文"列が異なっていても、"名前"列が同じ行は重複として判定されましたね。

次に、"名前"列と"注文"列を指定してみます。

# .duplicateメソッドで真偽値を出力
df.duplicated(subset=["名前", "注文"], keep=False)
# 0     True
# 1    False
# 2     True
# 3    False
# 4     True
# dtype: bool

# 重複行の抽出
df[df.duplicated(subset=["名前", "注文"], keep=False)]
#    名前   注文    値段
# 0  田中  カレー  1000
# 2  田中  カレー  1000
# 4  田中  カレー  1000

今度は、"名前"列と"注文"列の両方が同じ行のみが重複行として認識されました。

重複でTrueを返す行の指定|keep

.duplicated()のデフォルト設定では、最初に現れた重複行以外に対してTrueを返します。

サンプルコードで挙動を確認してみましょう。

df
#    名前   注文    値段
# 0  田中  カレー  1000  ← 0, 2, 4行目が重複
# 1  田中   寿司   1200
# 2  田中  カレー  1000  ← 0, 2, 4行目が重複
# 3  伊藤   寿司   1200
# 4  田中  カレー  1000  ← 0, 2, 4行目が重複

df.duplicated()
# 0    False  ← 0, 2, 4行目が重複(デフォルトでは、一つ目の重複行はFalse)
# 1    False
# 2     True  ← 0, 2, 4行目が重複
# 3    False
# 4     True  ← 0, 2, 4行目が重複
# dtype: bool

上の例では、0, 2, 4行目が重複しているので、2回目以降の重複である2, 4行目に対してTrueが返されています。

この挙動は引数keepで変更可能です。

引数の指定方法 設定内容
keep='first'(デフォルト) 最初に現れた重複行以外にTrue
           'last' 最後に現れた重複行以外にTrue
           False 全ての重複行にTrue

サンプルコードで挙動を確認してみましょう。

# keep="first"(デフォルトなので、省略可能)
df.duplicated(keep="first")
# 0    False  ← 0, 2, 4行目が重複(最初の重複行, Falseになる)
# 1    False
# 2     True  ← 0, 2, 4行目が重複(2個目の重複行)
# 3    False
# 4     True  ← 0, 2, 4行目が重複(最後の重複行)
# dtype: bool

# keep="last"
df.duplicated(keep="last")
# 0     True  ← 0, 2, 4行目が重複(最初の重複行)
# 1    False
# 2     True  ← 0, 2, 4行目が重複(2個目の重複行)
# 3    False
# 4    False  ← 0, 2, 4行目が重複(最後の重複行, Falseになる)
# dtype: bool

# keep = False
df.duplicated(keep=False)
# 0     True  ← 0, 2, 4行目が重複(最初の重複行)
# 1    False
# 2     True  ← 0, 2, 4行目が重複(最初の重複行)
# 3    False
# 4     True  ← 0, 2, 4行目が重複(最初の重複行)
# dtype: bool

なぜ引数名がkeepなのかというと、これはdf.drop_duplicate()と関係があります。

df.drop_duplicate()では、引数keepで指定した行が保持(keep)されるようになるためです。

重複行の削除|.drop_duplicates()

重複列の削除には、.drop_duplicates()メソッドを使用します。

デフォルトでは、次のような挙動になります。

  • 全てのデータ列が同じ値を持つ行がある場合に重複とみなされる
  • 2回目以降に現れた重複行を削除したDataFrame or Seriesを返す

重複の判定等は.duplicated()と類似した挙動ですね。

サンプルコードで挙動を確認してみましょう。

df
#    名前   注文    値段
# 0  田中  カレー  1000  ← 0, 2, 4行目が重複
# 1  田中   寿司   1200
# 2  田中  カレー  1000  ← 0, 2, 4行目が重複
# 3  伊藤   寿司   1200
# 4  田中  カレー  1000  ← 0, 2, 4行目が重複

df.drop_duplicates()

#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200

最初に現れた重複行以外の重複行が削除されましたね。

.drop_duplicates()の引数

.drop_duplicates()メソッドでは、引数を指定して次のような詳細設定を行うことができます。

設定内容 引数名 適用
重複判定する列の指定 subset="列名" or
              "列名のリスト"
デフォルト:全列が同じだと重複と判定
subsetで調査列を指定可能
.drop_duplicates()
実行後に残す重複行の指定
keep="first", or
           "last", or
            False
"first"(デフォルト):一個目の重複行を残す
"last":最後の重複行を残す
False:全ての重複行を削除(残さない)
インデックスの振り直し ignore_index=False or
                         True
False(デフォルト): 振り直しなし
True: 連番で振り直し
dfの上書き inplace=False or
                True
False(デフォルト): 新規dfを生成
True: 既存dfを上書き

.duplicated()と類似した引数ですね。

重複行処理で特に重要なsubsetkeepignore_indexについて、詳しく見ていきましょう。

特定の列で重複判定|subset

特定の列を指定して重複判定する場合には、引数subsetで列名を指定します。

  • 重複判定する列を指定:subset="列名" or ["列名"のリスト]

次のサンプルデータで挙動を確認してみましょう。

# "名前"列を指定
df.drop_duplicates(subset="名前")
#    名前   注文    値段
# 0  田中  カレー  1000
# 3  伊藤   寿司  1200

# ["名前", "注文"]を指定
df.drop_duplicates(subset=["名前", "注文"])
#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200

subsetの指定方法がわかると、重複処理の自由度も上がってきますね

削除しない行の指定|keep

drop_duplicatesのデフォルトでは、一つ目の重複行を残して、二つ目以降の重複行が削除します。

この挙動は引数keepで変更可能です。

引数の指定方法 設定内容
keep='first'(デフォルト) 最初の重複行を残す
           'last' 最後の重複行を残す
           False 全ての重複行を削除

実際にサンプルデータで挙動を確認してみましょう。

# keep="first"(デフォルトなので、省略可能)
df.drop_duplicates(keep="first")
#  名前   注文    値段
# 0  田中  カレー  1000 <-最初の重複行が残った
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200

# keep="last"
df.drop_duplicates(keep="last")
#    名前   注文    値段
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200
# 4  田中  カレー  1000 <-最後の重複行が残った

# keep = False:重複行が残らない
df.drop_duplicates(keep=False)
#    名前  注文    値段
# 1  田中  寿司  1200
# 3  伊藤  寿司  1200

インデックスの再設定

デフォルト挙動では、削除された行のインデックスは特段追加処理されることがありません。

そのため、drop_duplicatesによってインデックスが飛び飛びの数字になる場合があります。

df.drop_duplicates(keep="last")
# 名前   注文    値段
# 1  田中   寿司  1200
# 3  伊藤   寿司  1200
# 4  田中  カレー  1000

ignore_index=Trueを指定すると、既存のインデックスを無視して、連番のインデックスを振り直すことができます。

df.drop_duplicates(ignore_index=True)
#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 2  伊藤   寿司  1200

既存のインデックス列がある場合も、連番を振り直すことができます。

df2 = df.copy()
df2.index = pd.Index(["001", "002", "003","004","005"], name="ID")
#      名前   注文    値段
# ID                
# 001  田中  カレー  1000
# 002  田中   寿司  1200
# 003  田中  カレー  1000
# 004  伊藤   寿司  1200
# 005  田中  カレー  1000

df2.drop_duplicates(ignore_index=True)
#    名前   注文    値段
# 0  田中  カレー  1000
# 1  田中   寿司  1200
# 2  伊藤   寿司  1200

元のインデックス列が削除されて、連番のインデックスが振りなおされましたね。

  • 【参考】欠損値の削除については、次の記事を参考にしてください。
≫図解で解決!pandasの欠損値NaNをdropnaで除去する方法
pandasのDataFrame, Seriesでは、.dropna()メソッドを使うと簡単に欠損値NaNを含む行・列を削除できます。しかし、実際に欠損値除去する際には次のような疑問を抱くことも…。①行を除去するか、列を除去するか指定したい!②行の要素が全部NaNのとき、or 一つでもあるときに行・列を除去するには?③特定の列にNaNがあるときだけ、行を削除する方法はある?この記事では、これらの疑問を解決するために.dropna()メソッドについて、図解・サンプルコード付きで詳しく解説します。
www.yutaka-note.com/entry/pandas_dropna
 

インデックス列の重複判定

インデックス列の重複を判定する場合は、indexオブジェクトのメソッドを使います。

メソッド名はdfと同様で.duplicated()です。

  • df.index.duplicated()

インデックスが重複したサンプルデータを用意して挙動を確認してみましょう。

df3=df.set_index("名前")
#      注文    値段
# 名前           
# 田中  カレー  1000
# 田中   寿司  1200
# 田中  カレー  1000
# 伊藤   寿司  1200
# 田中  カレー  1000

df.index.duplicated()で、重複判定をしてみます。

df3.index.duplicated()
# array([False,  True,  True, False,  True])

# 重複インデックスの抽出
df3[df3.index.duplicated(keep=False)]
#      注文    値段
# 名前           
# 田中  カレー  1000
# 田中   寿司  1200
# 田中  カレー  1000
# 田中  カレー  1000
  • インデックスの操作に関しては、次の記事で詳しく解説しているで参考にしてください
≫pandas インデックス列の基本操作|要素にアクセス、検索、欠損値処理
pandasのDataFrameでは、インデックス列の操作方法に関して、網羅的に解説!①インデックスの基本構造②インデックス内要素へのアクセス方法③インデックス内のデータ検索、並べ替え、重複処理、欠損値処理。サンプルコード付きでわかりやすく解説!
www.yutaka-note.com/entry/pandas_index_manip
 

【応用編】重複行に連番を振る方法

重複行に連番を振る場合には、.groupby().cumcount()で要素数を数えればOKです。

  • df.groupby("列名"など).cumcount()

これはDataFrameではなく連番のSeriesを返すだけなので、結果をdfに渡したほうが見やすくなります。

df.groupby("名前").cumcount()

df["重複"]=df.groupby("名前").cumcount()
#    名前   注文    値段  重複
# 0  田中  カレー  1000   0
# 1  田中   寿司  1200   1
# 2  田中  カレー  1000   2
# 3  伊藤   寿司  1200   0
# 4  田中  カレー  1000   3
  • .groupby()に関しては、次の記事で詳しく解説しているで参考にしてください
≫pandas groupbyでグループ化|図解でわかりやすく解説
pandasの.groupby()を使うと、DataFrameの要素をもとにデータをグループ分けして、簡単に集計することができます。①そもそもどうやって.groupbyで、グループ分けするの?②グループ分け結果の確認方法は?③具体的にどうやってグループごとの集計するの?こんな悩みを図解・サンプルコート付きでわかりやすく解決します!
www.yutaka-note.com/entry/pandas_groupby
 

オススメ|pandasとデータ分析の勉強方法

今回はpandasの重複処理について図解付きで解説しました。

pandasは便利すぎて操作方法がわかりにくいことがよくあります…。

結局はコツコツ学ぶのが、pandasマスターの近道ですよね!

≫【ブログカテゴリー:pandas】

データ分析初心者の方にはこちらの記事もおススメです。

私がこれまで勉強してきた経験をもとに考えたおススメの勉強本の紹介記事です。

何から始めて、どうやってレベルアップしていけばいいのか、初心者の方にぜひおススメしたい本を紹介しました。

≫独学でデータ分析を勉強するオススメ学習本
独学でのpythonデータ分析勉強に役立ったおススメ書籍を紹介していきます。業務でそれなりにデータ分析を行えるまで、いろいろな試行錯誤をしてきましたが、もし自分が今ゼロから勉強する立場ならどうするのがいいのか考えてみました。以下では、入門書、個別モジュール用、実践用の3つの視点でおススメ本を紹介していきます。
www.yutaka-note.com/entry/data_analysis
 

オススメのpandas本については、次の記事で紹介しています。

≫【レビュー】「Python実践データ分析100本ノック」|100本終えたらpandasが好きになっていた
Python実践データ分析100本ノックで、実際に100本終了したレビューです。pythonでのデータ分析の入門書としてかなりの良書だったと思います。・python2~3冊目に何を勉強しようか迷っている人・時間をかけずにデータ分析の基本を学びたい人・pandasへの抵抗を減らしたい人
www.yutaka-note.com/entry/nock_100
 
≫【レビュー】「Pythonによるデータ分析入門」| pandas開発者によるpandasユーザーのためのpandasの教科書!
「Pythonによるデータ分析入門」を、最初から最後まで実際に実践してみたレビューです。具体的にどのようなことができるようになったかを実例付きで紹介します!・DataFrameの生成方法・欠損値の処理方法・グラフ化の方法気になる学習時間は…?
www.yutaka-note.com/entry/2019/12/07/230219