Векторизация соответствия строк - PullRequest
0 голосов
/ 05 июля 2018

Вопрос: Можно ли векторизовать совпадение строк двух DataFrames / Series?

Концепция: У меня есть два кадра данных (df_address, df_world_city):

  1. df_address: содержит столбец с адресными данными (например, "Sherlock Str .; Paris;")
  2. df_world_city: содержит столбец с названиями городов и соответствующей страны ("FRA", "Paris")

Я просматриваю каждый адрес и пытаюсь сопоставить все города, чтобы выяснить, какой город указан в адресе, и добавить к нему соответствующую страну. Соответствующие города сохраняются в списке, и список представляет собой значение словаря со страной в качестве ключа ({'FRA': ['Paris']}).

В настоящее время я использую в основном циклы for, чтобы пройти по адресу и городам, чтобы соответствовать им. С многопроцессорной обработкой (48 процессов) и большим количеством данных (df_address: 160 000 строк; df_wordl_city: 2 200 000 строк) это занимает около 4-5 дней.

def regex_city_matching(target, location):

    if type(target) != str or type(location) != str or len(target) <= 3:
        # Skip NaN and to short cities
        return False
    # Match city only as full word, not a substring of another word
    pattern = re.compile('(^|[\W])' + re.escape(target) + '($|[\W])', re.IGNORECASE)
    result = re.search(pattern, location)
    if result:
        return True
    return False


def city_matching_no_country_multi_dict_simple(self, df_world_city, df_address):

 col_names = ['node_id', 'name', 'city_iso']
 df_matched_city_no_country = pd.DataFrame(columns=col_names)

 for index_city in df_world_city.index:
     # Iterate over each city
     w_city = df_world_city.at[index_city, 'city']
     if type(w_city) != str or len(w_city) <= 3:
         # Skip NaN and to short cities
         continue

     w_country = df_world_city.at[index_city, 'iso']

     for ind_address in df_address.index:
         if self.regex_city_matching(w_city, df_address.at[ind_address, 'name']):
             node_id = df_address.at[ind_address, 'node_id']
             address = df_address.at[ind_address, 'name']
             if (df_matched_city_no_country['node_id'] == node_id).any():
                 # append new city / country
                 ind_append_address = df_matched_city_no_country.loc[df_matched_city_no_country.node_id == node_id].index[0]
                          if w_country in df_matched_city_no_country.at[ind_append_address, 'city_iso']:
                     # Country in dictionary
                     df_matched_city_no_country.at[ind_append_address, 'city_iso'][w_country].append(w_city)
                 else:
                     # Country not in dictionary
                     df_matched_city_no_country.at[ind_append_address, 'city_iso'][w_country] = [w_city]
             else:
                 # add new address with city / country
                 dict_iso_city = {w_country: [w_city]}
                 df_matched_city_no_country = df_matched_city_no_country.append(
                     {'node_id': node_id, 'name': address, 'city_iso': dict_iso_city},
                     ignore_index=True)

return df_matched_city_no_country

EDIT: Спасибо @lenik! Сопоставление с набором городов намного эффективнее и выполняется довольно быстро.

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

1 Ответ

0 голосов
/ 05 июля 2018

Вы должны создать обратный словарь с { 'city' : 'COUNTRY', }, так что вам не нужно проходить циклом, просто иметь прямой доступ в постоянное (O (1)) время.

Кроме того, я бы сделал set() из известных городов, поэтому мне не нужно ничего просматривать, просто быстрый просмотр, и я знаю, неизвестен ли этот город.

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

После внесения всех этих изменений обработка 160 тыс. Адресов в 2M известных городах может занять 10-15 секунд.

Скажите, пожалуйста, нужны ли вам примеры кода?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...