Понимание Pyparsing для адресов улиц

При поиске способов создания лучшего локатора адресов для обработки таблицы адресов из одного поля я наткнулся на модуль Pyparsing. На странице примеров есть скрипт под названием streetAddressParser (автор неизвестен), который я полностью скопировал ниже. Хотя я прочитал документацию и посмотрел учебники O'Reilly Recursive Decent Parser, я все еще не уверен в коде этого парсера адресов. Я знаю, что этот синтаксический анализатор будет представлять только один компонент приложения локатора адресов, но мой опыт работы с Python ограничен сценариями ГИС, и я изо всех сил пытаюсь понять определенные части этого кода.

Во-первых, какова цель определения чисел как "Ноль Один Два Три... Одиннадцать Двенадцать Тринадцать... Десять Двадцать Тридцать..."? Если мы знаем, что адресное поле начинается с целых чисел, представляющих номер улицы, почему бы просто не извлечь его в качестве первого токена?

Во-вторых, почему этот скрипт использует так много побитовых операторов (^, |, ~)? Это из-за увеличения производительности или они по-разному обрабатываются в модуле Pyparsing? Могут ли другие операторы использоваться вместо них и давать такой же результат?

Я благодарен за любые предложенные рекомендации, и я ценю ваше терпение в чтении этого. Спасибо!

from pyparsing import *

# define number as a set of words
units = oneOf("Zero One Two Three Four Five Six Seven Eight Nine Ten"
          "Eleven Twelve Thirteen Fourteen Fifteen Sixteen Seventeen Eighteen Nineteen",
          caseless=True)
tens = oneOf("Ten Twenty Thirty Forty Fourty Fifty Sixty Seventy Eighty Ninety",caseless=True)
hundred = CaselessLiteral("Hundred")
thousand = CaselessLiteral("Thousand")
OPT_DASH = Optional("-")
numberword = ((( units + OPT_DASH + Optional(thousand) + OPT_DASH + 
                  Optional(units + OPT_DASH + hundred) + OPT_DASH + 
                  Optional(tens)) ^ tens ) 
               + OPT_DASH + Optional(units) )

# number can be any of the forms 123, 21B, 222-A or 23 1/2
housenumber = originalTextFor( numberword | Combine(Word(nums) + 
                    Optional(OPT_DASH + oneOf(list(alphas))+FollowedBy(White()))) + 
                    Optional(OPT_DASH + "1/2")
                    )
numberSuffix = oneOf("st th nd rd").setName("numberSuffix")
streetnumber = originalTextFor( Word(nums) + 
                 Optional(OPT_DASH + "1/2") +
                 Optional(numberSuffix) )

# just a basic word of alpha characters, Maple, Main, etc.
name = ~numberSuffix + Word(alphas)

# types of streets - extend as desired
type_ = Combine( MatchFirst(map(Keyword,"Street St Boulevard Blvd Lane Ln Road Rd Avenue Ave "
                        "Circle Cir Cove Cv Drive Dr Parkway Pkwy Court Ct Square Sq"
                        "Loop Lp".split())) + Optional(".").suppress())

# street name 
nsew = Combine(oneOf("N S E W North South East West NW NE SW SE") + Optional("."))
streetName = (Combine( Optional(nsew) + streetnumber + 
                        Optional("1/2") + 
                        Optional(numberSuffix), joinString=" ", adjacent=False )
                ^ Combine(~numberSuffix + OneOrMore(~type_ + Combine(Word(alphas) + Optional("."))), joinString=" ", adjacent=False) 
                ^ Combine("Avenue" + Word(alphas), joinString=" ", adjacent=False)).setName("streetName")

# PO Box handling
acronym = lambda s : Regex(r"\.?\s*".join(s)+r"\.?")
poBoxRef = ((acronym("PO") | acronym("APO") | acronym("AFP")) + 
             Optional(CaselessLiteral("BOX"))) + Word(alphanums)("boxnumber")

# basic street address
streetReference = streetName.setResultsName("name") + Optional(type_).setResultsName("type")
direct = housenumber.setResultsName("number") + streetReference
intersection = ( streetReference.setResultsName("crossStreet") + 
                 ( '@' | Keyword("and",caseless=True)) +
                 streetReference.setResultsName("street") )
streetAddress = ( poBoxRef("street")
                  ^ direct.setResultsName("street")
                  ^ streetReference.setResultsName("street")
                  ^ intersection )

tests = """\
    3120 De la Cruz Boulevard
    100 South Street
    123 Main
    221B Baker Street
    10 Downing St
    1600 Pennsylvania Ave
    33 1/2 W 42nd St.
    454 N 38 1/2
    21A Deer Run Drive
    256K Memory Lane
    12-1/2 Lincoln
    23N W Loop South
    23 N W Loop South
    25 Main St
    2500 14th St
    12 Bennet Pkwy
    Pearl St
    Bennet Rd and Main St
    19th St
    1500 Deer Creek Lane
    186 Avenue A
    2081 N Webb Rd
    2081 N. Webb Rd
    1515 West 22nd Street
    2029 Stierlin Court
    P.O. Box 33170
    The Landmark @ One Market, Suite 200
    One Market, Suite 200
    One Market
    One Union Square
    One Union Square, Apt 22-C
    """.split("\n")

# how to add Apt, Suite, etc.
suiteRef = (
            oneOf("Suite Ste Apt Apartment Room Rm #", caseless=True) + 
            Optional(".") + 
            Word(alphanums+'-')("suitenumber"))
streetAddress = streetAddress + Optional(Suppress(',') + suiteRef("suite"))

for t in map(str.strip,tests):
    if t:
        #~ print "1234567890"*3
        print t
        addr = streetAddress.parseString(t, parseAll=True)
        #~ # use this version for testing
        #~ addr = streetAddress.parseString(t)
        print "Number:", addr.street.number
        print "Street:", addr.street.name
        print "Type:", addr.street.type
        if addr.street.boxnumber:
            print "Box:", addr.street.boxnumber
        print addr.dump()
        print

1 ответ

Решение
  1. В некоторых адресах основной номер записывается как слово, как вы можете видеть по нескольким адресам в их тестах, ближе к концу списка. Ваше утверждение "Если мы знаем, что адресное поле начинается с целых чисел, представляющих номер улицы..." - это большое "если". Многие, многие адреса не начинаются с цифры.

  2. Побитовые операторы, вероятно, используются для установки флагов, чтобы классифицировать токены как имеющие определенные свойства. Для установки битов / флагов побитовые операторы очень эффективны и удобны.

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

Однако стоит отметить, что этот синтаксический анализатор выглядит так, как будто он пропустит широкий спектр адресов. Кажется, он не рассматривает некоторые из специальных форматов адресов, распространенных в Юте, Висконсине и сельской местности. Также отсутствует значительное количество вторичных обозначений и уличных суффиксов.

Другие вопросы по тегам