Вопрос:
Можно ли векторизовать совпадение строк двух DataFrames / Series?
Концепция:
У меня есть два кадра данных (df_address, df_world_city):
- df_address: содержит столбец с адресными данными (например, "Sherlock Str .; Paris;")
- 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! Сопоставление с набором городов намного эффективнее и выполняется довольно быстро.
Но он не был полностью реализован, поскольку тест показал, что количество ложных срабатываний слишком велико.