自然地对列表进行排序,将字母数字值移动到末尾
Naturally sort a list moving alphanumeric values to the end
我有一个要自然排序的字符串列表:
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 - 甚至是字母数字字符串。
我知道我可以使用 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
并将其简化为单个排序调用。
natsort
有一个函数 natsort_key
可以根据完成的排序将项目转换为元组。
因此您可以将其用作:
sorted(c, key=lambda x: <b>(</b>not x.isdigit()<b>, *ns.natsort_key(x))</b>)
这会产生:
>>> 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)))
非常感谢 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
%timeit sorted(d, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))
1 loop, best of 3: 2.78 s per loop
%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
您实际上可以使用 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']
键returns一个以原始输入作为第二个元素的元组。数字字符串放在前面,所有其他字符串放在后面,然后子集单独排序。
作为旁注, 使用 natsort_key
,从 natsort
版本 3.0.4 开始已弃用(如果您在解释器中打开 DeprecationWarning
你会看到的,并且该功能现在没有记录)。它实际上效率很低……最好使用 natort_keygen
,其中 returns 是自然排序键。 natsort_key
在幕后调用它,因此对于每个输入,您都在创建一个新函数,然后调用它一次。
下面我重复显示的测试 ,我使用 natsorted
方法添加我的解决方案以及使用 natsort_keygen
而不是 [=14] 的其他解决方案的时间=].
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
我有一个要自然排序的字符串列表:
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 - 甚至是字母数字字符串。
我知道我可以使用 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
并将其简化为单个排序调用。
natsort
有一个函数 natsort_key
可以根据完成的排序将项目转换为元组。
因此您可以将其用作:
sorted(c, key=lambda x: <b>(</b>not x.isdigit()<b>, *ns.natsort_key(x))</b>)
这会产生:
>>> 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)))
非常感谢 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
%timeit sorted(d, key=lambda x: (not x.isdigit(), ns.natsort_key(x)))
1 loop, best of 3: 2.78 s per loop
%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
您实际上可以使用 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']
键returns一个以原始输入作为第二个元素的元组。数字字符串放在前面,所有其他字符串放在后面,然后子集单独排序。
作为旁注,natsort_key
,从 natsort
版本 3.0.4 开始已弃用(如果您在解释器中打开 DeprecationWarning
你会看到的,并且该功能现在没有记录)。它实际上效率很低……最好使用 natort_keygen
,其中 returns 是自然排序键。 natsort_key
在幕后调用它,因此对于每个输入,您都在创建一个新函数,然后调用它一次。
下面我重复显示的测试 natsorted
方法添加我的解决方案以及使用 natsort_keygen
而不是 [=14] 的其他解决方案的时间=].
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