36

最近のバージョンのPythonで、以前のcmp関数からキー関数を渡すようになったため、特定のオブジェクトに対して複雑な並べ替えを実行するのが難しくなっています。sort()

たとえば、文字列のタイブレーカーフィールドのセットを使用して、オブジェクトのセットを新しいものから古いものへと並べ替えたいとします。したがって、日付を逆の順序にし、文字列を自然な順序にします。比較関数を使用すると、文字列フィールドと比較した日付フィールドの比較を逆にすることができます。しかし、キー関数を使用して、日付または文字列のいずれかを反転/反転する方法を見つける必要があります。

数字を扱うのは(醜いですが)簡単です-何かからそれらを引くだけです-しかし、日付(別の日付からそれらを引いてタイムデルタを比較する)と文字列(...私にはわかりません)の同様のハックを見つける必要がありますか?ロケールに依存しない方法で順序を逆にする方法)。

の存在は知っていますが、 「主に、比較関数がサポートされなくなったPython3に変換されるプログラムの移行ツールとして使用さfunctools.cmp_to_key()れる」と説明されています。これは、キーメソッドを使用してやりたいことができるはずだということを意味しますが、どうすればよいでしょうか。

4

7 に答える 7

26

これを行う最も一般的な方法は、各キーで順番に個別に並べ替えることです。Pythonの並べ替えは常に安定しているため、これを行うのは安全です。

sort(data, key=tiebreakerkey)
sort(data, key=datekey, reverse=True)

(主要な機能に関連する定義を想定して)日付の降順とタイブレーカーの昇順でソートされたデータを提供します。

この方法で行うと、2つの完全な並べ替えが行われるため、単一の複合キー関数を作成するよりも時間がかかることに注意してください。したがって、より良い複合キーを作成できれば、それを別々の並べ替えに分割すると、柔軟性が大幅に向上します。 :各列にキー関数を指定すると、それらの任意の組み合わせを作成し、個々の列にリバースを指定できます。

完全に一般的なオプションの場合:

keys = [ (datekey, True), (tiebreakerkey, False) ]
for key, rev in reversed(keys):
    sort(data, key=key, reverse=rev)

完全を期すために、可能な限り避けるべきだと私は本当に思っています。

from functools import cmp_to_key
sort(data, key=cmp_to_key(your_old_comparison_function))

これを避けるべきだと思う理由は、キー関数の呼び出し(またはソートを2回実行するときの呼び出し)n log nと比較して、比較関数の呼び出しに戻ることです。n2n

于 2012-06-26T12:26:52.967 に答える
18

これを行うには遅いがエレガントな方法は、順序を逆にした値ラッパーを作成することです。

from functools import total_ordering
@total_ordering
class ReversedOrder:
    def __init__(self, value):
        self.value = value
    def __eq__(self, other):
        return other.value == self.value
    def __lt__(self, other):
        return other.value < self.value

がない場合はfunctools.total_ordering、6つの比較すべてを実装する必要があります。例:

import operator
class ReversedOrder:
    def __init__(self, value):
        self.value = value
for x in ['__lt__', '__le__', '__eq__', '__ne__', '__ge__', '__gt__']:
    op = getattr(operator, x)
    setattr(ReversedOrder, x, lambda self, other, op=op: op(other.value, self.value))
于 2012-06-26T12:42:36.143 に答える
12

ドキュメントは不完全だと思います。私は「主に」という言葉を、cmp_to_keyを使用する理由がまだあることを意味すると解釈します。これはその1つです。 cmpそれが「魅力的な迷惑」だったので削除されました:keyより良い選択であったとしても、人々はそれに引き寄せられるでしょう。

cmpしかし、あなたのケースは関数として明らかに優れているので、それcmp_to_keyを実装するために使用してください。

于 2012-06-26T12:41:09.140 に答える
6

各キーで1回、逆に1回、合計2回並べ替えます。

(Pythonsort安定しています。つまり、必要がない限り、元のリストの順序は変更されません。)

等しい要素がどのようにソートされるかを気にする場合は、ソートを実行する順序は重要です。

于 2012-06-26T12:23:10.000 に答える
2

1つの方法は、pandaslibraryとargsを使用ascendingして、昇順で並べ替える列と降順で並べ替える列を設定することです。ascending=[True,False,False]

これは、2つのレベル(例datetimestr)だけでなく、必要な任意の数のレベルに対して実行できます。

たとえば、

d = [[1, 2, datetime(2017,1,2)], 
     [2, 2, datetime(2017,1,4)],
     [2, 3, datetime(2017,1,3)],
     [2, 3, datetime(2017,1,4)], 
     [2, 3, datetime(2017,1,5)], 
     [2, 4, datetime(2017,1,1)], 
     [3, 1, datetime(2017,1,2)]]

あなたはあなたのを設定することができますdf

df = pd.DataFrame(d)

と使用sort_values

sorted_df = df.sort_values(by=[0,1,2], ascending=[True,False,False])
sorted_list = sorted_df.agg(list, 1).tolist()


[[1, 2, Timestamp('2017-01-02 00:00:00')],
 [2, 4, Timestamp('2017-01-01 00:00:00')],
 [2, 3, Timestamp('2017-01-05 00:00:00')],
 [2, 3, Timestamp('2017-01-04 00:00:00')],
 [2, 3, Timestamp('2017-01-03 00:00:00')],
 [2, 2, Timestamp('2017-01-04 00:00:00')],
 [3, 1, Timestamp('2017-01-02 00:00:00')]]

最初の列は昇順でソートされ、2番目と3番目は降順であることに注意してください。これは、もちろん設定によるものですascending=[True,False,False]

于 2018-07-09T20:05:31.287 に答える
0

文字列の場合、整数の場合と同様に、一般的に認められている最大値(2^16や2^32など)を使用し、chr()、unicode()、ord()を使用して計算を行うことができます。

私の仕事の1つで、utf8で文字列を処理し、その序数が0xffff未満であることを知っているので、次のように書きました。

def string_inverse(s):
    inversed_string = ''
    max_char_val = 0xffff
    for c in s:
        inversed_string += unicode(max_char_val-ord(c))
    return inversed_string        

result.sort(key=lambda x:(x[1], string_inverse(x[0])), reverse=True)

xの型は:(文字列、整数)なので、SQLを悪用することができます。

select * from result order by x[1] desc, x[0] asc;
于 2016-04-14T10:02:48.380 に答える
0

これを試して:

>>> import functools
>>> reverse_key = functools.cmp_to_key(lambda a, b: (a < b) - (a > b))
>>> reverse_key(3) < reverse_key(4)
False
>>> reverse_key(3) > reverse_key(4)
True
>>> reverse_key('a') < reverse_key('b')
False
于 2021-01-06T03:04:57.773 に答える