8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZOZOAdvent Calendar 2023

Day 1

BigQueryのRemote Functionで大量の日本語住所の正規化を行う

Last updated at Posted at 2023-11-30

日本の住所は表記ゆれがよく見られます。例えば、以下は全て国会議事堂の住所であり、人間が見れば同一住所であることがわかりますが、コンピューターの扱う文字列としては全て別物です。

東京都千代田区永田町1丁目7−1
東京都千代田区永田町1-7−1
千代田区永田町1-7−1
東京都千代田区永田町一丁目7番1号
東京都千代田区永田町1の7の1
東京都千代田区永田町一-七−一
東京都千代田区永田町1-7−1

出典:

そのため、住所を使ったデータ処理をするために住所の正規化が必要になるケースもあります。
この記事ではGeoloniaさんのnormalize-japanese-addressesとBigQueryのRemote Functionsを組み合わせて大量の住所の正規化を行ってみます。

BigQuery Remote Functionsとは

BigQueryのRemote Functions機能はGoogle CloudのFaaSであるCloud FunctionsとDWHであるBigQueryを統合する機能です。この機能を使うことでBigQueryからCloud Functionsで作成された処理を呼び出して結果を得ることができます。

Cloud Functionsの用意

まずは以下のCloud Functionsを作成します。

index.ts
import type { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
const { config, normalize } = require('@geolonia/normalize-japanese-addresses')
const fs = require('fs');

export const normalizeJapaneseAddress: HttpFunction = async (req, res) => {
  // 住所データは予めダウンロードしておく
  const addressDataPath = __dirname + "/../../static/japanese-addresses-master/api/ja";
  console.log(addressDataPath);
  if(fs.existsSync(addressDataPath)) {
    console.log(`using address data in ${addressDataPath}`);
    config.japaneseAddressesApi = `file://${addressDataPath}`;
  }

  // 複数の住所データが配列で渡される
  // https://cloud.google.com/bigquery/docs/remote-functions#input_format
  const calls: string[][] = req.body.calls
  console.log(`processing ${calls.length} rows`)
  try {
    const normalizeResults = await Promise.allSettled(calls.map(async (call: string[]) => {
      const address = call[0];
      return JSON.stringify(await normalize(address));
    }));

    const replies = normalizeResults.map((normalizeResult) => {
      if(normalizeResult.status === "fulfilled") {
        return normalizeResult.value;
      }else {
        return JSON.stringify({
          pref: "",
          city: "",
          town: "",
          addr: "",
          lat: null,
          lng: null,
          level: 0,
        });
      }
    });

    res.send({replies: replies});
  } catch(e) {
    console.error(e);
    res.send({errorMessage: `unexpected error occured: ${e}`});
    res.sendStatus(500);
  }
};

そして、以下のコマンドでデプロイをします。サービスアカウントは予め作成しておく必要があります。

PROJECT_ID=<プロジェクトID>

gcloud functions deploy normalize_japanese_address \
  --project=$PROJECT_ID \
  --region=us-central1 \
  --gen2 \
  --runtime node18 \
  --entry-point normalize_japanese_address \
  --trigger-http \
  --no-allow-unauthenticated \
  --run-service-account normalize_japanese_address@$PROJECT_ID.iam.gserviceaccount.com

BigQueryとCloud Functionsを繋ぎこむ

次にBigQueryとCloud Functionsを繋ぐために以下のリソースを作成します。ここで作成しているリソースの詳細は以下のブログで説明しているので、こちらも併せてご確認ください。

resource "google_bigquery_connection" "cloud_resource" {
  connection_id = "cloud_resource"
  location      = "US"
  description   = "Connection for Cloud Resource"
  cloud_resource {}
}

data "google_cloud_run_service" "normalize_japanese_address" {
  name     = "normalize_japanese_address"
  location = "us-central1"
}

resource "google_cloud_run_service_iam_member" "normalize_japanese_address" {
  location = data.google_cloud_run_service.normalize_japanese_address.location
  service  = data.google_cloud_run_service.normalize_japanese_address.name
  role     = "roles/run.invoker"
  member   = "serviceAccount:${google_bigquery_connection.cloud_resource.cloud_resource[0].service_account_id}"
}
CREATE FUNCTION `<プロジェクトID>.<データセットID>.`.normalize_japanese_address(address STRING) RETURNS STRING
REMOTE WITH CONNECTION `<プロジェクトID>.US.<コネクション名>`
OPTIONS (
  endpoint = '<Cloud FunctionsのエンドポイントURL>',
  max_batching_rows = 100 -- 大きくするほどスループットは上がるが、やり過ぎるとFunctionsがタイムアウトするので注意
)

性能検証

ZOZOTOWNの配送先住所から100万件を抽出した住所データを用いて性能検証を行います。

どこまでを判別ができたのか(都道府県レベル・市区町村レベルなど)を表すlevelの内訳も以下に示します。
ほとんどのデータが町丁目まで判別できていることがわかります。

level 件数 割合(%)
0 - 都道府県も判別できなかった。 518 0.05
1 - 都道府県まで判別できた。 10 0.00
2 - 市区町村まで判別できた。 9706 0.97
3 - 町丁目まで判別できた。 989766 98.98

謝辞

このような非常に便利なライブラリを作成し、MITライセンスという扱いやすい形式で公開してくださっているGeoloniaさんに感謝いたします。

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?