文字列から使用可能な住所、都市、州、郵便番号を解析する [クローズド]

StackOverflow https://stackoverflow.com/questions/16413

質問

問題:Access データベースのアドレス フィールドを SQL Server 2005 に変換しました。このフィールドにはすべてが 1 つのフィールドにまとめられています。住所の個々のセクションを解析して、正規化されたテーブル内の適切なフィールドに入れる必要があります。これを約 4,000 レコードに対して実行する必要があり、反復可能である必要があります。

仮定:

  1. (今のところ) 米国に住所があると仮定します

  2. 入力文字列には、宛先 (宛先を指定される人) および/または 2 番目の番地 (つまり、住所) が含まれる場合があると仮定します。スイートB)

  3. 州は省略される場合があります

  4. 郵便番号は標準の 5 桁または zip+4 です。

  5. 場合によってはタイプミスがあります

アップデート:提起された質問に対して、標準は普遍的に準拠されていないため、ジオコードだけでなく個々の値を保存する必要があり、エラーはタイプミスを意味します (上で修正)

サンプルデータ:

  • A.P.Croll & Son 2299 Lewes-Georgetown Hwy、ジョージタウン、DE 19947

  • 11522 Shawnee Road、グリーンウッド DE 19950

  • 144 キングス ハイウェイ、S.W.デラウェア州ドーバー 19901

  • 統合定数サービス 2 Penns Way Suite 405 New Castle, DE 19720

  • Humes Realty 33 Bridle Ridge Court、ルイス、DE 19958

  • ニコルズ発掘 2742 Plaski Hwy Newark, DE 19711

  • 2284 Bryn Zion Road、スマーナ、DE 19904

  • VEI Dover Crossroads, LLC 1500 Serpentine Road, Suite 100 Baltimore MD 21

  • 580 North Dupont Highway ドーバー、DE 19901

  • 私書箱ボックス 778、ドーバー、デラウェア州 19903

役に立ちましたか?

解決

私はこの種の解析に関して多くの作業を行ってきました。エラーがあるため、100% の精度は得られませんが、ほとんどの精度を得るためにできることがいくつかあり、それから視覚的な BS テストを実行します。一般的な方法は次のとおりです。これはコードではありません。それを記述するのは非常に学術的であり、奇妙な点はなく、文字列処理がたくさんあるだけです。

(サンプルデータをいくつか投稿したので、いくつかの小さな変更を加えました)

  1. 逆向きに作業します。終わり近くにある郵便番号から開始し、次の 2 つの既知の形式のいずれかを使用します。XXXXX または XXXXX-XXXX。これが表示されない場合は、以下の州の都市部にいると考えてください。
  2. 次に、zip の前にあるのは状態であり、2 文字形式か単語として表示されます。これらが何になるかはご存知のとおりです。50 個しかありません。また、スペルミスを補うために単語を音声化することもできます。
  3. その前に街があって、それが おそらく 国家と同じ線上にある。を使用できます 郵便番号データベース 郵便番号に基づいて都市と州をチェックするか、少なくとも BS 検出器として使用します。
  4. 住所は通常 1 行または 2 行になります。2 行目は通常、スイート番号 (ある場合) ですが、私書箱の場合もあります。
  5. 1 行目や 2 行目にある名前を検出することはほぼ不可能ですが、数字の接頭辞が付いていなければ (あるいは「attn:」や「attention to:」の接頭辞が付いていれば)、次のようなヒントが得られる可能性があります。それが名前行であるか住所行であるかに関係なく。

これが少しでも役立つことを願っています。

他のヒント

問題をアウトソーシングすることが最善の策だと思います。それを Google (または Yahoo) ジオコーダーに送信します。ジオコーダーは、緯度/経度 (ここでは関係ありません) だけでなく、送信していないフィールド (ZIP+4 や郡など) が入力された、住所の豊富な解析結果も返します。

たとえば、「1600 Amphitheatre Parkway, Mountain View, CA」を解析すると、次のようになります。

{
  "name": "1600 Amphitheatre Parkway, Mountain View, CA, USA",
  "Status": {
    "code": 200,
    "request": "geocode"
  },
  "Placemark": [
    {
      "address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
      "AddressDetails": {
        "Country": {
          "CountryNameCode": "US",
          "AdministrativeArea": {
            "AdministrativeAreaName": "CA",
            "SubAdministrativeArea": {
              "SubAdministrativeAreaName": "Santa Clara",
              "Locality": {
                "LocalityName": "Mountain View",
                "Thoroughfare": {
                  "ThoroughfareName": "1600 Amphitheatre Pkwy"
                },
                "PostalCode": {
                  "PostalCodeNumber": "94043"
                }
              }
            }
          }
        },
        "Accuracy": 8
      },
      "Point": {
        "coordinates": [-122.083739, 37.423021, 0]
      }
    }
  ]
}

それは 解析可能!

オリジナルの投稿者はおそらくずっと前に進んでいたと思いますが、私は Perl の移植に挑戦しました。 地理::番地:米国 によって使用されるモジュール ジオコーダー.us C# に変換し、CodePlex にダンプしました。将来この質問に遭遇した人が役立つかもしれないと考えています。

米国住所パーサー

プロジェクトのホームページでは、その(非常に現実的な)限界について語ろうとしています。有効な住所の USPS データベースによって裏付けられていないため、解析があいまいになる可能性があり、指定された住所の有効性を確認も否定もできません。文字列からデータを取り出そうとするだけです。

これは、主に適切なフィールドで一連のデータを取得する必要がある場合、またはデータ入力へのショートカットを提供したい場合 (ユーザーが複数のフィールド間をタブ移動するのではなく、テキスト ボックスにアドレスを貼り付けられるようにする) を目的としています。それは ない 住所の到達可能性を確認することを目的としています。

道路境界線より上の部分は解析しようとしませんが、正規表現を使用してかなり近いものを取得することはできるでしょう。おそらく家の番号で区切るだけでしょう。

過去にこれをやったことがあります。

それを手動で行うか (ユーザーが迅速に実行できるようにする優れた GUI を構築する)、自動化して最近の住所データベースと照合して (それを購入する必要があります)、手動でエラーを処理します。

手動での処理にはそれぞれ約 10 秒かかります。つまり、1 時間あたり 3600/10 = 360 回の処理ができるため、4000 回の処理には約 11 ~ 12 時間かかります。これにより、高い精度が得られます。

自動化するには、 必要 最近の米国の住所データベースを取得し、それに対してルールを微調整します。正規表現についてはあまり気にしないことをお勧めします (長期間維持するのが難しく、例外が多いため)。データベースとの 90% の一致を目指し、残りは手動で実行します。

Postal Addressing Standards (USPS) のコピーを次の URL から入手してください。 http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf 130 ページ以上あることに注目してください。それを正規表現で実装するのは大変なことです。

国際住所の場合、すべての賭けは無効になります。米国に拠点を置く労働者は検証できないだろう。

あるいは、データ サービスを使用します。ただし、推奨事項はありません。

さらに:郵送するときは(それが目的ですよね?)封筒の正しい場所に「住所修正依頼」と必ず記入してください。 アップデート データベース。(フロントデスク担当者がそれを行うための簡単な GUI を作成しました。実際に郵便物を仕分ける人)

最後に、データをスクラブしたら、重複を探します。

ここでのアドバイスを受けて、私は VB で次の関数を考案しました。これは、必ずしも完璧ではありませんが、まあまあの使用可能なデータ (会社名とスイートの行が指定されている場合は、スイートと都市を組み合わせたもの) を作成します。私自身のルールに違反した場合など、お気軽にコメント/リファクタリング/怒鳴ってください。

Public Function parseAddress(ByVal input As String) As Collection
    input = input.Replace(",", "")
    input = input.Replace("  ", " ")
    Dim splitString() As String = Split(input)
    Dim streetMarker() As String = New String() {"street", "st", "st.", "avenue", "ave", "ave.", "blvd", "blvd.", "highway", "hwy", "hwy.", "box", "road", "rd", "rd.", "lane", "ln", "ln.", "circle", "circ", "circ.", "court", "ct", "ct."}
    Dim address1 As String
    Dim address2 As String = ""
    Dim city As String
    Dim state As String
    Dim zip As String
    Dim streetMarkerIndex As Integer

    zip = splitString(splitString.Length - 1).ToString()
    state = splitString(splitString.Length - 2).ToString()
    streetMarkerIndex = getLastIndexOf(splitString, streetMarker) + 1
    Dim sb As New StringBuilder

    For counter As Integer = streetMarkerIndex To splitString.Length - 3
        sb.Append(splitString(counter) + " ")
    Next counter
    city = RTrim(sb.ToString())
    Dim addressIndex As Integer = 0

    For counter As Integer = 0 To streetMarkerIndex
        If IsNumeric(splitString(counter)) _
            Or splitString(counter).ToString.ToLower = "po" _
            Or splitString(counter).ToString().ToLower().Replace(".", "") = "po" Then
                addressIndex = counter
            Exit For
        End If
    Next counter

    sb = New StringBuilder
    For counter As Integer = addressIndex To streetMarkerIndex - 1
        sb.Append(splitString(counter) + " ")
    Next counter

    address1 = RTrim(sb.ToString())

    sb = New StringBuilder

    If addressIndex = 0 Then
        If splitString(splitString.Length - 2).ToString() <> splitString(streetMarkerIndex + 1) Then
            For counter As Integer = streetMarkerIndex To splitString.Length - 2
                sb.Append(splitString(counter) + " ")
            Next counter
        End If
    Else
        For counter As Integer = 0 To addressIndex - 1
            sb.Append(splitString(counter) + " ")
        Next counter
    End If
    address2 = RTrim(sb.ToString())

    Dim output As New Collection
    output.Add(address1, "Address1")
    output.Add(address2, "Address2")
    output.Add(city, "City")
    output.Add(state, "State")
    output.Add(zip, "Zip")
    Return output
End Function

Private Function getLastIndexOf(ByVal sArray As String(), ByVal checkArray As String()) As Integer
    Dim sourceIndex As Integer = 0
    Dim outputIndex As Integer = 0
    For Each item As String In checkArray
        For Each source As String In sArray
            If source.ToLower = item.ToLower Then
                outputIndex = sourceIndex
                If item.ToLower = "box" Then
                    outputIndex = outputIndex + 1
                End If
            End If
            sourceIndex = sourceIndex + 1
        Next
        sourceIndex = 0
    Next
    Return outputIndex
End Function

を通過 parseAddress 機能「A.P.Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947」は次のように返されます。

2299 Lewes-Georgetown Hwy
A. P. Croll & Son  
Georgetown
DE
19947

私はアドレス処理ドメインで約 5 年間働いていますが、特効薬はありません。正しい解決策はデータの値によって異なります。あまり価値がない場合は、他の回答が示唆しているように、パーサーを通してスローします。それが多少でも価値がある場合は、パーサーのすべての結果を人間に評価/修正してもらう必要があります。完全に自動化された反復可能なソリューションをお探しの場合は、Group1 や Trillium などのアドレス修正ベンダーに相談することをお勧めします。

SmartyStreets には、任意の入力文字列から住所を抽出する新機能があります。(注記:私は SmartyStreets では働いていません。)

上記の質問で指定されたサンプル入力からすべてのアドレスが正常に抽出されました。(ちなみに、10 個のアドレスのうち有効なのは 9 個だけです。)

出力の一部を次に示します。enter image description here

同じリクエストの CSV 形式の出力は次のとおりです。

ID,Start,End,Segment,Verified,Candidate,Firm,FirstLine,SecondLine,LastLine,City,State,ZIPCode,County,DpvFootnotes,DeliveryPointBarcode,Active,Vacant,CMRA,MatchCode,Latitude,Longitude,Precision,RDI,RecordType,BuildingDefaultIndicator,CongressionalDistrict,Footnotes
1,32,79,"2299 Lewes-Georgetown Hwy, Georgetown, DE 19947",N,,,,,,,,,,,,,,,,,,,,,,
2,81,119,"11522 Shawnee Road, Greenwood DE 19950",Y,0,,11522 Shawnee Rd,,Greenwood DE 19950-5209,Greenwood,DE,19950,Sussex,AABB,199505209226,Y,N,N,Y,38.82865,-75.54907,Zip9,Residential,S,,AL,N#
3,121,160,"144 Kings Highway, S.W. Dover, DE 19901",Y,0,,144 Kings Hwy,,Dover DE 19901-7308,Dover,DE,19901,Kent,AABB,199017308444,Y,N,N,Y,39.16081,-75.52377,Zip9,Commercial,S,,AL,L#
4,190,232,"2 Penns Way Suite 405 New Castle, DE 19720",Y,0,,2 Penns Way Ste 405,,New Castle DE 19720-2407,New Castle,DE,19720,New Castle,AABB,197202407053,Y,N,N,Y,39.68332,-75.61043,Zip9,Commercial,H,,AL,N#
5,247,285,"33 Bridle Ridge Court, Lewes, DE 19958",Y,0,,33 Bridle Ridge Cir,,Lewes DE 19958-8961,Lewes,DE,19958,Sussex,AABB,199588961338,Y,N,N,Y,38.72749,-75.17055,Zip7,Residential,S,,AL,L#
6,306,339,"2742 Pulaski Hwy Newark, DE 19711",Y,0,,2742 Pulaski Hwy,,Newark DE 19702-3911,Newark,DE,19702,New Castle,AABB,197023911421,Y,N,N,Y,39.60328,-75.75869,Zip9,Commercial,S,,AL,A#
7,341,378,"2284 Bryn Zion Road, Smyrna, DE 19904",Y,0,,2284 Bryn Zion Rd,,Smyrna DE 19977-3895,Smyrna,DE,19977,Kent,AABB,199773895840,Y,N,N,Y,39.23937,-75.64065,Zip7,Residential,S,,AL,A#N#
8,406,450,"1500 Serpentine Road, Suite 100 Baltimore MD",Y,0,,1500 Serpentine Rd Ste 100,,Baltimore MD 21209-2034,Baltimore,MD,21209,Baltimore,AABB,212092034250,Y,N,N,Y,39.38194,-76.65856,Zip9,Commercial,H,,03,N#
9,455,495,"580 North Dupont Highway Dover, DE 19901",Y,0,,580 N DuPont Hwy,,Dover DE 19901-3961,Dover,DE,19901,Kent,AABB,199013961803,Y,N,N,Y,39.17576,-75.5241,Zip9,Commercial,S,,AL,N#
10,497,525,"P.O. Box 778 Dover, DE 19903",Y,0,,PO Box 778,,Dover DE 19903-0778,Dover,DE,19903,Kent,AABB,199030778781,Y,N,N,Y,39.20946,-75.57012,Zip5,Residential,P,,AL,

私は最初にサービスを作成した開発者でした。私たちが実装したアルゴリズムは、ここでの特定の回答とは少し異なりますが、抽出された各アドレスはアドレス検索 API に対して検証されるため、それが有効かどうかを確認できます。それぞれの検証結果は保証されていますが、他の結果が完璧ではないことはわかっています。 十分にクリアな このスレッドでは、アドレスは人間にとってさえ予測できない場合があります。

これは問題を解決しませんが、これらのアドレスにLAT/LONGデータのみが必要な場合、GoogleマップAPIは、フォーマットされていないアドレスをかなりうまく解析します。

良い提案です。あるいは、各住所に対して Google マップへの CURL リクエストを実行すると、適切な形式の住所が返されます。そこから、思う存分正規表現を行うことができます。

ジェームス A.に +1ローゼン氏が提案した解決策は私にとってはうまくいきましたが、完全主義者にとってこのサイトは興味深い読み物であり、世界中の住所を文書化する中で私が見た中で最高の試みです。 http://www.columbia.edu/kermit/postal.html

住所の記録方法に何か基準はあるのでしょうか?例えば:

  1. street1 と street2、city、state、zip を区切るには常にカンマまたは改行が必要ですか?
  2. 住所の種類 (道路、街路、大通りなど) は常に詳しく説明されていますか?いつも略されてる?それぞれのいくつか?
  3. 「エラー」を定義します。

私の一般的な答えは一連の正規表現ですが、その複雑さは答えによって異なります。そして、一貫性がまったくない場合は、正規表現では部分的な成功しか達成できない可能性があります (つまり:郵便番号と州をフィルタリングして除外し、残りを手作業で行う必要があります(または、少なくとも残りの部分を慎重に調べて、間違いを確実に見つけられるようにする必要があります)。

サンプルデータの追加リクエスト。

前述したように、zip から逆方向に作業します。

zip を取得したら、zip データベースにクエリを実行し、結果を保存し、文字列からそれらと zip を削除します。

そうなるとアドレスが混乱してしまいます。ほとんどの (すべて?) アドレスは数字で始まるため、残りの文字列で最初に出現する数字を見つけて、そこから文字列の (新しい) 終わりまでのすべてを取得します。それがあなたの住所になります。その番号の左側にあるものは、おそらく宛先です。

これで、市区町村、州、郵便番号がテーブルに保存され、場合によっては 2 つの文字列 (宛先と住所) が保存されるはずです。アドレスについては、「スイート」または「apt」の存在を確認してください。等それを 2 つの値 (アドレス行 1 と 2) に分割します。

宛先の場合は、その文字列の最後の単語をパントして姓として取得し、残りを名のフィールドに入力します。それをしたくない場合は、最初に挨拶 (Mr.、Ms.、Dr. など) をチェックし、スペースの数に基づいて名前がどのようなものであるかを推測する必要があります。作り物。

100%の精度で解析できる方法はないと思います。

試す www.address-parser.com. 。オンラインでテストできる Web サービスを使用します

サンプルデータに基づいて:

  1. 文字列の最後から始めます。郵便番号 (いずれかの形式) を解析します。終わりから最初のスペースまで読みます。郵便番号が見つからない場合はエラーになります。

  2. スペースと特殊文字 (カンマ) の末尾をトリミングします。

  3. 次に、[状態] に進み、再びスペースを区切り文字として使用します。おそらく、ルックアップ リストを使用して 2 文字の州コードと完全な州名を検証します。有効な状態が見つからない場合は、エラーになります。

  4. 末尾からスペースとカンマを再度削除します。

  5. City は注意が必要です。実際には、市内で大量のデータが取得される危険を承知で、ここではカンマを使用します。カンマまたは行の先頭を探してください。

  6. 文字列にまだ文字が残っている場合は、そのすべてをアドレス フィールドに押し込みます。

これは完璧ではありませんが、かなり良い出発点となるはずです。

それが人間が入力したデータの場合、例外を回避するコードを作成するのに非常に多くの時間を費やすことになります。

試す:

  1. 郵便番号を抽出するための正規表現

  2. 正しい住所を取得するための郵便番号検索 (適切な政府 DB 経由)

  3. インターンに新しいデータが古いデータと一致することを手動で確認してもらいます

これでは問題は解決しませんが、これらの住所の緯度/経度データのみが必要な場合、Google Maps API は書式設定されていない住所を適切に解析します。

RecogniContact は、米国およびヨーロッパの住所を解析する Windows COM オブジェクトです。すぐに試着できますhttp://www.loquisoft.com/index.php?page=8

これをチェックしてみるといいかもしれません!! http://jgeocoder.sourceforge.net/parser.html私にとっては魔法のように働きました。

この種の問題は、データの根底にあるあいまいさのため解決が困難です。

ここでは、正規表現に基づいて再帰降下文法ツリーを定義し、有効な住所の多くの組み合わせを解析する Perl ベースのソリューションを示します。 http://search.cpan.org/~kimryan/Lingua-EN-AddressParse-1.20/lib/Lingua/EN/AddressParse.pm 。これには、アドレス内の次のようなサブ プロパティが含まれます。12 1st Avenue N Suite # 2 Somewhere CA 12345 USA

に似ています http://search.cpan.org/~timb/Geo-StreetAddress-US-1.03/US.pm 前述しましたが、英国、オーストラリア、カナダなど、米国以外の住所でも機能します。

以下は、サンプル アドレスの 1 つの出力です。最初に「A.」から名前セクションを削除する必要があることに注意してください。P.Croll & Son 2299 Lewes-Georgetown Hwy, Georgetown, DE 19947」を「2299 Lewes-Georgetown Hwy, Georgetown, DE 19947」に縮小します。これは、文字列内で見つかった最初の数値までのすべてのデータを削除することで簡単に実現できます。

Non matching part       ''
Error                   '0'
Error descriptions      ''
Case all                '2299 Lewes-Georgetown Hwy Georgetown DE 19947'
COMPONENTS              ''
country                 ''
po_box_type             ''
post_box                ''
post_code               '19947'
pre_cursor              ''
property_identifier     '2299'
property_name           ''
road_box                ''
street                  'Lewes-Georgetown'
street_direction        ''
street_type             'Hwy'
sub_property_identifier ''
subcountry              'DE'
suburb                  'Georgetown'

Word ではエラーが発生する可能性があるため、SOUNDEX と LCS アルゴリズムを組み合わせて文字列を比較することを検討してください。これは非常に役立ちます。

Google APIを使用して

$d=str_replace(" ", "+", $address_url);
$completeurl ="http://maps.googleapis.com/maps/api/geocode/xml?address=".$d."&sensor=true"; 
$phpobject = simplexml_load_file($completeurl);
print_r($phpobject);

Ruby または Rails 開発者向けに、と呼ばれる素晴らしい gem が用意されています。 住所. 。私は自分のプロジェクトの 1 つでこれを使用しており、必要な作業を行ってくれます。

私が抱えていた唯一の問題は、アドレスがこの形式である場合です P. O. Box 1410 Durham, NC 27702 nil が返されたため、「P.O.「」をボックスに追加すると、その後、それを解析できました。

郵便番号を指定すると、その郵便番号の通りの名前のリストが得られるデータ サービスがあります。

正規表現を使用して郵便番号または市区町村を抽出します。正しいものを見つけるか、エラーで両方が取得された場合は見つけます。から通りのリストを取得します 情報元 都市と州を修正し、次に番地を修正します。有効な住所行 1、都市、州、郵便番号を取得したら、住所行 2..3 を推測できます。

これがどの程度実現可能かはわかりませんが、これについて言及されているのを見たことがなかったので、先に進んでこれを提案したいと思いました。

厳密に米国にいる場合...すべての郵便番号、州、都市、通りの巨大なデータベースを入手します。次に、アドレス内でこれらを探します。たとえば、見つけた都市が見つけた州に存在するかどうかをテストしたり、見つけた通りが見つけた都市に存在するかどうかをチェックしたりすることで、見つけたものを検証できます。そうでない場合、ジョンはジョンの住所ではなく、宛先の名前である可能性があります...基本的には、できる限り多くの情報を入手し、それと照らし合わせて住所を確認してください。極端な例としては、A さんの米国内のすべての住所のリストを取得し、各住所に最も関連性の高い一致があるものを見つけることです。

Perl Geo::StreetAddress::US パッケージの JavaScript ポートがあります。 https://github.com/hassansin/parse-address 。これは正規表現ベースであり、かなりうまく機能します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top