Лучший способ перетасовать два массива numpy в унисон
у меня есть два массива numpy разных форм, но с одинаковой длиной (ведущий размер). Я хочу перетасовать каждый из них так, чтобы соответствующие элементы продолжали соответствовать, т. е. перетасовать их в унисон относительно их ведущих индексов.
этот код работает, и иллюстрирует мои цели:
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
например:
>>> 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]))
однако это кажется неуклюжим, неэффективным и медленным, и для этого требуется сделать копию массивов - я бы предпочел перемешайте их на месте, так как они будут довольно большими.
есть ли лучший способ сделать это? Более быстрое выполнение и меньшее использование памяти-мои основные цели, но элегантный код тоже был бы хорош.
еще одна мысль у меня было это:
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)
это работает...но это немного страшно, поскольку я вижу небольшую гарантию, что он будет продолжать работать-это не похоже на то, что гарантированно выживет в версии numpy, например.
11 ответов:
ваше "страшное" решение не кажется мне страшным. Звоню
shuffle()
для двух последовательностей одинаковой длины приводит к одинаковому количеству вызовов генератора случайных чисел,и это единственные "случайные" элементы в алгоритме перемешивания. Сбросив состояние, вы гарантируете, что вызовы генератора случайных чисел дадут те же результаты во втором вызовеshuffle()
, Так что весь алгоритм будет генерировать одну и ту же перестановку.если вам это не нравится, а другим решением было бы хранить ваши данные в одном массиве вместо двух с самого начала и создавать два представления в этом одном массиве, имитируя два массива, которые у вас есть сейчас. Вы можете использовать один массив для перетасовки и представления для всех других целей.
пример: предположим, что массивы
a
иb
выглядит так: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.]])
теперь мы можем создать один массив, содержащий все данные:
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.]])
теперь мы создаем вид имитация оригинала
a
иb
:a2 = c[:, :a.size//len(a)].reshape(a.shape) b2 = c[:, a.size//len(a):].reshape(b.shape)
данные
a2
иb2
совместно сc
. Чтобы перетасовать оба массива одновременно, используйтеnumpy.random.shuffle(c)
.в производственном коде вы, конечно, попытаетесь избежать создания оригинала
a
иb
вообще и сразу создатьc
,a2
иb2
.это решение может быть адаптировано к случаю, что
a
иb
имеют разные типы dtypes.
вы можете использовать NumPy и обратно в индексирование массива:
def unison_shuffled_copies(a, b): assert len(a) == len(b) p = numpy.random.permutation(len(a)) return a[p], b[p]
это приведет к созданию отдельных массивов, перемешанных в унисон.
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)
более подробно см. В разделе http://scikit-learn.org/stable/modules/generated/sklearn.utils.shuffle.html
очень простое решение:
randomize = np.arange(len(x)) np.random.shuffle(randomize) x = x[randomize] y = y[randomize]
два массива x, y теперь случайным образом перемешиваются одинаково
Джеймс написал в 2015 году sklearn решение что очень полезно. Но он добавил случайную переменную состояния, которая не нужна. В приведенном ниже коде автоматически принимается случайное состояние из numpy.
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)
один из способов, которым можно выполнить перетасовку на месте для подключенных списков, - использовать семя (оно может быть случайным) и использовать numpy.случайность.перемешайте, чтобы сделать перетасовку.
# 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)
вот и все. Это будет перетасовать и a и b точно так же. Это также делается на месте, что всегда является плюсом.
редактировать, не использовать np.случайность.seed () использовать np.случайность.Вместо RandomState
def shuffle(a, b, seed): rand_state = np.random.RandomState(seed) rand_state.shuffle(a) rand_state.seed(seed) rand_state.shuffle(b)
при вызове его просто передать в любое семя, чтобы накормить случайных состояние:
a = [1,2,3,4] b = [11, 22, 33, 44] shuffle(a, b, 12345)
выход:
>>> a [1, 4, 2, 3] >>> b [11, 44, 22, 33]
Edit: исправлен код для повторного заполнения случайного состояния
вы можете сделать массив как:
s = np.arange(0, len(a), 1)
затем перетасуйте:
np.random.shuffle(s)
Теперь используйте этот s в качестве аргумента ваших массивов. одинаковым аргументы вернуть одинаковым векторов.
x_data = x_data[s] x_label = x_label[s]
перемешать любое количество массивов вместе, на месте, используя только 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)
и может использоваться так
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])
дополнительная информация:
- утверждение гарантирует, что все входные массивы имеют одинаковую длину вдоль их первое измерение.
- массивы перемешиваются на месте по их первому измерению-ничего не возвращается.
- случайное семя в пределах положительного диапазона int32.
- при необходимости повторить перемешивание , можно задать начальное значение.
после перемешивания данные можно разделить с помощью
np.split
или ссылаться с помощью срезов - в зависимости от приложения.
Если вы хотите избежать копирования массивов, то я бы предложил вместо того, чтобы генерировать список перестановок, вы проходите через каждый элемент массива и случайным образом меняете его на другую позицию в массиве
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]
это реализует алгоритм перемешивания кнута-Фишера-Йейтса.
С примером, это то, что я делаю:
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)
я расширил случайный python.shuffle () чтобы взять второй 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]
таким образом, я могу быть уверен, что перетасовка происходит на месте, и функция не слишком длинная или сложная.