summaryrefslogtreecommitdiff
path: root/doc/source/user/how-to-partition.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/source/user/how-to-partition.rst')
-rw-r--r--doc/source/user/how-to-partition.rst268
1 files changed, 268 insertions, 0 deletions
diff --git a/doc/source/user/how-to-partition.rst b/doc/source/user/how-to-partition.rst
new file mode 100644
index 000000000..224519364
--- /dev/null
+++ b/doc/source/user/how-to-partition.rst
@@ -0,0 +1,268 @@
+.. _how-to-partition:
+
+=================================================
+How to create arrays with regularly-spaced values
+=================================================
+
+There are a few NumPy functions that are similar in application, but which
+provide slightly different results, which may cause confusion if one is not sure
+when and how to use them. The following guide aims to list these functions and
+describe their recommended usage.
+
+The functions mentioned here are
+
+* `numpy.linspace`
+* `numpy.arange`
+* `numpy.geomspace`
+* `numpy.logspace`
+* `numpy.meshgrid`
+* `numpy.mgrid`
+* `numpy.ogrid`
+
+1D domains (intervals)
+======================
+
+``linspace`` vs. ``arange``
+---------------------------
+
+Both `numpy.linspace` and `numpy.arange` provide ways to partition an interval
+(a 1D domain) into equal-length subintervals. These partitions will vary
+depending on the chosen starting and ending points, and the **step** (the length
+of the subintervals).
+
+* **Use** `numpy.arange` **if you want integer steps.**
+
+ `numpy.arange` relies on step size to determine how many elements are in the
+ returned array, which excludes the endpoint. This is determined through the
+ ``step`` argument to ``arange``.
+
+ Example::
+
+ >>> np.arange(0, 10, 2) # np.arange(start, stop, step)
+ array([0, 2, 4, 6, 8])
+
+ The arguments ``start`` and ``stop`` should be integer or real, but not
+ complex numbers. `numpy.arange` is similar to the Python built-in
+ :py:class:`range`.
+
+ Floating-point inaccuracies can make ``arange`` results with floating-point
+ numbers confusing. In this case, you should use `numpy.linspace` instead.
+
+* **Use** `numpy.linspace` **if you want the endpoint to be included in the
+ result, or if you are using a non-integer step size.**
+
+ `numpy.linspace` *can* include the endpoint and determines step size from the
+ `num` argument, which specifies the number of elements in the returned
+ array.
+
+ The inclusion of the endpoint is determined by an optional boolean
+ argument ``endpoint``, which defaults to ``True``. Note that selecting
+ ``endpoint=False`` will change the step size computation, and the subsequent
+ output for the function.
+
+ Example::
+
+ >>> np.linspace(0.1, 0.2, num=5) # np.linspace(start, stop, num)
+ array([0.1 , 0.125, 0.15 , 0.175, 0.2 ])
+ >>> np.linspace(0.1, 0.2, num=5, endpoint=False)
+ array([0.1, 0.12, 0.14, 0.16, 0.18])
+
+ `numpy.linspace` can also be used with complex arguments::
+
+ >>> np.linspace(1+1.j, 4, 5, dtype=np.complex64)
+ array([1. +1.j , 1.75+0.75j, 2.5 +0.5j , 3.25+0.25j, 4. +0.j ],
+ dtype=complex64)
+
+Other examples
+--------------
+
+1. Unexpected results may happen if floating point values are used as ``step``
+ in ``numpy.arange``. To avoid this, make sure all floating point conversion
+ happens after the computation of results. For example, replace
+
+ ::
+
+ >>> list(np.arange(0.1,0.4,0.1).round(1))
+ [0.1, 0.2, 0.3, 0.4] # endpoint should not be included!
+
+ with
+
+ ::
+
+ >>> list(np.arange(1, 4, 1) / 10.0)
+ [0.1, 0.2, 0.3] # expected result
+
+2. Note that
+
+ ::
+
+ >>> np.arange(0, 1.12, 0.04)
+ array([0. , 0.04, 0.08, 0.12, 0.16, 0.2 , 0.24, 0.28, 0.32, 0.36, 0.4 ,
+ 0.44, 0.48, 0.52, 0.56, 0.6 , 0.64, 0.68, 0.72, 0.76, 0.8 , 0.84,
+ 0.88, 0.92, 0.96, 1. , 1.04, 1.08, 1.12])
+
+ and
+
+ ::
+
+ >>> np.arange(0, 1.08, 0.04)
+ array([0. , 0.04, 0.08, 0.12, 0.16, 0.2 , 0.24, 0.28, 0.32, 0.36, 0.4 ,
+ 0.44, 0.48, 0.52, 0.56, 0.6 , 0.64, 0.68, 0.72, 0.76, 0.8 , 0.84,
+ 0.88, 0.92, 0.96, 1. , 1.04])
+
+ These differ because of numeric noise. When using floating point values, it
+ is possible that ``0 + 0.04 * 28 < 1.12``, and so ``1.12`` is in the
+ interval. In fact, this is exactly the case::
+
+ >>> 1.12/0.04
+ 28.000000000000004
+
+ But ``0 + 0.04 * 27 >= 1.08`` so that 1.08 is excluded::
+
+ >>> 1.08/0.04
+ 27.0
+
+ Alternatively, you could use ``np.arange(0, 28)*0.04`` which would always
+ give you precise control of the end point since it is integral::
+
+ >>> np.arange(0, 28)*0.04
+ array([0. , 0.04, 0.08, 0.12, 0.16, 0.2 , 0.24, 0.28, 0.32, 0.36, 0.4 ,
+ 0.44, 0.48, 0.52, 0.56, 0.6 , 0.64, 0.68, 0.72, 0.76, 0.8 , 0.84,
+ 0.88, 0.92, 0.96, 1. , 1.04, 1.08])
+
+
+``geomspace`` and ``logspace``
+------------------------------
+
+``numpy.geomspace`` is similar to ``numpy.linspace``, but with numbers spaced
+evenly on a log scale (a geometric progression). The endpoint is included in the
+result.
+
+Example::
+
+ >>> np.geomspace(2, 3, num=5)
+ array([2. , 2.21336384, 2.44948974, 2.71080601, 3. ])
+
+``numpy.logspace`` is similar to ``numpy.geomspace``, but with the start and end
+points specified as logarithms (with base 10 as default)::
+
+ >>> np.logspace(2, 3, num=5)
+ array([ 100. , 177.827941 , 316.22776602, 562.34132519, 1000. ])
+
+In linear space, the sequence starts at ``base ** start`` (``base`` to the power
+of ``start``) and ends with ``base ** stop``::
+
+ >>> np.logspace(2, 3, num=5, base=2)
+ array([4. , 4.75682846, 5.65685425, 6.72717132, 8. ])
+
+nD domains
+==========
+
+nD domains can be partitioned into *grids*. This can be done using one of the
+following functions.
+
+``meshgrid``
+------------
+
+The purpose of ``numpy.meshgrid`` is to create a rectangular grid out of a set
+of one-dimensional coordinate arrays.
+
+Given arrays
+
+ ::
+
+ >>> x = np.array([0, 1, 2, 3])
+ >>> y = np.array([0, 1, 2, 3, 4, 5])
+
+``meshgrid`` will create two coordinate arrays, which can be used to generate
+the coordinate pairs determining this grid.
+
+ ::
+
+ >>> xx, yy = np.meshgrid(x, y)
+ >>> xx
+ array([[0, 1, 2, 3],
+ [0, 1, 2, 3],
+ [0, 1, 2, 3],
+ [0, 1, 2, 3],
+ [0, 1, 2, 3],
+ [0, 1, 2, 3]])
+ >>> yy
+ array([[0, 0, 0, 0],
+ [1, 1, 1, 1],
+ [2, 2, 2, 2],
+ [3, 3, 3, 3],
+ [4, 4, 4, 4],
+ [5, 5, 5, 5]])
+
+ >>> import matplotlib.pyplot as plt
+ >>> plt.plot(xx, yy, marker='.', color='k', linestyle='none')
+
+.. plot:: user/plots/meshgrid_plot.py
+ :align: center
+ :include-source: 0
+
+``mgrid``
+---------
+
+``numpy.mgrid`` can be used as a shortcut for creating meshgrids. It is not a
+function, but when indexed, returns a multidimensional meshgrid.
+
+::
+
+ >>> xx, yy = np.meshgrid(np.array([0, 1, 2, 3]), np.array([0, 1, 2, 3, 4, 5]))
+ >>> xx.T, yy.T
+ (array([[0, 0, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1],
+ [2, 2, 2, 2, 2, 2],
+ [3, 3, 3, 3, 3, 3]]),
+ array([[0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5]]))
+
+ >>> np.mgrid[0:4, 0:6]
+ array([[[0, 0, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1],
+ [2, 2, 2, 2, 2, 2],
+ [3, 3, 3, 3, 3, 3]],
+ <BLANKLINE>
+ [[0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5],
+ [0, 1, 2, 3, 4, 5]]])
+
+
+``ogrid``
+---------
+
+Similar to ``numpy.mgrid``, ``numpy.ogrid`` returns an *open* multidimensional
+meshgrid. This means that when it is indexed, only one dimension of each
+returned array is greater than 1. This avoids repeating the data and thus saves
+memory, which is often desirable.
+
+These sparse coordinate grids are intended to be use with :ref:`broadcasting`.
+When all coordinates are used in an expression, broadcasting still leads to a
+fully-dimensonal result array.
+
+::
+
+ >>> np.ogrid[0:4, 0:6]
+ [array([[0],
+ [1],
+ [2],
+ [3]]), array([[0, 1, 2, 3, 4, 5]])]
+
+All three methods described here can be used to evaluate function values on a
+grid.
+
+::
+
+ >>> g = np.ogrid[0:4, 0:6]
+ >>> zg = np.sqrt(g[0]**2 + g[1]**2)
+ >>> g[0].shape, g[1].shape, zg.shape
+ ((4, 1), (1, 6), (4, 6))
+ >>> m = np.mgrid[0:4, 0:6]
+ >>> zm = np.sqrt(m[0]**2 + m[1]**2)
+ >>> np.array_equal(zm, zg)
+ True