Понимание списка с вложенными циклами for в подсписках в столбцах pandas df с функцией

#python #python-3.x #pandas #dataframe #list-comprehension

#python #python-3.x #pandas #фрейм данных #понимание списка

Вопрос:

Краткие сведения

Мне нужно запустить функцию (полный приведенный ниже код воспроизводим / исполним для фрейма данных, как использовать функцию и функцию — см. Ниже), которая принимает каждый элемент в строке col1 ( myllc для строки 1) и запускает функцию get_top_matches для каждого элемента в строке для каждого подсписка в col2 .


Как выглядит DF:

 parent_org_name_list    children_org_name_sublists
0   [myllc,]    [[myalyk, oleksandr, nychyporovych, pp], [myli...
1   [ydea, srl,]    [[yd, confecco, ltda], [yda], [yda, insaat, sa...
2   [hyonix,]   [[hymax, talk, solutions], [hynix, semiconduct...
3   [mjn, enterprises,] [[mjm, interant, inc], [mjn, enterprises], [sh...
4   [ltd, yuriapharm,]  [[ltd, yuriapharm], [yuriypra, law, offic, pc]]
  

Что код должен делать для каждой строки:

  • Возьмите элемент в col1 (например, [myllc,]) и запустите get_top_matches функцию в [myalyk, alexandr, nychyporovych, pp], а затем запустите его в следующем подсписке [‘myliu’, ‘srl’] … и сделайте это для каждого подсписка соответствующей строки в col2

Что делает использование функции:

  • Функция принимает два аргумента: строку и список и сравнивает строку с каждым элементом в списке, вот так:
 get_top_matches('myllc', [
                   'myalyk oleksandr nychyporovych pp'
                  ,'myliu srl'
                  ,'myllc'
                  ,'myloc manag IT ag'])

results: 
[('myllc', 1.0),
 ('myloc manag IT ag', 0.77),
 ('myliu srl', 0.75),
 ('myalyk oleksandr nychyporovych pp', 0.65)]
  

Вот что у меня пока есть:

  • Мне нужно создать столбец df с результатами, показанными ниже, но они должны содержать каждое из слов в каждом подсписке с оценкой в форме кортежа. Я ужасен в понимании списка, это так сбивает с толку.
 df['func_scores'] = [
[[df.agg(lambda x: get_top_matches(u,v), axis=1) for u in x ]
    for v in zip(*y)]
        for x,y in zip(df['col1'], df1['col2'])
]

results: #it only grabs the first word of the sublists and runs the function 3 times for those same 3 words...
[[0    [(myllc, 0.97), (myloc, 0.88), (myliu, 0.79), 
...1    [(myllc, 0.97), (myloc, 0.88), (myliu, 0.79), 
...2    [(myllc, 0.97), (myloc, 0.88), (myliu, 0.79), 
...3    [(myllc, 0.97), (myloc, 0.88), (myliu, 0.79), 
...4    [(myllc, 0.97), (myloc, 0.88), (myliu, 0.79), 
...dtype: object]]
  

Вот и все. Выше приведен вопрос, что я пробовал до сих пор, пример вывода и функции, а ниже приведен исполняемый код для df и функции — так что вам не нужно ничего воссоздавать!


Ожидание

Это выдуманные числа!

(Этот пример: строка 1 имеет 4 подсписка, строка 2 имеет 2 подсписка. функция выполняется для каждого слова в каждом столбце 1 для каждого слова в каждом подсписке в столбце 2 и помещает результаты в подсписк в новом столбце.)

 [[['myalyk',.97], ['oleksandr',.54], ['nychyporovych',.3], ['pp',0]], [['myliu',.88], ['srl',.43]], [['myllc',1.0]], [['myloc',1.0], ['manag',.45], ['IT',.1], ['ag',0]]], 
[[['ltd',.34], ['yuriapharm',.76]], [['yuriypra',.65], ['law',.54], ['offic',.45], ['pc',.34]]],
...
  

.
.
.

.
.
.

.
.
.

Executable code snippets: just run these two:

Dataframe

 data = {'col1':  [['myllc,'],
                 ['ydea', 'srl,'],
                 ['hyonix,'],
                 ['mjn', 'enterprises,'],
                 ['ltd', 'yuriapharm,']]
        ,
        'col2': [[['myalyk', 'oleksandr', 'nychyporovych', 'pp'],
                  ['myliu', 'srl'],
                  ['myllc'],
                  ['myloc', 'manag', 'IT', 'ag']],
                 [['yd', 'confecco', 'ltda'],
                  ['yda'],
                  ['yda', 'insaat', 'sanayi', 'veticaret', 'as'],
                  ['ydea'],
                  ['ydea', 'srl'],
                  ['ydea', 'srl'],
                  ['ydh'],
                  ['ydh', 'japan', 'inc']],
                 [['hymax', 'talk', 'solutions'],
                  ['hynix', 'semiconductor', 'inc'],
                  ['hyonix'],
                  ['hyonix', 'llc'],
                  ['intercan', 'hyumok'],
                  ['kim', 'hyang', 'soon'],
                  ['sk', 'hynix', 'america'],
                  ['smecla2012022843470sam', 'hyang', 'precis', 'corporation'],
                  ['smecpz2017103044085sung', 'hyung', 'precis', 'CO', 'inc']],
                 [['mjm', 'interant', 'inc'],
                  ['mjn', 'enterprises'],
                  ['shanti', 'town', 'mjini', 'clients']],
                 [['ltd', 'yuriapharm'], ['yuriypra', 'law', 'offic', 'pc']]]
        }

df = pd.DataFrame (data, columns = ['col1','col2'])
df
  

Функции:

Функция внизу get_top_matches — единственная функция, которую я запускаю, но она использует все остальные функции. Все, что делают эти функции, — это генерируют оценку того, насколько близко расположены две строки друг к другу (расстояния между символами и тому подобное):

 #jaro version
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):
    """
    :param first: word to calculate distance for
    :param second: word to calculate distance with
    :param winkler: same as winkler_ajustment
    :param winkler_ajustment: add an adjustment factor to the Jaro of the distance
    :param scaling: scaling factor for the Winkler adjustment
    :return: Jaro distance adjusted (or not)
    """
    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:
#     for val in value_list.split():
        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)
  

Ответ №1:

Иллюстрирует два метода

  1. Понимание списка
  2. Использование фрейма данных Применяется

Код

 # Generate DataFrame
df = pd.DataFrame (data, columns = ['col1','col2'])

# Clean Data (strip out trailing commas on some words)
df['col1'] = df['col1'].map(lambda lst: [x.rstrip(',') for x in lst])

# 1. List comprehension Technique
# zip provides pairs of col1, col2 rows
result = [[get_top_matches(u, [v]) for u in x for w in y for v in w] for x, y in zip(df['col1'], df['col2'])]

# 2. DataFrame Apply Technique
def func(x, y):
return [get_top_matches(u, [v]) for u in x for w in y for v in w] 

df['func_scores'] = df.apply(lambda row: func(row['col1'], row['col2']), axis = 1)

# Verify two methods are equal
print(df['func_scores'].equals(pd.Series(result)))  # True

print(df['func_scores'].to_string(index=False))
  

Вывод

 [[(myalyk, 0.76)], [(oleksandr, 0.44)], [(nychyporovych, 0.52)], [(pp, 0.0)], [(myliu, 0.81)], [(srl, 0.51)], [(myllc, 1.0)], [(myloc, 0.91)], [(manag, 0.52)], [(IT, 0.0)], [(ag, 0.0)]]
 [[(yd, 0.87)], [(confecco, 0.46)], [(ltda, 0.67)], [(yda, 0.93)], [(yda, 0.93)], [(insaat, 0.47)], [(sanayi, 0.47)], [(veticaret, 0.57)], [(as, 0.0)], [(ydea, 1.0)], [(ydea, 1.0)], [(srl, 0.0)], [(ydea, 1.0)], [(srl, 0.0)], [(ydh, 0.78)], [(ydh, 0.78)], [(japan, 0.48)], [(inc, 0.0)], [(yd, 0.0)], [(confecco, 0.0)], [(ltda, 0.0)], [(yda, 0.0)], [(yda, 0.0)], [(insaat, 0.0)], [(sanayi, 0.55)], [(veticaret, 0.0)], [(as, 0.61)], [(ydea, 0.0)], [(ydea, 0.0)], [(srl, 1.0)], [(ydea, 0.0)], [(srl, 1.0)], [(ydh, 0.0)], [(ydh, 0.0)], [(japan, 0.0)], [(inc, 0.0)]]
                                                                             
[[(hymax, 0.76)], [(talk, 0.0)], [(solutions, 0.52)], [(hynix, 0.96)], [(semiconductor, 0.47)], [(inc, 0.0)], [(hyonix, 1.0)], [(hyonix, 1.0)], [(llc, 0.0)], [(intercan, 0.43)], [(hyumok, 0.73)], [(kim, 0.0)], [(hyang, 0.76)], [(soon, 0.61)], [(sk, 0.0)], [(hynix, 0.96)], [(america, 0.44)], [(smecla2012022843470sam, 0.0)], [(hyang, 0.76)], [(precis, 0.44)], [(corporation, 0.42)], [(smecpz2017103044085sung, 0.0)], [(hyung, 0.76)], [(precis, 0.44)], [(CO, 0.56)], [(inc, 0.0)]]
                                                                                                                                                                                                                                                     
[[(mjm, 0.82)], [(interant, 0.49)], [(inc, 0.56)], [(mjn, 1.0)], [(enterprises, 0.47)], [(shanti, 0.5)], [(town, 0.53)], [(mjini, 0.89)], [(clients, 0.0)], [(mjm, 0.0)], [(interant, 0.54)], [(inc, 0.47)], [(mjn, 0.47)], [(enterprises, 1.0)], [(shanti, 0.59)], [(town, 0.39)], [(mjini, 0.43)], [(clients, 0.65)]]
                                                                                                                                                                                                                                                                                                                                                                        
[[(ltd, 1.0)], [(yuriapharm, 0.0)], [(yuriypra, 0.0)], [(law, 0.6)], [(offic, 0.0)], [(pc, 0.0)], [(ltd, 0.0)], [(yuriapharm, 1.0)], [(yuriypra, 0.89)], [(law, 0.0)], [(offic, 0.43)], [(pc, 0.0)]]
  

Чтобы получить оценки за func_sores

  1. Мы получаем оценку, используя get_top_matches(u, [v])[0][1]
  2. На основе результата get_top_matches(…) формы [(имя, значение)]
  3. Преобразование списка с повторным циклом (get_top_matches(u, [v])[0][1])

Код

 # List comprehension Technique
result = [[[(get_top_matches(u, [v])[0][1]) for v in w] for u in x for w in y] for x, y in zip(df['col1'], df['col2'])]

# DataFrame Apply Technique
def func(x, y):
    return [[(get_top_matches(u, [v])[0][1]) for v in w] for u in x for w in y] 

df['func_scores'] = df.apply(lambda row: func(row['col1'], row['col2']), axis = 1)

# Verify two are equal
print(df['func_scores'].equals(pd.Series(result)))  # True

print(df['func_scores'].to_string(index=False))

# Output
[[0.76, 0.44, 0.52, 0.0], [0.81, 0.51], [1.0], [0.91, 0.52, 0.0, 0.0]]
 [[0.87, 0.46, 0.67], [0.93], [0.93, 0.47, 0.47, 0.57, 0.0], [1.0], [1.0, 0.0], [1.0, 0.0], [0.78], [0.78, 0.48, 0.0], [0.0, 0.0, 0.0], [0.0], [0.0, 0.0, 0.55, 0.0, 0.61], [0.0], [0.0, 1.0], [0.0, 1.0], [0.0], [0.0, 0.0, 0.0]]
                                                          
[[0.76, 0.0, 0.52], [0.96, 0.47, 0.0], [1.0], [1.0, 0.0], [0.43, 0.73], [0.0, 0.76, 0.61], [0.0, 0.96, 0.44], [0.0, 0.76, 0.44, 0.42], [0.0, 0.76, 0.44, 0.56, 0.0]]
                                                                                                           
[[0.82, 0.49, 0.56], [1.0, 0.47], [0.5, 0.53, 0.89, 0.0], [0.0, 0.54, 0.47], [0.47, 1.0], [0.59, 0.39, 0.43, 0.65]]
                                                                                                                                                        
[[1.0, 0.0], [0.0, 0.6, 0.0, 0.0], [0.0, 1.0], [0.89, 0.0, 0.43, 0.0]]
  

Комментарии:

1. @max- Относительно 1 — я думал, что он проходит через col2 в виде списка, такого как ['myalyk oleksandr nychyporovych pp', 'myliu srl', 'myllc', 'myloc manag IT ag'] . Разве это не то, что показывает `get_top_matches(‘myllc’, [ ‘myalyk alexandr nychyporovych pp’, ‘myliu srl’, ‘myllc’, ‘myloc manag IT ag’])?

2. @max — Я не понимаю, как вы получаете ожидаемые значения. Можете ли вы показать код для этого?

3. @max — Я в замешательстве, так как, get_top_matches('myllc', ['myalyk', 'oleksandr', 'nychyporovych', 'pp', 'myliu', 'srl', 'myllc', 'myloc', 'manag', 'IT', 'ag'])) например, это [('myllc', 1.0), ('myloc', 0.91), ('myliu', 0.81), ('myalyk', 0.76), ('nychyporovych', 0.52), ('manag', 0.52), ('srl', 0.51), ('oleksandr', 0.44), ('pp', 0.0), ('IT', 0.0), ('ag', 0.0)] так, что я пытался увидеть связь с вашими числами.

4. @DarrylG, я тоже согласен. Как вы подходите к этим числам. Я могу понять myllc для 1.0 . Что myloc равно 0.91 ?

5. @JoeFerndz — я получаю их из функции get_top_matches, вызывая: get_top_matches('myllc', ['myalyk', 'oleksandr', 'nychyporovych', 'pp', 'myliu', 'srl', 'myllc', 'myloc', 'manag', 'IT', 'ag']))