def mann_whitney(a_distribution, b_distribution, use_continuity=True):
    """Returns (u, p_value)"""
    MINIMUM_VALUES = 20

    all_values = sorted(set(a_distribution) | set(b_distribution))

    count_so_far = 0
    a_rank_sum = 0
    b_rank_sum = 0
    a_count = 0
    b_count = 0

    variance_adjustment = 0

    for v in all_values:
        a_for_value = a_distribution.get(v, 0)
        b_for_value = b_distribution.get(v, 0)
        total_for_value = a_for_value + b_for_value
        average_rank = count_so_far + (1 + total_for_value) / 2.0

        a_rank_sum += average_rank * a_for_value
        b_rank_sum += average_rank * b_for_value
        a_count += a_for_value
        b_count += b_for_value
        count_so_far += total_for_value

        variance_adjustment += total_for_value ** 3 - total_for_value

    if a_count < MINIMUM_VALUES or b_count < MINIMUM_VALUES:
        return 0, None

    a_u = a_rank_sum - a_count * (a_count + 1) / 2.0
    b_u = b_rank_sum - b_count * (b_count + 1) / 2.0

    small_u = min(a_u, b_u)
    big_u = max(a_u, b_u)

    # These need adjusting for the huge number of ties we will have
    total_count = float(a_count + b_count)
    u_distribution_mean = a_count * b_count / 2.0
    u_distribution_sd = (
        (a_count * b_count / (total_count * (total_count - 1))) ** 0.5 *
        ((total_count ** 3 - total_count - variance_adjustment) / 12.0) ** 0.5)

    if u_distribution_sd == 0:
        return small_u, None

    if use_continuity:
        # normal approximation for prob calc with continuity correction
        z_score = abs((big_u - 0.5 - u_distribution_mean) / u_distribution_sd)
    else:
        # normal approximation for prob calc
        z_score = abs((big_u - u_distribution_mean) / u_distribution_sd)

    return small_u, 1 - zprob(z_score)
def mann_whitney ( a_distribution , b_distribution , use_continuity = True ) : MINIMUM_VALUES = 20 all_values = sorted ( set ( a_distribution ) | set ( b_distribution ) ) count_so_far = 0 a_rank_sum = 0 b_rank_sum = 0 a_count = 0 b_count = 0 variance_adjustment = 0 for v in all_values : a_for_value = a_distribution . get ( v , 0 ) b_for_value = b_distribution . get ( v , 0 ) total_for_value = a_for_value + b_for_value average_rank = count_so_far + ( 1 + total_for_value ) / 2.0 a_rank_sum += average_rank * a_for_value b_rank_sum += average_rank * b_for_value a_count += a_for_value b_count += b_for_value count_so_far += total_for_value variance_adjustment += total_for_value ** 3 - total_for_value if a_count < MINIMUM_VALUES or b_count < MINIMUM_VALUES : return 0 , None a_u = a_rank_sum - a_count * ( a_count + 1 ) / 2.0 b_u = b_rank_sum - b_count * ( b_count + 1 ) / 2.0 small_u = min ( a_u , b_u ) big_u = max ( a_u , b_u ) total_count = float ( a_count + b_count ) u_distribution_mean = a_count * b_count / 2.0 u_distribution_sd = ( ( a_count * b_count / ( total_count * ( total_count - 1 ) ) ) ** 0.5 * ( ( total_count ** 3 - total_count - variance_adjustment ) / 12.0 ) ** 0.5 ) if u_distribution_sd == 0 : return small_u , None if use_continuity : z_score = abs ( ( big_u - 0.5 - u_distribution_mean ) / u_distribution_sd ) else : z_score = abs ( ( big_u - u_distribution_mean ) / u_distribution_sd ) return small_u , 1 - zprob ( z_score )
Returns (u, p_value)