Eseguire una funzione su ogni elemento in una colonna di elenchi di dataframe
Questo è un po 'complicato per me.
Dataframe:
parent children
0 MAX [MAX, amx, akd]
1 Sam ['Sam','sammy','samsam']
2 Larry ['lar','lair','larrylamo']
Ho una funzione in cui se passo solo una stringa, confronterà le due stringhe e stamperà un numero che descrive quanto sono vicini i caratteri (in lontananza). Simile all'equazione di Levenshtein.
Come posso eseguire questa funzione su un dataframe? Devo confrontare ogni record nella prima colonna ('genitore) con l'elenco corrispondente nella seconda colonna (' figli ')?
Attualmente, posso semplicemente eseguirlo e ottenere questi risultati:
>>> reference = 'larry'
>>> value_list = ['lar','lair','larrylamo']
>>> get_top_matches(reference,value_list)
>>> [('lar',0.91),('larrylamo',0.91),('lair',0.83)]
Sto cercando di creare una terza colonna composta da tuple per ogni riga delle corrispondenze, in questo modo:
parent children func_results
0 MAX [MAX, amx, akd] [('MAX',1.0),('amx',0.89),('akd',0.56)]
1 Sam ['Sam','sammy','samsam'] [('Sam',1.0),('sammy',0.91), ('samsam',0.88)]
2 Larry ['lar','lair','larrylamo'] [('lar',0.91),('larrylamo',0.91), ('lair',0.83)]
Penso che la funzione dovrebbe essere in grado di funzionare così com'è se potessi capire come applicarla in un ciclo for contro df.
ecco le funzioni:
import math
import re
def sort_token_alphabetically(word):
token = re.split('[,. ]', word)
sorted_token = sorted(token)
return ' '.join(sorted_token)
def get_jaro_distance(first, second, winkler=True, winkler_ajustment=True,
scaling=0.1, sort_tokens=True):
if sort_tokens:
first = sort_token_alphabetically(first)
second = sort_token_alphabetically(second)
if not first or not second:
raise JaroDistanceException(
"Cannot calculate distance from NoneType ({0}, {1})".format(
first.__class__.__name__,
second.__class__.__name__))
jaro = _score(first, second)
cl = min(len(_get_prefix(first, second)), 4)
if all([winkler, winkler_ajustment]): # 0.1 as scaling factor
return round((jaro + (scaling * cl * (1.0 - jaro))) * 100.0) / 100.0
return jaro
def _score(first, second):
shorter, longer = first.lower(), second.lower()
if len(first) > len(second):
longer, shorter = shorter, longer
m1 = _get_matching_characters(shorter, longer)
m2 = _get_matching_characters(longer, shorter)
if len(m1) == 0 or len(m2) == 0:
return 0.0
return (float(len(m1)) / len(shorter) +
float(len(m2)) / len(longer) +
float(len(m1) - _transpositions(m1, m2)) / len(m1)) / 3.0
def _get_diff_index(first, second):
if first == second:
pass
if not first or not second:
return 0
max_len = min(len(first), len(second))
for i in range(0, max_len):
if not first[i] == second[i]:
return i
return max_len
def _get_prefix(first, second):
if not first or not second:
return ""
index = _get_diff_index(first, second)
if index == -1:
return first
elif index == 0:
return ""
else:
return first[0:index]
def _get_matching_characters(first, second):
common = []
limit = math.floor(min(len(first), len(second)) / 2)
for i, l in enumerate(first):
left, right = int(max(0, i - limit)), int(
min(i + limit + 1, len(second)))
if l in second[left:right]:
common.append(l)
second = second[0:second.index(l)] + '*' + second[
second.index(l) + 1:]
return ''.join(common)
def _transpositions(first, second):
return math.floor(
len([(f, s) for f, s in zip(first, second) if not f == s]) / 2.0)
def get_top_matches(reference, value_list, max_results=None):
scores = []
if not max_results:
max_results = len(value_list)
for val in value_list:
score_sorted = get_jaro_distance(reference, val)
score_unsorted = get_jaro_distance(reference, val, sort_tokens=False)
scores.append((val, max(score_sorted, score_unsorted)))
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:max_results]
class JaroDistanceException(Exception):
def __init__(self, message):
super(Exception, self).__init__(message)
reference = 'larry'
value_list = ['lar','lair','larrylamo']
get_top_matches(reference, value_list)

Risposte
Presumo che il tuo set di dati reale abbia esattamente 2 colonne come campione. Usa agg
sull'asse = 1
df['func_results'] = df.agg(lambda x: get_top_matches(*x), axis=1)
Out[366]:
parent ... func_results
0 MAX ... [(MAX, 1.0), (amx, 0.89), (akd, 0.56)]
1 Sam ... [(Sam, 1.0), (sammy, 0.87), (samsam, 0.83)]
2 Larry ... [(lar, 0.87), (larrylamo, 0.85), (lair, 0.78)]
[3 rows x 3 columns]