段階的なソートについて説明します。
段階的なソートとは、例えば3行3列の2次元リストがある時に、2列目を基準にソートした後に3列目を基準にソートすることです。
sortedを1回使用する場合と複数回使用する場合がありますが、 sortedを1回使用する場合の方がおすすめです。考え方が直感的であり、コードも短いためです。但し、任意の列を降順にソートする場合は、sortedを複数回使用する必要があります。
段階的なソート
下記、リストを血液型→体重の優先度でソートすることを考えます。優先度が高い列の値が辞書順で先か、小さいほどソート後に上の行になります 。
本章では、リストの列を基準として昇順にソート行うことを前提に説明をします。つまり、ソートすると辞書順で先である文字列、小さい数値ほど上の行になります。
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
sortedを1回使用する場合
itemgetterを使用
下記により、段階的なソートができます。
sorted(ソート対象のリスト, key=itemgetter(ソートの基準とする列1, ソートの基準とする列 2、…))
優先度が高い列を左から順に、itemgetterの引数として与えてください。
血液型→体重の優先度でソートするため、下記コードでは itemgetter(血液型の列、体重の列)としてソートしています。
from operator import itemgetter
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
#正しい例:
s1 = sorted(human, key=itemgetter(1, 2))
print(*s1, sep="\n")
"""出力
('masasi', 'A', 70)
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
"""
#誤った例:
s2 = sorted(human, key=itemgetter(2, 1))
print(*s2, sep="\n")
"""出力
('tadasi', 'O', 50)
('masasi', 'A', 70)
('kiyosi', 'O', 100)
"""
ラムダ式を使用
ラムダ式を使用した方法を示します。
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
#正しい例:
s1 = sorted(human, key=lambda x:(x[1], x[2]))
print(*s1, sep="\n")
"""出力
('masasi', 'A', 70)
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
"""
sortedを複数回使用する場合
itemgetterを使用
下記により、段階的なソートができます。
リスト① = sorted(ソート対象のリスト, key=itemgetter(ソートの基準とする列1))
リスト② = sorted(ソート対象のリスト(リスト①), key=itemgetter(ソートの基準とする列 2))
優先度が低い列から順にソートしてください。そうしないと、最終結果が望んだ優先度順でソートされたものになりません。
血液型→体重の優先度でソートしたい場合は、体重を基準としてソートした後に、血液型を基準にソートする必要があります。
from operator import itemgetter
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
#正しい例:体重でソート → 血液型でソート
s1 = sorted(human, key=itemgetter(2))
print(*s1, sep="\n")
"""出力
('tadasi', 'O', 50)
('masasi', 'A', 70)
('kiyosi', 'O', 100)
"""
s1 = sorted(s1, key=itemgetter(1))
print(*s1, sep="\n")
"""出力
('masasi', 'A', 70)
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
"""
#誤った例:血液型でソート → 体重でソート
s2 = sorted(human, key=itemgetter(1))
print(*s2, sep="\n")
"""出力
('masasi', 'A', 70)
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
"""
s2 = sorted(s2, key=itemgetter(2))
print(*s2, sep="\n")
"""出力
('tadasi', 'O', 50)
('masasi', 'A', 70)
('kiyosi', 'O', 100)
"""
ラムダ式を使用
ラムダ式を使用した方法を示します。
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
#正しい例:体重でソート → 血液型でソート
s1 = sorted(human, key=lambda x: x[2])
print(*s1, sep="\n")
"""出力
('tadasi', 'O', 50)
('masasi', 'A', 70)
('kiyosi', 'O', 100)
"""
s1 = sorted(s1, key=lambda x: x[1])
print(*s1, sep="\n")
"""出力
('masasi', 'A', 70)
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
"""
降順にソート
2次元リストの任意の列を降順にソートすることを考えます。
「sortedを1回使用する方法」でreverse=Trueとして降順にソートする場合、2次元リストの全ての列が降順にソートされてしまいます。
そのため、2次元リストの任意の列のみを降順にソートする場合は、「sortedを複数回使用する方法」でソートする必要があります。
下記コード( 血液型→体重の優先度でソート)から、 「sortedを1回使用する方法」でreverse=Trueとして降順にソートする場合、2次元リストの全ての列が降順にソートされることがわかります。
from operator import itemgetter
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
s1 = sorted(human, key=itemgetter(1, 2), reverse=True)
print(*s1, sep="\n")
"""出力
('kiyosi', 'O', 100)
('tadasi', 'O', 50)
('masasi', 'A', 70)
"""
下記コード ( 血液型→体重の優先度でソート) では、 2次元リストの任意の列のみを降順にソートしています。
from operator import itemgetter
human = [
#名前 , 血液型, 体重
('tadasi', 'O', 50 ),
('kiyosi', 'O', 100 ),
('masasi', 'A', 70 ),
]
#体重のみ降順にソート
s1 = sorted(human, key=itemgetter(2), reverse=True)
print(*s1, sep="\n")
"""出力
('kiyosi', 'O', 100)
('tadasi', 'O', 50)
('masasi', 'A', 70)
"""
s1 = sorted(s1, key=itemgetter(1))
print(*s1, sep="\n")
"""出力
('masasi', 'A', 70)
('kiyosi', 'O', 100)
('tadasi', 'O', 50)
"""
#血液型のみ降順にソート
s1 = sorted(human, key=itemgetter(2))
print(*s1, sep="\n")
"""出力
('tadasi', 'O', 50)
('masasi', 'A', 70)
('kiyosi', 'O', 100)
"""
s1 = sorted(s1, key=itemgetter(1), reverse=True)
print(*s1, sep="\n")
"""出力
('tadasi', 'O', 50)
('kiyosi', 'O', 100)
('masasi', 'A', 70)
"""
参考
ソート HOW TO — Python 3.9.4 ドキュメント
ソートの安定性と複合的なソート
Complex sort with multiple parameters?
ラムダ式を使用した方法

コメント