以pythonic方式组合具有特定合并顺序的列表?

dan*_*iev 24 python

我想建构列表x由两个列表yz.我希望所有元素y都放在ypos元素指向的位置.例如:

y = [11, 13, 15]
z = [12, 14]
ypos = [1, 3, 5]
Run Code Online (Sandbox Code Playgroud)

所以,x一定是[11, 12, 13, 14, 15]

另一个例子:

y = [77]
z = [35, 58, 74]
ypos = [3]
Run Code Online (Sandbox Code Playgroud)

所以,x一定是[35, 58, 77, 74]

我写的函数可以完成我想要的但看起来很难看:

def func(y, z, ypos):
    x = [0] * (len(y) + len(z))
    zpos = list(range(len(y) + len(z)))
    for i, j in zip(y, ypos):
        x[j-1] = i
        zpos.remove(j-1)
    for i, j in zip(z, zpos):
        x[j] = i
    return x
Run Code Online (Sandbox Code Playgroud)

如何用pythonic方式编写它?

tob*_*s_k 35

如果列表很长,重复调用insert 可能效率不高.或者,您可以iterators从列表中创建两个并通过next从任一迭代器获取元素来构造列表,具体取决于当前索引是否在ypos(或set其中):

>>> ity = iter(y)
>>> itz = iter(z)
>>> syp = set(ypos)
>>> [next(ity if i+1 in syp else itz) for i in range(len(y)+len(z))]
[11, 12, 13, 14, 15]
Run Code Online (Sandbox Code Playgroud)

注意:这将y按照它们出现的顺序插入元素y,即第一个元素y插入最低索引ypos,而不是第一个索引ypos.如果对元件y应的索引处插入相应的元素ypos,则要么ypos必须按升序排列(即,第一索引ypos也是最低),或者迭代y已经由相同的顺序进行排序指数ypos(之后,ypos本身不必排序,因为我们正在将其转化为set无论如何).

>>> ypos = [5,3,1]   # y and z being same as above
>>> ity = iter(e for i, e in sorted(zip(ypos, y)))
>>> [next(ity if i+1 in syp else itz) for i in range(len(y)+len(z))]
[15, 12, 13, 14, 11]
Run Code Online (Sandbox Code Playgroud)

  • 优秀的方法.它模仿你从两层甲板上分发卡片的方式. (2认同)
  • 一开始并不清楚,但是你基本上再次排序`ypos`,因为你用增加的'i`进行迭代.你可以试试`f([15,13,​​11],[12,14],[5,3,1])`.它返回`[15,12,13,14,11]`,好像`ypos`是`[1,3,5]`. (2认同)

Joe*_*don 12

你应该使用list.insert,这就是它的用途!

def func(y, z, ypos):
    x = z[:]
    for pos, val in zip(ypos, y):
        x.insert(pos-1, val)
    return x
Run Code Online (Sandbox Code Playgroud)

和测试:

>>> func([11, 13, 15], [12, 14], [1,3,5])
[11, 12, 13, 14, 15]
Run Code Online (Sandbox Code Playgroud)


Eri*_*nil 8

对于大型列表,使用它可能是个好主意numpy.

算法

  • 创建一个大的新数组 y + z
  • 计算z值的坐标
  • y值赋给xatypos
  • z值赋给xatzpos

复杂性应该是O(n),与n正在值的总数.

import numpy as np

def distribute_values(y_list, z_list, y_pos):
    y = np.array(y_list)
    z = np.array(z_list)
    n = y.size + z.size
    x = np.empty(n, np.int)
    y_indices = np.array(y_pos) - 1
    z_indices = np.setdiff1d(np.arange(n), y_indices, assume_unique=True)
    x[y_indices] = y
    x[z_indices] = z
    return x

print(distribute_values([11, 13, 15], [12, 14], [1, 3, 5]))
# [11 12 13 14 15]
print(distribute_values([77], [35, 58, 74], [3]))
# [35 58 77 74]
Run Code Online (Sandbox Code Playgroud)

作为奖励,当ypos没有排序时它也可以正常工作:

print(distribute_values([15, 13, 11], [12, 14], [5, 3, 1]))
# [11 12 13 14 15]
print(distribute_values([15, 11, 13], [12, 14], [5, 1, 3]))
# [11 12 13 14 15]
Run Code Online (Sandbox Code Playgroud)

性能

随着n集到100万,这种方法比快一点@ tobias_k的回答和速度超过500倍@ Joe_Iddon的答案.

列表是这样创建的:

from random import random, randint
N = 1000000
ypos = [i+1 for i in range(N) if random()<0.4]
y = [randint(0, 10000) for _ in ypos]
z = [randint(0, 1000) for _ in range(N - len(y))
Run Code Online (Sandbox Code Playgroud)

以下是%timeit与IPython 的结果:

%timeit eric(y, z, ypos)
131 ms ± 1.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit tobias(y, z, ypos)
224 ms ± 977 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit joe(y,z, ypos)
54 s ± 1.48 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
Run Code Online (Sandbox Code Playgroud)

  • @tobias_k:是的,这取决于系统和底层库.如此大的清单,因子1.5或2并不多.乔对小名单的回答让我感到惊讶.使用'n = 100`,它实际上比你的快一点. (2认同)