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

Defined in `awkward.operations.structure <https://github.com/scikit-hep/awkward-1.0/blob/80bbef0738a6b7928333d7c705ee1b359991de5b/src/awkward/operations/structure.py>`__ on `line 3114 <https://github.com/scikit-hep/awkward-1.0/blob/80bbef0738a6b7928333d7c705ee1b359991de5b/src/awkward/operations/structure.py#L3114>`__.

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


    :param arrays: Arrays on which to compute the
               Cartesian product.
    :type arrays: dict or iterable 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.layout.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.layout.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.layout.Content` subclass.
    :type highlevel: bool
    :param behavior: Custom :py:obj:`ak.behavior` for the output array, if
                 high-level.
    :type behavior: 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.to_list(ak.cartesian([one, two], axis=0))
    [(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.to_list(ak.cartesian([one, two], axis=0, nested=True))
    [[(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.to_list(ak.cartesian([one, two]))
    [[(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.to_list(ak.cartesian([one, two], nested=True))
    [[[(1, 'a'), (1, 'b')], [(2, 'a'), (2, 'b')], [(3, 'a'), (3, 'b')]],
     [],
     [[(4, 'd')], [(5, 'd')]],
     [[(6, 'e'), (6, 'f')]]]

These tuples are :py:obj:`ak.layout.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.to_list(ak.cartesian({"x": one, "y": two}))
    [
     [{'x': 1, 'y': 'a'},
      {'x': 1, 'y': 'b'},
      {'x': 2, 'y': 'a'},
      {'x': 2, '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.to_list(ak.cartesian([one, two, three], axis=0))
    [
     (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`` (adding 1 more dimension):

.. code-block:: python


    >>> ak.to_list(ak.cartesian([one, two, three], axis=0, nested=[0]))
    [
     [(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.to_list(ak.cartesian([one, two, three], axis=0, nested=[0, 1]))
    [
     [
      [(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')]]
    ]

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

.. code-block:: python


    >>> ak.to_list(ak.cartesian([one, two, three], axis=0, nested=[1]))
    [
     [(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. Thus, it is not possible to group by ``three``
in the example above.

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__`.

