What’s New or Different#

NumPy 1.17.0 introduced Generator as an improved replacement for the legacy RandomState. Here is a quick comparison of the two implementations.

Feature

Older Equivalent

Notes

Generator

RandomState

Generator requires a stream source, called a BitGenerator A number of these are provided. RandomState uses the Mersenne Twister MT19937 by default, but can also be instantiated with any BitGenerator.

random

random_sample, rand

Access the values in a BitGenerator, convert them to float64 in the interval [0.0., `` 1.0)``. In addition to the size kwarg, now supports dtype='d' or dtype='f', and an out kwarg to fill a user- supplied array.

Many other distributions are also supported.

integers

randint, random_integers

Use the endpoint kwarg to adjust the inclusion or exclusion of the high interval endpoint

  • The normal, exponential and gamma generators use 256-step Ziggurat methods which are 2-10 times faster than NumPy’s default implementation in standard_normal, standard_exponential or standard_gamma. Because of the change in algorithms, it is not possible to reproduce the exact random values using Generator for these distributions or any distribution method that relies on them.

In [1]: import numpy.random

In [2]: rng = np.random.default_rng()

In [3]: %timeit -n 1 rng.standard_normal(100000)
   ...: %timeit -n 1 numpy.random.standard_normal(100000)
   ...: 
2.16 ms +- 801 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
The slowest run took 8.24 times longer than the fastest. This could mean that an intermediate result is being cached.
5.56 ms +- 6.24 ms per loop (mean +- std. dev. of 7 runs, 1 loop each)
In [4]: %timeit -n 1 rng.standard_exponential(100000)
   ...: %timeit -n 1 numpy.random.standard_exponential(100000)
   ...: 
1.42 ms +- 611 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
3.02 ms +- 259 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
In [5]: %timeit -n 1 rng.standard_gamma(3.0, 100000)
   ...: %timeit -n 1 numpy.random.standard_gamma(3.0, 100000)
   ...: 
3.66 ms +- 706 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
8.08 ms +- 1.08 ms per loop (mean +- std. dev. of 7 runs, 1 loop each)
  • integers is now the canonical way to generate integer random numbers from a discrete uniform distribution. This replaces both randint and the deprecated random_integers.

  • The rand and randn methods are only available through the legacy RandomState.

  • Generator.random is now the canonical way to generate floating-point random numbers, which replaces RandomState.random_sample, sample, and ranf, all of which were aliases. This is consistent with Python’s random.random.

  • All bit generators can produce doubles, uint64s and uint32s via CTypes (ctypes) and CFFI (cffi). This allows these bit generators to be used in numba.

  • The bit generators can be used in downstream projects via Cython.

  • All bit generators use SeedSequence to convert seed integers to initialized states.

  • Optional dtype argument that accepts np.float32 or np.float64 to produce either single or double precision uniform random variables for select distributions. integers accepts a dtype argument with any signed or unsigned integer dtype.

In [6]: rng = np.random.default_rng()

In [7]: rng.random(3, dtype=np.float64)
Out[7]: array([0.56599812, 0.27980804, 0.61902996])

In [8]: rng.random(3, dtype=np.float32)
Out[8]: array([0.8754037 , 0.64888024, 0.6355138 ], dtype=float32)

In [9]: rng.integers(0, 256, size=3, dtype=np.uint8)
Out[9]: array([120, 240,  69], dtype=uint8)
  • Optional out argument that allows existing arrays to be filled for select distributions

    This allows multithreading to fill large arrays in chunks using suitable BitGenerators in parallel.

In [10]: rng = np.random.default_rng()

In [11]: existing = np.zeros(4)

In [12]: rng.random(out=existing[:2])
Out[12]: array([0.34847961, 0.1433444 ])

In [13]: print(existing)
[0.34847961 0.1433444  0.         0.        ]
  • Optional axis argument for methods like choice, permutation and shuffle that controls which axis an operation is performed over for multi-dimensional arrays.

In [14]: rng = np.random.default_rng()

In [15]: a = np.arange(12).reshape((3, 4))

In [16]: a
Out[16]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [17]: rng.choice(a, axis=1, size=5)
Out[17]: 
array([[ 2,  0,  1,  1,  3],
       [ 6,  4,  5,  5,  7],
       [10,  8,  9,  9, 11]])

In [18]: rng.shuffle(a, axis=1)        # Shuffle in-place

In [19]: a
Out[19]: 
array([[ 2,  3,  1,  0],
       [ 6,  7,  5,  4],
       [10, 11,  9,  8]])
  • Added a method to sample from the complex normal distribution (complex_normal)