首页 > 解决方案 > 字符串匹配的向量化

问题描述

问题:是否可以对两个 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

编辑:谢谢@lenik!与一组城市的匹配效率更高,并且完成得非常快。

但它并没有完全实施,因为测试表明误报的数量很高。

标签: pythonpandas

解决方案


你应该用 制作一个逆字典{ 'city' : 'COUNTRY', },所以你不必循环,只需在恒定(O(1))时间内直接访问。

除此之外,我会创建一个set()已知城市,所以我不需要遍历任何东西,只需快速查找,我就知道这个城市是否未知。

最后,我会在不使用非常昂贵的正则表达式的情况下简化地址解析,将所有字符转换为大写或小写,用空格替换非字母字符,并且只是.split()为了获取单词列表而不是你现在正在做的事情。

完成所有这些更改后,处理 200 万个已知城市的 16 万个地址可能需要 10-15 秒。

请告诉我您是否需要代码示例?


推荐阅读