Нечеткий поиск столбца в Pandas

Есть ли способ найти значение в столбце фрейма данных, используя FuzzyWuzzy или аналогичную библиотеку? Я пытаюсь найти значение в одном столбце, которое соответствует значению в другом, принимая во внимание нечеткое соответствие. Так

Так, например, если у меня есть названия штатов в одном столбце и коды штатов в другом, как мне найти код штата для Флориды, то есть FL, с учетом таких сокращений, как Флор?

Другими словами, я хочу найти совпадение для имени штата, соответствующего Флору, и получить соответствующий код штата FL.

Любая помощь приветствуется.


person christfan868    schedule 30.09.2020    source источник


Ответы (1)


Если все сокращения являются префиксами, вы можете использовать строковый метод .startswith() либо для короткой, либо для длинной версии состояния.

>>> test_value = "Flor"
>>> test_value.upper().startswith("FL")
True
>>> "Florida".lower().startswith(test_value.lower())
True

Однако если у вас есть более сложные сокращения, difflib.get_close_matches, вероятно, делай что хочешь!

>>> import pandas as pd
>>> import difflib
>>> df = pd.DataFrame({"states": ("Florida", "Texas"), "st": ("FL", "TX")})
>>> df
    states  st
0  Florida  FL
1    Texas  TX
>>> difflib.get_close_matches("Flor", df["states"].to_list())
['Florida']
>>> difflib.get_close_matches("x", df["states"].to_list(), cutoff=0.2)
['Texas']
>>> df["st"][df.index[df["states"]=="Texas"]].iloc[0]
'TX'

Вы, вероятно, захотите попробовать/исключить IndexError при чтении первого члена возвращаемого списка из difflib и, возможно, настроить отсечку, чтобы получить меньше ложных совпадений с близкими состояниями (возможно, предложить все состояния как возможности для некоторых пользователя или требуют больше букв для близких состояний).

Вы также можете увидеть лучшие результаты, сочетая два; сначала проверьте префиксы, прежде чем пытаться найти нечеткое совпадение.

Собираем все вместе

def state_from_partial(test_text, df, col_fullnames, col_shortnames):
    if len(test_text) < 2:
        raise ValueError("must have at least 2 characters")

    # if there's exactly two characters, try to directly match short name
    if len(test_text) == 2 and test_text.upper() in df[col_shortnames]:
        return test_text.upper()

    states = df[col_fullnames].to_list()
    match = None
    # this will definitely fail at least for states starting with M or New
    #for state in states:
    #    if state.lower().startswith(test_text.lower())
    #        match = state
    #        break  # leave loop and prepare to find the prefix

    if not match:
        try:  # see if there's a fuzzy match
            match = difflib.get_close_matches(test_text, states)[0]  # cutoff=0.6
        except IndexError:
            pass  # consider matching against a list of problematic states with different cutoff

    if match:
        return df[col_shortnames][df.index[df[col_fullnames]==match]].iloc[0]

    raise ValueError("couldn't find a state matching partial: {}".format(test_text))

Остерегайтесь состояний, которые начинаются с «Новый» или «М» (и, возможно, других), которые все довольно близки и, вероятно, потребуют специальной обработки. Тестирование здесь творит чудеса.

person ti7    schedule 30.09.2020