ak.cartesian
------------

.. py:module: ak.cartesian

Defined in `awkward.operations.ak_cartesian <https://github.com/scikit-hep/awkward/blob/36da52cfa8846355c390beb6555eac1d31c27c26/src/awkward/operations/ak_cartesian.py>`__ on `line 31 <https://github.com/scikit-hep/awkward/blob/36da52cfa8846355c390beb6555eac1d31c27c26/src/awkward/operations/ak_cartesian.py#L31>`__.

.. py:function:: ak.cartesian(arrays, axis=1, *, nested=None, parameters=None, with_name=None, highlevel=True, behavior=None, attrs=None)


    :param arrays: Each value in this mapping or
               sequence can be any array-like data that :py:obj:`ak.to_layout` recognizes.
    :type arrays: mapping or sequence of arrays
    :param axis: The dimension at which this operation is applied. The
             outermost dimension is ``0``, followed by ``1``, etc., and negative
             values count backward from the innermost: ``-1`` is the innermost
             dimension, ``-2`` is the next level up, etc.
    :type axis: int
    :param nested: If None or
               False, all combinations of elements from the ``arrays`` are
               produced at the same level of nesting; if True, they are grouped
               in nested lists by combinations that share a common item from
               each of the ``arrays``; if an iterable of str or int, group common
               items for a chosen set of keys from the ``array`` dict or integer
               slots of the ``array`` iterable.
    :type nested: None, True, False, or iterable of str or int
    :param parameters: Parameters for the new
                   :py:obj:`ak.contents.RecordArray` node that is created by this operation.
    :type parameters: None or dict
    :param with_name: Assigns a ``"__record__"`` name to the new
                  :py:obj:`ak.contents.RecordArray` node that is created by this operation
                  (overriding ``parameters``, if necessary).
    :type with_name: None or str
    :param highlevel: If True, return an :py:obj:`ak.Array`; otherwise, return
                  a low-level :py:obj:`ak.contents.Content` subclass.
    :type highlevel: bool
    :param behavior: Custom :py:obj:`ak.behavior` for the output array, if
                 high-level.
    :type behavior: None or dict
    :param attrs: Custom attributes for the output array, if
              high-level.
    :type attrs: None or dict

Computes a Cartesian product (i.e. cross product) of data from a set of
``arrays``. This operation creates records (if ``arrays`` is a dict) or tuples
(if ``arrays`` is another kind of iterable) that hold the combinations
of elements, and it can introduce new levels of nesting.

As a simple example with ``axis=0``, the Cartesian product of

.. code-block:: python


    >>> one = ak.Array([1, 2, 3])
    >>> two = ak.Array(["a", "b"])

is

.. code-block:: python


    >>> ak.cartesian([one, two], axis=0).show()
    [(1, 'a'),
     (1, 'b'),
     (2, 'a'),
     (2, 'b'),
     (3, 'a'),
     (3, 'b')]

With nesting, a new level of nested lists is created to group combinations
that share the same element from ``one`` into the same list.

.. code-block:: python


    >>> ak.cartesian([one, two], axis=0, nested=True).show()
    [[(1, 'a'), (1, 'b')],
     [(2, 'a'), (2, 'b')],
     [(3, 'a'), (3, 'b')]]

The primary purpose of this function, however, is to compute a different
Cartesian product for each element of an array: in other words, ``axis=1``.
The following arrays each have four elements.

.. code-block:: python


    >>> one = ak.Array([[1, 2, 3], [], [4, 5], [6]])
    >>> two = ak.Array([["a", "b"], ["c"], ["d"], ["e", "f"]])

The default ``axis=1`` produces 6 pairs from the Cartesian product of
``[1, 2, 3]`` and ``["a", "b"]``, 0 pairs from ``[]`` and ``["c"]``, 1 pair from
``[4, 5]`` and ``["d"]``, and 1 pair from ``[6]`` and ``["e", "f"]``.

.. code-block:: python


    >>> ak.cartesian([one, two]).show()
    [[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')],
     [],
     [(4, 'd'), (5, 'd')],
     [(6, 'e'), (6, 'f')]]

The nesting depth is the same as the original arrays; with ``nested=True``,
the nesting depth is increased by 1 and tuples are grouped by their
first element.

.. code-block:: python


    >>> ak.cartesian([one, two], nested=True).show()
    [[[(1, 'a'), (1, 'b')], [(2, 'a'), (2, ...)], [(3, 'a'), (3, 'b')]],
     [],
     [[(4, 'd')], [(5, 'd')]],
     [[(6, 'e'), (6, 'f')]]]

These tuples are :py:obj:`ak.contents.RecordArray` nodes with unnamed fields. To
name the fields, we can pass ``one`` and ``two`` in a dict, rather than a list.

.. code-block:: python


    >>> ak.cartesian({"x": one, "y": two}).show()
    [[{x: 1, y: 'a'}, {x: 1, y: 'b'}, {...}, ..., {x: 3, y: 'a'}, {x: 3, y: 'b'}],
     [],
     [{x: 4, y: 'd'}, {x: 5, y: 'd'}],
     [{x: 6, y: 'e'}, {x: 6, y: 'f'}]]

With more than two elements in the Cartesian product, ``nested`` can specify
which are grouped and which are not. For example,

.. code-block:: python


    >>> one = ak.Array([1, 2, 3, 4])
    >>> two = ak.Array([1.1, 2.2, 3.3])
    >>> three = ak.Array(["a", "b"])

can be left entirely ungrouped:

.. code-block:: python


    >>> ak.cartesian([one, two, three], axis=0).show()
    [(1, 1.1, 'a'),
     (1, 1.1, 'b'),
     (1, 2.2, 'a'),
     (1, 2.2, 'b'),
     (1, 3.3, 'a'),
     (1, 3.3, 'b'),
     (2, 1.1, 'a'),
     (2, 1.1, 'b'),
     (2, 2.2, 'a'),
     (2, 2.2, 'b'),
     ...,
     (3, 2.2, 'b'),
     (3, 3.3, 'a'),
     (3, 3.3, 'b'),
     (4, 1.1, 'a'),
     (4, 1.1, 'b'),
     (4, 2.2, 'a'),
     (4, 2.2, 'b'),
     (4, 3.3, 'a'),
     (4, 3.3, 'b')]

can be grouped by ``one`` (adding 1 more dimension):

.. code-block:: python


    >>> ak.cartesian([one, two, three], axis=0, nested=[0]).show()
    [[(1, 1.1, 'a'), (1, 1.1, 'b'), (1, 2.2, 'a')],
     [(1, 2.2, 'b'), (1, 3.3, 'a'), (1, 3.3, 'b')],
     [(2, 1.1, 'a'), (2, 1.1, 'b'), (2, 2.2, 'a')],
     [(2, 2.2, 'b'), (2, 3.3, 'a'), (2, 3.3, 'b')],
     [(3, 1.1, 'a'), (3, 1.1, 'b'), (3, 2.2, 'a')],
     [(3, 2.2, 'b'), (3, 3.3, 'a'), (3, 3.3, 'b')],
     [(4, 1.1, 'a'), (4, 1.1, 'b'), (4, 2.2, 'a')],
     [(4, 2.2, 'b'), (4, 3.3, 'a'), (4, 3.3, 'b')]]

can be grouped by ``one`` and ``two`` (adding 2 more dimensions):

.. code-block:: python


    >>> ak.cartesian([one, two, three], axis=0, nested=[0, 1]).show()
    [[[(1, 1.1, 'a'), (1, 1.1, 'b')], [...], [(1, 3.3, 'a'), (1, 3.3, ...)]],
     [[(2, 1.1, 'a'), (2, 1.1, 'b')], [...], [(2, 3.3, 'a'), (2, 3.3, ...)]],
     [[(3, 1.1, 'a'), (3, 1.1, 'b')], [...], [(3, 3.3, 'a'), (3, 3.3, ...)]],
     [[(4, 1.1, 'a'), (4, 1.1, 'b')], [...], [(4, 3.3, 'a'), (4, 3.3, ...)]]]

or grouped by unique ``one``-``two`` pairs (adding 1 more dimension):

.. code-block:: python


    >>> ak.cartesian([one, two, three], axis=0, nested=[1]).show()
    [[(1, 1.1, 'a'), (1, 1.1, 'b')],
     [(1, 2.2, 'a'), (1, 2.2, 'b')],
     [(1, 3.3, 'a'), (1, 3.3, 'b')],
     [(2, 1.1, 'a'), (2, 1.1, 'b')],
     [(2, 2.2, 'a'), (2, 2.2, 'b')],
     [(2, 3.3, 'a'), (2, 3.3, 'b')],
     [(3, 1.1, 'a'), (3, 1.1, 'b')],
     [(3, 2.2, 'a'), (3, 2.2, 'b')],
     [(3, 3.3, 'a'), (3, 3.3, 'b')],
     [(4, 1.1, 'a'), (4, 1.1, 'b')],
     [(4, 2.2, 'a'), (4, 2.2, 'b')],
     [(4, 3.3, 'a'), (4, 3.3, 'b')]]

The order of the output is fixed: it is always lexicographical in the
order that the ``arrays`` are written.

To emulate an SQL or Pandas "group by" operation, put the keys that you
wish to group by *first* and use ``nested=[0]`` or ``nested=[n]`` to group by
unique n-tuples. If necessary, record keys can later be reordered with a
list of strings in :py:obj:`ak.Array.__getitem__`.

To get list index positions in the tuples/records, rather than data from
the original ``arrays``, use :py:obj:`ak.argcartesian` instead of :py:obj:`ak.cartesian`. The
:py:obj:`ak.argcartesian` form can be particularly useful as nested indexing in
:py:obj:`ak.Array.__getitem__`.