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

时间:2017-11-22 20:24:21

标签: python

我想从两个列表xy构建列表z。我希望y中的所有元素都放在ypos元素指向的位置。例如:

y = [11, 13, 15]
z = [12, 14]
ypos = [1, 3, 5]

因此,x必须为[11, 12, 13, 14, 15]

另一个例子:

y = [77]
z = [35, 58, 74]
ypos = [3]

因此,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

如何用pythonic方式编写它?

6 个答案:

答案 0 :(得分:35)

如果列表很长,请反复拨打n=4 might not be very efficient。或者,您可以从列表中创建两个1/4 2/4 3/4 1 并通过从任一迭代器获取insert元素来构造列表,具体取决于当前索引是否在iterators(或{{1} 1}}}):

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] 中的元素,即y的第一个元素插入最低 y中的索引,不一定是y中的第一个索引。如果ypos的元素应插入ypos对应元素的索引处,那么y必须按升序排列(即第一个ypos的索引也是最低的),或者ypos的迭代器必须按照与ypos中的索引相同的顺序排序(之后,y本身不会必须进行排序,因为我们无论如何都将它变成ypos

ypos

答案 1 :(得分:12)

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

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

和测试:

>>> func([11, 13, 15], [12, 14], [1,3,5])
[11, 12, 13, 14, 15]

答案 2 :(得分:8)

使用大型列表时,最好使用numpy

算法

  • 创建一个与y + z
  • 一样大的新数组
  • 计算z值的坐标
  • y
  • x值分配给ypos
  • z
  • x值分配给zpos

复杂性应为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]

作为奖励,当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]

性能

n设置为100万,此方法比@tobias_k's answer快一点,比@Joe_Iddon's answer快500倍。

列表以这种方式创建:

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))

以下是%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)

答案 3 :(得分:2)

假设ypos索引已经排序,这是另一个使用迭代器的解决方案,虽然这个也支持未知或无限长度的ypos

import itertools

def func(y, ypos, z):
    y = iter(y)
    ypos = iter(ypos)
    z = iter(z)
    next_ypos = next(ypos, -1)
    for i in itertools.count(start=1):
        if i == next_ypos:
            yield next(y)
            next_ypos = next(ypos, -1)
        else:
            yield next(z)

答案 4 :(得分:2)

如果您希望将ypos中的元素放在x索引处,ypos中每个元素的索引应该与相同的y索引相对应#39; s元素:

  1. 使用所有空值将x初始化为所需大小。
  2. 遍历压缩的yypos元素,将每个对应的y元素填入x
  3. 遍历x并将每个剩余的空值替换为z值,其中每个替换将从z中选择增加 顺序。
  4. y = [11, 13, 15]
    z = [12, 14]
    ypos = [1, 5, 3]
    
    x = [None] * (len(y) + len(z))
    for x_ypos, y_elem in zip(ypos, y):
        x[x_ypos - 1] = y_elem
    
    z_iter = iter(z)
    x = [next(z_iter) if i is None else i for i in x]
    # x -> [11, 12, 15, 14, 13]
    

答案 5 :(得分:1)

Pythonic方式

y = [11, 13, 15]
z = [12, 14]
ypos = [1, 3, 5]

x = z[:]

for c, n in enumerate(ypos):
    x.insert(n - 1, y[c])

print(x)

输出

  

[11,12,13,14,15]

在函数

def func(y, ypos, z):
    x = z[:]
    for c,n in enumerate(ypos):
        x.insert(n-1,y[c])
    return x

print(func([11,13,15],[1,2,3],[12,14]))

outoput

  

[11,12,13,14,15]

使用zip

y, z, ypos = [11, 13, 15], [12, 14], [1, 3, 5]

for i, c in zip(ypos, y):
    z.insert(i - 1, c)

print(z)

[out:]

> [11, 12, 13, 14, 15]