Определить способ сопоставления Stata взвешенной команды Xtile с использованием Python?

Для проекта мне нужно скопировать некоторые результаты, которые в настоящее время существуют в выходных файлах Stata (.dta) и были вычислены из более старого сценария Stata. Новая версия проекта должна быть написана на Python.

Конкретная часть, с которой у меня возникают трудности, - это сопоставление вычислений квантильной точки останова на основе взвешенной версии Stata.xtile command, Обратите внимание, что связи между точками данных не имеют значения для весов, и веса, которые я использую, получены из непрерывной величины, поэтому связи крайне маловероятны (и в моем наборе тестовых данных нет связей). Так что неправильная классификация из-за связей это не так.

Я прочиталСтатья в Википедии о взвешенных процентилях а такжеэтот кросс-валидированный пост описание альтернативного алгоритма, который должен воспроизводить квантили типа 7 R.

Я реализовал оба взвешенных алгоритма (код внизу), но я все еще не очень хорошо сопоставлялся с вычисленными квантилями в выходных данных Stata.

Кто-нибудь знает конкретный алгоритм, используемый подпрограммой Stata? Документы не описали это четко. Он говорит что-то о взятии среднего значения на плоских участках CDF, чтобы инвертировать его, но это едва ли описывает реальный алгоритм и неоднозначно говорит о том, выполняет ли он какую-либо другую интерполяцию.

Обратите внимание, чтоnumpy.percentile а такжеscipy.stats.mstats.mquantiles не принимают веса и не могут выполнять взвешенные квантили, только обычные равные весы. Суть моей проблемы заключается в необходимости использовать веса.

Примечание: я отлаживал оба метода ниже довольно часто, но не стесняйтесь предлагать ошибку в комментарии, если вы ее видите. Я проверил оба метода на меньших наборах данных, и результаты хорошие, а также соответствуют выводам R для случаев, когда я могу гарантировать, какой метод R используется. Код пока не такой элегантный и слишком много копируется между двумя типами, но все это будет исправлено позже, когда я верю, что вывод - это то, что мне нужно.

Проблема в том, что я не знаю метод Stataxtile использует, и я хочу уменьшить несоответствия между кодом ниже и Stataxtile при запуске на том же наборе данных.

Algorithms that I've tried:

import numpy as np

def mark_weighted_percentiles(a, labels, weights, type):
# a is an input array of values.
# weights is an input array of weights, so weights[i] goes with a[i]
# labels are the names you want to give to the xtiles
# type refers to which weighted algorithm. 
#      1 for wikipedia, 2 for the stackexchange post.

# The code outputs an array the same shape as 'a', but with
# labels[i] inserted into spot j if a[j] falls in x-tile i.
# The number of xtiles requested is inferred from the length of 'labels'.


# First type, "vanilla" weights from Wikipedia article.
if type == 1:

    # Sort the values and apply the same sort to the weights.
    N = len(a)
    sort_indx = np.argsort(a)
    tmp_a = a[sort_indx].copy()
    tmp_weights = weights[sort_indx].copy()

    # 'labels' stores the name of the x-tiles the user wants,
    # and it is assumed to be linearly spaced between 0 and 1
    # so 5 labels implies quintiles, for example.
    num_categories = len(labels)
    breaks = np.linspace(0, 1, num_categories+1)

    # Compute the percentile values at each explicit data point in a.
    cu_weights = np.cumsum(tmp_weights)
    p_vals = (1.0/cu_weights[-1])*(cu_weights - 0.5*tmp_weights)

    # Set up the output array.
    ret = np.repeat(0, len(a))
    if(len(a)<num_categories):
        return ret

    # Set up the array for the values at the breakpoints.
    quantiles = []


    # Find the two indices that bracket the breakpoint percentiles.
    # then do interpolation on the two a_vals for those indices, using
    # interp-weights that involve the cumulative sum of weights.
    for brk in breaks:
        if brk <= p_vals[0]: 
            i_low = 0; i_high = 0;
        elif brk >= p_vals[-1]:
            i_low = N-1; i_high = N-1;
        else:
            for ii in range(N-1):
                if (p_vals[ii] <= brk) and (brk < p_vals[ii+1]):
                    i_low  = ii
                    i_high = ii + 1       

        if i_low == i_high:
            v = tmp_a[i_low]
        else:
            # If there are two brackets, then apply the formula as per Wikipedia.
            v = tmp_a[i_low] + ((brk-p_vals[i_low])/(p_vals[i_high]-p_vals[i_low]))*(tmp_a[i_high]-tmp_a[i_low])

        # Append the result.
        quantiles.append(v)

    # Now that the weighted breakpoints are set, just categorize
    # the elements of a with logical indexing.
    for i in range(0, len(quantiles)-1):
        lower = quantiles[i]
        upper = quantiles[i+1]
        ret[ np.logical_and(a>=lower, a<upper) ] = labels[i] 

    #make sure upper and lower indices are marked
    ret[a<=quantiles[0]] = labels[0]
    ret[a>=quantiles[-1]] = labels[-1]

    return ret

# The stats.stackexchange suggestion.
elif type == 2:

    N = len(a)
    sort_indx = np.argsort(a)
    tmp_a = a[sort_indx].copy()
    tmp_weights = weights[sort_indx].copy()


    num_categories = len(labels)
    breaks = np.linspace(0, 1, num_categories+1)

    cu_weights = np.cumsum(tmp_weights)

    # Formula from stats.stackexchange.com post.
    s_vals = [0.0];
    for ii in range(1,N):
        s_vals.append( ii*tmp_weights[ii] + (N-1)*cu_weights[ii-1])
    s_vals = np.asarray(s_vals)

    # Normalized s_vals for comapring with the breakpoint.
    norm_s_vals = (1.0/s_vals[-1])*s_vals 

    # Set up the output variable.
    ret = np.repeat(0, N)
    if(N < num_categories):
        return ret

    # Set up space for the values at the breakpoints.
    quantiles = []


    # Find the two indices that bracket the breakpoint percentiles.
    # then do interpolation on the two a_vals for those indices, using
    # interp-weights that involve the cumulative sum of weights.
    for brk in breaks:
        if brk <= norm_s_vals[0]: 
            i_low = 0; i_high = 0;
        elif brk >= norm_s_vals[-1]:
            i_low = N-1; i_high = N-1;
        else:
            for ii in range(N-1):
                if (norm_s_vals[ii] <= brk) and (brk < norm_s_vals[ii+1]):
                    i_low  = ii
                    i_high = ii + 1   

        if i_low == i_high:
            v = tmp_a[i_low]
        else:
            # Interpolate as in the type 1 method, but using the s_vals instead.
            v = tmp_a[i_low] + (( (brk*s_vals[-1])-s_vals[i_low])/(s_vals[i_high]-s_vals[i_low]))*(tmp_a[i_high]-tmp_a[i_low])
        quantiles.append(v)

    # Now that the weighted breakpoints are set, just categorize
    # the elements of a as usual. 
    for i in range(0, len(quantiles)-1):
        lower = quantiles[i]
        upper = quantiles[i+1]
        ret[ np.logical_and( a >= lower, a < upper ) ] = labels[i] 

    #make sure upper and lower indices are marked
    ret[a<=quantiles[0]] = labels[0]
    ret[a>=quantiles[-1]] = labels[-1]

    return ret

Ответы на вопрос(2)

Ваш ответ на вопрос