python - Mejor forma de barajar dos matrices numpy al unísono
random shuffle (9)
Con un ejemplo, esto es lo que estoy haciendo:
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)
Tengo dos matrices numpy de diferentes formas, pero con la misma longitud (dimensión principal). Quiero mezclar cada uno de ellos, de modo que los elementos correspondientes sigan correspondiendo, es decir, mezclarlos al unísono con respecto a sus índices principales.
Este código funciona e ilustra mis 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 ejemplo:
>>> 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]))
Sin embargo, esto se siente torpe, ineficiente y lento, y requiere hacer una copia de los arreglos; prefiero mezclarlos en el lugar, ya que serán bastante grandes.
¿Hay una mejor manera de hacerlo? La ejecución más rápida y el menor uso de memoria son mis objetivos principales, pero un código elegante también sería agradable.
Otro pensamiento que tuve fue este:
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)
Esto funciona ... pero da un poco de miedo, ya que veo pocas garantías de que continúe funcionando; por ejemplo, no parece el tipo de cosas que garanticen la supervivencia en la versión numpy.
Extendí el random.shuffle () de python para tomar un 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]
De esa manera puedo estar seguro de que la mezcla se realiza en el lugar, y la función no es demasiado larga ni complicada.
Si desea evitar la copia de matrices, le sugiero que en lugar de generar una lista de permutación, examine cada elemento de la matriz y la cambie aleatoriamente a otra posición en la 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]
Esto implementa el algoritmo aleatorio Knuth-Fisher-Yates.
Solución muy simple:
randomize = np.arange(len(x))
np.random.shuffle(randomize)
x = x[randomize]
y = y[randomize]
las dos matrices x, y ahora se barajan aleatoriamente de la misma manera
Tu puedes usar la indexación de matrices de NumPy:
def unison_shuffled_copies(a, b):
assert len(a) == len(b)
p = numpy.random.permutation(len(a))
return a[p], b[p]
Esto dará como resultado la creación de matrices barajadas al unísono por separado.
Tu solución "aterradora" no me parece aterradora. Llamar a shuffle()
para dos secuencias de la misma longitud da como resultado el mismo número de llamadas al generador de números aleatorios, y estos son los únicos elementos "aleatorios" en el algoritmo de mezcla aleatoria. Al restablecer el estado, se asegura de que las llamadas al generador de números aleatorios darán los mismos resultados en la segunda llamada a shuffle()
, de modo que todo el algoritmo generará la misma permutación.
Si no te gusta esto, una solución diferente sería almacenar tus datos en una matriz en lugar de dos desde el principio, y crear dos vistas en esta única matriz que simula las dos matrices que tienes ahora. Puede usar la única matriz para mezclar y las vistas para todos los demás fines.
Ejemplo: Supongamos que las matrices a
y b
ven así:
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.]])
Ahora podemos construir una única matriz que contenga todos los datos:
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.]])
Ahora creamos vistas simulando el original a
y b
:
a2 = c[:, :a.size//len(a)].reshape(a.shape)
b2 = c[:, a.size//len(a):].reshape(b.shape)
Los datos de a2
y b2
se comparten con c
. Para mezclar ambas matrices simultáneamente, use numpy.random.shuffle(c)
.
En el código de producción, por supuesto, tratarías de evitar crear el original a
y b
y crear de inmediato c
, a2
y b2
.
Esta solución podría adaptarse al caso de que b
tengan diferentes tipos de letra.
Una forma en que se puede barajar en el lugar para las listas conectadas es usar una semilla (podría ser aleatoria) y usar numpy.random.shuffle para hacer la mezcla.
# 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)
Eso es. Esto mezclará tanto a como b de la misma manera. Esto también se realiza in situ, lo que siempre es un plus.
EDITAR, no use np.random.seed () use np.random.RandomState en su lugar
def shuffle(a, b, seed):
rand_state = np.random.RandomState(seed)
rand_state.shuffle(a)
rand_state.seed(seed)
rand_state.shuffle(b)
Cuando lo llame simplemente pase cualquier semilla para alimentar el estado aleatorio:
a = [1,2,3,4]
b = [11, 22, 33, 44]
shuffle(a, b, 12345)
Salida:
>>> a
[1, 4, 2, 3]
>>> b
[11, 44, 22, 33]
Editar: código fijo para volver a inicializar el estado aleatorio
puedes hacer una matriz como:
s = np.arange(0, len(a), 1)
luego baraja:
np.random.shuffle(s)
ahora usa esto como argumento de tus matrices. los mismos argumentos mezclados devuelven los mismos vectores mezclados.
x_data = x_data[s]
x_label = x_label[s]
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 obtener más información, consulte http://scikit-learn.org/stable/modules/generated/sklearn.utils.shuffle.html