def examine_relation(pk_frame, fk_frame):
    """
    Examine the cardinality of the relation between key columns
    in two DataFrames.

    Parameters
    ----------
    pk_frame : pandas.DataFrame
        Frame with primary key columns. The number of columns should
        be the same as for the `fk_frame`.
    fk_frame : pandas.DataFrame
        Frame with foreign key columns. The number of columns should
        be the same as for the `fk_frame`.

    Returns
    -------
    a tuple of two 2-tuples with the minimum and maximum cardinality
    for the left and right sides of the relation. By convention,
    `pk_frame` is placed to the left and `fk_frame` to the right.
    """

    # Check if there are relations at all
    if fk_frame.isnull().all(axis=1).all():
        left = (0, 0)
        right = (0, 0)
    else:
        # Examine the left side of the relation
        if fk_frame.notnull().any(axis=1).all():
            left = (1, 1)
        else:
            left = (0, 1)

        # Examine the right side of the relation
        merged = pk_frame.merge(
            fk_frame, how='left', left_on=list(pk_frame.columns),
            right_on=list(fk_frame.columns), indicator=True)
        if (merged._merge == 'left_only').any():
            right_min = 0
        else:
            right_min = merged.loc[
                merged._merge == 'both', list(fk_frame.columns)
            ].groupby(by=list(fk_frame.columns)).size().min()
        right_max = (
            fk_frame.dropna(how='all')
            .groupby(by=list(fk_frame.columns))
            .size().max())
        right = (right_min, right_max)

    return left, right