Uses the geNorm algorithm to determine the most stably expressed
    genes from amongst ref_targets in your sample.

    See Vandesompele et al.'s 2002 Genome Biology paper for information about
    the algorithm: http://dx.doi.org/10.1186/gb-2002-3-7-research0034

    :param DataFrame sample_frame: A sample data frame.
    :param iterable ref_targets: A sequence of targets from the Target column
        of sample_frame to consider for ranking.
    :param string ref_sample: The name of a sample from the Sample
        column of sample_frame. It doesn't really matter what it is but it
        should exist for every target.
    :return: a sorted DataFrame with two columns, 'Target' and 'M' (the
        relative stability; lower means more stable).
    :rtype: DataFrame

def rank_targets(sample_frame, ref_targets, ref_sample):
    """Uses the geNorm algorithm to determine the most stably expressed
    genes from amongst ref_targets in your sample.

    See Vandesompele et al.'s 2002 Genome Biology paper for information about
    the algorithm: http://dx.doi.org/10.1186/gb-2002-3-7-research0034

    :param DataFrame sample_frame: A sample data frame.
    :param iterable ref_targets: A sequence of targets from the Target column
        of sample_frame to consider for ranking.
    :param string ref_sample: The name of a sample from the Sample
        column of sample_frame. It doesn't really matter what it is but it
        should exist for every target.
    :return: a sorted DataFrame with two columns, 'Target' and 'M' (the
        relative stability; lower means more stable).
    :rtype: DataFrame
    """
    table = collect_expression(sample_frame, ref_targets, ref_sample)
    all_samples = sample_frame['Sample'].unique()
    t = table.groupby(['Sample', 'Target']).mean()
    logt = log2(t)
    ref_targets = set(ref_targets)

    worst = []
    worst_m = []
    while len(ref_targets) - len(worst) > 1:
        M = []
        for test_target in ref_targets:
            if test_target in worst: continue
            Vs = []
            for ref_target in ref_targets:
                if ref_target == test_target or ref_target in worst: continue
                A = logt.ix[zip(all_samples, repeat(test_target)), ref_target]
                Vs.append(A.std())
            M.append( (sum(Vs)/(len(ref_targets)-len(worst)-1), test_target) )
        worst.append(max(M)[1])
        worst_m.append(max(M)[0])
    best = ref_targets - set(worst)
    worst.reverse()
    worst_m.reverse()
    worst_m = [worst_m[0]] + worst_m
    return pd.DataFrame({'Target': list(best) + worst, 'M': worst_m}, columns=['Target', 'M'])