Skip to main content
`Ctrl`+`K`

# 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)
       ...:
    1.02 ms +- 9.03 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    1.75 ms +- 15.3 us 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)
       ...:
    487 us +- 6.98 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    1.25 ms +- 6.35 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)
       ...:
    1.85 ms +- 24.4 us per loop (mean +- std. dev. of 7 runs, 1 loop each)
    3.42 ms +- 27.1 us 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.

  - Uniforms (`random` and `integers`)

  - Normals (`standard_normal`)

  - Standard Gammas (`standard_gamma`)

  - Standard Exponentials (`standard_exponential`)

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

    In [7]: rng.random(3, dtype=np.float64)
    Out[7]: array([0.15428534, 0.69863672, 0.45724786])

    In [8]: rng.random(3, dtype=np.float32)
    Out[8]: array([0.1729908 , 0.0745824 , 0.42687505], dtype=float32)

    In [9]: rng.integers(0, 256, size=3, dtype=np.uint8)
    Out[9]: array([183, 218, 161], dtype=uint8)

- Optional `out` argument that allows existing arrays to be filled for select distributions

  - Uniforms (`random`)

  - Normals (`standard_normal`)

  - Standard Gammas (`standard_gamma`)

  - Standard Exponentials (`standard_exponential`)

  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.99546415, 0.1408341 ])

    In [13]: print(existing)
    [0.99546415 0.1408341  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,  2,  3,  2,  1],
           [ 6,  6,  7,  6,  5],
           [10, 10, 11, 10,  9]])

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

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

- Added a method to sample from the complex normal distribution (complex_normal)