【发布时间】:2014-06-08 23:53:50
【问题描述】:
我得到了一些数据,需要创建一个排序映射。实际排序由 C 代码完成,它从我的代码中获取整数列表 flt_neworder。这是我目前的解决方案:
# Demo data
data = [
"Option A", # 0
"Option B", # 1
"Blabla", # 2
"Some text" # 3
]
class Item:
def __init__(self, label):
self.label = label
col = [Item(d) for d in data]
# Create sorting mapping
flt_neworder = [
x[1] for x in sorted(
zip(
[x[0] for x in sorted(enumerate(col), key=lambda x: x[1].label)],
range(len(col))
)
)
]
# Output: [1,2,0,3]
print(flt_neworder)
所需的输出:
[1,2,0,3],不是[2,0,1,3]!flt_neworder中的位置 =col中项目的原始索引整数 值 = 新位置
什么是更高效或至少可读性更好的解决方案?
我成功测试了这个单行:
tuple({k: i for i, (k, v) in enumerate(sorted(enumerate(data), key=operator.itemgetter(1)))}.values())
但它仍然很难阅读,我相信我正在利用 dicts 在 CPython 实现中排序的事实......
编辑
我想出的另一个解决方案:
flt_neworder = [None] * len(col)
for j, (_, i) in enumerate(sorted(zip((item.label for item in col), range(len(col))))): flt_neworder[i] = j
还有一个,但速度很慢:
flt_neworder = list(map(get(0), sorted(enumerate(sorted(enumerate(item.label for item in col), key=get(1))), key=get(1))))
感谢 Ryan P 提供替代解决方案和测试时间的脚本!
我在一个大型数据集(1k 个唯一字符串,full script)上测试了解决方案,与 Ryan 的小型数据集相比,它们在时间上存在惊人的差异:
orig: 2.116799074272876
origmod: 2.118176033553482
orignew: 1.1691872433702883
orig3: 1.4400411206224817
orig4: 2.0643228139915664
rewrite: 26.06907118537356
rewriteop: 25.91357442379376
rewriteuniq: 10.783081019086694
获胜者是orignew(),而rewriteuniq() 对小型数据集来说速度很快,但对大型数据集却不是很好。
【问题讨论】:
-
你能解释一下“排序映射”是什么意思吗?
-
我需要提供一个
old index -> new index映射,其中旧索引是结果列表中的位置,包含的值是新索引(0 = 项目将移至顶部)。[1,2,0]表示数据中的第一项应该在中间(位置 1)结束,第二项在末尾(位置 2),第三项在顶部(位置 0)。