human
Complete this python program:
def cohn_numbers(df, observations, censorship):
assistant
#Here is the Python program you requested.

    """Computes the Cohn numbers for the detection limits in the dataset.
    The Cohn Numbers are:
    - :math:`A_j =` the number of uncensored obs above the jth
    threshold.
    - :math:`B_j =` the number of observations (cen & uncen) below
    the jth threshold.
    - :math:`C_j =` the number of censored observations at the jth
    threshold.
    - :math:`\mathrm{PE}_j =` the probability of exceeding the jth
    threshold
    - :math:`\mathrm{DL}_j =` the unique, sorted detection limits
    - :math:`\mathrm{DL}_{j+1} = \mathrm{DL}_j` shifted down a
    single index (row)
    Parameters
    dataframe : pandas.DataFrame
    observations : str
    Name of the column in the dataframe that contains observed
    values. Censored values should be set to the detection (upper)
    limit.
    censorship : str
    Name of the column in the dataframe that indicates that a
    observation is left-censored. (i.e., True -> censored,
    False -> uncensored)
    Returns
    cohn : pandas.DataFrame
    """
    def nuncen_above(row):
        '    A,    the    number    of    uncensored    obs    above    the    given    threshold.\n                                '
        above = (df[observations] >= row['lower_dl'])
        below = (df[observations] < row['upper_dl'])
        detect = (df[censorship] == False)
        return df[((above & below) & detect)].shape[0]
    def nobs_below(row):
        '    B,    the    number    of    observations    (cen    &    uncen)    below    the    given\n                                threshold\n                                '
        less_than = (df[observations] < row['lower_dl'])
        less_thanequal = (df[observations] <= row['lower_dl'])
        uncensored = (df[censorship] == False)
        censored = (df[censorship] == True)
        LTE_censored = df[(less_thanequal & censored)].shape[0]
        LT_uncensored = df[(less_than & uncensored)].shape[0]
        return (LTE_censored + LT_uncensored)
    def ncen_equal(row):
        '    C,    the    number    of    censored    observations    at    the    given\n                                threshold.\n                                '
        censored_index = df[censorship]
        censored_data = df[observations][censored_index]
        censored_below = (censored_data == row['lower_dl'])
        return censored_below.sum()
    def set_upper_limit(cohn):
        '    Sets    the    upper_dl    DL    for    each    row    of    the    Cohn    dataframe.    '
        if (cohn.shape[0] > 1):
            return cohn['lower_dl'].shift((-1)).fillna(value=numpy.inf)
        else:
            return [numpy.inf]
    def compute_PE(A, B):
        '    Computes    the    probability    of    excedance    for    each    row    of    the\n                                Cohn    dataframe.    '
        N = len(A)
        PE = numpy.empty(N, dtype='float64')
        PE[(-1)] = 0.0
        for j in range((N - 2), (-1), (-1)):
            PE[j] = (PE[(j + 1)] + (((1 - PE[(j + 1)]) * A[j]) / (A[j] + B[j])))
        return PE
   ...