Как правило, вы хотите исключить использование списков при работе с numpy. Массивы намного более эффективны с точки зрения времени и пространства. Вам также следует избегать использования явных циклов Python, поскольку векторизация, предоставляемая numpy, будет намного быстрее.
Допустим, вы хотели сделать это для Z
различных прогулок, где Z = 5000
в вашем примере, но вы хотите, чтобы оно было больше в общем случае. Вы можете использовать тот факт, что большинство отдельных функций могут быть применены к определенной оси, чтобы выполнить это:
import numpy as np
from matplotlib import pyplot as plt
Z = 5000
N = 5000
d = 2
all_walks = d * np.cumsum(2 * np.random.binomial(1, 0.6, size=(Z, N)) - 1, axis=1)
Установка размера шагов на (Z, N)
даст вам матрицу, содержащую Z
строк, каждая строка с N
шагами. Взятие совокупной суммы с axis=1
означает суммирование по столбцам. Теперь каждый ряд - это самостоятельная прогулка. Вы можете использовать очень простые нарезки, чтобы получить любой столбец, который вы хотите. n
-й столбец будет содержать n
-й шаг от каждой из Z
прогулок. Причина, по которой вы хотите создать столбцы срезов, заключается в том, что построение графиков становится намного проще.
Давайте посмотрим на n=1000
:
n1k = all_walks[:, 1000]
plt.hist(n1k, bins=20, histtype='step', density=True)
Пока все хорошо. Чтобы взять каждые 1000 выборок, используйте размер шага в индексе:
n = 1000
samples = all_walks[:, n::n]
samples
теперь массив Zx (N // n) -1 ((5000, 4)
), содержащий шаги с индексами 1000, 3000, 4000
в каждом массиве. Похоже, вы хотите иметь пять образцов в этом примере. На мой взгляд, есть три разумных способа сделать это:
- Начать с индекса
0
: samples = all_walks[:, ::n]
- Начать с индекса
n-1
: samples = all_walks[:, n-1::n]
Добавьте дополнительный шаг (столбец) к вашим прогулкам:
all_walks = ... size=(Z, N+1) ...
samples = all_walks[:, n::n]
Я не думаю, что возиться с пробелами из-за непоследовательного добавления индекса -1
- это особенно хорошая идея. Вместо этого я буду использовать вариант № 2 (помните, что индекс 999 содержит 1000-й шаг).
Хорошей новостью является то, что matplotlib позволяет вам строить столбцы вектора одновременно:
plt.hist(samples, bins=20, histtype='step', density=True)
Таким образом, весь сценарий на самом деле довольно короткий:
import numpy as np
from matplotlib import pyplot as plt
Z = 5000
N = 5000
d = 2
n = 1000
all_walks = d * np.cumsum(2 * np.random.binomial(1, 0.6, size=(Z, N)) - 1, axis=1)
samples = all_walks[:, n-1::n]
plt.hist(samples, bins=20, histtype='step', density=True)
plt.show()
Если по какой-то причине вы не можете держать массив размером Z * N
, плавающий в памяти все сразу, вы можете реализовать строку all_walks
, используя цикл, который генерирует одну прогулку за раз, что-то вроде того, что вы изначально пытались делать. Помните, это только в том случае, если вы набрали Z
какое-то невероятно большое число или по какой-то причине у вас нет ОЗУ:
samples = np.empty((Z, N//n))
for row in range(Z):
walk = d * np.cumsum(2 * np.random.binomial(1, 0.6, size=N) - 1)
samples[row] = walk[n-1::n]
Если вы начинаете с одного и того же случайного начального числа, оба метода должны давать одинаковые результаты. Основное отличие состоит в том, что первое занимает больше памяти, а второе занимает больше времени.