我想用新实体压缩实体列表以生成坐标列表(2元组),但我想确保(i,j)i< j总是如此。
但是,我对目前的解决方案并不十分满意:
from itertools import repeat
mems = range(1, 10, 2)
mem = 8
def ij(i, j):
if i < j:
return (i, j)
else:
return (j, i)
def zipij(m=mem, ms=mems, f=ij):
return map(lambda i: f(i, m), ms)
def zipij2(m=mem, ms=mems):
return map(lambda i: tuple(sorted([i, m])), ms)
def zipij3(m=mem, ms=mems):
return [tuple(sorted([i, m])) for i in ms]
def zipij4(m=mem, ms=mems):
mems = zip(ms, repeat(m))
half1 = [(i, j) for i, j in mems if i < j]
half2 = [(j, i) for i, j in mems[len(half1):]]
return half1 + half2
def zipij5(m=mem, ms=mems):
mems = zip(ms, repeat(m))
return [(i, j) for i, j in mems if i < j] + [(j, i) for i, j in mems if i > j]
以上输出:
>>> print zipij() # or zipij{2-5}
[(1, 8), (3, 8), (5, 8), (7, 8), (8, 9)]
而不是正常:
>>> print zip(mems, repeat(mem))
[(1, 8), (3, 8), (5, 8), (7, 8), (9, 8)]
计时: 剪断(不再相关,请在下面的答案中看到更快的结果)
对于len(mems) == 5
,任何解决方案都没有真正的问题,但是对于zipij5(),例如,当i > j
已经评估为True
时,第二个列表理解会不必要地返回前四个值。对于第一次理解的人来说,len(mems)
。
就我的目的而言,我很肯定(i, j)
永远不会超过~10000,如果这有助于形成任何最佳解决方案的答案。为了解释我的用例(我发现它很有趣),我将存储一个稀疏的,上三角形的各种相似性矩阵,因此我需要在{{1}处不重复坐标(j, i)
}。我说 of of sort 因为我将利用2.7中的新Counter()
对象来执行准矩阵矩阵和矩阵向量加法。然后,我只需向counter_obj.update()
提供一个2元组列表,然后将这些坐标增加多少次。对于我的用例,SciPy稀疏矩阵运行速度慢了大约50倍,让我感到沮丧...所以我很快放弃了它们。
所以无论如何,我对我的结果感到惊讶......我提出的第一个方法是zipij4
和zipij5
,但它们仍然是最快的,尽管构建正常{{1然后在更改值后生成一个新zip。相对而言,我对Python仍然很陌生(Alex Martelli,你能听见我吗?),所以这是我天真的结论:
zip()
非常昂贵(为什么?)tuple(sorted([i, j]))
似乎总是比列表组件更差(我想我已经读过这个并且它有意义了)map(lambda ...)
并没有太慢,尽管两次检查列表以检查i-j不等式。 (这是为什么?)最后,我想知道哪个被认为是最有效的......或者是否还有其他快速且内存便宜的方式,我还没有想到。谢谢。
zipij5()
## Most BRIEF, Quickest with UNSORTED input list:
## truppo's
def zipij9(m=mem, ms=mems):
return [(i, m) if i < m else (m, i) for i in ms]
## Quickest with pre-SORTED input list:
## Michal's
def zipij10(m=mem, ms=mems):
i = binsearch(m, ms) ## See Michal's answer for binsearch()
return zip(ms[:i], repeat(m)) + zip(repeat(m), ms[i:])
计时正在使用# Michal's
Presorted - 410µs per loop
Unsorted - 2.09ms per loop ## Due solely to the expensive sorted()
# truppo's
Presorted - 880µs per loop
Unsorted - 896µs per loop ## No sorted() needed
,其长度仅为约5000。 mems = range(1, 10000, 2)
可能会在较高的值时变得更糟,并且列表会更加混乱。 sorted()
用于“未排序”时间。
答案 0 :(得分:2)
(在我的机器上使用Python 2.6.4发布时最快。)
更新3:由于我们全力以赴,让我们进行二分搜索 - 不需要将m
注入mems
:
def binsearch(x, lst):
low, high = -1, len(lst)
while low < high:
i = (high - low) // 2
if i > 0:
i += low
if lst[i] < x:
low = i
else:
high = i
else:
i = high
high = low
return i
def zipij(m=mem, ms=mems):
i = binsearch(m, ms)
return zip(ms[:i], repeat(m)) + zip(repeat(m), ms[i:])
在我的机器上运行828μs = 0.828 ms与OP的当前解决方案的1.14 ms相比。假设输入列表已排序(当然,测试用例是常用的)。
此二进制搜索实现返回给定列表中第一个元素的索引,该索引不小于要搜索的对象。因此,不需要将m
注入mems
并对整个事物进行排序(例如在OP的当前解决方案中使用.index(m)
),或者逐步遍历列表的开头(就像我一样)以前做过)找到应该划分的偏移量。
这个怎么样? (下面In [25]
旁边提出的解决方案,到zipij5的3.13毫秒2.42毫秒。)
In [24]: timeit zipij5(m = mem, ms = mems)
100 loops, best of 3: 3.13 ms per loop
In [25]: timeit [(i, j) if i < j else (j, i) for (i, j) in zip(mems, repeat(mem))]
100 loops, best of 3: 2.42 ms per loop
In [27]: [(i, j) if i < j else (j, i) for (i, j) in zip(mems, repeat(mem))] == zipij5(m=mem, ms=mems)
Out[27]: True
更新:这似乎与OP的自我答案完全一样快。但是,似乎更直接。
更新2:OP提议的简化解决方案的实施:
def zipij(m=mem, ms=mems):
split_at = 0
for item in ms:
if item < m:
split_at += 1
else:
break
return [(item, m) for item in mems[:split_at]] + [(m, item) for item in mems[split_at:]]
In [54]: timeit zipij()
1000 loops, best of 3: 1.15 ms per loop
此外,truppo的解决方案在我的机器上运行1.36毫秒。我猜以上是目前为止最快的。注意在将mems
传递给此函数之前需要对其进行排序!如果您使用range
生成它,它当然已经排序了。
答案 1 :(得分:1)
为什么不直接内联你的ij() - 函数?
def zipij(m=mem, ms=mems):
return [(i, m) if i < m else (m, i) for i in ms]
(这在我的计算机上以0.64毫秒而不是2.12毫秒运行)
一些基准:
zipit.py:
from itertools import repeat
mems = range(1, 50000, 2)
mem = 8
def zipij7(m=mem, ms=mems):
cpy = sorted(ms + [m])
loc = cpy.index(m)
return zip(ms[:(loc)], repeat(m)) + zip(repeat(m), ms[(loc):])
def zipinline(m=mem, ms=mems):
return [(i, m) if i < m else (m, i) for i in ms]
排序:
>python -m timeit -s "import zipit" "zipit.zipinline()"
100 loops, best of 3: 4.44 msec per loop
>python -m timeit -s "import zipit" "zipit.zipij7()"
100 loops, best of 3: 4.8 msec per loop
未排序:
>python -m timeit -s "import zipit, random; random.shuffle(zipit.mems)" "zipit.zipinline()"
100 loops, best of 3: 4.65 msec per loop
p>python -m timeit -s "import zipit, random; random.shuffle(zipit.mems)" "zipit.zipij7()"
100 loops, best of 3: 17.1 msec per loop
答案 2 :(得分:0)
def zipij7(m=mem, ms=mems):
cpy = sorted(ms + [m])
loc = cpy.index(m)
return zip(ms[:(loc)], repeat(m)) + zip(repeat(m), ms[(loc):])
对我来说,长凳比truppo的速度快一点,比Michal的速度慢了30%。 (现在调查)
我可能已找到答案(暂时)。我似乎忘了为`zipij()``:
创建一个列表comp版本def zipij1(m=mem, ms=mems, f=ij):
return [f(i, m) for i in ms]
它仍然依赖于我愚蠢的ij()
辅助功能,所以它当然不会因为简洁而获奖,但时间有所改善:
# 10000
1.27s
# 50000
6.74s
所以它现在是我当前的“赢家”,也不需要生成多个列表,或者使用很多函数调用,而不是ij()
帮助器,所以我相信它也是效率最高。
但是,我认为这仍然可以改进......我认为不需要进行N ij()
个函数调用(其中N是结果列表的长度):
mem
适合mems
的索引zip(part1, repeat(mem))
zip(repeat(mem), part2)
它基本上是对zipij4()
的改进,这避免了N个额外的函数调用,但我不确定速度/内存的好处而不是简洁成本。如果我弄明白的话,我可能会将这个版本添加到这个答案中。