Вероятностная запись связи в пандах - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть два кадра данных (X & Y).Я хотел бы связать их вместе и предсказать вероятность того, что каждое потенциальное совпадение является правильным.

X = pd.DataFrame({'A': ["One", "Two", "Three"]})
Y = pd.DataFrame({'A': ["One", "To", "Free"]})

1 Ответ

0 голосов
/ 19 февраля 2019

Метод A

Я еще не полностью понял теорию, но есть подход, представленный в:

Sayers, A., Ben-Shlomo, Y., Blom, A.W. and Steele, F., 2015. Probabilistic record linkage. International journal of epidemiology, 45(3), pp.954-964.

Вот моя попытка реализовать ее вПанды:

# Probability that Matches are True Matches
m = 0.95

# Probability that non-Matches are True non-Matches
u = min(len(X), len(Y)) / (len(X) * len(Y))

# Priors
M_Pr = u
U_Pr = 1 - M_Pr
O_Pr = M_Pr / U_Pr # Prior odds of a match

# Combine the dataframes
X['key'] = 1
Y['key'] = 1
Z = pd.merge(X, Y, on='key')
Z = Z.drop('key',axis=1)
X = X.drop('key',axis=1)
Y = Y.drop('key',axis=1)

# Levenshtein distance
def Levenshtein_distance(s1, s2):
    if len(s1) > len(s2):
        s1, s2 = s2, s1
    distances = range(len(s1) + 1)
    for i2, c2 in enumerate(s2):
        distances_ = [i2+1]
        for i1, c1 in enumerate(s1):
            if c1 == c2:
                distances_.append(distances[i1])
            else:
                distances_.append(1 + min((distances[i1], distances[i1 + 1], distances_[-1])))
        distances = distances_
    return distances[-1]
L_D = np.vectorize(Levenshtein_distance, otypes=[float])
Z["D"] = L_D(Z['A_x'], Z['A_y'])

# Max string length
def Max_string_length(X, Y):
    return max(len(X), len(Y))
M_L = np.vectorize(Max_string_length, otypes=[float])
Z["L"] = M_L(Z['A_x'], Z['A_y'])

# Agreement weight
def Agreement_weight(D, L):
    return 1 - ( D / L )
A_W = np.vectorize(Agreement_weight, otypes=[float])
Z["C"] = A_W(Z['D'], Z['L'])

# Likelihood ratio
def Likelihood_ratio(C):
    return (m/u) - ((m/u) - ((1-m) / (1-u))) * (1-C)
L_R = np.vectorize(Likelihood_ratio, otypes=[float])
Z["G"] = L_R(Z['C'])

# Match weight
def Match_weight(G):
    return math.log(G) * math.log(2)
M_W = np.vectorize(Match_weight, otypes=[float])
Z["R"] = M_W(Z['G'])

# Posterior odds
def Posterior_odds(R):
    return math.exp( R / math.log(2)) * O_Pr
P_O = np.vectorize(Posterior_odds, otypes=[float])
Z["O"] = P_O(Z['R'])

# Probability
def Probability(O):
    return O / (1 + O)
Pro = np.vectorize(Probability, otypes=[float])
Z["P"] = Pro(Z['O'])

Я убедился, что это дает те же результаты, что и в статье.Вот проверка чувствительности на m, показывающая, что она не имеет большого значения: enter image description here

Метод B

Этидопущения не будут применяться ко всем приложениям, но в некоторых случаях каждая строка X должна соответствовать строке Y. В этом случае:

  • Вероятности должны составлять 1

  • Если есть много заслуживающих доверия кандидатов, с которыми можно сравниться, это должно снизить вероятность получения правильного

, тогда:

X["I"] = X.index

# Combine the dataframes
X['key'] = 1
Y['key'] = 1
Z = pd.merge(X, Y, on='key')
Z = Z.drop('key',axis=1)
X = X.drop('key',axis=1)
Y = Y.drop('key',axis=1)

# Levenshtein distance
def Levenshtein_distance(s1, s2):
    if len(s1) > len(s2):
        s1, s2 = s2, s1
    distances = range(len(s1) + 1)
    for i2, c2 in enumerate(s2):
        distances_ = [i2+1]
        for i1, c1 in enumerate(s1):
            if c1 == c2:
                distances_.append(distances[i1])
            else:
                distances_.append(1 + min((distances[i1], distances[i1 + 1], distances_[-1])))
        distances = distances_
    return distances[-1]
L_D = np.vectorize(Levenshtein_distance, otypes=[float])
Z["D"] = L_D(Z['A_x'], Z['A_y'])

# Max string length
def Max_string_length(X, Y):
    return max(len(X), len(Y))
M_L = np.vectorize(Max_string_length, otypes=[float])
Z["L"] = M_L(Z['A_x'], Z['A_y'])

# Agreement weight
def Agreement_weight(D, L):
    return 1 - ( D / L )
A_W = np.vectorize(Agreement_weight, otypes=[float])
Z["C"] = A_W(Z['D'], Z['L'])

# Normalised Agreement Weight
T = Z .groupby('I') .agg({'C' : sum})
D = pd.DataFrame(T)
D.columns = ['T']
J = Z.set_index('I').join(D)
J['P1'] = J['C'] / J['T']

Сравнение с методом A: enter image description here

Метод C

Это объединяет метод A с методом B:

# Normalised Probability
U = Z .groupby('I') .agg({'P' : sum})
E = pd.DataFrame(U)
E.columns = ['U']
K = Z.set_index('I').join(E)
K['P1'] = J['P1']
K['P2'] = K['P'] / K['U']

Мы видим, что метод B (P1) не учитывает неопределенность, тогда как метод C (P2) делает. enter image description here

...