title: Better way to shuffle two numpy arrays in unison
body: <p>I have two numpy arrays of different shapes, but with the same length (leading dimension). I want to shuffle each of them, such that corresponding elements continue to correspond -- i.e. shuffle them in unison with respect to their leading indices.</p>
  
  <p>This code works, and illustrates my goals:</p>
  
  <pre><code>def shuffle_in_unison(a, b):
      assert len(a) == len(b)
      shuffled_a = numpy.empty(a.shape, dtype=a.dtype)
      shuffled_b = numpy.empty(b.shape, dtype=b.dtype)
      permutation = numpy.random.permutation(len(a))
      for old_index, new_index in enumerate(permutation):
          shuffled_a[new_index] = a[old_index]
          shuffled_b[new_index] = b[old_index]
      return shuffled_a, shuffled_b
  </code></pre>
  
  <p>For example:</p>
  
  <pre><code>&gt;&gt;&gt; a = numpy.asarray([[1, 1], [2, 2], [3, 3]])
  &gt;&gt;&gt; b = numpy.asarray([1, 2, 3])
  &gt;&gt;&gt; shuffle_in_unison(a, b)
  (array([[2, 2],
         [1, 1],
         [3, 3]]), array([2, 1, 3]))
  </code></pre>
  
  <p>However, this feels clunky, inefficient, and slow, and it requires making a copy of the arrays -- I'd rather shuffle them in-place, since they'll be quite large.</p>
  
  <p>Is there a better way to go about this? Faster execution and lower memory usage are my primary goals, but elegant code would be nice, too.</p>
  
  <p>One other thought I had was this:</p>
  
  <pre><code>def shuffle_in_unison_scary(a, b):
      rng_state = numpy.random.get_state()
      numpy.random.shuffle(a)
      numpy.random.set_state(rng_state)
      numpy.random.shuffle(b)
  </code></pre>
  
  <p>This works...but it's a little scary, as I see little guarantee it'll continue to work -- it doesn't look like the sort of thing that's guaranteed to survive across numpy version, for example.</p>