a
    ߙfb                  	   @   s  d Z ddlmZ ddlZddlZddlmZmZ ddlmZm	Z	 ddl
ZddlmZ ddlmZ dd	lmZmZmZmZmZ dd
lmZ ddlmZ ddlmZmZ g dZdgdgdZdDddZdd Z dd Z!dEddZ"dFddZ#dGdddddgddd d!d"Z$dHd#d$Z%dId&d'Z&dJd(d)Z'dKd*d+Z(dLd.d/Z)dMd0d1Z*d2d3 Z+d4d5 Z,d6d7 Z-d8d9 Z.dddddgdddddf	d:d;Z/d<d= Z0d>d? Z1dNd@dAZ2dOdBdCZ3dS )PzU
High-level table operations:

- join()
- setdiff()
- hstack()
- vstack()
- dstack()
    )deepcopyN)OrderedDictCounter)MappingSequence)metadata)Masked   )TableQTableRowColumnMaskedColumn)Quantity)	_np_utils)fix_column_nameTableMergeError)joinsetdiffhstackvstackuniquejoin_skycoordjoin_distanceZscipy)r   r   warnc                 C   sB   t |d j}|dd  D ]}tj||j|d}q| j| d S )Nr   r	   metadata_conflicts)r   metar   mergeupdate)outtablesr   Zout_metatable r#   7lib/python3.9/site-packages/astropy/table/operations.py_merge_table_meta"   s    r%   c                 C   s   t | ts| g} t| dkr$tdt| D ]\}}t |tr@q,t |trXt|| |< q,t |trrt|g| |< q,zt|g| |< W q, tt	fy } zt	d| d|W Y d}~q,d}~0 0 q,| S )zl
    Check that tables is a Table or sequence of Tables.  Returns the
    corresponding list of Tables.
    r   zno values provided to stack.zCannot convert z to table column.N)

isinstancer   len
ValueError	enumerater
   r   r   r   	TypeError)r!   iivalerrr#   r#   r$   _get_list_of_tables)   s     



*r.   c                    s`   | d j  | dd D ]}t|j  r|j  qt fdd| D r\tddd | D  S )	a  
    From a list of input objects ``objs`` get merged output object class.

    This is just taken as the deepest subclass. This doesn't handle complicated
    inheritance schemes, but as a special case, classes which share ``info``
    are taken to be compatible.
    r   r	   Nc                 3   s*   | ]"}t  |jp j|jju  V  qd S N)
issubclass	__class__info.0objZ	out_classr#   r$   	<genexpr>V   s   z!_get_out_class.<locals>.<genexpr>zunmergeable object classes {}c                 S   s   g | ]}|j jqS r#   )r1   __name__r3   r#   r#   r$   
<listcomp>Y       z"_get_out_class.<locals>.<listcomp>)r1   r0   anyr(   format)Zobjsr5   r#   r6   r$   _get_out_classI   s    
r=   search_around_skyc              
      s   t trVddlm} zt|W qr tyR } ztd|W Y d}~qrd}~0 0 nddlm} |srtd fdd}|S )a
  Helper function to join on SkyCoord columns using distance matching.

    This function is intended for use in ``table.join()`` to allow performing a
    table join where the key columns are both ``SkyCoord`` objects, matched by
    computing the distance between points and accepting values below
    ``distance``.

    The distance cross-matching is done using either
    `~astropy.coordinates.search_around_sky` or
    `~astropy.coordinates.search_around_3d`, depending on the value of
    ``distance_func``.  The default is ``'search_around_sky'``.

    One can also provide a function object for ``distance_func``, in which case
    it must be a function that follows the same input and output API as
    `~astropy.coordinates.search_around_sky`. In this case the function will
    be called with ``(skycoord1, skycoord2, distance)`` as arguments.

    Parameters
    ----------
    distance : `~astropy.units.Quantity` ['angle', 'length']
        Maximum distance between points to be considered a join match.
        Must have angular or distance units.
    distance_func : str or function
        Specifies the function for performing the cross-match based on
        ``distance``. If supplied as a string this specifies the name of a
        function in `astropy.coordinates`. If supplied as a function then that
        function is called directly.

    Returns
    -------
    join_func : function
        Function that accepts two ``SkyCoord`` columns (col1, col2) and returns
        the tuple (ids1, ids2) of pair-matched unique identifiers.

    Examples
    --------
    This example shows an inner join of two ``SkyCoord`` columns, taking any
    sources within 0.2 deg to be a match.  Note the new ``sc_id`` column which
    is added and provides a unique source identifier for the matches.

      >>> from astropy.coordinates import SkyCoord
      >>> import astropy.units as u
      >>> from astropy.table import Table, join_skycoord
      >>> from astropy import table

      >>> sc1 = SkyCoord([0, 1, 1.1, 2], [0, 0, 0, 0], unit='deg')
      >>> sc2 = SkyCoord([0.5, 1.05, 2.1], [0, 0, 0], unit='deg')

      >>> join_func = join_skycoord(0.2 * u.deg)
      >>> join_func(sc1, sc2)  # Associate each coordinate with unique source ID
      (array([3, 1, 1, 2]), array([4, 1, 2]))

      >>> t1 = Table([sc1], names=['sc'])
      >>> t2 = Table([sc2], names=['sc'])
      >>> t12 = table.join(t1, t2, join_funcs={'sc': join_skycoord(0.2 * u.deg)})
      >>> print(t12)  # Note new `sc_id` column with the IDs from join_func()
      sc_id   sc_1    sc_2
            deg,deg deg,deg
      ----- ------- --------
          1 1.0,0.0 1.05,0.0
          1 1.1,0.0 1.05,0.0
          2 2.0,0.0  2.1,0.0

    r   Nz7distance_func must be a function in astropy.coordinates)
isfunctionz'distance_func must be a str or functionc                    s   | | \}}}}t jt| td}t jt|td}d}t||D ]T\}	}
||	 dkrh||	 ||
< qF||
 dkr||
 ||	< qF|||	< |||
< |d7 }qF||fD ](}t |dkD ]}|||< |d7 }qq||fS )Ndtyper	   r   )npzerosr'   intzipflatnonzero)Zsc1Zsc2Zidxs1idxs2Zd2dZd3dids1ids2id_idx1idx2idsidxdistancedistance_funcr#   r$   	join_func   s"    
z join_skycoord.<locals>.join_func)	r&   strZastropy.coordinatesZcoordinatesgetattrAttributeErrorr(   inspectr?   )rP   rQ   Zcoordsr-   r?   rR   r#   rO   r$   r   ^   s    A
"&r   c              
      sn   zddl m  W n. ty> } ztd|W Y d}~n
d}~0 0 du rLi du rXi  fdd}|S )a  Helper function to join table columns using distance matching.

    This function is intended for use in ``table.join()`` to allow performing
    a table join where the key columns are matched by computing the distance
    between points and accepting values below ``distance``. This numerical
    "fuzzy" match can apply to 1-D or 2-D columns, where in the latter case
    the distance is a vector distance.

    The distance cross-matching is done using `scipy.spatial.cKDTree`. If
    necessary you can tweak the default behavior by providing ``dict`` values
    for the ``kdtree_args`` or ``query_args``.

    Parameters
    ----------
    distance : float or `~astropy.units.Quantity` ['length']
        Maximum distance between points to be considered a join match
    kdtree_args : dict, None
        Optional extra args for `~scipy.spatial.cKDTree`
    query_args : dict, None
        Optional extra args for `~scipy.spatial.cKDTree.query_ball_tree`

    Returns
    -------
    join_func : function
        Function that accepts (skycoord1, skycoord2) and returns the tuple
        (ids1, ids2) of pair-matched unique identifiers.

    Examples
    --------

      >>> from astropy.table import Table, join_distance
      >>> from astropy import table

      >>> c1 = [0, 1, 1.1, 2]
      >>> c2 = [0.5, 1.05, 2.1]

      >>> t1 = Table([c1], names=['col'])
      >>> t2 = Table([c2], names=['col'])
      >>> t12 = table.join(t1, t2, join_type='outer', join_funcs={'col': join_distance(0.2)})
      >>> print(t12)
      col_id col_1 col_2
      ------ ----- -----
           1   1.0  1.05
           1   1.1  1.05
           2   2.0   2.1
           3   0.0    --
           4    --   0.5

    r   )cKDTreez(scipy is required to use join_distance()Nc                    s  | j dks|j dkrtdttrF| j} |j}j}nt| } t|}}| j dkrt| j	d | _	|j dkr|j	d |_	 | fi } |fi }|j
|fd|i}tjt| td}tjt|td}d}t|D ]b\}	}
|
D ]T}||	 dkr||	 ||< q|| dkr8|| ||	< q|||	< |||< |d7 }qq||fD ],}t|dkD ]}|||< |d7 }qnq\||fS )N   z4columns for isclose_join must be 1- or 2-dimensionalr	   r	   rr@   r   )ndimr(   r&   r   Zto_valueZunitvaluerB   ZasarrayshapeZquery_ball_treerC   r'   rD   r)   rF   )Zcol1Zcol2ZdistZkd1Zkd2ZnearsrH   rI   rJ   rK   rG   rL   rM   rN   rW   rP   kdtree_args
query_argsr#   r$   rR     sB    




z join_distance.<locals>.join_func)Zscipy.spatialrW   ImportError)rP   r_   r`   excrR   r#   r^   r$   r      s    2 Ar   inner{col_name}_{table_name}12)	keys_left
keys_rightuniq_col_nametable_namesr   
join_funcsc                C   s^   t | tst| } t |ts$t|}t }
t| ||||||
||	||d}t|| |g|d |S )a  
    Perform a join of the left table with the right table on specified keys.

    Parameters
    ----------
    left : `~astropy.table.Table`-like object
        Left side table in the join. If not a Table, will call ``Table(left)``
    right : `~astropy.table.Table`-like object
        Right side table in the join. If not a Table, will call ``Table(right)``
    keys : str or list of str
        Name(s) of column(s) used to match rows of left and right tables.
        Default is to use all columns which are common to both tables.
    join_type : str
        Join type ('inner' | 'outer' | 'left' | 'right' | 'cartesian'), default is 'inner'
    keys_left : str or list of str or list of column-like, optional
        Left column(s) used to match rows instead of ``keys`` arg. This can be
        be a single left table column name or list of column names, or a list of
        column-like values with the same lengths as the left table.
    keys_right : str or list of str or list of column-like, optional
        Same as ``keys_left``, but for the right side of the join.
    uniq_col_name : str or None
        String generate a unique output column name in case of a conflict.
        The default is '{col_name}_{table_name}'.
    table_names : list of str or None
        Two-element list of table names used when generating unique output
        column names.  The default is ['1', '2'].
    metadata_conflicts : str
        How to proceed with metadata conflicts. This should be one of:
            * ``'silent'``: silently pick the last conflicting meta-data value
            * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default)
            * ``'error'``: raise an exception.
    join_funcs : dict, None
        Dict of functions to use for matching the corresponding key column(s).
        See `~astropy.table.join_skycoord` for an example and details.

    Returns
    -------
    joined_table : `~astropy.table.Table` object
        New table containing the result of the join operation.
    )rg   rh   r   )r&   r
   r   _joinr%   )leftrightkeys	join_typerg   rh   ri   rj   r   rk   col_name_mapr    r#   r#   r$   r   S  s    /


r   c                 C   s   |du r| j }| df|dffD ]2\}}t||j }t|dkrtd||q| jdd}i |_|| t	t| |d< |jdd}i |_|| tj
t|tjd	|d
< t||d|dd}t|d
 dr|d
 j}	|d |	 }
| |
 }n| g  }|S )a  
    Take a set difference of table rows.

    The row set difference will contain all rows in ``table1`` that are not
    present in ``table2``. If the keys parameter is not defined, all columns in
    ``table1`` will be included in the output table.

    Parameters
    ----------
    table1 : `~astropy.table.Table`
        ``table1`` is on the left side of the set difference.
    table2 : `~astropy.table.Table`
        ``table2`` is on the right side of the set difference.
    keys : str or list of str
        Name(s) of column(s) used to match rows of left and right tables.
        Default is to use all columns in ``table1``.

    Returns
    -------
    diff_table : `~astropy.table.Table`
        New table containing the set difference between tables. If the set
        difference is none, an empty table will be returned.

    Examples
    --------
    To get a set difference between two tables::

      >>> from astropy.table import setdiff, Table
      >>> t1 = Table({'a': [1, 4, 9], 'b': ['c', 'd', 'f']}, names=('a', 'b'))
      >>> t2 = Table({'a': [1, 5, 9], 'b': ['c', 'b', 'f']}, names=('a', 'b'))
      >>> print(t1)
       a   b
      --- ---
        1   c
        4   d
        9   f
      >>> print(t2)
       a   b
      --- ---
        1   c
        5   b
        9   f
      >>> print(setdiff(t1, t2))
       a   b
      --- ---
        4   d

      >>> print(setdiff(t2, t1))
       a   b
      --- ---
        5   b
    Ntable1table2r   zAThe {} columns are missing from {}, cannot take a set difference.FZ	copy_dataZ
__index1__r@   Z
__index2__rm   silent)rp   ro   r   mask)colnamesrB   Z	setdiff1dr'   r(   r<   copyr   Zkeep_columnsarangerC   uint8rl   hasattrrv   )rr   rs   ro   ZtblZtbl_strZ	diff_keyst1t2Zt12diffrN   Zt12_diffr#   r#   r$   r     s2    5




r   outerc           
   	   C   s  t |d t| } t| dkr&| d S tdd | D }t|dkrLtd| }t| ||}|j D ]\}}|| }t| |f|j	dd  }z t| |f|j	dd  |_	W n t
y   ||}Y n0 tt|j	}	ddg|	dd< |jj|||	d	d
 qj|S )a  
    Stack columns within tables depth-wise

    A ``join_type`` of 'exact' means that the tables must all have exactly
    the same column names (though the order can vary).  If ``join_type``
    is 'inner' then the intersection of common columns will be the output.
    A value of 'outer' (default) means the output will have the union of
    all columns, with table values being masked where no common values are
    available.

    Parameters
    ----------
    tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof
        Table(s) to stack along depth-wise with the current table
        Table columns should have same shape and name for depth-wise stacking
    join_type : str
        Join type ('inner' | 'exact' | 'outer'), default is 'outer'
    metadata_conflicts : str
        How to proceed with metadata conflicts. This should be one of:
            * ``'silent'``: silently pick the last conflicting meta-data value
            * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default)
            * ``'error'``: raise an exception.

    Returns
    -------
    stacked_table : `~astropy.table.Table` object
        New table containing the stacked data from the input tables.

    Examples
    --------
    To stack two tables along rows do::

      >>> from astropy.table import vstack, Table
      >>> t1 = Table({'a': [1, 2], 'b': [3, 4]}, names=('a', 'b'))
      >>> t2 = Table({'a': [5, 6], 'b': [7, 8]}, names=('a', 'b'))
      >>> print(t1)
       a   b
      --- ---
        1   3
        2   4
      >>> print(t2)
       a   b
      --- ---
        5   7
        6   8
      >>> print(dstack([t1, t2]))
      a [2]  b [2]
      ------ ------
      1 .. 5 3 .. 7
      2 .. 6 4 .. 8
    dstackr	   r   c                 s   s   | ]}t |V  qd S r/   r'   )r4   r"   r#   r#   r$   r7   ,  r:   zdstack.<locals>.<genexpr>z'Table lengths must all match for dstackNrX   T)Z	validated)_check_join_typer.   r'   setr(   popr   columnsitemsr]   rU   ZreshaperB   ry   __setitem__Z	transpose)
r!   rp   r   n_rowsZn_rowr    namecolZ	new_shapeZaxesr#   r#   r$   r     s(    4
 r   c                 C   sL   t |d t| } t| dkr&| d S t }t| |||}t|| |d |S )a  
    Stack tables vertically (along rows)

    A ``join_type`` of 'exact' means that the tables must all have exactly
    the same column names (though the order can vary).  If ``join_type``
    is 'inner' then the intersection of common columns will be the output.
    A value of 'outer' (default) means the output will have the union of
    all columns, with table values being masked where no common values are
    available.

    Parameters
    ----------
    tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof
        Table(s) to stack along rows (vertically) with the current table
    join_type : str
        Join type ('inner' | 'exact' | 'outer'), default is 'outer'
    metadata_conflicts : str
        How to proceed with metadata conflicts. This should be one of:
            * ``'silent'``: silently pick the last conflicting meta-data value
            * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default)
            * ``'error'``: raise an exception.

    Returns
    -------
    stacked_table : `~astropy.table.Table` object
        New table containing the stacked data from the input tables.

    Examples
    --------
    To stack two tables along rows do::

      >>> from astropy.table import vstack, Table
      >>> t1 = Table({'a': [1, 2], 'b': [3, 4]}, names=('a', 'b'))
      >>> t2 = Table({'a': [5, 6], 'b': [7, 8]}, names=('a', 'b'))
      >>> print(t1)
       a   b
      --- ---
        1   3
        2   4
      >>> print(t2)
       a   b
      --- ---
        5   7
        6   8
      >>> print(vstack([t1, t2]))
       a   b
      --- ---
        1   3
        2   4
        5   7
        6   8
    r   r	   r   r   )r   r.   r'   r   _vstackr%   )r!   rp   r   rq   r    r#   r#   r$   r   O  s    5
r   c                 C   sN   t |d t| } t| dkr&| d S t }t| ||||}t|| |d |S )a3  
    Stack tables along columns (horizontally)

    A ``join_type`` of 'exact' means that the tables must all
    have exactly the same number of rows.  If ``join_type`` is 'inner' then
    the intersection of rows will be the output.  A value of 'outer' (default)
    means the output will have the union of all rows, with table values being
    masked where no common values are available.

    Parameters
    ----------
    tables : `~astropy.table.Table` or `~astropy.table.Row` or list thereof
        Tables to stack along columns (horizontally) with the current table
    join_type : str
        Join type ('inner' | 'exact' | 'outer'), default is 'outer'
    uniq_col_name : str or None
        String generate a unique output column name in case of a conflict.
        The default is '{col_name}_{table_name}'.
    table_names : list of str or None
        Two-element list of table names used when generating unique output
        column names.  The default is ['1', '2', ..].
    metadata_conflicts : str
        How to proceed with metadata conflicts. This should be one of:
            * ``'silent'``: silently pick the last conflicting meta-data value
            * ``'warn'``: pick the last conflicting meta-data value,
              but emit a warning (default)
            * ``'error'``: raise an exception.

    Returns
    -------
    stacked_table : `~astropy.table.Table` object
        New table containing the stacked data from the input tables.

    See Also
    --------
    Table.add_columns, Table.replace_column, Table.update

    Examples
    --------
    To stack two tables horizontally (along columns) do::

      >>> from astropy.table import Table, hstack
      >>> t1 = Table({'a': [1, 2], 'b': [3, 4]}, names=('a', 'b'))
      >>> t2 = Table({'c': [5, 6], 'd': [7, 8]}, names=('c', 'd'))
      >>> print(t1)
       a   b
      --- ---
        1   3
        2   4
      >>> print(t2)
       c   d
      --- ---
        5   7
        6   8
      >>> print(hstack([t1, t2]))
       a   b   c   d
      --- --- --- ---
        1   3   5   7
        2   4   6   8
    r   r	   r   r   )r   r.   r'   r   _hstackr%   )r!   rp   ri   rj   r   rq   r    r#   r#   r$   r     s    ?

r   Ffirstc                 C   s  |dvrt dt|tr |g}|du r0| j}ntt|t|krLt d|dd D ]@}| | }t|drXt|j	rX|st d
||||= qXt|dkrt d| |}|jj}|d	kr|dd
 }n4|dkr|dd d }n|dd
 t|dk }|| S )ae	  
    Returns the unique rows of a table.

    Parameters
    ----------
    input_table : table-like
    keys : str or list of str
        Name(s) of column(s) used to create unique rows.
        Default is to use all columns.
    keep : {'first', 'last', 'none'}
        Whether to keep the first or last row for each set of
        duplicates. If 'none', all rows that are duplicate are
        removed, leaving only rows that are already unique in
        the input.
        Default is 'first'.
    silent : bool
        If `True`, masked value column(s) are silently removed from
        ``keys``. If `False`, an exception is raised when ``keys``
        contains masked value column(s).
        Default is `False`.

    Returns
    -------
    unique_table : `~astropy.table.Table` object
        New table containing only the unique rows of ``input_table``.

    Examples
    --------
    >>> from astropy.table import unique, Table
    >>> import numpy as np
    >>> table = Table(data=[[1,2,3,2,3,3],
    ... [2,3,4,5,4,6],
    ... [3,4,5,6,7,8]],
    ... names=['col1', 'col2', 'col3'],
    ... dtype=[np.int32, np.int32, np.int32])
    >>> table
    <Table length=6>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3
        2     3     4
        3     4     5
        2     5     6
        3     4     7
        3     6     8
    >>> unique(table, keys='col1')
    <Table length=3>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3
        2     3     4
        3     4     5
    >>> unique(table, keys=['col1'], keep='last')
    <Table length=3>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3
        2     5     6
        3     6     8
    >>> unique(table, keys=['col1', 'col2'])
    <Table length=5>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3
        2     3     4
        2     5     6
        3     4     5
        3     6     8
    >>> unique(table, keys=['col1', 'col2'], keep='none')
    <Table length=4>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3
        2     3     4
        2     5     6
        3     6     8
    >>> unique(table, keys=['col1'], keep='none')
    <Table length=1>
     col1  col2  col3
    int32 int32 int32
    ----- ----- -----
        1     2     3

    )r   lastZnonez/'keep' should be one of 'first', 'last', 'none'Nzduplicate key namesrv   z^cannot use columns with masked values as keys; remove column '{}' from keys and rerun unique()r   zRno column remained in ``keys``; unique() cannot work with masked value key columnsr   r   r	   )r(   r&   rS   rw   r'   r   r{   rB   r;   rv   r<   indexZgroup_bygroupsindicesr~   )Zinput_tablero   ru   Zkeepkeyr   Zgrouped_tabler   r#   r#   r$   r     s8    [

r   c                    s  t  fddg }|du r4dd tt D }t D ]\}}|| }|jD ]p}|v rv|vr| n@t }	|	| t	fdd|	D r|j
|d}|| | |< qRq<t|}
d	d |
 D }|rtd

|tfdd|D S )a  
    Find the column names mapping when merging the list of tables
    ``arrays``.  It is assumed that col names in ``common_names`` are to be
    merged into a single column while the rest will be uniquely represented
    in the output.  The args ``uniq_col_name`` and ``table_names`` specify
    how to rename columns in case of conflicts.

    Returns a dict mapping each output column name to the input(s).  This takes the form
    {outname : (col_name_0, col_name_1, ...), ... }.  For key columns all of input names
    will be present, while for the other non-key columns the value will be (col_name_0,
    None, ..) or (None, col_name_1, ..) etc.
    c                      s   d gt   S r/   r   r#   )arraysr#   r$   <lambda>q  r:   z"get_col_name_map.<locals>.<lambda>Nc                 S   s   g | ]}t |d  qS rY   )rS   r4   r+   r#   r#   r$   r9   u  r:   z$get_col_name_map.<locals>.<listcomp>c                 3   s   | ]} |j v V  qd S r/   rw   )r4   other)r   r#   r$   r7     r:   z#get_col_name_map.<locals>.<genexpr>)
table_nameZcol_namec                 S   s   g | ]\}}|d kr|qS rY   r#   )r4   r   countr#   r#   r$   r9     r:   zgMerging column names resulted in duplicates: {}.  Change uniq_col_name or table_names args to fix this.c                 3   s   | ]}| | fV  qd S r/   r#   r4   r   )rq   r#   r$   r7     r:   )collectionsdefaultdictranger'   r)   rw   appendlistr   r;   r<   r   r   r   r   )r   Zcommon_namesri   rj   Zcol_name_listrN   arrayr   out_nameZothersZcol_name_countZrepeated_namesr#   )r   rq   r   r$   get_col_name_mapb  s2    


r   c                 C   s   g }|  D ]\}}dd t| |D }dd |D }zt|}W n< ty~ } z$td|d |j|W Y d}~n
d}~0 0 tdd |D }	t|	d	krtd
|d|	 }
|	t
|||
f q|S )z
    Find the dtypes descrs resulting from merging the list of arrays' dtypes,
    using the column name mapping ``col_name_map``.

    Return a list of descrs for the output.
    c                 S   s    g | ]\}}|d ur|| qS r/   r#   r4   arrr   r#   r#   r$   r9     r:   zget_descrs.<locals>.<listcomp>c                 S   s   g | ]}|d ur|qS r/   r#   r   r#   r#   r$   r9     r:   ,The '{}' columns have incompatible types: {}r   Nc                 s   s   | ]}|j d d V  qdS )r	   N)r]   )r4   r   r#   r#   r$   r7     r:   zget_descrs.<locals>.<genexpr>r	   zKey columns z have different shape)r   rE   common_dtyper   r<   _incompat_typesr   r'   r   r   r   )r   rq   
out_descrsr   in_namesZin_colsnamesrA   tmeZuniq_shapesr]   r#   r#   r$   
get_descrs  s$    
r   c              
   C   sV   zt | W S  t jyP } z*td|j }|j|_||W Y d}~n
d}~0 0 dS )z
    Use numpy to find the common dtype for a list of columns.

    Only allow columns within the following fundamental numpy data types:
    np.bool_, np.object_, np.number, np.character, np.void
    z Columns have incompatible types N)r   r   MergeConflictErrorr   r   )colsr-   r   r#   r#   r$   r     s    r   c                 C   s  d}g }g }i }i }| D ]}|| j  }	|| j  }
t|	t|
krPtdt|	|
D ]\}}|jdd  }||jdd  krtd|dkrtd|dt|}|| |||< |||< t	||g}|||f |d7 }qZqt|}t
j|t| |d}|D ].}|| || d |< || || |d < q|j|d	}|| }t
d
g|dd  |d d kd
gf}t
|}||fS )Nr   zmismatch in sort cols lengthsr	   z.mismatch in shape of left vs. right sort arrayr#   zsort key column z must be 1-dr@   )orderTr   )r2   Zget_sortable_arraysr'   RuntimeErrorrE   r]   r(   rS   r   r   rB   emptyZargsortZconcatenaterF   )ro   rm   rn   r+   Zsort_keys_dtypesZ	sort_keysZ	sort_leftZ
sort_rightr   Zleft_sort_colsZright_sort_colsZleft_sort_colZright_sort_colr]   Zsort_keyZ	dtype_strlen_leftZsortable_tableidx_sortZsorted_tableZdiffsidxsr#   r#   r$   _get_join_sort_idxs  sB    
(
r   c                    s   | j dd} |j dd}| D ]\}||  | \}}d   | jv sZ |jv rl dd d  qFt fdd|D }| j|d d	 |j|d d	 q | ||fS )
zApply join_funcs
    Frt   Z_idNc                 3   s   | ]}|kr n|V  qd S r/   r#   )r4   Zorig_keyZid_keyr   r#   r$   r7     r:   z$_apply_join_funcs.<locals>.<genexpr>r   )r   r   )rx   r   r   tupleZ
add_column)rm   rn   ro   rk   rR   rH   rI   r#   r   r$   _apply_join_funcs  s    r   c           +         s  |}d}|dvrt d||dkrx r2t d|r>t d| jdd} jddtd	| |< td	|< |f |	d
us|
d
ur| }}t|  |	|
|\}   d
u rtfdd| jD  t d	krt	dnt
 tr f | dfdffD ]b\}} D ]T}||jvr*t	d||t|| dr
t|| jr
t	d||q
q|d
urt fdd|D st d|  d  t|  |\}  t| t }}|d	ks|d	krt dzt | \}}W n ty   tdY n0 |	d
us$|
d
ur0g  |} |t| g ||}t| g|}d	ddddd| }t||||\}}}}}}t| g }|D ]\}} }!||krq|| \}"}#|"rD|#rD| |" |# g}$t|$}%t|%jdstd|%j|%j|$|||||< t|| |" ||# ||| d
d
< qn@|"r`|"| ||f\}}&}'}(n$|#r||#||f\}}&}'}(nt	d|&| |' })|rht|(rht
|)trt
|)t s|j |)dd})t
|)t!rt
|)t"st"|)dd})|)j#d	 fdt|)j#d   |(_#t$|(|)j#}(z|)jj%|)|(< W n< t&yf }* z"td ||)j'j|*W Y d
}*~*n
d
}*~*0 0 |)||< qt
|t(r|)| |S )!aM  
    Perform a join of the left and right Tables on specified keys.

    Parameters
    ----------
    left : Table
        Left side table in the join
    right : Table
        Right side table in the join
    keys : str or list of str
        Name(s) of column(s) used to match rows of left and right tables.
        Default is to use all columns which are common to both tables.
    join_type : str
        Join type ('inner' | 'outer' | 'left' | 'right' | 'cartesian'), default is 'inner'
    uniq_col_name : str or None
        String generate a unique output column name in case of a conflict.
        The default is '{col_name}_{table_name}'.
    table_names : list of str or None
        Two-element list of table names used when generating unique output
        column names.  The default is ['1', '2'].
    col_name_map : empty dict or None
        If passed as a dict then it will be updated in-place with the
        mapping of output to input column names.
    metadata_conflicts : str
        How to proceed with metadata conflicts. This should be one of:
            * ``'silent'``: silently pick the last conflicting meta-data value
            * ``'warn'``: pick the last conflicting meta-data value, but emit a warning (default)
            * ``'error'``: raise an exception.
    join_funcs : dict, None
        Dict of functions to use for matching the corresponding key column(s).
        See `~astropy.table.join_skycoord` for an example and details.

    Returns
    -------
    joined_table : `~astropy.table.Table` object
        New table containing the result of the join operation.
    Z#__table_cartesian_join_temp_index__)rc   r   rm   rn   	cartesianzjThe 'join_type' argument should be in 'inner', 'outer', 'left', 'right', or 'cartesian' (got '{}' instead)r   z'cannot supply keys for a cartesian joinz-cannot supply join_funcs for a cartesian joinFrt   r   Nc                 3   s   | ]}| j v r|V  qd S r/   r   r   )rn   r#   r$   r7   r  r:   z_join.<locals>.<genexpr>z/No keys in common between left and right tablesZLeftZRightz&{} table does not have key column {!r}rv   z%{} key column {!r} has missing valuesc                 3   s   | ]}| v V  qd S r/   r#   )r4   r   )ro   r#   r$   r7     r:   zjoin_funcs keys z must be a subset of join keys z5input tables for join must both have at least one rowz(one or more key columns are not sortabler	   rX      new_likez-join unavailable for mixin column type(s): {}z*Unexpected column names (maybe one is ""?)rx   rY   zMjoin requires masking column '{}' but column type {} does not support masking)*r(   r<   rx   rB   rz   _join_keys_left_rightr   rw   r'   r   r&   rS   r{   r;   rv   allro   r   r   NotImplementedErrorr*   r   r   r   Z
join_innerr=   r2   r8   r   whereZtaker   r   r   r   r]   Zbroadcast_tomask_val	Exceptionr1   r   r   )+rm   rn   ro   rp   ri   rj   rq   r   rk   rg   rh   _col_name_mapZcartesian_index_nameZ	left_origZ
right_origr   Z	arr_labelr   r   Z	len_rightr   r   r   Zint_join_typeZmaskedZn_outZleft_outZ	left_maskZ	right_outZ
right_maskr    r   rA   r]   Z	left_nameZ
right_namer   col_clsr   Z	array_outZ
array_maskr   r-   r#   )ro   rn   r$   rl      s    ,


"

 
rl   c           	      C   s   dd }|durt d|du s(|du r0t d|dur@t d||| d}|||d}t|t|krpt d	d
d tt|D }| j||dd} |j||dd}| ||fS )an  Do processing to handle keys_left / keys_right args for join.

    This takes the keys_left/right inputs and turns them into a list of left/right
    columns corresponding to those inputs (which can be column names or column
    data values). It also generates the list of fake key column names (strings
    of "1", "2", etc.) that correspond to the input keys.
    c              	   S   s   t | tr| g} g }| D ]t}t |tr`z|||  W q ty\   t| d|Y q0 qt|t|krt| d| || q|S )Nz  table does not have key column z% table has different length from key )r&   rS   r   KeyErrorr(   r'   )ro   r"   Zlabelr   r   r#   r#   r$   _keys_to_cols  s    

z,_join_keys_left_right.<locals>._keys_to_colsNz7cannot supply join_funcs arg and keys_left / keys_rightz.keys_left and keys_right must both be providedz>keys arg must be None if keys_left and keys_right are suppliedrm   rn   z3keys_left and keys_right args must have same lengthc                 S   s   g | ]
}| qS r#   r#   r   r#   r#   r$   r9     r:   z)_join_keys_left_right.<locals>.<listcomp>F)r   rx   )r(   r'   r   r1   )	rm   rn   ro   rg   rh   rk   r   Z	cols_leftZ
cols_rightr#   r#   r$   r     s    r   c                 C   sJ   t | ts6d}t | tr.|d| d| d7 }t|| dvrFtddS )zCheck join_type arg in hstack and vstack.

    This specifically checks for the common mistake of call vstack(t1, t2)
    instead of vstack([t1, t2]). The subsequent check of
    ``join_type in ('inner', ..)`` does not raise in this case.
    z `join_type` arg must be a stringz. Did you accidentally call z(t1, t2, ..) instead of z([t1, t2], ..)?)rc   exactr   z:`join_type` arg must be one of 'inner', 'exact' or 'outer'N)r&   rS   r
   r*   r(   )rp   Z	func_namemsgr#   r#   r$   r     s    


r   c                 C   sh  |}t | dkr| d S ttjdd | D  }t| |}|dkrn| D ]}tdd |D rJtdqJd	}|d
krtdd |	 D }t |dkrtddd | D }t
|}t|  }|	 D ]\}	}
dd t| |
D }t|}t|jdstd|jz|j||||	}W n< tjy^ } z td|	|j|W Y d}~n
d}~0 0 d}t|
| D ]\}}|t | }||jv r|| |||< nt|trt|ts|j|dd}t|trt|tst|dd}z|jj|||< W n< ty: } z"td|	|jj|W Y d}~n
d}~0 0 |}qn|||	< qt|trd| | |S )a  
    Stack Tables vertically (by rows)

    A ``join_type`` of 'exact' (default) means that the arrays must all
    have exactly the same column names (though the order can vary).  If
    ``join_type`` is 'inner' then the intersection of common columns will
    be the output.  A value of 'outer' means the output will have the union of
    all columns, with array values being masked where no common values are
    available.

    Parameters
    ----------
    arrays : list of Tables
        Tables to stack by rows (vertically)
    join_type : str
        Join type ('inner' | 'exact' | 'outer'), default is 'outer'
    col_name_map : empty dict or None
        If passed as a dict then it will be updated in-place with the
        mapping of output to input column names.

    Returns
    -------
    stacked_table : `~astropy.table.Table` object
        New table containing the stacked data from the input tables.
    r	   r   c                 S   s   g | ]
}|j qS r#   r   r4   r   r#   r#   r$   r9   S  r:   z_vstack.<locals>.<listcomp>r   c                 s   s   | ]}|d u V  qd S r/   r#   r4   xr#   r#   r$   r7   Z  r:   z_vstack.<locals>.<genexpr>zeInconsistent columns in input arrays (use 'inner' or 'outer' join_type to allow non-matching columns)r   rc   c                 s   s,   | ]$\}}t d d |D r||fV  qdS )c                 s   s   | ]}|d uV  qd S r/   r#   r   r#   r#   r$   r7   c  r:   z$_vstack.<locals>.<genexpr>.<genexpr>N)r   )r4   r   r   r#   r#   r$   r7   b  s   
z&Input arrays have no columns in commonc                 S   s   g | ]}t |qS r#   r   r   r#   r#   r$   r9   g  r:   c                 S   s    g | ]\}}|d ur|| qS r/   r#   r   r#   r#   r$   r9   m  r:   r   z/vstack unavailable for mixin column type(s): {}r   NFr   zOvstack requires masking column '{}' but column type {} does not support masking)!r'   r   	itertoolschainr   valuesr;   r   r   r   sumr=   rE   r{   r2   r   r<   r8   r   r   r   r   rw   r&   r   r   r   r   r   r   r1   r   r   )r   rp   rq   r   r   r   Zlensr   r    r   r   r   r   r   r-   Zidx0r   r   rK   r#   r#   r$   r   1  sp    




r   c                    s  |}|du r"dd t t| D }t| t|kr:tdt| dkrN| d S t| g ||}dd | D }|dkrtt|dkrtd	d
}|dkrt| tt|dkr fdd| D }  fdd| D }t|}t|  }|	 D ]\}	}
t
|
| |D ]\}}}|du rq||krt|}d||d< || | }t|trlt|tsl|j|dd}t|trt|tst|dd}z|jj||d< W n< ty } z"td|	|jj|W Y d}~n
d}~0 0 n|| d| }|||	< qqt|tr|| |S )a"  
    Stack tables horizontally (by columns)

    A ``join_type`` of 'exact' (default) means that the arrays must all
    have exactly the same number of rows.  If ``join_type`` is 'inner' then
    the intersection of rows will be the output.  A value of 'outer' means
    the output will have the union of all rows, with array values being
    masked where no common values are available.

    Parameters
    ----------
    arrays : List of tables
        Tables to stack by columns (horizontally)
    join_type : str
        Join type ('inner' | 'exact' | 'outer'), default is 'outer'
    uniq_col_name : str or None
        String generate a unique output column name in case of a conflict.
        The default is '{col_name}_{table_name}'.
    table_names : list of str or None
        Two-element list of table names used when generating unique output
        column names.  The default is ['1', '2', ..].

    Returns
    -------
    stacked_table : `~astropy.table.Table` object
        New table containing the stacked data from the input tables.
    Nc                 S   s   g | ]}|d   qS rY   r#   r   r#   r#   r$   r9     r:   z_hstack.<locals>.<listcomp>z1Number of arrays must match number of table_namesr	   r   c                 S   s   g | ]}t |qS r#   r   r   r#   r#   r$   r9     r:   r   ziInconsistent number of rows in input arrays (use 'inner' or 'outer' join_type to allow non-matching rows)r   rc   c                    s   g | ]}|d   qS r/   r#   r   Zmin_arr_lenr#   r$   r9     r:   c                    s   g | ]} qS r#   r#   r   r   r#   r$   r9     r:   Fr   zOhstack requires masking column '{}' but column type {} does not support masking)r   r'   r(   r   r   r   minmaxr=   r   rE   rB   ry   r&   r   r   r   r   r2   r   r   r   r<   r1   r8   r   r   )r   rp   ri   rj   rq   r   Zarr_lensr   r    r   r   r   r   Zarr_lenr   r   r-   r#   r   r$   r     s^    




r   )r   )r>   )NN)Nrc   )N)r   r   )r   r   )r   rd   Nr   )NFr   )rd   N)r   Nr   )r   rd   NN)4__doc__rx   r   r   r   r   r   collections.abcr   r   ZnumpyrB   Zastropy.utilsr   Zastropy.utils.maskedr   r"   r
   r   r   r   r   Zastropy.unitsr    r   Znp_utilsr   r   __all__Z__doctest_requires__r%   r.   r=   r   r   r   r   r   r   r   r   r   r   r   r   r   rl   r   r   r   r   r#   r#   r#   r$   <module>   sp   
 
u
 A
^
]
D   
N
   
8%=
 K4
j  