pytransit.stat_tools — TRANSIT v2.3.2 documentation

# Source code for pytransit.stat_tools

    import math
    import numpy
    import scipy.stats

    def sample_trunc_norm_post(data, S, mu0, s20, k0, nu0):
        n = len(data)
        s2 = numpy.var(data,ddof=1)
        ybar = numpy.mean(data)
        kn = k0+n
        nun = nu0+n
        mun = (k0*mu0 + n*ybar)/float(kn)
        s2n = (1.0/nun) * (nu0*s20 + (n-1)*s2 + (k0*n/float(kn))*numpy.power(ybar-mu0,2))

        s2_post = 1.0/scipy.stats.gamma.rvs(nun/2.0, scale=2.0/(s2n*nun), size=S)

        # Truncated Normal since counts can't be negative
        min_mu = 0
        max_mu = 1000000
        trunc_a = (min_mu-mun)/numpy.sqrt(s2_post/float(kn))
        trunc_b = (max_mu-mun)/numpy.sqrt(s2_post/float(kn))

        mu_post = scipy.stats.truncnorm.rvs(a=trunc_a, b=trunc_b, loc=mun, scale=numpy.sqrt(s2_post/float(kn)), size=S)

        return (mu_post, s2_post)

    #

    def FWER_Bayes(X):
        ii = numpy.argsort(numpy.argsort(X))
        P_NULL = numpy.sort(X)
        W = 1 - P_NULL
        N = len(P_NULL)
        P_ALT = numpy.zeros(N)
        for i in range(N):
            P_ALT[i] = 1.0 - numpy.prod(W[:i+1])
        return P_ALT[ii]

    #

    def bFDR(X):
        N = len(X)
        ii = numpy.argsort(numpy.argsort(X))
        P_NULL = numpy.sort(X)
        P_ALT = numpy.zeros(N)
        for i in range(N):
            P_ALT[i] = numpy.mean(P_NULL[:i+1])
        return P_ALT[ii]

    #

    def HDI_from_MCMC(posterior_samples, credible_mass=0.95):
        # Credit to 'user72564'
        # https://stackoverflow.com/questions/22284502/highest-posterior-density-region-and-central-credible-region
        # Computes highest density interval from a sample of representative values,
        # estimated as the shortest credible interval
        # Takes Arguments posterior_samples (samples from posterior) and credible mass (normally .95)
        sorted_points = sorted(posterior_samples)
        ciIdxInc = numpy.ceil(credible_mass * len(sorted_points)).astype('int')
        nCIs = len(sorted_points) - ciIdxInc
        ciWidth = [0]*nCIs
        for i in range(0, nCIs):
            ciWidth[i] = sorted_points[i + ciIdxInc] - sorted_points[i]
        HDImin = sorted_points[ciWidth.index(min(ciWidth))]
        HDImax = sorted_points[ciWidth.index(min(ciWidth))+ciIdxInc]
        return(HDImin, HDImax)

    #

    def transformToRange(X, new_min, new_max, old_min=None, old_max=None):

        if old_min == None:
            old_min = min(X)
        if old_max == None:
            old_max = max(X)

        old_range = old_max - old_min

        new_range = new_max - new_min
        return [float(x - old_min) / old_range * new_range + new_min for x in X]

    #

    def fact(n):
        if n == 0: return (1)
        else: return reduce(lambda x,y: x*y, range(1,n+1))

    #

    def comb1(n,k):
        prod = 1
        for i in range(1,k+1):
            prod = prod * (n - (k-i))/float(i)
        return(prod)

    #

    def comb(n, k):
        if k < 0 or k > n:
            return 0
        if k > n - k: # take advantage of symmetry
            k = n - k
        c = 1
        for i in range(k):
            c = c * (n - (k - (i+1)))
            c = c // (i+1)
        return c

    #

    def norm(x, mu,sigma):
        """Normal distribution"""
        sigma = float(sigma)
        return(1/(sigma*(math.sqrt(2*math.pi))) * math.exp( -0.5 * math.pow( (x-mu)/sigma,2)))

    #

    def binom(k,n,p):
        """Binomial distribution. Uses Normal approximation for large 'n' """
        if n >= 100:
            return(norm(k, n*p, math.sqrt(n*p*(1-p)) ) )
        else:
            return(comb(n,k) * math.pow(p, k) * math.pow(1-p, n-k))

    #

    def binom_cdf(k,n,p):
        """CDF of the binomial distribution"""
        return(sum([binom(i,n,p) for i in range(0,k+1)]))

    #

    def binom_test(k,n,p, type="two-sided"):
        """Does a binomial test given success, trials and probability."""
        if type == "less": return(binom_cdf(k,n,p))
        elif type == "greater": return(1-binom_cdf(k-1,n,p))
        else:
            if p == 0: return(1) #return(k == 0)
            elif p == 1: return(1) #return(k == n)
            else:
                relErr = 1 + 1e-7
                d = binom(k,n,p)
                m = n * p
                if k == m: return(1)
                elif (k < m):
                    ri = range(int(math.ceil(m)), n+1)
                    y = sum([1 for j in ri if binom(j,n,p) <= d*relErr])
                    return(binom_cdf(k,n,p) + (1-binom_cdf(int(n-y),n,p)))
                else:
                    ri = range(0, int(math.floor(m)))
                    y = sum([1 for j in ri if binom(j,n,p) <= d*relErr])
                    return(binom_cdf(y-1,n,p) + (1-binom_cdf(k-1,n,p)))

    ##############################
    # Bernoulli Diff Distribution
    def dberndiff(d, peq, p01, p10):
        N = numpy.size(d)
        if N == 0:
            return 0.0
        if N == 1:
            if type(d) == type(()):
                d = d[0]
            if d == 0:
                return peq
            else:
                if d == -1:
                    return p01
                if d == 1:
                    return p10
                return 0.0
    #
        else:
            d = numpy.array(d)
            result = numpy.zeros(N)
            result[d == -1] = p01
            result[d == 0] = peq
            result[d == 1] = p10
            return result

    #

    def qberndiff(d, peq, p01, p10):
        return numpy.sum([ dberndiff(x, peq, p01, p10) for x in range(-1, d + 1) ])

    #############################
    # Binomial Diff Distribution

    def dbinomdiff(d, n, P):
        S = numpy.array(my_perm(d, n))
        return numpy.sum(multinomial(S, P))

    #

    def qbinomdiff(d, n, peq, p01, p10):
        return numpy.sum([ dbinomdiff(x, n, peq, p01, p10) for x in range(-n, d + 1) ])

    #

    def my_perm(d, n):
        S = []
        if d == 0:
            for i in range(n + 1):
                r = n - i
                if isEven(r):
                    S.append((int(r / 2.0), i, int(r / 2.0)))
    #
        if d > 0:
            for i in range(d, n + 1):
                r = n - i
                if i == d:
                    S.append((0, n - d, d))
                elif i > d:
                    r = n - (i + (i - d))
                    if 0 <= r <= n:
                        S.append((i - d, r, i))
    #
        if d < 0:
            for i in range(abs(d), n + 1):
                r = n - i
                if i == abs(d):
                    S.append((-d, n + d, 0))
                elif i > d:
                    r = n - (i + (i + d))
                    if 0 <= r <= n:
                        S.append((i, r, i + d))
    #
        return S

    #

    def multinomial(K, P):
        N = numpy.sum(K, 1)
        if K.shape == P.shape:
            return tricoeff(N, K) * numpy.prod([ numpy.power(P[i], K[i]) for i in range(len(K)) ], 1)
        else:
            return tricoeff(N, K) * numpy.prod([ numpy.power(P, K[i]) for i in range(len(K)) ], 1)

    #

    def log_fac(n):
        return numpy.sum(numpy.log(numpy.arange(2, n + 1)))

    #

    def tricoeff(N, S):
        try:
            LOG_FAC
        except NameError:
            LOG_FAC = []
            for i in range(numpy.max(N) + 1):
                LOG_FAC.append(log_fac(i))
    #
            LOG_FAC = numpy.array(LOG_FAC)
    #
        return numpy.exp(LOG_FAC[N] - (LOG_FAC[S[:, 0]] + LOG_FAC[S[:, 1]] + LOG_FAC[S[:, 2]]))

    #

    def isEven(x):
        return x % 2 == 0

    #

    def regress(X,Y):
        """Performs linear regression given two vectors, X, Y."""
        N = len(X)
        xbar = numpy.average(X)
        ybar = numpy.average(Y)
        xybar = numpy.average([X[i]*Y[i] for i in range(N)])
        x2bar = numpy.average([X[i]*X[i] for i in range(N)])
        B = (xybar - xbar*ybar)/(x2bar - xbar*xbar)
        A0 = ybar - B*xbar

        yfit = [ A0 + B *X[i] for i in range(N)]
        yres = [Y[i] - (A0 + B *X[i]) for i in range(N)]
        var = sum([math.pow(yres[i],2) for i in range(N) ])/(N-2)
        std = math.sqrt(var)

        return(B, A0, std)

    #

    def boxcoxtransform(x, lambdax):
        """
        Performs a box-cox transformation to data vector X.
        WARNING: elements of X should be all positive!
        Fixed: '>' has changed to '<'
        """
        if x <= 0:
            raise ArgumentError, "Nonpositive value(s) in X vector"
        if abs(lambdax) < 1.0e-5:
            return(math.log(x))
        else:
            return((x**lambdax - 1.0)/lambdax)
        #return math.log(x) if abs(lambdax) < 1.0e-5 else (x**lambdax - 1.0)/lambdax

    #

    def loglik(X, lambdax):
        """
        Computes the log-likelihood function for a transformed vector Xtransform.
        """
        n = len(X)
        Xtrans = [boxcoxtransform(x, lambdax) for x in X]
        meanX = sum(Xtrans) / float(n)
        S2 = (lambdax - 1.0) * sum([math.log(x) for x in X])
        S = sum([(x-meanX) **2 for x in Xtrans])
        S1= (-n/2.0)*math.log(S/n)
        return  S2+S1

    #

    def boxcoxTable(X, minlambda, maxlambda, dellambda):
        """
        Returns a table of (loglik function, lambda) pairs
        for the data.
        """
        # Create a table (lambda, loglik)
        out = []
        vallambda = minlambda
        while vallambda <= maxlambda+1.0e-5:
            llik = loglik(X, vallambda)
            out.append((llik, vallambda))
            vallambda += dellambda
        return out

    #

    def phi_coefficient(X,Y):
        """Calculates the phi-coefficient for two bool arrays"""
        N = len(X)
        assert len(X) == len(Y), "Length of arrays must be equal"
        x1y1 = sum([int(X[j]) == int(Y[j])  ==  1 for j in range(N)])
        x1y0 = sum([int(X[j]) == 1 and int(Y[j])  ==  0 for j in range(N)])
        x0y1 = sum([int(X[j]) == 0 and int(Y[j])  ==  1 for j in range(N)])
        x0y0 = sum([int(X[j]) == int(Y[j])  ==  0 for j in range(N)])
        x1 = x1y1 + x1y0
        x0 = x0y1 + x0y0
        y1 = x1y1 + x0y1
        y0 = x1y0 + x0y0
        phi_coeff = (x1y1*x0y0 - x1y0*x0y1)/math.sqrt(x1*x0*y1*y0)
        return phi_coeff

    #

    def BH_fdr_correction(X):
        """Adjusts p-values using the Benjamini Hochberg procedure"""
        n = len(X)
        qvalues = numpy.zeros(n)
        pvalues = numpy.array(X)
        pvalues.sort()
        pvalues = pvalues[::-1]

        for i in xrange(n):
            rank = n - i
            qvalues[i] = n/float(rank) * pvalues[i]

        for i in xrange(0, n-1):
            if qvalues[i] < qvalues[i+1]:
                qvalues[i+1] = qvalues[i]

        p2qval = dict([(p,q) for (p,q) in zip(pvalues,qvalues)])
        return numpy.array([p2qval[p] for p in X])

    #

    def bayesian_ess_thresholds(Z_raw, ALPHA=0.05):
        """Returns Essentiality Thresholds using a BH-like procedure"""
        Z = numpy.sort(Z_raw)[::-1]
        W = 1 - Z
        N = len(Z)

        ess_threshold = 1.00
        INDEX = range(3, N+1)
        count = 0
        for i in INDEX:
            count +=1
            wi = 1 - Z[i-1]
            ai_n = (ALPHA*i)/N
            mean_wi = numpy.average(W[0:i-2])
            delta_w = wi - mean_wi
            #if count < 30: print i, wi, ai_n, delta_w
            if delta_w > ai_n:
                ess_threshold = Z[i-1]
                #print "i", i
                break

        noness_threshold = 0.00
        count = 0
        INDEX = range(0, N+1)
        INDEX.sort(reverse=True)
        for i in INDEX:
            wi = Z[N-i+1]
            ai_n = (ALPHA*i)/N
            mean_wi = numpy.average(Z[N-i+1:])
            delta_w = Z[N-i+1] - mean_wi
            count +=1
            #print count
            #if count < 20:
            #   print i, wi, ai_n, mean_wi, delta_w, N-i+1, N-1, W[N-i-1], W[i-1]

            if ai_n > delta_w:
            #   print i, wi, ai_n, mean_wi, delta_w, N-i+1, N-1, W[N-i-1], W[i-1]
                break
            noness_threshold = Z[N-i]

        return(ess_threshold, noness_threshold)

    #

    def tricube(X):
        #TODO: Write docstring
        result = numpy.zeros(len(X))
        ii = numpy.logical_and(X >= -1, X <= 1)
        result[ii] = numpy.power(1 - numpy.power(numpy.abs(X[ii]), 3), 3)
        return result

    #

    def loess(X, Y, h=10000):
        #TODO: Write docstring
        smoothed = numpy.zeros(len(Y))
        for i,x in enumerate(X):
            W = tricube((X-x)/float(h))
            sW = numpy.sum(W)
            wsX = numpy.sum(W*X)
            wsY = numpy.sum(W*Y)
            wsXY = numpy.sum(W*X*Y)
            sXX = numpy.sum(X*X)
            B = (sW * wsXY - wsX * wsY)/(sW * sXX - numpy.power(wsX,2))
            A = (wsY - B*wsX) / sW
            smoothed[i] = B*x + A
        return smoothed

    #

    def loess_correction(X, Y, h=10000, window=100):
        #TODO: Write docstring
        Y = numpy.array(Y)
        size = len(X)/window + 1
        x_w = numpy.zeros(size)
        y_w = numpy.zeros(size)
        for i in range(len(X)/window + 1):
            x_w[i] = window*i
            y_w[i] = sum(Y[window*i:window*(i+1)])

        ysmooth = loess(x_w, y_w, h)
        mline = numpy.mean(y_w)
        y_w * (ysmooth/mline)

        normalized_Y = numpy.zeros(len(Y))
        for i in range(size):
            normalized_Y[window*i:window*(i+1)] = Y[window*i:window*(i+1)] * (ysmooth[i]/mline)

        return normalized_Y

    #

    def F_mean_diff_flat(*args, **kwargs):
        A = args[0]
        B = args[1]
        return numpy.mean(B) - numpy.mean(A)

    #

    def F_sum_diff_flat(*args, **kwargs):
        A = args[0]
        B = args[1]
        return numpy.sum(B) - numpy.sum(A)

    #

    def F_mean_diff_dict(*args, **kwargs):
        D = args[0]
        data1_total = 0; data2_total = 0;
        data1_size = 0; data2_size = 0;
        for L in D:
            data1_total+= numpy.sum(D[L][0])
            data1_size+= len(D[L][0])
            data2_total+= numpy.sum(D[L][1])
            data2_size+= len(D[L][1])
        return (data2_total/float(data2_size)) - (data1_total/float(data1_size))

    #

    def F_sum_diff_dict(*args, **kwargs):
        D = args[0]
        data1_total = 0; data2_total = 0;
        for L in D:
            data1_total+= numpy.sum(D[L][0])
            data2_total+= numpy.sum(D[L][1])
        return data2_total - data1_total

    #

    def F_shuffle_flat(*args, **kwargs):
        X = args[0]
        return numpy.random.permutation(X)

    #

    def F_shuffle_dict_libraries(*args, **kwargs):
        D = args[0]
        E = {}
        for L in D:
            #print "L", L
            n1 = len(D[L][0])
            combined = numpy.append(D[L][0], D[L][1])
            #print "combined", combined
            perm = numpy.random.permutation(combined)
            #print "perm", perm
            #print "perm[:n1]", perm[:n1]
            E[L] = numpy.array([perm[:n1], perm[n1:]])
            #print "D[L]", D[L]
        return E

    #

    def resampling(data1, data2, S=10000, testFunc=F_mean_diff_flat,
                permFunc=F_shuffle_flat, adaptive=False, lib_str1="", lib_str2=""):
        """Does a permutation test on two sets of data.

        Performs the resampling / permutation test given two sets of data using a
        function defining the test statistic and a function defining how to permute
        the data.

        Args:
            ar: List or numpy array with the first set of observations.
            data2: List or numpy array with the second set of observations.
            S: Number of permutation tests (or samples) to obtain.
            testFunc: Function defining the desired test statistic. Should accept
                    two lists as arguments. Default is difference in means between
                    the observations.
            permFunc: Function defining the way to permute the data. Should accept
                    one argument, the combined set of data. Default is random
                    shuffle.
            adaptive: Cuts-off resampling early depending on significance.

        Returns:
            Tuple with described values
                - test_obs -- Test statistic of observation.
                - mean1 -- Arithmetic mean of first set of data.
                - mean2 -- Arithmetic mean of second set of data.
                - log2FC -- Normalized log2FC the means.
                - pval_ltail -- Lower tail p-value.
                - pval_utail -- Upper tail p-value.
                - pval_2tail -- Two-tailed p-value.
                - test_sample -- List of samples of the test statistic.

        :Example:
            >>> import pytransit.stat_tools as stat_tools
            >>> import numpy
            >>> X = numpy.random.random(100)
            >>> Y = numpy.random.random(100)
            >>> (test_obs, mean1, mean2, log2fc, pval_ltail, pval_utail, pval_2tail, test_sample) = stat_tools.resampling(X,Y)
            >>> pval_2tail
            0.2167
            >>> test_sample[:3]
            [0.076213992904990535, -0.0052513291091412784, -0.0038425140184765172]

        """

        # Do basic sanity checks:
        # - Check library strings match in some way
        lib_diff = set(lib_str1) ^ set(lib_str2)
        if lib_diff:
            raise ValueError("At least one library string has a letter not used by the other:\ %s" % ", ".join(lib_diff))

        # - Check input has some data
        assert len(data1) > 0, "Data1 cannot be empty"
        assert len(data2) > 0, "Data2 cannot be empty"

        count_ltail = 0
        count_utail = 0
        count_2tail = 0

        test_list = []

        # Calculate basic statistics for the input data:
        n1 = len(data1)
        n2 = len(data2)

        mean1 = 0
        if n1 > 0:
            mean1 = numpy.mean(data1)
        mean2 = 0
        if n2 > 0:
            mean2 = numpy.mean(data2)

        try:
            # Only adjust log2FC if one of the means is zero
            if mean1 > 0 and mean2 > 0:
                log2FC = math.log((mean2)/(mean1),2)
            else:
                log2FC = math.log((mean2+1.0)/(mean1+1.0),2)
        except:
            log2FC = 0.0

        # Get stats and info based on whether working with libraries or not:
        nTAs = 0
        if lib_str1:
            # Get number of TA sites implied
            nTAs = len(data1.flatten())/len(lib_str1)
            assert len(data2.flatten())/len(lib_str2) == nTAs, "Datasets do not have matching sites;\
                 check input data and library strings."
            # Get data
            perm = get_lib_data_dict(data1, lib_str1, data2, lib_str2, nTAs)
            test_obs = testFunc(perm)
        else:
            try:
                test_obs = testFunc(data1, data2)
            except Exception as e:
                print ""
                print "!"*100
                print "Error: Could not apply test function to input data!"
                print "data1", data1
                print "data2", data2
                print ""
                print "\t%s" % e
                print "!"*100
                print ""
                return None

            perm = numpy.zeros(n1+n2)
            perm[:n1] = data1
            perm[n1:] = data2

        count_ltail = 0
        count_utail = 0
        count_2tail = 0
        test_list = []
        s_performed = 0
        for s in range(S):
            if len(perm) >0:
                perm = permFunc(perm)
                if not lib_str1:
                    test_sample = testFunc(perm[:n1], perm[n1:])
                else:
                    test_sample = testFunc(perm)
            else:
                test_sample = 0

            test_list.append(test_sample)
            if test_sample <= test_obs: count_ltail+=1
            if test_sample >= test_obs: count_utail+=1
            if abs(test_sample) >= abs(test_obs): count_2tail+=1

            s_performed+=1
            if adaptive:
                if s_performed == round(S*0.01) or s_performed == round(S*0.1) or s_performed == round(S*1):
                        if count_2tail >= round(S*0.01*0.10):
                            break

        pval_ltail = count_ltail/float(s_performed)
        pval_utail = count_utail/float(s_performed)
        pval_2tail = count_2tail/float(s_performed)

        return (test_obs, mean1, mean2, log2FC, pval_ltail, pval_utail,  pval_2tail, test_list)

    #

    def cumulative_average(new_x, n, prev_avg):
        return ((new_x + (n*prev_avg))/(n+1.0), n+1)

    #

    def text_histogram(X, nBins = 20, resolution=200, obs = None):
        MIN = numpy.min(X)
        MAX = numpy.max(X)
        bin_list = numpy.linspace(MIN, MAX, nBins)
        hit_flag = "->"; empty_flag = "  "
        for b_l, b_u in zip(bin_list[:-2], bin_list[1:]):
            Z = numpy.logical_and(b_l <= X,  X < b_u)
            density = numpy.mean(Z)
            if obs != None and (b_l <= obs < b_u):
                flag = hit_flag
            else:
                flag  = empty_flag
            print "%-12f\t%s|%s" % (b_l, flag, "#"*int(resolution*density))
        Z = numpy.logical_and(bin_list[-1] <= X,  X < float("inf"))
        density = numpy.mean(Z)
        if obs != None and (bin_list[-1] <= obs < float("inf")):
            flag = hit_flag
        else:
            flag = empty_flag
        print "%-12f\t%s|%s" % (bin_list[-1], flag,  "#"*int(resolution*density))

    def parse_lib_index(nData, libstr, nTAs):
        full_index = numpy.arange(nData)
        lib_to_index = {}
        for (k,L) in enumerate(libstr):
            if L not in lib_to_index: lib_to_index[L] = []
            lib_to_index[L] += list(full_index[k*nTAs:((k+1)*nTAs)])
        for L,index in lib_to_index.items():
            lib_to_index[L] = numpy.array(index)
        return lib_to_index

    #

    def combine_lib_dicts(L1, L2):
        KEYS = L1.keys()
        DATA = {}
        for K in KEYS:
            DATA[K] = numpy.array([L1[K], L2[K]])
        return DATA

    #

    def get_lib_data_dict(data1, ctrl_lib_str, data2, exp_lib_str, nTAs):
        lib1_index_dict = parse_lib_index(len(data1), ctrl_lib_str, nTAs)
        lib2_index_dict = parse_lib_index(len(data2), exp_lib_str, nTAs)

        lib1_data_dict = dict([(L, data1[lib1_index_dict[L]]) for L in sorted(lib1_index_dict)])
        lib2_data_dict = dict([(L, data2[lib2_index_dict[L]]) for L in sorted(lib2_index_dict)])

        data_dict = combine_lib_dicts(lib1_data_dict, lib2_data_dict)
        return data_dict

    #TEST-CASES

    if __name__ == "__main__":

        """
        n = 20
        p = 0.5
        k = 14
        print ""
        print "#########################################"
        print "############ BINOM TEST #################"
        print "#########################################"
        print "Coin Tosses: %d" % n
        print "Success Prob: %3.2f" % p
        print "Observed: %d" % k

        print ""
        print "Left-Tail Test:"
        print "%d tosses, p-value = %f" % (k, binom_test(k,n,p,"less"))

        print ""
        print "Right-Tail Test:"
        print "%d tosses, p-value = %f" % (k, binom_test(k,n,p,"greater"))

        print ""
        print "Two-Sided Test:"
        print "%d tosses, p-value = %f" % (k, binom_test(k,n,p,"two-sided"))

        print ""
        print ""
        print "#########################################"
        print "############ RESAMPLING #################"
        print "#########################################"

        data1 = scipy.stats.norm.rvs(100,10, size=1000)
        data2 = scipy.stats.norm.rvs(105,10, size=1000)

        (test_obs, mean1, mean2, log2FC, pval_ltail, pval_utail,  pval_2tail, test_list) = resampling(data1, data2, S=10000)
        print "Data1:"
        text_histogram(data1, nBins = 20)
        print ""
        print "Data2:"
        text_histogram(data2, nBins = 20)
        print ""
        print "Results:", (test_obs, mean1, mean2, log2FC, pval_ltail, pval_utail,  pval_2tail)
        print ""
        print "Resampling Histogram:"
        text_histogram(test_list, nBins = 20, obs=test_obs)

        """

        ## TEST
        import pytransit.transit_tools as transit_tools
        import pytransit.tnseq_tools as tnseq_tools
        import pytransit.norm_tools as norm_tools
        import sys

        ctrldata = ["/pacific/home/mdejesus/transit/tests/GI/H37Rv_day0_rep1.wig",
                    "/pacific/home/mdejesus/transit/tests/GI/Rv2680_day0_rep1.wig",
                    "/pacific/home/mdejesus/transit/tests/GI/H37Rv_day0_rep2.wig",
                    "/pacific/home/mdejesus/transit/tests/GI/Rv2680_day0_rep2.wig"]

        expdata = ["/pacific/home/mdejesus/transit/tests/GI/H37Rv_day32_rep1.wig",
                   "/pacific/home/mdejesus/transit/tests/GI/H37Rv_day32_rep2.wig",
                   "/pacific/home/mdejesus/transit/tests/GI/H37Rv_day32_rep3.wig",
                   "/pacific/home/mdejesus/transit/tests/GI/Rv2680_day32_rep1.wig",
                   "/pacific/home/mdejesus/transit/tests/GI/Rv2680_day32_rep2.wig",
                   "/pacific/home/mdejesus/transit/tests/GI/Rv2680_day32_rep3.wig"]

        annotation = "/pacific/home/mdejesus/transit/tests/GI/H37Rv.prot_table"

        i = 202
        if len(sys.argv) > 1:
            i = int(sys.argv[1])
        DO_LIB = True
        if len(sys.argv) > 2:
            DO_LIB = bool(int(sys.argv[2]))

        if DO_LIB:
            ctrl_lib_str = "ABAB"
            exp_lib_str = "AAABBB"
        else:
            ctrl_lib_str = ""
            exp_lib_str = ""

        Kctrl = len(ctrldata)
        Kexp  = len(expdata)

        (data, position) = transit_tools.get_validated_data(ctrldata+expdata)
        (K,N) = data.shape

        (data, factors) = norm_tools.normalize_data(data, "TTR", ctrldata+expdata, annotation)

        G = tnseq_tools.Genes(ctrldata + expdata, annotation, data=data, position=position)

        gene = G[i]

        print "\n\n"
        print "#"*100
        print "#  (%s)  NEW TEST:   %s"  % (DO_LIB, gene)
        print "#"*100
        print ""

        ii = numpy.ones(gene.n) == 1

        data1 = gene.reads[:Kctrl,ii].flatten()
        data2 = gene.reads[Kctrl:,ii].flatten()

        data_dict = get_lib_data_dict(data1, ctrl_lib_str, data2, exp_lib_str, gene.n)

        if DO_LIB:
            (test_obs, mean1, mean2, log2FC, pval_ltail, pval_utail,  pval_2tail, testlist) =  resampling(data1, data2, S=10000, testFunc=F_mean_diff_dict, permFunc=F_shuffle_dict_libraries, adaptive=False, lib_str1=ctrl_lib_str, lib_str2=exp_lib_str)
        else:
            (test_obs, mean1, mean2, log2FC, pval_ltail, pval_utail,  pval_2tail, testlist) =  resampling(data1, data2, S=10000, testFunc=F_mean_diff_flat, permFunc=F_shuffle_flat, adaptive=False, lib_str1=ctrl_lib_str, lib_str2=exp_lib_str)

        print "Resampling Histogram:"
        text_histogram(testlist, nBins = 20, obs=test_obs)