自然地将列表移动字母数字值排序到最后

时间:2017-11-11 16:30:19

标签: python sorting natural-sort

我有一个我想要自然排序的字符串列表:

c = ['0', '1', '10', '11', '2', '2Y', '3', '3Y', '4', '4Y', '5', '5Y', '6', '7', '8', '9', '9Y']

除了自然排序,我想将所有非纯数字字符串的条目移到最后。我的预期输出是:

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '2Y', '3Y', '4Y', '5Y', '9Y']

请注意,一切都必须被纳入 - 甚至是字母数字字符串。

我知道我可以使用natsort包来获得我想要的东西,但仅凭这一点并不适合我。我需要使用两个排序调用来执行此操作 - 一个用于自然排序,另一个用于将非纯数字字符串移动到最后。

import natsort as ns
r = sorted(ns.natsorted(c), key=lambda x: not x.isdigit())

print(r)
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '2Y', '3Y', '4Y', '5Y', '9Y']

我想知道是否可以以狡猾的方式使用natsort并将其减少为单个排序调用。

3 个答案:

答案 0 :(得分:4)

natsort有一个函数 natsort_key ,可以根据完成的排序将项目转换为元组。

所以你可以用它作为:

sorted(c, key=lambda x: (not x.isdigit(), *ns.natsort_key(x)))

这会产生:

>>> sorted(c, key=lambda x: (not x.isdigit(), *ns.natsort_key(x)))
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '2Y', '3Y', '4Y', '5Y', '9Y']

您也可以在没有可迭代解包的情况下使用它,因为在这种情况下我们有两个2元组,并且如果第一个项目有抢占式,它将比较natsort_key调用的结果:

sorted(c, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))

答案 1 :(得分:2)

我非常感谢Willem Van Onsem发表他的回答。但是,我应该注意到原始功能的性能要快一个数量级。考虑到PM2 Ring的建议,这两种方法之间有一些基准:

<强>设置

c = \
['0',
 '1',
 '10',
 '11',
 '2',
 '2Y',
 '3',
 '3Y',
 '4',
 '4Y',
 '5',
 '5Y',
 '6',
 '7',
 '8',
 '9',
 '9Y']
d = c * (1000000 // len(c) + 1)  # approximately 1M elements

Willem's solution

%timeit sorted(d, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))
1 loop, best of 3: 2.78 s per loop

Original (w/ PM 2Ring's enhancement)

%timeit sorted(ns.natsorted(d), key=str.isdigit, reverse=True)
1 loop, best of 3: 796 ms per loop

对原始版本的高性能的解释是因为Tim Sort似乎针对几乎排序的列表进行了高度优化。

完整性检查

x = sorted(d, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))
y = sorted(ns.natsorted(d), key=str.isdigit, reverse=True)

all(i == j for i, j in zip(x, y))
True

答案 2 :(得分:2)

您可以使用natsorted和正确选择的key来实际执行此操作。

>>> ns.natsorted(d, key=lambda x: (not x.isdigit(), x))
['0',
 '1',
 '2',
 '3',
 '4',
 '5',
 '6',
 '7',
 '8',
 '9',
 '10',
 '11',
 '2Y',
 '3Y',
 '4Y',
 '5Y',
 '9Y']

该键返回一个元组,原始输入为第二个元素。数字字符串放在前面,所有其他字符串放在后面,然后子集被单独排序。

作为旁注,Willem Van Onsem's solution使用的natsort_key已从natsort 3.0.4版开始弃用(如果您在翻译中打开DeprecationWarning,则会{看到了,该功能现在没有记录)。它实际上效率很低......最好使用返回自然排序键的natort_keygennatsort_key在引擎盖下调用它,因此对于每个输入,您都要创建一个新函数,然后再调用一次。

下面我重复显示here所显示的测试,并使用natsorted方法添加我的解决方案,并使用natsort_keygen代替natsort_key添加其他解决方案的时间

In [13]: %timeit sorted(d, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))
1 loop, best of 3: 33.3 s per loop

In [14]: natsort_key = ns.natsort_keygen()

In [15]: %timeit sorted(d, key=lambda x: (not x.isdigit(), natsort_key(x)))
1 loop, best of 3: 11.2 s per loop

In [16]: %timeit sorted(ns.natsorted(d), key=str.isdigit, reverse=True)
1 loop, best of 3: 9.77 s per loop

In [17]: %timeit ns.natsorted(d, key=lambda x: (not x.isdigit(), x))
1 loop, best of 3: 23.8 s per loop