概要
標準的な位置情報データでは、近くに何があるかはわかりますが、「このエリアは自分にとってどれくらい良いか」というより重要な質問に答えることはできません。ユーザーのニーズは複雑です。幼い子供のいる家族と犬を飼っている若い専門職では、優先順位が異なります。お客様が自信を持って意思決定できるように、こうした具体的なニーズを反映した分析情報を提供する必要があります。カスタム ロケーション スコアは、この価値を提供し、差別化されたユーザー エクスペリエンスを生み出すための強力なツールです。
このドキュメントでは、BigQuery の Places Insights データセットを使用して、カスタムの多面的な位置情報スコアを作成する方法について説明します。POI データを意味のある指標に変換することで、不動産、小売、旅行のアプリケーションを充実させ、ユーザーが必要とする関連情報を提供できます。また、BigQuery で生成 AI を使用して位置情報のスコアを計算する強力な方法も提供しています。
カスタマイズされたスコアでビジネス価値を高める
次の例は、位置情報の生データをユーザー中心の強力な指標に変換して、アプリを強化する方法を示しています。
- 不動産開発業者は、「家族向けスコア」や「通勤者向けスコア」を作成して、購入者や賃借人がライフスタイルに合った最適な近隣地域を選択できるようにします。これにより、ユーザー エンゲージメントの向上、質の高い見込み顧客の獲得、コンバージョンの迅速化につながります。
- 旅行・宿泊施設のエンジニアは、「ナイトライフ スコア」や「観光客向けパラダイス スコア」を構築して、旅行者が自分の旅行スタイルに合ったホテルを選べるようにし、予約率と顧客満足度を高めることができます。
- 小売アナリストは「フィットネスとウェルネスのスコア」を生成して、近隣の相補的なビジネスに基づいて新しいジムや健康食品店の最適な場所を特定し、適切なユーザー層をターゲットにする可能性を最大限に高めることができます。
このガイドでは、BigQuery で直接 Places データを使用して、あらゆる種類のカスタム ロケーション スコアを構築するための柔軟な 3 部構成の方法論について説明します。このパターンを説明するために、家族向けスコアとペットオーナー向けスコアという 2 つの異なるスコアの例を作成します。このアプローチでは、場所の数だけでなく、Places Insights データセット内の詳細な属性を活用できます。営業時間、子ども連れに適しているかどうか、犬の同伴が可能かどうかなどの情報を使用して、ユーザーにとって有益で高度な指標を作成できます。
ソリューションのワークフロー

このチュートリアルでは、単一の強力な SQL クエリを使用して、任意のユースケースに適応できるカスタム スコアを構築します。このプロセスを、架空のアパートのリスティングのセットに対して 2 つのスコアの例を作成することで説明します。
前提条件
始める前に、こちらの手順に沿ってプレイス分析を設定してください。
1. 基盤を確立する: 関心のある場所
スコアを作成する前に、分析する場所のリストが必要です。まず、このデータが BigQuery のテーブルとして存在することを確認します。各位置情報の一意の識別子と、その座標を保存する GEOGRAPHY 列が必要です。
次のようなクエリを使用して、スコアリングする位置情報のテーブルを作成してデータを入力できます。
CREATE OR REPLACE TABLE `your_project.your_dataset.apartment_listings`
(
id INT64,
name STRING,
location GEOGRAPHY
);
INSERT INTO `your_project.your_dataset.apartment_listings` VALUES
(1, 'The Downtowner', ST_GEOGPOINT(-74.0077, 40.7093)),
(2, 'Suburban Oasis', ST_GEOGPOINT(-73.9825, 40.7507)),
(3, 'Riverside Lofts', ST_GEOGPOINT(-73.9470, 40.8079))
-- More rows can be added here
. . . ;
位置情報データに対して SELECT * を実行すると、次のようになります。

2. コアロジックを開発する: スコアリング クエリ
場所が特定できたら、次のステップとして、カスタム スコアに関連する近くの場所を検索、フィルタ、カウントします。これらはすべて、単一の SELECT ステートメント内で行われます。
地理空間検索で近くにあるものを探す
まず、Places Insights データセットから、各店舗から一定の距離内にあるすべての場所を見つける必要があります。この場合は、BigQuery 関数 ST_DWITHIN が最適です。apartment_listings テーブルと places_insights テーブルの間で JOIN を実行して、半径 800 メートル以内のすべての場所を見つけます。LEFT JOIN を使用すると、近くに一致する場所が見つからなくても、元の場所がすべて結果に含まれます。
高度な属性で関連性をフィルタする
ここでは、スコアの抽象的なコンセプトを具体的なデータフィルタに変換します。2 つのスコア例では、基準が異なります。
- 「ファミリー フレンドリー スコア」について: 子どもに特に適した公園、博物館、レストランを重視しています。
- 「ペットオーナーの楽園スコア」では、公園、動物病院、ペットショップ、犬同伴可のレストランやカフェを重視しています。
これらの特定の属性は、クエリの WHERE 句で直接フィルタできます。
各拠点の分析情報を集計する
最後に、各アパートメントについて、関連する場所がいくつ見つかったかを数える必要があります。GROUP BY 句は結果を集計し、COUNTIF 関数は各スコアの特定の条件に一致する場所をカウントします。
次のクエリは、これらの 3 つの手順を組み合わせて、1 回のパスで両方のスコアの生カウントを計算します。
-- This Common Table Expression (CTE) will hold the raw counts for each score component.
WITH insight_counts AS (
SELECT WITH AGGREGATION_THRESHOLD -- Correctly includes the mandatory aggregation threshold
apartments.id,
apartments.name,
COUNTIF(places.primary_type = 'park') AS park_count,
COUNTIF(places.primary_type = 'museum') AS museum_count,
COUNTIF(places.primary_type = 'restaurant' AND places.good_for_children = TRUE) AS family_restaurant_count,
COUNTIF(places.primary_type IN ('veterinary_care', 'pet_store')) AS pet_service_count,
COUNTIF(places.allows_dogs = TRUE) AS dog_friendly_place_count
FROM
`your_project.your_dataset.apartment_listings` AS apartments
LEFT JOIN
`your-project.places_insights___us.places` AS places -- Corrected table name for the US dataset
ON ST_DWITHIN(apartments.location, places.point, 800) -- Find places within 800 meters
GROUP BY
apartments.id, apartments.name
)
SELECT * FROM insight_counts;
このクエリの結果は次のようになります。

次のセクションでは、これらの結果を基に説明します。
3. スコアを作成する
これで、場所の数と、場所の種類ごとの重み付けが、場所ごとに取得できたので、カスタムの場所スコアを生成できます。このセクションでは、BigQuery で独自のカスタム計算を使用する方法と、BigQuery で生成 AI 関数を使用する方法の 2 つの方法について説明します。
オプション 1: BigQuery で独自のカスタム計算を使用する
前のステップの未加工のカウントは有益ですが、目標は単一のユーザー フレンドリーなスコアです。最後のステップは、重みを使用してこれらのカウントを組み合わせ、結果を 0 ~ 10 のスケールに正規化することです。
カスタムの重みを適用する 重みの選択は、芸術と科学の両面があります。ビジネス上の優先事項や、ユーザーにとって最も重要だと考えられることを反映する必要があります。「家族向け」スコアの場合、公園は博物館の 2 倍重要であると判断するかもしれません。最良の仮説から始め、ユーザー フィードバックに基づいてイテレーションを行います。
スコアの正規化 次のクエリでは、2 つの共通テーブル式(CTE)を使用します。1 つ目は以前と同様に未加工のカウントを計算し、2 つ目は重み付けされたスコアを計算します。最後の SELECT ステートメントは、重み付けされたスコアに対して最小値と最大値の正規化を実行します。例の apartment_listings テーブルの location 列が出力され、地図上のデータ可視化が可能になります。
WITH
-- CTE 1: Count nearby amenities of interest for each apartment listing.
insight_counts AS (
SELECT WITH AGGREGATION_THRESHOLD
apartments.id,
apartments.name,
COUNTIF(places.primary_type = 'park') AS park_count,
COUNTIF(places.primary_type = 'museum') AS museum_count,
COUNTIF(places.primary_type = 'restaurant' AND places.good_for_children = TRUE) AS family_restaurant_count,
COUNTIF(places.primary_type IN ('veterinary_care', 'pet_store')) AS pet_service_count,
COUNTIF(places.allows_dogs = TRUE) AS dog_friendly_place_count
FROM
`your_project.your_dataset.apartment_listings` AS apartments
LEFT JOIN
`your-project.places_insights___us.places` AS places
ON ST_DWITHIN(apartments.location, places.point, 800)
GROUP BY
apartments.id,
apartments.name
),
-- CTE 2: Apply custom weighting to the amenity counts to generate raw scores.
raw_scores AS (
SELECT
id,
name,
(park_count * 3.0) + (museum_count * 1.5) + (family_restaurant_count * 2.5) AS family_friendliness_score,
(park_count * 2.0) + (pet_service_count * 3.5) + (dog_friendly_place_count * 2.5) AS pet_paradise_score
FROM
insight_counts
)
-- Final Step: Normalize scores to a 0-10 scale and rejoin to retrieve the location geometry.
SELECT
raw_scores.id,
raw_scores.name,
apartments.location,
raw_scores.family_friendliness_score,
raw_scores.pet_paradise_score,
-- Normalize Family Score using a MIN/MAX window function.
ROUND(
COALESCE(
SAFE_DIVIDE(
(raw_scores.family_friendliness_score - MIN(raw_scores.family_friendliness_score) OVER ()),
(MAX(raw_scores.family_friendliness_score) OVER () - MIN(raw_scores.family_friendliness_score) OVER ())
) * 10,
0
),
2
) AS normalized_family_score,
-- Normalize Pet Score using a MIN/MAX window function.
ROUND(
COALESCE(
SAFE_DIVIDE(
(raw_scores.pet_paradise_score - MIN(raw_scores.pet_paradise_score) OVER ()),
(MAX(raw_scores.pet_paradise_score) OVER () - MIN(raw_scores.pet_paradise_score) OVER ())
) * 10,
0
),
2
) AS normalized_pet_score
FROM
raw_scores
JOIN
`your_project.your_dataset.apartment_listings` AS apartments
ON raw_scores.id = apartments.id;
クエリの結果は次のようになります。最後の 2 つの列は正規化されたスコアです。

正規化されたスコアについて
この最終的な正規化ステップがなぜ重要なのかを理解することが重要です。重み付けされた未加工のスコアは、場所の都市密度に応じて 0 から非常に大きな数値まで変動します。コンテキストがないと、ユーザーにとって 500 のスコアは意味がありません。
正規化により、これらの抽象的な数値が相対的なランキングに変換されます。結果を 0 ~ 10 の範囲にスケーリングすることで、特定のデータセット内の各ロケーションが他のロケーションと比較してどう評価されているかをスコアで明確に把握できます。
- スコア 10 が、最も高い生スコアの場所に割り当てられ、現在のセットで最適なオプションとしてマークされます。
- スコア 0 は、未加工のスコアが最も低い場所に割り当てられ、比較の基準値となります。これは、その場所のアメニティがゼロであることを意味するのではなく、評価対象の他のオプションと比較して最も適していないことを意味します。
- その他のスコアは、その間に比例して配置されるため、ユーザーはオプションを一目で比較できます。
オプション 2: AI.GENERATE 関数を使用する(Gemini)
固定の数式を使用する代わりに、BigQuery AI.GENERATE 関数を使用して、SQL ワークフロー内でカスタムのロケーション スコアを直接計算できます。
方法 1 は、アメニティの数に基づく純粋な定量的スコアリングには優れていますが、質的データを簡単に考慮することはできません。AI.GENERATE 関数を使用すると、Places Insights クエリの数値と、アパートのリスティングのテキスト説明(「2 ベッドルーム、2 バスルーム、バルコニー付き」など)のような非構造化データを組み合わせることができます。「この場所は家族連れに適しており、夜は静かです」)や、ユーザー プロフィールの特定の好み(例: 「このユーザーは家族で予約しており、中心部の静かなエリアを希望しています」など)。これにより、アメニティの密度が高いが「子供には騒がしい」と説明されている場所など、厳密なカウントでは見逃される可能性のある微妙なニュアンスを検出する、より微妙なスコアを生成できます。
プロンプトを作成する
この関数を使用するには、集計(ステップ 2)の結果を自然言語プロンプトにフォーマットします。これは、モデルの指示を含むデータ列を連結することで、SQL で動的に行うことができます。
次のクエリでは、insight_counts とアパートのテキスト説明を組み合わせて、各行のプロンプトを作成します。スコア付けのガイドとなるターゲット ユーザー プロファイルも定義されます。
SQL でスコアを生成する
次のクエリは、BigQuery でオペレーション全体を実行します。。
- プレイスの数を集計します(ステップ 2 で説明したとおり)。
- 各ロケーションのプロンプトを構築します。
- 呼び出し:
AI.GENERATE関数を呼び出して、Gemini モデルを使用してプロンプトを分析します。 - 結果をアプリケーションで使用できる構造化された形式に解析します。
WITH
-- CTE 1: Aggregate Place counts (Same as Step 2)
insight_counts AS (
SELECT WITH AGGREGATION_THRESHOLD
apartments.id,
apartments.name,
apartments.description, -- Assuming your table has a description column
COUNTIF(places.primary_type = 'park') AS park_count,
COUNTIF(places.primary_type = 'museum') AS museum_count,
COUNTIF(places.primary_type = 'restaurant' AND places.good_for_children = TRUE) AS family_restaurant_count
FROM
`your-project.your_dataset.apartment_listings` AS apartments
LEFT JOIN
`your-project.places_insights___us.places` AS places
ON ST_DWITHIN(apartments.location, places.point, 800)
GROUP BY
apartments.id, apartments.name, apartments.description
),
-- CTE 2: Construct the Prompt
prepared_prompts AS (
SELECT
id,
name,
FORMAT("""
You are an expert real estate analyst. Generate a 'Family-Friendliness Score' (0-10) for this location.
Target User: Young family with a toddler, looking for a balance of activity and quiet.
Location Data:
- Name: %s
- Description: %s
- Parks nearby: %d
- Museums nearby: %d
- Family-friendly restaurants nearby: %d
Scoring Rules:
- High importance: Proximity to parks and high restaurant count.
- Negative modifiers: Descriptions indicating excessive noise or nightlife focus.
- Positive modifiers: Descriptions indicating quiet streets or backyards.
""", name, description, park_count, museum_count, family_restaurant_count) AS prompt_text
FROM insight_counts
)
-- Final Step: Call AI.GENERATE
SELECT
id,
name,
-- Access the structured fields returned by the model
generated.family_friendliness_score,
generated.reasoning
FROM
prepared_prompts,
AI.GENERATE(
prompt_text,
endpoint => 'gemini-flash-latest',
output_schema => 'family_friendliness_score FLOAT64, reasoning STRING'
) AS generated;
構成を理解する
- 費用認識: この関数は、入力を Gemini モデルに渡し、呼び出されるたびに Vertex AI で料金が発生します。多数の場所(数千件のアパートのリスティングなど)が分析される場合は、まずデータセットを最も関連性の高い候補にフィルタリングすることをおすすめします。費用を最小限に抑える方法の詳細については、ベスト プラクティスをご覧ください。
endpoint: この例では、速度と費用対効果を優先するため、gemini-flash-latestが指定されています。ただし、ニーズに最適なモデルを選択できます。さまざまなバージョン(Gemini Pro など)を比較して、ユースケースに最適なモデルを見つけてください。output_schema: 生テキストを解析する代わりに、スキーマが適用されます(スコアはFLOAT64、推論はSTRING)。これにより、後処理なしで、アプリケーションや可視化ツールで出力をすぐに使用できるようになります。
出力例
クエリは、カスタム スコアとモデルの推論を含む標準の BigQuery テーブルを返します。
| id | name | family_friendliness_score | 推論 |
|---|---|---|---|
| 1 | The Downtowner | 5.5 | 優れたアメニティ数(公園、レストラン)で、定量的な指標を満たしている。しかし、定性データは、週末の騒音が過剰で、ナイトライフに重点が置かれていることを示しており、ターゲット ユーザーの静けさのニーズと直接的に矛盾しています。 |
| 2 | Suburban Oasis | 9.8 | 優れた定量データと、ターゲット ファミリーのプロファイルに完全に合致する説明(「静かな並木道」)を組み合わせます。正の修飾子が高いほど、スコアはほぼ完璧になります。 |
この手順では、個々のユーザーに合わせて調整された、わかりやすいスコアリングを 1 つの SQL クエリ内で実現できます。
4. スコアを地図上に可視化する
BigQuery Studio には、GEOGRAPHY 列を含むクエリ結果の統合マップの可視化機能が含まれています。クエリは location 列を出力するため、スコアをすぐに可視化できます。
[Visualization] タブをクリックすると地図が表示され、[Data Column] プルダウンで可視化する位置情報のスコアを制御できます。この例では、normalized_pet_score はオプション 1 の例から可視化されています。この例では、apartment_listings テーブルにロケーションが追加されています。

データを可視化すると、作成したスコアに最適な場所が一目でわかります。この例では、緑色の円が濃いほど normalized_pet_score が高い場所を示しています。Places Insights データの可視化オプションの詳細については、クエリ結果を可視化するをご覧ください。
まとめ
これで、ニュアンスのあるロケーション スコアを作成するための強力で再現可能な方法論が確立されました。まず、ロケーションから、ST_DWITHIN を使用して近くの場所を検索し、good_for_children や allows_dogs などの高度な属性でフィルタし、COUNTIF で結果を集計する単一の SQL クエリを BigQuery で作成しました。カスタムの重みを適用して結果を正規化することで、実用的な詳細な分析情報を提供する、ユーザーフレンドリーな単一のスコアが生成されました。このパターンを直接適用して、未加工の位置情報データを大きな競争優位性に変換できます。
次のアクション
次は、あなたが構築する番です。このチュートリアルでは、テンプレートを提供します。Places Insights スキーマで利用可能な豊富なデータを使用して、ユースケースに最も必要なスコアを作成できます。構築できる他のスコアは次のとおりです。
- 「ナイトライフ スコア」:
primary_type(bar、night_club)、price_level、深夜の営業時間に関するフィルタを組み合わせて、暗くなってから最も活気のあるエリアを見つけます。 - 「フィットネス&ウェルネス スコア」: 近くの
gyms、parks、health_food_storesをカウントし、serves_vegetarian_foodを含むレストランをフィルタして、健康志向のユーザー向けに場所をスコア化します。 - 「通勤者の理想のスコア」:
transit_stationやparkingの近くにある場所を特定し、交通機関へのアクセスを重視するユーザーをサポートします。
寄稿者
Henrik Valve | DevX エンジニア