Python – sort() と sorted() を使ってリストをソートする方法

この短いガイドでは、組み込みの sort()sorted() 関数を使って Python でリストをソートする方法について学びます。

  • sort()list クラスのメソッドで、リストをインプレースでソートし、 None を返します。
  • sort()` は Python の名前空間に組み込まれたメソッドで、リストを場外にソートし、元のリストに影響を与えることなく、ソートされたコピーを返します。

一般的に言って、大きなデータセットでは sort() の方が効率的です。

一方、 sorted() はリストのコピーを返し、元のリストはそのままなので、より便利なメソッドです。

注意: どちらのメソッドも、デフォルトでは要素の比較に論理和演算子 (<) を使用し、昇順にソートされます。

デフォルトの比較関数、ひいては比較の論理とソート順の両方をオーバーライドすることができます。

次のセクションでは、カスタム比較関数、降順など、さまざまなデータ型のリストをソートする方法と、この2つに関するパフォーマンスベンチマークを実行します。

sort()とsorted()でリストを並べ替える。

sort()関数は、ソートしたいlistに対して呼び出され、その場でソートし、None` を返します。

# my_list is sorted in-place - the original list is changed
my_list.sort()


デフォルトでは昇順にソートされます。

降順でソートするには、関数に reverse=True という引数を与えます。

my_list.sort(reverse=True)


sorted()関数はsort()関数とほとんど同じように動作し、同じ引数を受け取ることができます。

しかし、sorted()`は与えられたリストのコピーを作成し、そのコピーをソートして、オリジナルをそのままにして返します。

# Sorts copy of `my_list` and returns it
sorted_list = sorted(my_list)


比較がどのように行われるかは、リストの要素のデータ型に依存します。

文字列と整数の比較は異なりますし、整数がカスタムオブジェクトと比較されることも異なります。

文字列のリストをソートする

文字列は、>演算子で比較すると、辞書順にソートされます。

string_list = ['I', 'see', 'skies', 'of', 'blue', 'red', 'roses', 'too']


string_list.sort()
print(string_list)
# ['I', 'blue', 'of', 'red', 'roses', 'see', 'skies', 'too']


同じロジックが sorted() 関数にも適用されます。

sorted_list = sorted(string_list)
print(sorted_list)
# ['I', 'blue', 'of', 'red', 'roses', 'see', 'skies', 'too']


Iblueよりも小さい辞書上の値を持っています。

たとえbi` よりも前にあるべき場合でも、大文字は常に小文字よりも小さい辞書上の値を持っているからです。

整数のリストをソートする

整数は、>演算子と比較すると、より分かりやすいと思います。

int_list = [1, 7, 2, 3, 8, 5, 6]
int_list.sort()


print(int_list)
# [1, 2, 3, 5, 6, 7, 8]


または、 sorted() と比較します。

sorted_list = sorted(int_list)
print(sorted_list)
# [1, 2, 3, 5, 6, 7, 8]


タプルのリストをソートする

タプルは値ではなく、キーでソートされます。

例えば、好みのプログラミング言語のリーダーボードが (language, rank) 形式のタプルに格納されているとします。

tuple_list = [('Java', 2), ('Python', 1), ('JavaScript', 3)]
tuple_list.sort()


print(tuple_list)
# [('Java', 2), ('JavaScript', 3), ('Python', 1)]


あるいは、 sorted() を使ってタプルのリストをソートすることもできます。

sorted_tuples = sorted(tuple_list)
print(sorted_tuples)
# [('Java', 2), ('JavaScript', 3), ('Python', 1)]


タプルはキーでソートされるので、このタプルのリストはキーとして使用された文字列によって辞書式にソートされます。

カスタムキーでタプルのリストをソートする

タプルそのものを変更せずに、タプルをソートするための項目を変更するには、 key 引数としてタプルの任意の項目を指定することができます。

一般的には、 lambda 関数を使用して、キーをタプルのリスト内の別のアイテムにマップするのが最も簡単です。

tuple_list = [('Java', 2), ('Python', 1), ('JavaScript', 3)]
tuple_list.sort(key=lambda x:x[1])
print(tuple_list)
# [('Python', 1), ('Java', 2), ('JavaScript', 3)]


または、 sorted() を使用します。

sorted_tuples = sorted(tuple_list, key=lambda x:x[1])
print(sorted_tuples)
# [('Python', 1), ('Java', 2), ('JavaScript', 3)]


ここでは、ソートの基準となるキーをタプルの2番目の項目(インデックスは0ベース)にマップして、2番目の項目(整数)でソートするようにしています。

ラムダ関数についてもっと知りたい方は、Pythonのラムダ関数ガイドをご覧ください! ラムダ関数についてもっと知りたい方は、Pythonのラムダ関数ガイドをご覧ください! ラムダ関数についてもっと知りたい方は、Pythonのラムダ関数ガイドをご覧ください。

注意:key はタプルの最初の値には対応しません。

これは、しばしば「キー-値」ペアのように「キー」と呼ばれるものです。

キーは、 sort() メソッドが要素をソートするためのキーです。

これは、タプル要素の数に関係なく当てはまります。

tuple_list = [('Java', 2, 'General purpose'), ('Python', 1, 'General purpose'), ('JavaScript', 3, 'Web-oriented')]
tuple_list.sort(key=lambda x:x[1])


print(tuple_list)
# [('Python', 1, 'General purpose'), ('Java', 2, 'General purpose'), ('JavaScript', 3, 'Web-oriented')]


または、 sorted() を使用した場合。

sorted_tuples = sorted(tuple_list, key=lambda x:x[1])
print(sorted_tuples)
# [('Python', 1, 'General purpose'), ('Java', 2, 'General purpose'), ('JavaScript', 3, 'Web-oriented')]


カスタムコンパレータによるリストの並べ替え

最終的には、 sort()sorted()key 引数にカスタムコンパレータを指定したいと思うかもしれません!コンパレータとは、単純に比較可能な戻り値の型を返す関数のことです。

コンパレータとは、単純に比較可能な戻り値の型を返す関数のことです。

例えば、 len() 関数を渡すことで、長さでソートすることができます。

string_list = ['I', 'see', 'skies', 'of', 'blue', 'red', 'roses', 'too']
string_list.sort(key=len)


print(string_list)
# ['I', 'of', 'see', 'red', 'too', 'blue', 'skies', 'roses']


あるいは、 sorted() を使って。

sorted_list = sorted(string_list, key=len)
print(sorted_list)
# ['I', 'of', 'see', 'red', 'too', 'blue', 'skies', 'roses']


同様に、任意のカスタム関数でソートすることができます。

def custom_comparator(element):
    return element[-1]


string_list = ['I', 'see', 'skies', 'of', 'blue', 'red', 'roses', 'too']
string_list.sort(key=custom_comparator)


print(string_list)
# ['I', 'red', 'see', 'blue', 'of', 'too', 'skies', 'roses']


または、sorted()でソートします。

sorted_list = sorted(string_list, key=custom_comparator)


print(sorted_list)
# ['I', 'red', 'see', 'blue', 'of', 'too', 'skies', 'roses']


ここでは、スライス記法を使って文字列の最後の文字を返し、その返された文字でソートしています。

各単語の最後の文字(大文字を除く)に注目すると、辞書順でソートされていることがわかります。

sort() と sorted() のベンチマーク比較

先に述べたように – sorted()sort() よりもわずかに効率が悪いです。

これは主に、元のコレクションを変更するのではなく、コピーを作成してそのコピーをソートするからです。

これはハードウェアやその仕様など、さまざまな要因に依存しますが、複数の入力サイズに基づいて、どちらがよりうまく動作するかを確認するための非常に簡単なテストを実行することができます。

10個、100個、1000個の要素を持つリストをそれぞれソートし、 timeit を使ってこれらの関数の実行時間を計ることにしましょう。

このテストが公正であることを確認するために、以下のことを確認したいと思います。

  • 要素のリストは timeit() を呼び出す前に生成されるので、生成ロジックはベンチマークの時間を考慮する必要がありません。
  • メソッドは全く同じ入力に対して実行されます。

sort()はリストをインプレースで変更するので、まずsorted()を実行し、次にsort()` が同じリストを処理するのにかかる時間をベンチマークします。

import timeit
import random


def generate_random_strings(num):
    result = []
    for i in range(num):
        s = ''.join(random.choice([chr(i) for i in range(ord('a'),ord('z'))]) for _ in range(5))
        result.append(s)
    return result


ten = generate_random_strings(10)
hundred = generate_random_strings(100)
thousand = generate_random_strings(1000)


# For eval() statements where input is translated to list names
mapping = {
    10:'ten',
    100:'hundred',
    1000:'thousand'
}


# Based on input, evaluate the expression to sort adequate list
def run_sort(num):
    eval(f'{mapping[num]}.sort()')


# Based on input, evaluate the expression to sort adequate list
def run_sorted(num):
    eval(f'sorted({mapping[num]})')


for index, num_samples in enumerate([10, 100, 1000]):
    result = timeit.timeit(f"run_sorted({num_samples})", number=100000, globals=globals())
    print(f'sorted() on {num_samples} took {result} seconds')


print('____________________________________________________')    

for index, num_samples in enumerate([10, 100, 1000]):
    result = timeit.timeit(f"run_sort({num_samples})", number=100000, globals=globals())
    print(f'sort() on {num_samples} took {result} seconds')


このコードでは、generate_random_strings()メソッドで生成した10、100、1000、1000000個の要素を持つ同じリストに対して、 run_sort()run_sorted() メソッドをそれぞれ100k回ずつ実行した場合の時間を比較しています。

sorted() on 10 took 0.5450385000003735 seconds
sorted() on 100 took 0.9972869999996874 seconds
sorted() on 1000 took 10.934083999999984 seconds
____________________________________________________
sort() on 10 took 0.4839348999998947 seconds
sort() on 100 took 0.5398832000000766 seconds
sort() on 1000 took 1.3094285000001946 seconds


10個の要素では、時間は実質的に同じで、~0.5sです。

しかし、100 要素になると、sort() は同じリストをソートするのに半分の時間を要するようになります。

最後に、1000 要素になると、 sort()sort() の 10 倍近くの計算時間を要します。

扱うデータセットが大きければ大きいほど、場違いなソートが必要ないのであれば sorted() の代わりに sort() を使うことでより多くの利益を得ることができるでしょう。

結論

この短いガイドでは、 sort()sorted() を使って Python でリストをソートする方法について見てきました。

そして、昇順の代わりに降順でソートしたり、別のソートキー`を設定したり、カスタムのソートメソッドを書いたりする方法を探りました。

最後に、2 つのメソッドのベンチマークを行い、入力のサイズが大きくなったときにどのように動作するかを調べました。

タイトルとURLをコピーしました