python 第一个元素上的元组随机排序保持第二顺序
python tuples shuffle on first element keep second ordering
是否可以打乱 python 个元组的列表,同时保持第二个元素的顺序。
输入:
[(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
示例输出:
[(3,1), (1,1), (2,2), (3,2), (2, 3), (1,2), (1,3), (3,3)]
本质上,我想随机化第一个元素以保持第二个元素的顺序。例如:
np.random.seed(41)
x = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
np.random.shuffle(x)
会输出
[(3, 2), (3, 1), (1, 2), (3, 3), (1, 3), (2, 3), (2, 2), (1, 1)]
第二个元素没有排序,也不是我们想要的。
你可以随机使用:
import random
l = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
print(sorted(l, key=lambda x: (x[1], random.random())))
如果第二个元素的顺序是任意的(即不总是递增),则排序是不够的。一种方法是跟踪字典中的第二个元素顺序并打乱(重复的)第一个元素,然后使用字典按顺序将第二个元素映射到结果。迭代器可以存储在字典中,以允许对元组的第二部分使用 next() 函数。
T = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
from random import sample
groups = {g:iter([t for t in T if t[0]==g]) for g in dict(T)}
result = [next(groups[v0]) for v0,_ in sample(T,len(T))]
print(result)
[(3, 1), (3, 2), (1, 1), (3, 3), (2, 2), (1, 2), (1, 3), (2, 3)]
它的工作原理是首先构建一个由第一个元素分组的元组的字典,并在关联列表中保留原始顺序。
{
1: [(1,1), (1,2), (1,3)],
2: [(2,2), (2,3)],
3: [(3,1), (3,2), (3,3)]
}
然后整个元组列表被打乱(使用 sample() 函数)。
[(3, 3), (3, 2), (1, 2), (3, 1), (2, 2), (1, 3), (1, 2), (2, 3)]
这不会保留第二个元素的顺序,因此我们将打乱后的列表中的每个元组替换为与其在字典中的第一个元素对应的元组,以匹配原始顺序。最后,打乱元组的目的只是为了打乱第一个元素,因为打乱后的顺序中的第二个元素被忽略了。
[(3, 3), (3, 2), (1, 2), (3, 1), (2, 2), (1, 3), (1, 2), (2, 3)] # shuffled
: – : – : – : – : – : – : – : –
: : : : : : : :
1: [ : : (1, 1), : : (1, 2), (1, 3)] : #
2: [ : : | : (2, 2), | | (2, 3)] # groups
3: [(3, 1), (3, 2), | (3, 3)] | | | | #
| | | | | | | |
v v v v v v v v
[(3, 1), (3, 2), (1, 1), (3, 3), (2, 2), (1, 2), (1, 3), (2, 3)] # result
iter() 函数只是一个处理列表理解内映射的技巧。您可以使用 groups[v0].pop(0) 以原始顺序使用分组列表来做同样的事情(尽管这可能效率较低)
特别感谢Kelly Bundy帮助调试优化方案
是否可以打乱 python 个元组的列表,同时保持第二个元素的顺序。
输入:
[(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
示例输出:
[(3,1), (1,1), (2,2), (3,2), (2, 3), (1,2), (1,3), (3,3)]
本质上,我想随机化第一个元素以保持第二个元素的顺序。例如:
np.random.seed(41)
x = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
np.random.shuffle(x)
会输出
[(3, 2), (3, 1), (1, 2), (3, 3), (1, 3), (2, 3), (2, 2), (1, 1)]
第二个元素没有排序,也不是我们想要的。
你可以随机使用:
import random
l = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
print(sorted(l, key=lambda x: (x[1], random.random())))
如果第二个元素的顺序是任意的(即不总是递增),则排序是不够的。一种方法是跟踪字典中的第二个元素顺序并打乱(重复的)第一个元素,然后使用字典按顺序将第二个元素映射到结果。迭代器可以存储在字典中,以允许对元组的第二部分使用 next() 函数。
T = [(1,1), (1,2), (1,3), (2,2), (2,3), (3,1), (3,2), (3,3)]
from random import sample
groups = {g:iter([t for t in T if t[0]==g]) for g in dict(T)}
result = [next(groups[v0]) for v0,_ in sample(T,len(T))]
print(result)
[(3, 1), (3, 2), (1, 1), (3, 3), (2, 2), (1, 2), (1, 3), (2, 3)]
它的工作原理是首先构建一个由第一个元素分组的元组的字典,并在关联列表中保留原始顺序。
{
1: [(1,1), (1,2), (1,3)],
2: [(2,2), (2,3)],
3: [(3,1), (3,2), (3,3)]
}
然后整个元组列表被打乱(使用 sample() 函数)。
[(3, 3), (3, 2), (1, 2), (3, 1), (2, 2), (1, 3), (1, 2), (2, 3)]
这不会保留第二个元素的顺序,因此我们将打乱后的列表中的每个元组替换为与其在字典中的第一个元素对应的元组,以匹配原始顺序。最后,打乱元组的目的只是为了打乱第一个元素,因为打乱后的顺序中的第二个元素被忽略了。
[(3, 3), (3, 2), (1, 2), (3, 1), (2, 2), (1, 3), (1, 2), (2, 3)] # shuffled
: – : – : – : – : – : – : – : –
: : : : : : : :
1: [ : : (1, 1), : : (1, 2), (1, 3)] : #
2: [ : : | : (2, 2), | | (2, 3)] # groups
3: [(3, 1), (3, 2), | (3, 3)] | | | | #
| | | | | | | |
v v v v v v v v
[(3, 1), (3, 2), (1, 1), (3, 3), (2, 2), (1, 2), (1, 3), (2, 3)] # result
iter() 函数只是一个处理列表理解内映射的技巧。您可以使用 groups[v0].pop(0) 以原始顺序使用分组列表来做同样的事情(尽管这可能效率较低)
特别感谢Kelly Bundy帮助调试优化方案