web-dev-qa-db-fra.com

Meilleure façon de mélanger deux tableaux numpy à l'unisson

J'ai deux tableaux numpy de formes différentes, mais de même longueur (dimension principale). Je souhaite mélanger chacun d’eux, de sorte que les éléments correspondants continuent de correspondre, c’est-à-dire les mélanger à l’unisson en ce qui concerne leurs indices principaux.

Ce code fonctionne et illustre mes objectifs:

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

Par exemple:

>>> 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]))

Cependant, cela semble fastidieux, inefficace et lent, et cela nécessite de faire une copie des tableaux - je préfère les mélanger sur place, car ils seront assez volumineux.

Y a-t-il une meilleure façon de s'y prendre? Une exécution plus rapide et une utilisation réduite de la mémoire sont mes objectifs principaux, mais un code élégant serait également agréable.

Une autre pensée que j'avais était la suivante:

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)

Cela fonctionne ... mais c'est un peu effrayant, car je ne vois aucune garantie que cela continuera à fonctionner - cela ne ressemble pas au genre de chose qui est garanti pour survivre avec la version numpy, par exemple.

201

Votre solution "effrayante" ne me semble pas effrayante. L'appel de shuffle() pour deux séquences de même longueur entraîne le même nombre d'appels au générateur de nombres aléatoires, et il s'agit des seuls éléments "aléatoires" de l'algorithme de lecture aléatoire. En réinitialisant l'état, vous vous assurez que les appels au générateur de nombres aléatoires donneront les mêmes résultats lors du deuxième appel à shuffle(), de sorte que tout l'algorithme générera la même permutation.

Si vous n'aimez pas cela, une solution différente serait de stocker vos données dans un tableau au lieu de deux dès le début, et de créer deux vues dans ce tableau unique simulant les deux tableaux que vous avez maintenant. Vous pouvez utiliser le tableau unique pour le brassage et les vues pour toutes les autres fins.

Exemple: supposons que les tableaux a et b ressemblent à ceci:

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.]])

Nous pouvons maintenant construire un seul tableau contenant toutes les données:

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.]])

Maintenant, nous créons des vues simulant l'original a et b:

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

Les données de a2 Et de b2 Sont partagées avec c. Pour mélanger les deux tableaux simultanément, utilisez numpy.random.shuffle(c).

Dans le code de production, vous devriez bien sûr éviter de créer les a et b originaux et créer immédiatement c, a2 Et b2.

Cette solution pourrait être adaptée au cas où a et b ont des types différents.

61
Sven Marnach

Vous pouvez utiliser NumPy's indexation de tableaux :

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

Cela se traduira par la création de tableaux séparés mélangés à l'unisson.

293
mtrw
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)

Pour en savoir plus, voir http://scikit-learn.org/stable/modules/generated/sklearn.utils.shuffle.html

146
James

Solution très simple:

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

les deux tableaux x, y sont maintenant tous les deux brassés de manière aléatoire de la même manière

27
connor

James a écrit en 2015 un sklearn solution qui est utile. Mais il a ajouté une variable d'état aléatoire, ce qui n'est pas nécessaire. Dans le code ci-dessous, l'état aléatoire de numpy est automatiquement supposé.

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)
14
Daniel

Mélangez un nombre quelconque de matrices ensemble, sur place, en utilisant uniquement 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)

Et peut être utilisé comme ça

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])

Quelques points à noter:

  • L'assertion garantit que tous les tableaux d'entrée ont la même longueur sur leur première dimension.
  • Les tableaux mélangés sur place par leur première dimension - rien n'est revenu.
  • Graine aléatoire dans la plage int32 positive.
  • Si un mélange répétable est nécessaire, vous pouvez définir une valeur de départ.

Après la lecture aléatoire, les données peuvent être fractionnées à l'aide de np.split ou référencé à l'aide de tranches - en fonction de l'application.

9
Isaac B

vous pouvez faire un tableau comme:

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

puis mélangez-le:

np.random.shuffle(s)

maintenant, utilisez ceci comme argument de vos tableaux. les mêmes arguments mélangés renvoient les mêmes vecteurs mélangés.

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]
7
benjaminjsanders

Une façon de procéder au brassage sur place pour les listes connectées consiste à utiliser une graine (elle peut être aléatoire) et à utiliser numpy.random.shuffle pour effectuer le brassage.

# 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)

C'est ça. Cela mélangera a et b exactement de la même manière. Cela se fait également sur place, ce qui est toujours un avantage.

EDIT, n'utilisez pas np.random.seed (), utilisez plutôt np.random.RandomState

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

Lorsque vous l'appelez, transmettez simplement une graine pour alimenter l'état aléatoire:

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

Sortie:

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

Edit: code fixe pour ré-ensemencer l'état aléatoire

3
Adam Snaider

Il existe une fonction bien connue qui peut gérer ceci:

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

Si vous définissez test_size sur 0, vous éviterez les fractionnements et vous donnerons des données mélangées. Bien qu'il soit généralement utilisé pour séparer les données de train et de test, il les mélange également.
De documentation

Diviser des tableaux ou matrices en trains aléatoires et tester des sous-ensembles

Utilitaire rapide qui encapsule la validation d'entrée et les actions suivantes (ShuffleSplit (). Split (X, y)) et l'application permettant de saisir des données en un seul appel pour le fractionnement (et éventuellement le sous-échantillonnage) des données dans un doubleur.

3
sziraqui

Disons que nous avons deux tableaux: a et 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]]) 

On peut d'abord obtenir des index de lignes en permutant la première dimension

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

Ensuite, utilisez l'indexation avancée. Ici, nous utilisons les mêmes indices pour mélanger les deux tableaux à l’unisson.

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

Ceci est équivalent à

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]]
2
wedran

J'ai étendu random.shuffle () de python pour prendre un deuxième argument:

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 cette façon, je peux être sûr que le brassage a lieu sur place et que la fonction n’est pas trop longue ni compliquée.

0
Ivo

Avec un exemple, voici ce que je fais:

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)
0
ajfbiw.s

Si vous souhaitez éviter de copier des tableaux, je vous suggère alors de ne pas générer une liste de permutation, mais de parcourir tous les éléments du tableau et de les échanger de manière aléatoire vers un autre emplacement du tableau.

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]

Ceci implémente l'algorithme de mélange de Knuth-Fisher-Yates.

0
DaveP