Pergunta

Eu tenho duas matrizes numpy de formas diferentes, mas com o mesmo comprimento (dimensão principal). Quero embaralhar cada um deles, de modo que os elementos correspondentes continuem a corresponder - ou seja, embaralhá -los em uníssono em relação aos seus principais índices.

Este código funciona e ilustra meus objetivos:

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

Por exemplo:

>>> a = numpy.asarray([[1, 1], [2, 2], [3, 3]])
>>> b = numpy.asarray([1, 2, 3])
>>> shuffle_in_unison(a, b)
(array([[2, 2],
       [1, 1],
       [3, 3]]), array([2, 1, 3]))

No entanto, isso parece desajeitado, ineficiente e lento, e exige fazer uma cópia das matrizes-eu prefiro embaralhar-as no local, já que serão bastante grandes.

Existe uma maneira melhor de fazer isso? A execução mais rápida e o uso mais baixo da memória são meus objetivos principais, mas o código elegante também seria bom.

Outro pensou que eu tinha era o seguinte:

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)

Isso funciona ... mas é um pouco assustador, pois vejo pouca garantia de que continuará funcionando - não parece o tipo de coisa que é garantida para sobreviver na versão Numpy, por exemplo.

Foi útil?

Solução

Sua solução "assustadora" não me parece assustadora. Chamando shuffle() Para duas sequências do mesmo comprimento, resulta no mesmo número de chamadas para o gerador de números aleatórios, e esses são os únicos elementos "aleatórios" no algoritmo de Shuffle. Ao redefinir o estado, você garante que as chamadas para o gerador de números aleatórios darão os mesmos resultados na segunda chamada para shuffle(), então todo o algoritmo gerará a mesma permutação.

Se você não gosta disso, uma solução diferente seria armazenar seus dados em uma matriz, em vez de dois, desde o início, e criar duas visualizações nessa matriz única simulando as duas matrizes que você tem agora. Você pode usar a matriz única para embaralhar e as vistas para todos os outros fins.

Exemplo: vamos supor as matrizes a e b parece com isso:

a = numpy.array([[[  0.,   1.,   2.],
                  [  3.,   4.,   5.]],

                 [[  6.,   7.,   8.],
                  [  9.,  10.,  11.]],

                 [[ 12.,  13.,  14.],
                  [ 15.,  16.,  17.]]])

b = numpy.array([[ 0.,  1.],
                 [ 2.,  3.],
                 [ 4.,  5.]])

Agora podemos construir uma única matriz contendo todos os dados:

c = numpy.c_[a.reshape(len(a), -1), b.reshape(len(b), -1)]
# array([[  0.,   1.,   2.,   3.,   4.,   5.,   0.,   1.],
#        [  6.,   7.,   8.,   9.,  10.,  11.,   2.,   3.],
#        [ 12.,  13.,  14.,  15.,  16.,  17.,   4.,   5.]])

Agora criamos visualizações simulando o original a e b:

a2 = c[:, :a.size//len(a)].reshape(a.shape)
b2 = c[:, a.size//len(a):].reshape(b.shape)

Os dados de a2 e b2 é compartilhado com c. Para embaralhar as duas matrizes simultaneamente, use numpy.random.shuffle(c).

No código de produção, é claro que você tentaria evitar criar o original a e b de jeito nenhum e imediatamente crie c, a2 e b2.

Esta solução pode ser adaptada ao caso que a e b tem dtypes diferentes.

Outras dicas

Seu pode usar o Numpy's indexação de matriz:

def unison_shuffled_copies(a, b):
    assert len(a) == len(b)
    p = numpy.random.permutation(len(a))
    return a[p], b[p]

Isso resultará na criação de matrizes separadas de abafado uníssono.

X = np.array([[1., 0.], [2., 1.], [0., 0.]])
y = np.array([0, 1, 2])
from sklearn.utils import shuffle
X, y = shuffle(X, y, random_state=0)

Para saber mais, veja http://scikit-learn.org/stable/modules/generated/sklearn.utils.shuffle.html

Solução muito simples:

randomize = np.arange(len(x))
np.random.shuffle(randomize)
x = x[randomize]
y = y[randomize]

as duas matrizes x, y agora estão arrastadas aleatoriamente da mesma maneira

James escreveu em 2015 um sklearn solução o que é útil. Mas ele adicionou uma variável de estado aleatório, que não é necessário. No código abaixo, o estado aleatório de Numpy é assumido automaticamente.

X = np.array([[1., 0.], [2., 1.], [0., 0.]])
y = np.array([0, 1, 2])
from sklearn.utils import shuffle
X, y = shuffle(X, y)

Reduza qualquer número de matrizes juntas, no local, usando apenas Numpy.

import numpy as np


def shuffle_arrays(arrays, set_seed=-1):
    """Shuffles arrays in-place, in the same order, along axis=0

    Parameters:
    -----------
    arrays : List of NumPy arrays.
    set_seed : Seed value if int >= 0, else seed is random.
    """
    assert all(len(arr) == len(arrays[0]) for arr in arrays)
    seed = np.random.randint(0, 2**(32 - 1) - 1) if set_seed < 0 else set_seed

    for arr in arrays:
        rstate = np.random.RandomState(seed)
        rstate.shuffle(arr)

E pode ser usado assim

a = np.array([1, 2, 3, 4, 5])
b = np.array([10,20,30,40,50])
c = np.array([[1,10,11], [2,20,22], [3,30,33], [4,40,44], [5,50,55]])

shuffle_arrays([a, b, c])

Algumas coisas a serem observadas:

  • A afirmação garante que todas as matrizes de entrada tenham o mesmo comprimento ao longo de sua primeira dimensão.
  • Matrizes embarcadas no local por sua primeira dimensão - nada devolveu.
  • Semente aleatória dentro da faixa INT32 positiva.
  • Se for necessário um shuffle repetível, o valor da semente poderá ser definido.

Após o shuffle, os dados podem ser divididos usando np.split ou referenciado usando fatias - dependendo do aplicativo.

Você pode fazer uma matriz como:

s = np.arange(0, len(a), 1)

Então embaralhe:

np.random.shuffle(s)

Agora use isso como argumento de suas matrizes. Os mesmos argumentos embaralhados retornam os mesmos vetores embaralhados.

x_data = x_data[s]
x_label = x_label[s]
from np.random import permutation
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data #numpy array
y = iris.target #numpy array

# Data is currently unshuffled; we should shuffle 
# each X[i] with its corresponding y[i]
perm = permutation(len(X))
X = X[perm]
y = y[perm]

Uma maneira pela qual o Shuffling no local pode ser feito para listas conectadas é usar uma semente (pode ser aleatória) e usar numpy.random.shuffle para fazer o Shuffling.

# Set seed to a random number if you want the shuffling to be non-deterministic.
def shuffle(a, b, seed):
   np.random.seed(seed)
   np.random.shuffle(a)
   np.random.seed(seed)
   np.random.shuffle(b)

É isso. Isso embaralhará A e B da mesma maneira. Isso também é feito no local, o que é sempre uma vantagem.

Editar, não use np.random.seed () use np.random.randomstate em vez disso

def shuffle(a, b, seed):
   rand_state = np.random.RandomState(seed)
   rand_state.shuffle(a)
   rand_state.seed(seed)
   rand_state.shuffle(b)

Ao chamá -lo, basta passar em qualquer semente para alimentar o estado aleatório:

a = [1,2,3,4]
b = [11, 22, 33, 44]
shuffle(a, b, 12345)

Resultado:

>>> a
[1, 4, 2, 3]
>>> b
[11, 44, 22, 33]

Editar: o código corrigido para re-semeado o estado aleatório

Existe uma função bem conhecida que pode lidar com isso:

from sklearn.model_selection import train_test_split
X, _, Y, _ = train_test_split(X,Y, test_size=0.0)

Basta definir test_size para 0 evitará a divisão e fornecerá dados embaralhados. Embora geralmente seja usado para dividir os dados de trem e testar, também os embaralham.
A partir de documentação

Matrizes ou matrizes divididas em subconjuntos aleatórios de trem e teste

Utilitário rápido que envolve a validação de entrada e o próximo (shufflesplit (). Dividido (x, y) e aplicação para inserir dados em uma única chamada para divisão (e opcionalmente subamostragem) em um oneliner.

Digamos que temos duas matrizes: A e B.

a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = np.array([[9,1,1],[6,6,6],[4,2,0]]) 

Primeiro, podemos obter índices de linha, permitindo a primeira dimensão

indices = np.random.permutation(a.shape[0])
[1 2 0]

Em seguida, use indexação avançada. Aqui estamos usando os mesmos índices para embaralhar as duas matrizes em uníssono.

a_shuffled = a[indices[:,np.newaxis], np.arange(a.shape[1])]
b_shuffled = b[indices[:,np.newaxis], np.arange(b.shape[1])]

Isso é equivalente a

np.take(a, indices, axis=0)
[[4 5 6]
 [7 8 9]
 [1 2 3]]

np.take(b, indices, axis=0)
[[6 6 6]
 [4 2 0]
 [9 1 1]]

Se você quiser evitar copiar matrizes, sugiro que, em vez de gerar uma lista de permutação, você passa por todos os elementos da matriz e a trocasse aleatoriamente para outra posição na matriz

for old_index in len(a):
    new_index = numpy.random.randint(old_index+1)
    a[old_index], a[new_index] = a[new_index], a[old_index]
    b[old_index], b[new_index] = b[new_index], b[old_index]

Isso implementa o algoritmo Shuffle Knuth-Fisher-Yates.

Com um exemplo, é isso que estou fazendo:

combo = []
for i in range(60000):
    combo.append((images[i], labels[i]))

shuffle(combo)

im = []
lab = []
for c in combo:
    im.append(c[0])
    lab.append(c[1])
images = np.asarray(im)
labels = np.asarray(lab)

Eu estendi a Random.Shuffle () de Python para tomar um segundo arg:

def shuffle_together(x, y):
    assert len(x) == len(y)

    for i in reversed(xrange(1, len(x))):
        # pick an element in x[:i+1] with which to exchange x[i]
        j = int(random.random() * (i+1))
        x[i], x[j] = x[j], x[i]
        y[i], y[j] = y[j], y[i]

Dessa forma, posso ter certeza de que o Shuffling acontece no local e a função não é muito longa ou complicada.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top