В документации к функции numpy random.Generator.choice одним из аргументов является shuffle
, который по умолчанию равен True
.
В документации указано:
перетасовать bool, необязательно
Перетасовывается ли выборка при выборке без замены. По умолчанию установлено значение True, False обеспечивает ускорение.
Мне недостаточно информации, чтобы понять, что это значит. Я не понимаю, зачем нам перетасовывать, если это уже достаточно случайно, и я не понимаю, почему мне будет предоставлена возможность не перетасовывать, если это приведет к предвзятой выборке.
Если я установлю shuffle
на False
, получу ли я случайную (независимую) выборку? Мне также хотелось бы понять, почему мне вообще нужна настройка по умолчанию True
.
Вы по-прежнему получаете случайный выбор независимо от вашего выбора shuffle
. Однако если вы выберете shuffle=False
, порядок вывода не зависит от порядка ввода.
Это легче всего увидеть, когда количество выбранных элементов равно общему количеству элементов:
import numpy as np
rng = np.random.default_rng()
x = np.arange(10)
rng.choice(x, 10, replace=False, shuffle=False)
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
rng.choice(x, 10, replace=False, shuffle=True)
# array([8, 1, 3, 9, 6, 5, 0, 7, 4, 2])
Если вы уменьшите количество выбранных элементов и используете shuffle=False
, вы сможете убедиться, что недостающие элементы распределены должным образом.
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng()
x = np.arange(10)
set_x = set(x)
missing = []
for i in range(10000):
# By default, all `p` are equal, so which item is
# missing should be uniformly distributed
y = rng.choice(x, 9, replace=False, shuffle=False)
set_y = set(y)
missing.append(set_x.difference(set_y).pop())
plt.hist(missing)
Но вы увидите, что элементы, которые появлялись раньше в x
, как правило, появляются раньше в выводе, и наоборот. То есть порядок ввода и вывода коррелирует.
x = np.arange(10)
correlations = []
for i in range(10000):
y = rng.choice(x, 9, replace=False, shuffle=False)
correlations.append(stats.spearmanr(np.arange(9), y).statistic)
plt.hist(correlations)
Если это подходит для вашего приложения, смело устанавливайте shuffle=False
для ускорения.
%timeit rng.choice(10000, 5000, replace=False, shuffle=True)
# 187 µs ± 26.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit rng.choice(10000, 5000, replace=False, shuffle=False)
# 146 µs ± 18.4 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Чем больше элементов необходимо выбрать, тем более выражено ускорение.
%timeit rng.choice(10000, 1, replace=False, shuffle=True)
# 17.6 µs ± 3.64 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit rng.choice(10000, 1, replace=False, shuffle=False)
# 16.5 µs ± 2.47 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
против
%timeit rng.choice(10000, 9999, replace=False, shuffle=True)
# 214 µs ± 32.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit rng.choice(10000, 9999, replace=False, shuffle=False)
# 124 µs ± 27.5 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Поскольку @matt-haberland уже представил статистику, на случайность не влияет наличие или отсутствие перетасовки.
Однако на самом деле выбранные элементы одни и те же. Оператор assert
в приведенном ниже коде не подведет.
import numpy as np
n = 1000
m = 100
for seed in range(1000):
result_with_shuffle = np.random.default_rng(seed).choice(range(n), size=m, replace=False, shuffle=True)
result_without_shuffle = np.random.default_rng(seed).choice(range(n), size=m, replace=False, shuffle=False)
assert list(result_with_shuffle) != list(result_without_shuffle)
assert set(result_with_shuffle) == set(result_without_shuffle)
Кроме того, как упоминал @matt-haberland, самый простой способ увидеть, что порядок вывода не зависит (или нет) от порядка ввода, - это выбрать то же количество элементов, что и входные данные, но вы должны осознавая, что некоторые тенденции проявятся даже при гораздо меньших размерах.
import matplotlib.pyplot as plt
import numpy as np
n = 10000
m = 2000
seed = 0
sample_with_shuffle = np.random.default_rng(seed).choice(range(n), size=m, replace=False, shuffle=True)
sample_without_shuffle = np.random.default_rng(seed).choice(range(n), size=m, replace=False, shuffle=False)
def plot(sample, title, index):
plt.subplot(1, 2, index)
plt.bar(range(m), sample)
plt.title(title)
plt.xlabel("Index")
plt.ylabel("Value")
plt.figure(figsize=(14, 6))
plot(sample_with_shuffle, "shuffle=True", 1)
plot(sample_without_shuffle, "shuffle=False", 2)
plt.tight_layout()
plt.show()
Хотя выборка без перетасовки кажется случайной, если мы посмотрим на первые пару значений, на графике четко видна восходящая тенденция слева направо.
Таким образом, если порядок выходов имеет значение, вам всегда следует перетасовывать их. В противном случае нет никакого преимущества.