Python入門|リスト(list)同士の差分を求める方法【用途別3種類】のイメージ画像

Python入門|リスト(list)同士の差分を求める方法【用途別3種類】

  • 公開日:2018/07/01
  • 更新日:2018/10/19
  • 投稿者:n bit

2つのリストから共通する部分を削除し差分のデータを求めたい場合があります。今回はそれぞれの用途別に3種類のリスト差分の求め方を紹介します。3種類のパターンは2つのリスト内に重複したデータがあった場合に出力される結果が大きく変化します。

  • Python

この記事は約 分で読めます。(文字)

リスト(list)同士の差分を求める

2つのリストから共通する部分を削除し残りの差分データを求める方法を紹介します。それぞれのリストに重複したデータがあった場合、求め方によって出力される結果が変わってきます。ここではそれぞれの求める目的別に3種類の求め方を紹介します。

3種類の目的は以下の通りです。

  • 目的1:重複データを保持せずユニークな差分データだけを求める
  • 目的2:共通しない部分の差分データに関しては重複データも残して求める
  • 目的3:すべてのデーターで重複データを残して差分データを求める

言葉で書くととてもわかりにくいので下記に差分を求める2つのリストと、それぞれの目的別に出力される結果を記載しておきます。

今回説明に利用する2つのリストは以下の通りです。

  • リスト1:['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']
  • リスト2:['大山', '佐藤']

リスト1の方には2つずつ同じ名前の要素がリスト内に存在しています。

目的1で求める出力結果

  • ['山下', '平野']

目的1では完全に重複データは1つとして扱われ、リスト1とリスト2の差分を求めます。

目的2で求める出力結果

  • ['山下', '平野', '山下', '平野']

目的2ではリスト2と共通する『'大山', '佐藤'』は重複データも含めて全て削除され、逆に共通しない部分に関しては重複データも含めて全て残した差分を求めます。

目的3で求める出力結果

  • ['大山', '佐藤', '山下', '平野', '山下', '平野']

目的3ではリスト2と共通する『'大山', '佐藤'』部分も、共通しない部分に関しても全て重複データは残した状態で差分を求めます。

このようにそれぞれの特性を把握し求める出力結果に対して求め方を変更していきます。それでは具体的にこの3つの求め方のコードとやり方を解説していきます。

2つのリストから重複データを保持せずユニークな差分データだけを求める

それでは、「目的1:重複データを保持せずユニークな差分データだけを求める」のコードの解説から始めていきます。

リスト同士では直接マイナス記号を使って差分を求めることができませんので、1度【set】に変換してから差分を求め、その結果をもう1度【list】に戻します。

まずはサンプルよりももっと単純なリストをもとに差分を求めてみましょう。

l1 = ['大山', '佐藤', '山下', '平野']

l2 = ['大山', '佐藤']
result = list(set(l1) - set(l2))
print(result)

出力結果

['山下', '平野']

このコードと出力結果だけを確認すると、これで全て問題ないかのように思えますが先程のサンプルで利用した2つのリストで再度差分を求めてみると目的によっては問題が発生することがわかります。

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']

l2 = ['大山', '佐藤']
result = list(set(l1) - set(l2))
print(result)

出力結果

['山下', '平野']

先程の重複のないリストを使った場合と出力結果が全く同じになっていることが確認できます。これは【list】から【set】に変更する時に重複データが削除されてしまうために起こります。

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']

print(set(l1))

出力結果

{'平野', '佐藤', '大山', '山下'}

つまり、

  • ['大山', '佐藤', '山下', '平野']
  • ['大山', '佐藤']

の2つのリストで差分を求めているのと同じことになります。

また出力結果からもわかるようにデータの順序も保持されません。これらの特性は目的によっては問題となりますが、共通していないユニークなデータのみを差分で求めたいときにはとても有効です。

2つのリストから共通しない部分の差分データに関しては重複データも残して求める

次は、目的1と違い2つのリストで共通する部分に関しては重複データも含めて全て削除し、逆に共通しない部分に関しては重複データも保持したまますべてのデータを返すようにします。

これを求めるためには【filter】を利用します。

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']

l2 = ['大山', '佐藤']
result = list(filter(lambda x: x not in l2, l1))
print(result)

出力結果

['山下', '平野', '山下', '平野']

共通する部分のデータ『'大山', '佐藤'』に関しては重複データも含めて全て削除されているのが確認できます。逆に共通しない部分のデータ『'山下', '平野'』に関しては重複データも残された出力結果が返されているのが確認できますね。

2つのリストからすべてのデーターで重複データを残して差分データを求める

最後の方法は重複データを全てまとめることなく完全に個別に比較して差分データを求める方法です。

こちらに関してはPythonに標準で用意されている機能では対応できないため独自の簡単な関数を作成して求めます。

def list_difference(list1, list2):

result = list1.copy()
for value in list2:
if value in result:
result.remove(value)

return result

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']
l2 = ['大山', '佐藤']
result = list_difference(l1, l2)
print(result)

出力結果

['大山', '佐藤', '山下', '平野', '山下', '平野']

2つのリストの共通する部分『'大山', '佐藤'』に関しても、それ以外の共通しない部分に関しても重複データが全て残されているのが確認できます。またリスト内の順番通りにfor文でループさせているため順序も保持されます。

目的1と目的2に関しては少し用途が特殊で、この目的3に関しては完全に個別データの差分を求めるため一般的に言われる差分のイメージに近いと思います。

今日のdot

2つのリスト同士の差分のデータの求め方は目的別に3つの方法があります。

  • 目的1:重複データを保持せずユニークな差分データだけを求める場合
  • 目的2:共通しない部分の差分データに関しては重複データも残して求める場合
  • 目的3:すべてのデーターで重複データを残して差分データを求める場合

目的1:重複データを保持せずユニークな差分データだけを求める場合

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']

l2 = ['大山', '佐藤']
result = list(set(l1) - set(l2))
print(result)

出力結果

['山下', '平野']

目的2:共通しない部分の差分データに関しては重複データも残して求める場合

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']

l2 = ['大山', '佐藤']
result = list(filter(lambda x: x not in l2, l1))
print(result)

出力結果

['山下', '平野', '山下', '平野']

目的3:すべてのデーターで重複データを残して差分データを求める

def list_difference(list1, list2):

result = list1.copy()
for value in list2:
if value in result:
result.remove(value)

return result

l1 = ['大山', '佐藤', '大山', '佐藤', '山下', '平野', '山下', '平野']
l2 = ['大山', '佐藤']
result = list_difference(l1, l2)
print(result)

出力結果

['大山', '佐藤', '山下', '平野', '山下', '平野']