59

Say I have two simple lists,

a = ['Spears', "Adele", "NDubz", "Nicole", "Cristina"]
b = [1,2,3,4,5]
len(a) == len(b)

What I would like to do is randomize a and b but maintain the order. So, something like:

a = ["Adele", 'Spears', "Nicole", "Cristina", "NDubz"]
b = [2,1,4,5,3]

I am aware that I can shuffle one list using:

import random
random.shuffle(a)

But this just randomizes a, whereas, I would like to randomize a, and maintain the "randomized order" in list b.

Would appreciate any guidance on how this can be achieved.

JohnJ
  • 6,736
  • 13
  • 49
  • 82

7 Answers7

91

I'd combine the two lists together, shuffle that resulting list, then split them. This makes use of zip()

a = ["Spears", "Adele", "NDubz", "Nicole", "Cristina"]
b = [1, 2, 3, 4, 5]

combined = list(zip(a, b))
random.shuffle(combined)

a[:], b[:] = zip(*combined)
Shubham Chaudhary
  • 47,722
  • 9
  • 78
  • 80
Tim
  • 11,710
  • 4
  • 42
  • 43
  • 1
    Use `a[:], b[:] = zip(*combined)`. The OP seems to have intended in-place modification of the two lists. –  Nov 12 '12 at 12:08
  • Hi Tim, thanks so much for your reply. It definately works, however, I have one silly questions :( [1] when you have done "random.shuffle(combined)" you have not assigned this to any variable but then you use zip(*combined) - how does this work and what does the * operator do here? Could you please explain this? Sorry, Iam a python newbie here :( – JohnJ Nov 12 '12 at 12:15
  • 1
    `random.shuffle()` shuffles the list in place, so there is no need to assign it to anything. `zip(*combined)` unzips the list. I've linked the python docs in the answer. – Tim Nov 12 '12 at 12:17
  • Thanks so much Tim - I did not realize the shuffle is done "in place". Accepted your answer. Thanks again for the explanation. – JohnJ Nov 12 '12 at 12:20
  • Will this work properly with multiple lists, not just two, but e.g. 5? – Ivan Bilan Aug 17 '16 at 21:09
  • This does not work with multidimensional numpy arrays (x_train, y_train for example). Lists were [img_a, img_b] and [1, 2] and I got [img_b, img_b] and [1,2] after shuffle. The sklearn solution below from Nimrod Morag works fine. – lorenzo Oct 28 '19 at 11:24
20

Use zip which has the nice feature to work in 'both' ways.

import random

a = ['Spears', "Adele", "NDubz", "Nicole", "Cristina"]
b = [1,2,3,4,5]
z = zip(a, b)
# => [('Spears', 1), ('Adele', 2), ('NDubz', 3), ('Nicole', 4), ('Cristina', 5)]
random.shuffle(z)
a, b = zip(*z)
18

To avoid Reinventing The Wheel use sklearn

from sklearn.utils import shuffle

a, b = shuffle(a, b)
Nimrod Morag
  • 938
  • 9
  • 20
  • 1
    Nice and wise comment! I know by experience that subtle errors could arise from reinventing the wheel with this kind of apparently simple piece of code. Any quick and dirty piece of code will come back to haunt you. – Claude COULOMBE Feb 14 '19 at 06:54
10

Note that Tim's answer only works in Python 2, not Python 3. If using Python 3, you need to do:

combined = list(zip(a, b))
random.shuffle(combined)
a[:], b[:] = zip(*combined)

otherwise you get the error:

TypeError: object of type 'zip' has no len()
Adam_G
  • 7,337
  • 20
  • 86
  • 148
3

There's a simpler way that avoids zipping, copying and all of that heavy stuff. We can shuffle both of them separately, but using the same seed both times, which guarantees that the order of the shuffles will be the same.

import random as rd

A = list("abcde")
B = list(range(len(A)))
fixed_seed = rd.random()
rd.Random(fixed_seed).shuffle(A)
rd.Random(fixed_seed).shuffle(B)

A and B are then:

['e', 'a', 'c', 'b', 'd']
[ 4,   0,   2,   1,   3]

The more generic version, for an arbitrary number of lists:

def shuffle(*xss):
    seed = rd.random()
    for xs in xss:
        rd.Random(seed).shuffle(xs)
Pig
  • 485
  • 4
  • 14
2

Another way could be

a = ['Spears', "Adele", "NDubz", "Nicole", "Cristina"]
b = range(len(a)) # -> [0, 1, 2, 3, 4]
b_alternative = range(1, len(a) + 1) # -> [1, 2, 3, 4, 5]
random.shuffle(b)
a_shuffled = [a[i] for i in b] # or:
a_shuffled = [a[i - 1] for i in b_alternative]

It is the reverse approach, but could help you nevertheless.

glglgl
  • 89,107
  • 13
  • 149
  • 217
1

That's my way:

import random
def shuffleTogether(A, B):
    if len(A) != len(B):
        raise Exception("Lengths don't match")
    indexes = range(len(A))
    random.shuffle(indexes)
    A_shuffled = [A[i] for i in indexes]    
    B_shuffled = [B[i] for i in indexes]
    return A_shuffled, B_shuffled

A = ['a', 'b', 'c', 'd']
B = ['1', '2', '3', '4']
A_shuffled, B_shuffled = shuffleTogether(A, B)
print A_shuffled
print B_shuffled
Nathan B
  • 1,625
  • 1
  • 17
  • 15