我想从两个列表x
和y
构建列表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方式编写它?
答案 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元素:
x
初始化为所需大小。y
和ypos
元素,将每个对应的y
元素填入x
。x
并将每个剩余的空值替换为z
值,其中每个替换将从z
中选择增加
顺序。
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)
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]
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]