Оптимизация функции удаления почетных имен из имен

Проблема

У меня есть список около 1000 почетностьсм. образец ниже.

Учитывая входную строку имени, например "her majesty queen elizabeth windsor"функция должна вернуть "elizabeth windsor". Если нет почетного подарка в начале имени (чтобы упростить проблему), функция должна просто возвращать само имя (например, elizabeth windsor -> elizabeth windsor).

У меня довольно сильные ограничения на задержку, поэтому мне нужно максимально оптимизировать этот код.

Рабочий раствор

Вот мое рабочее решение, есть некоторые дополнительные ограничения для уменьшения ложных срабатываний (например, lance является и почетным именем, и именем), см. модульные тесты:

def strip_honorific(source: str, honorifics: List[str]) -> str:
    source_tokens = source.split()
    if len(source_tokens) > 2:
        for honorific in honorifics:
            if source.startswith(f"{honorific} "):
                stripped_source = source[len(honorific) + 1 :]
                if len(stripped_source.split()) > 1:
                    return stripped_source
    return source

Модульные тесты

def test_honorifics():
    assert strip_honorific(source="her majesty queen elizabeth windsor", honorifics = honorifics) == "elizabeth windsor"
    assert strip_honorific(source="elizabeth windsor", honorifics = honorifics) == "elizabeth windsor"
    assert strip_honorific(source="mrs elizabeth windsor", honorifics = honorifics) == "elizabeth windsor"
    assert strip_honorific(source="mrselizabeth windsor", honorifics = honorifics) == "mrselizabeth windsor"
    assert strip_honorific(source="mrselizabeth windsor", honorifics = honorifics) == "mrselizabeth windsor"
    assert strip_honorific(source="her majesty queen", honorifics = honorifics) == "her majesty queen"
    assert strip_honorific(source="her majesty queen elizabeth", honorifics = honorifics) == "her majesty queen elizabeth"
    assert strip_honorific(source="kapitan fred", honorifics = honorifics) == "kapitan fred"
    
    
test_honorifics()

Ориентир

Для базового теста я использовал приведенный ниже список почетных имен (без многоточия).

source_lst = [
    "her majesty queen elizabeth windsor",
    "mr fred wilson",
    "the rt hon nolan borak",
    "his most eminent highness simon smithson", 
    "kapteinis jurijs jakovļevs", 
    "miss nancy garland",
    "missnancy garland",
]


times = []
for _ in range(1000):
    for source in source_lst:
        t0 = time.time()
        strip_honorific(source=source, honorifics = honorifics)
        times.append(time.time() - t0)

print(f"Mean time: {sum(times)/ len(times)}s") # Mean time: 5.11584963117327e-06s

Почетный список

honorifics = [
    "mr", 
    "mrs", 
    "the hon",
    "the hon dr",
    "the hon lady",
    "the hon lord",
    "the hon mrs",
    "the hon sir",
    "the honourable",
    "the rt hon",
    "her majesty queen",
    "his majesty king",
    "vina",
    "flottiljamiral",
    "superintendent",
    "rabbi",
    "diraja",
    "domnul",
    "kindralleitnant",
    "countess",
    "pan",
    "khatib",
    "zur",
    "vice",
    "don",
    "flotiles",
    "dipl",
    "his most eminent highness", 
    ...
    "the reverend", 
    "archbishop", 
    "sheik", 
    "shaikh", 
    "the rt hon lord", 
    "la tres honorable"
    "ekselence", 
    "kapteinis", 
    "kapitan", 
    "excellenza"
    "mr", 
    "mrs", 
    "miss"
] 

1 ответ
1

Объедините все почетные знаки в регулярное выражение, чтобы их можно было протестировать сразу, а не проверять их один за другим. Отсортируйте почетные знаки от самого длинного до самого короткого

import re

honorifics.sort(key=len, reverse=True)
all_honorifics="|".join(f'{h} ' for h in honorifics)

honorifics_regex = re.compile(rf"\A(?:{all_honorifics})")

def strip_honorific(source, honorifics_regex):
    match = honorifics_regex.search(source)
    if match:
        stripped_source = source[match.end():]
        if len(stripped_source.split()) > 1:
            return stripped_source
        
    return source

Использовать timeit модуль из стандартной библиотеки:

import timeit

code = "for source in source_lst: name = strip_honorific(source, regex)"

timeit.timeit(code, globals=globals())

Версия регулярного выражения выполняется примерно за 5 мс по сравнению с 20 мс для исходного кода.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *