.. _quantity: Quantity ======== .. |quantity| replace:: :class:`~astropy.units.Quantity` The |quantity| object is meant to represent a value that has some unit associated with the number. Creating Quantity instances --------------------------- |quantity| objects are created through multiplication or division with :class:`~astropy.units.Unit` objects. For example, to create a |quantity| to represent 15 m/s: >>> import astropy.units as u >>> 15 * u.m / u.s .. note:: |quantity| objects are converted to float by default. As another example: >>> 1.25 / u.s You can also create instances using the |quantity| constructor directly, by specifying a value and unit: >>> u.Quantity(15, u.m / u.s) |quantity| objects can also be created automatically from Numpy arrays or Python sequences: >>> [1, 2, 3] * u.m >>> import numpy as np >>> np.array([1, 2, 3]) * u.m |quantity| objects can also be created from sequences of |quantity| objects, as long as all of their units are equivalent, and will automatically convert to Numpy arrays: >>> qlst = [60 * u.s, 1 * u.min] >>> u.Quantity(qlst, u.minute) Finally, the current unit and value can be accessed via the `~astropy.units.quantity.Quantity.unit` and `~astropy.units.quantity.Quantity.value` attributes: >>> q = 2.5 * u.m / u.s >>> q.unit Unit("m / s") >>> q.value 2.5 Converting to different units ----------------------------- |quantity| objects can be converted to different units using the :meth:`~astropy.units.quantity.Quantity.to` method: >>> q = 2.3 * u.m / u.s >>> q.to(u.km / u.h) # doctest: +FLOAT_CMP For convenience, the `~astropy.units.quantity.Quantity.si` and `~astropy.units.quantity.Quantity.cgs` attributes can be used to convert the |quantity| to base S.I. or c.g.s units: >>> q = 2.4 * u.m / u.s >>> q.si # doctest: +FLOAT_CMP >>> q.cgs .. _plotting-quantities: Plotting quantities ------------------- |quantity| objects can be conveniently plotted using matplotlib. This feature needs to be explicitly turned on: .. doctest-requires:: matplotlib >>> from astropy.visualization import quantity_support >>> quantity_support() # doctest: +IGNORE_OUTPUT Then |quantity| objects can be passed to matplotlib plotting functions. The axis labels are automatically labeled with the unit of the quantity: .. doctest-requires:: matplotlib >>> from matplotlib import pyplot as plt >>> plt.figure(figsize=(5,3)) <...> >>> plt.plot([1, 2, 3] * u.m) [...] .. plot:: from astropy import units as u from astropy.visualization import quantity_support quantity_support() from matplotlib import pyplot as plt plt.figure(figsize=(5,3)) plt.plot([1, 2, 3] * u.m) Quantities are automatically converted to the first unit set on a particular axis, so in the following, the y-axis remains in ``m`` even though the second line is given in ``cm``: .. doctest-requires:: matplotlib >>> plt.plot([1, 2, 3] * u.cm) [...] .. plot:: from astropy import units as u from astropy.visualization import quantity_support quantity_support() from matplotlib import pyplot as plt plt.figure(figsize=(5,3)) plt.plot([1, 2, 3] * u.m) plt.plot([1, 2, 3] * u.cm) Plotting a quantity with an incompatible unit will raise an exception: .. doctest-requires:: matplotlib >>> plt.plot([1, 2, 3] * u.kg) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... UnitConversionError: 'kg' (mass) and 'm' (length) are not convertible >>> plt.clf() To make sure unit support is turned off afterward, you can use `~astropy.visualization.quantity_support` with a ``with`` statement: .. doctest-requires:: matplotlib >>> from astropy.visualization import quantity_support >>> from matplotlib import pyplot as plt >>> with quantity_support(): ... plt.figure(figsize=(5,3)) ... plt.plot([1, 2, 3] * u.m) <...> [...] .. plot:: from astropy import units as u from astropy.visualization import quantity_support from matplotlib import pyplot as plt with quantity_support(): plt.figure(figsize=(5,3)) plt.plot([1, 2, 3] * u.m) Arithmetic ---------- Addition and Subtraction ~~~~~~~~~~~~~~~~~~~~~~~~ Addition or subtraction between |quantity| objects is supported when their units are equivalent. When the units are equal, the resulting object has the same unit: >>> 11 * u.s + 30 * u.s >>> 30 * u.s - 11 * u.s If the units are equivalent, but not equal (e.g. kilometer and meter), the resulting object **has units of the object on the left**: >>> 1100.1 * u.m + 13.5 * u.km >>> 13.5 * u.km + 1100.1 * u.m # doctest: +FLOAT_CMP >>> 1100.1 * u.m - 13.5 * u.km >>> 13.5 * u.km - 1100.1 * u.m # doctest: +FLOAT_CMP Addition and subtraction are not supported between |quantity| objects and basic numeric types: >>> 13.5 * u.km + 19.412 # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... UnitsError: Can only apply 'add' function to dimensionless quantities when other argument is not a quantity (unless the latter is all zero/infinity/nan) except for dimensionless quantities (see `Dimensionless quantities`_). Multiplication and Division ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Multiplication and division are supported between |quantity| objects with any units, and with numeric types. For these operations between objects with equivalent units, the **resulting object has composite units**: >>> 1.1 * u.m * 140.3 * u.cm # doctest: +FLOAT_CMP >>> 140.3 * u.cm * 1.1 * u.m # doctest: +FLOAT_CMP >>> 1. * u.m / (20. * u.cm) # doctest: +FLOAT_CMP >>> 20. * u.cm / (1. * u.m) For multiplication, you can change how to represent the resulting object by using the :meth:`~astropy.units.quantity.Quantity.to` method: >>> (1.1 * u.m * 140.3 * u.cm).to(u.m**2) # doctest: +FLOAT_CMP >>> (1.1 * u.m * 140.3 * u.cm).to(u.cm**2) # doctest: +FLOAT_CMP For division, if the units are equivalent, you may want to make the resulting object dimensionless by reducing the units. To do this, use the :meth:`~astropy.units.quantity.Quantity.decompose()` method: >>> (20. * u.cm / (1. * u.m)).decompose() # doctest: +FLOAT_CMP This method is also useful for more complicated arithmetic: >>> 15. * u.kg * 32. * u.cm * 15 * u.m / (11. * u.s * 1914.15 * u.ms) # doctest: +FLOAT_CMP >>> (15. * u.kg * 32. * u.cm * 15 * u.m / (11. * u.s * 1914.15 * u.ms)).decompose() # doctest: +FLOAT_CMP Numpy functions --------------- |quantity| objects are actually full Numpy arrays (the |quantity| object class inherits from and extends the ``numpy.ndarray`` class), and we have tried to ensure that most Numpy functions behave properly with quantities: >>> q = np.array([1., 2., 3., 4.]) * u.m / u.s >>> np.mean(q) >>> np.std(q) # doctest: +FLOAT_CMP including functions that only accept specific units such as angles: >>> q = 30. * u.deg >>> np.sin(q) # doctest: +FLOAT_CMP or dimensionless quantities: >>> from astropy.constants import h, k_B >>> nu = 3 * u.GHz >>> T = 30 * u.K >>> np.exp(-h * nu / (k_B * T)) # doctest: +FLOAT_CMP (see `Dimensionless quantities`_ for more details). Dimensionless quantities ------------------------ Dimensionless quantities have the characteristic that if they are added or subtracted from a Python scalar or unitless `~numpy.ndarray`, or if they are passed to a Numpy function that takes dimensionless quantities, the units are simplified so that the quantity is dimensionless and scale-free. For example: >>> 1. + 1. * u.m / u.km # doctest: +FLOAT_CMP which is different from: >>> 1. + (1. * u.m / u.km).value 2.0 In the latter case, the result is ``2.0`` because the unit of ``(1. * u.m / u.km)`` is not scale-free by default: >>> q = (1. * u.m / u.km) >>> q.unit Unit("m / km") >>> q.unit.decompose() Unit(dimensionless with a scale of 0.001) However, when combining with a non-quantity object, the unit is automatically decomposed to be scale-free, giving the expected result. This also occurs when passing dimensionless quantities to functions that take dimensionless quantities: >>> nu = 3 * u.GHz >>> T = 30 * u.K >>> np.exp(- h * nu / (k_B * T)) # doctest: +FLOAT_CMP The result is independent from the units the different quantities were specified in: >>> nu = 3.e9 * u.Hz >>> T = 30 * u.K >>> np.exp(- h * nu / (k_B * T)) # doctest: +FLOAT_CMP Converting to plain Python scalars ---------------------------------- Converting |quantity| objects does not work for non-dimensionless quantities: >>> float(3. * u.m) Traceback (most recent call last): ... TypeError: Only dimensionless scalar quantities can be converted to Python scalars Instead, only dimensionless values can be converted to plain Python scalars: >>> float(3. * u.m / (4. * u.m)) 0.75 >>> float(3. * u.km / (4. * u.m)) 750.0 >>> int(6. * u.km / (2. * u.m)) 3000 Functions Accepting Quantities ------------------------------ Validation of quantity arguments to functions can lead to many repetitions of the same checking code. A decorator is provided which verifies that certain arguments to a function are `~astropy.units.Quantity` objects and that the units are compatible with a desired unit. The decorator does not convert the unit to the desired unit, say arcseconds to degrees, it merely checks that such a conversion is possible, thus verifying that the `~astropy.units.Quantity` argument can be used in calculations. The decorator `~astropy.units.quantity_input` accepts keyword arguments to specify which arguments should be validated and what unit they are expected to be compatible with: >>> @u.quantity_input(myarg=u.deg) ... def myfunction(myarg): ... return myarg.unit >>> myfunction(100*u.arcsec) Unit("arcsec") Under Python 3 you can use the annotations syntax to provide the units: >>> @u.quantity_input # doctest: +SKIP ... def myfunction(myarg: u.arcsec): ... return myarg.unit >>> myfunction(100*u.arcsec) # doctest: +SKIP Unit("arcsec") Known issues with conversion to numpy arrays -------------------------------------------- Since |quantity| objects are Numpy arrays, we are not able to ensure that only dimensionless quantities are converted to Numpy arrays: >>> np.array([1, 2, 3] * u.m) array([ 1., 2., 3.]) Similarly, while most numpy functions work properly, a few have :ref:`known issues `, either ignoring the unit (e.g., ``np.dot``) or not reinitializing it properly (e.g., ``np.hstack``). This propagates to more complex functions such as ``np.linalg.norm`` and ``scipy.integrate.odeint``. Subclassing Quantity -------------------- To subclass |quantity|, one generally proceeds as one would when subclassing :class:`~numpy.ndarray`, i.e., one typically needs to override ``__new__`` (rather than ``__init__``) and uses the ``numpy.ndarray.__array_finalize__`` method to update attributes. For details, see the `numpy documentation on subclassing `__. For examples, one can look at |quantity| itself, where, e.g., the ``astropy.units.Quantity.__array_finalize__`` method is used to pass on the ``unit``, at :class:`~astropy.coordinates.Angle`, where strings are parsed as angles in the ``astropy.coordinates.Angle.__new__`` method and at :class:`~astropy.coordinates.Longitude`, where the ``astropy.coordinates.Longitude.__array_finalize__`` method is used to pass on the angle at which longitudes wrap. Another method that is meant to be overridden by subclasses, one specific to |quantity|, is ``astropy.units.Quantity.__quantity_subclass__``. This is called to decide which type of subclass to return, based on the unit of the quantity that is to be created. It is used, e.g., in :class:`~astropy.coordinates.Angle` to return a |quantity| if a calculation returns a unit other than an angular one.