创建 k-means 模型以对伦敦自行车租赁数据集进行聚类

本教程介绍了如何在 BigQuery ML 中使用 k-means 模型识别一组数据中的聚簇。

将数据分组为聚簇的 k-means 算法是非监督式机器学习的一种形式。监督式机器学习与预测分析有关,与此不同的是,非监督式机器学习与描述性分析有关。 非监督式机器学习可帮助您了解数据,以便您根据数据做出决策。

本教程中的查询使用地理空间分析中提供的地理位置函数。如需了解详情,请参阅地理空间分析简介

本教程使用伦敦自行车租赁公共数据集。数据包括起始和停止时间戳、车站名称和骑行时长。

创建数据集

创建 BigQuery 数据集以存储 k-means 模型:

  1. 在 Google Cloud 控制台中,前往 BigQuery 页面。

    转到 BigQuery 页面

  2. 在左侧窗格中,点击 Explorer

    突出显示的“探索器”窗格按钮。

    如果您没有看到左侧窗格,请点击 展开左侧窗格以打开该窗格。

  3. 探索器窗格中,点击您的项目名称。

  4. 点击 查看操作 > 创建数据集

    创建数据集。

  5. 创建数据集页面上,执行以下操作:

    • 数据集 ID 部分,输入 bqml_tutorial

    • 位置类型部分,选择多区域,然后选择 EU (multiple regions in European Union)(欧盟[欧盟的多个区域])。

      伦敦自行车租赁公共数据集存储在 EU 多区域。数据集必须位于同一位置。

    • 保持其余默认设置不变,然后点击创建数据集

      创建数据集页面。

检查训练数据

检查您将用于训练 k-means 模型的数据。在本教程中,您根据以下特性为自行车站划分聚簇:

  • 租赁时长
  • 每天的行程数量
  • 与市中心的距离

SQL

此查询提取有关自行车租赁的数据(包括 start_station_nameduration 列),并将此数据与车站信息联接。其中包括创建一个包含车站距离市中心的计算列。然后,查询会在 stationstats 列中计算车站的特性(包括平均骑行时长和行程数量),以及计算出的 distance_from_city_center 列。

请按照以下步骤检查训练数据:

  1. 在 Google Cloud 控制台中,前往 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,粘贴以下查询,然后点击运行

    WITH
    hs AS (
      SELECT
        h.start_station_name AS station_name,
        IF(
          EXTRACT(DAYOFWEEK FROM h.start_date) = 1
            OR EXTRACT(DAYOFWEEK FROM h.start_date) = 7,
          'weekend',
          'weekday') AS isweekday,
        h.duration,
        ST_DISTANCE(ST_GEOGPOINT(s.longitude, s.latitude), ST_GEOGPOINT(-0.1, 51.5)) / 1000
          AS distance_from_city_center
      FROM
        `bigquery-public-data.london_bicycles.cycle_hire` AS h
      JOIN
        `bigquery-public-data.london_bicycles.cycle_stations` AS s
        ON
          h.start_station_id = s.id
      WHERE
        h.start_date
        BETWEEN CAST('2015-01-01 00:00:00' AS TIMESTAMP)
        AND CAST('2016-01-01 00:00:00' AS TIMESTAMP)
    ),
    stationstats AS (
      SELECT
        station_name,
        isweekday,
        AVG(duration) AS duration,
        COUNT(duration) AS num_trips,
        MAX(distance_from_city_center) AS distance_from_city_center
      FROM
        hs
      GROUP BY
        station_name, isweekday
    )
    SELECT *
    FROM
    stationstats
    ORDER BY
    distance_from_city_center ASC;

结果应如下所示:

查询结果

BigQuery DataFrame

在尝试此示例之前,请按照《BigQuery 快速入门:使用 BigQuery DataFrames》中的 BigQuery DataFrames 设置说明进行操作。如需了解详情,请参阅 BigQuery DataFrames 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭证。如需了解详情,请参阅为本地开发环境设置 ADC

import datetime
import typing

import pandas as pd
from shapely.geometry import Point

import bigframes
import bigframes.bigquery as bbq
import bigframes.geopandas
import bigframes.pandas as bpd

bigframes.options.bigquery.project = your_gcp_project_id
# Compute in the EU multi-region to query the London bicycles dataset.
bigframes.options.bigquery.location = "EU"

# Extract the information you'll need to train the k-means model in this
# tutorial. Use the read_gbq function to represent cycle hires
# data as a DataFrame.
h = bpd.read_gbq(
    "bigquery-public-data.london_bicycles.cycle_hire",
    col_order=["start_station_name", "start_station_id", "start_date", "duration"],
).rename(
    columns={
        "start_station_name": "station_name",
        "start_station_id": "station_id",
    }
)

# Use GeoSeries.from_xy and BigQuery.st_distance to analyze geographical
# data. These functions determine spatial relationships between
# geographical features.
cycle_stations = bpd.read_gbq("bigquery-public-data.london_bicycles.cycle_stations")
s = bpd.DataFrame(
    {
        "id": cycle_stations["id"],
        "xy": bigframes.geopandas.GeoSeries.from_xy(
            cycle_stations["longitude"], cycle_stations["latitude"]
        ),
    }
)
s_distance = bbq.st_distance(s["xy"], Point(-0.1, 51.5), use_spheroid=False) / 1000
s = bpd.DataFrame({"id": s["id"], "distance_from_city_center": s_distance})

# Define Python datetime objects in the UTC timezone for range comparison,
# because BigQuery stores timestamp data in the UTC timezone.
sample_time = datetime.datetime(2015, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
sample_time2 = datetime.datetime(2016, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)

h = h.loc[(h["start_date"] >= sample_time) & (h["start_date"] <= sample_time2)]

# Replace each day-of-the-week number with the corresponding "weekday" or
# "weekend" label by using the Series.map method.
h = h.assign(
    isweekday=h.start_date.dt.dayofweek.map(
        {
            0: "weekday",
            1: "weekday",
            2: "weekday",
            3: "weekday",
            4: "weekday",
            5: "weekend",
            6: "weekend",
        }
    )
)

# Supplement each trip in "h" with the station distance information from
# "s" by merging the two DataFrames by station ID.
merged_df = h.merge(
    right=s,
    how="inner",
    left_on="station_id",
    right_on="id",
)

# Engineer features to cluster the stations. For each station, find the
# average trip duration, number of trips, and distance from city center.
stationstats = typing.cast(
    bpd.DataFrame,
    merged_df.groupby(["station_name", "isweekday"]).agg(
        {"duration": ["mean", "count"], "distance_from_city_center": "max"}
    ),
)
stationstats.columns = pd.Index(
    ["duration", "num_trips", "distance_from_city_center"]
)
stationstats = stationstats.sort_values(
    by="distance_from_city_center", ascending=True
).reset_index()

# Expected output results: >>> stationstats.head(3)
# station_name	isweekday duration  num_trips	distance_from_city_center
# Borough Road...	weekday	    1110	    5749	    0.12624
# Borough Road...	weekend	    2125	    1774	    0.12624
# Webber Street...	weekday	    795	        6517	    0.164021
#   3 rows × 5 columns

创建 k-means 模型

使用伦敦自行车租赁训练数据创建 k-means 模型。

SQL

在以下查询中,CREATE MODEL 语句指定要使用的聚簇数量为 4。在 SELECT 语句中,EXCEPT 子句不包括 station_name 列,因为此列不包含特征。此查询为每个 station_name 创建一个唯一行,并且 SELECT 语句中只提及特征。

请按照以下步骤创建 k-means 模型:

  1. 在 Google Cloud 控制台中,前往 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,粘贴以下查询,然后点击运行

    CREATE OR REPLACE MODEL `bqml_tutorial.london_station_clusters`
    OPTIONS (
      model_type = 'kmeans',
      num_clusters = 4)
    AS
    WITH
    hs AS (
      SELECT
        h.start_station_name AS station_name,
        IF(
          EXTRACT(DAYOFWEEK FROM h.start_date) = 1
            OR EXTRACT(DAYOFWEEK FROM h.start_date) = 7,
          'weekend',
          'weekday') AS isweekday,
        h.duration,
        ST_DISTANCE(ST_GEOGPOINT(s.longitude, s.latitude), ST_GEOGPOINT(-0.1, 51.5)) / 1000
          AS distance_from_city_center
      FROM
        `bigquery-public-data.london_bicycles.cycle_hire` AS h
      JOIN
        `bigquery-public-data.london_bicycles.cycle_stations` AS s
        ON
          h.start_station_id = s.id
      WHERE
        h.start_date
        BETWEEN CAST('2015-01-01 00:00:00' AS TIMESTAMP)
        AND CAST('2016-01-01 00:00:00' AS TIMESTAMP)
    ),
    stationstats AS (
      SELECT
        station_name,
        isweekday,
        AVG(duration) AS duration,
        COUNT(duration) AS num_trips,
        MAX(distance_from_city_center) AS distance_from_city_center
      FROM
        hs
      GROUP BY
        station_name, isweekday
    )
    SELECT *
    EXCEPT (station_name, isweekday)
    FROM
    stationstats;

BigQuery DataFrame

在尝试此示例之前,请按照《BigQuery 快速入门:使用 BigQuery DataFrames》中的 BigQuery DataFrames 设置说明进行操作。如需了解详情,请参阅 BigQuery DataFrames 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭证。如需了解详情,请参阅为本地开发环境设置 ADC


from bigframes.ml.cluster import KMeans

# To determine an optimal number of clusters, construct and fit several
# K-Means objects with different values of num_clusters, find the error
# measure, and pick the point at which the error measure is at its minimum
# value.
cluster_model = KMeans(n_clusters=4)
cluster_model.fit(stationstats)
cluster_model.to_gbq(
    your_model_id,  # For example: "bqml_tutorial.london_station_clusters"
    replace=True,
)

解读数据聚簇

模型的评估标签页中的信息可以帮助您解读模型生成的聚簇。

请按照以下步骤查看模型的评估信息:

  1. 在 Google Cloud 控制台中,前往 BigQuery 页面。

    转到 BigQuery

  2. 在左侧窗格中,点击 Explorer

    突出显示的“探索器”窗格按钮。

  3. 探索器窗格中,展开您的项目,然后点击数据集

  4. 点击 bqml_tutorial 数据集,然后前往模型标签页。

  5. 选择 london_station_clusters 模型。

  6. 选择评估标签页。此标签页显示 k-means 模型标识的聚簇的可视化。在数值特征部分中,条形图显示每个形心的最重要的数字特征值。每个形心都代表一个给定的数据聚簇。您可以从下拉菜单中选择要可视化的特征。

    数字特征图表

    此模型会创建以下形心:

    • 形心 1 显示一个不太繁忙的城市车站,租期较短。
    • 形心 2 显示第二个城市车站,该车站不太繁忙,用于较长的租期。
    • 形心 3 显示靠近市中心的一个繁忙城市车站。
    • 形心 4 显示一个行程较长的郊区车站。

    如果您经营的是自行车租赁业务,就可以利用这些信息为业务决策提供依据。例如:

    • 假设您需要对一种新型车锁进行实验。您应该选择哪些车站作为此次实验的对象?形心 1、形心 2 或形心 4 中的车站似乎是合乎逻辑的选择,因为它们不是最繁忙的车站。

    • 假设您想要在一些车站投放赛车。您应该选择哪些车站?形心 4 是距离市中心较远且行程最长的车站组。这些车站适合作为赛车的候选车站。

使用 ML.PREDICT 函数预测车站的聚簇

使用 ML.PREDICT SQL 函数或 predict BigQuery DataFrames 函数来识别特定车站所属的聚簇。

SQL

以下查询使用 REGEXP_CONTAINS 函数查找 station_name 列中包含字符串 Kennington 的所有条目。ML.PREDICT 函数使用这些值来预测哪些聚簇将包含这些车站。

请按照以下步骤预测名称中包含字符串 Kennington 的每个车站的聚簇:

  1. 在 Google Cloud 控制台中,前往 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,粘贴以下查询,然后点击运行

    WITH
    hs AS (
      SELECT
        h.start_station_name AS station_name,
        IF(
          EXTRACT(DAYOFWEEK FROM h.start_date) = 1
            OR EXTRACT(DAYOFWEEK FROM h.start_date) = 7,
          'weekend',
          'weekday') AS isweekday,
        h.duration,
        ST_DISTANCE(ST_GEOGPOINT(s.longitude, s.latitude), ST_GEOGPOINT(-0.1, 51.5)) / 1000
          AS distance_from_city_center
      FROM
        `bigquery-public-data.london_bicycles.cycle_hire` AS h
      JOIN
        `bigquery-public-data.london_bicycles.cycle_stations` AS s
        ON
          h.start_station_id = s.id
      WHERE
        h.start_date
        BETWEEN CAST('2015-01-01 00:00:00' AS TIMESTAMP)
        AND CAST('2016-01-01 00:00:00' AS TIMESTAMP)
    ),
    stationstats AS (
      SELECT
        station_name,
        isweekday,
        AVG(duration) AS duration,
        COUNT(duration) AS num_trips,
        MAX(distance_from_city_center) AS distance_from_city_center
      FROM
        hs
      GROUP BY
        station_name, isweekday
    )
    SELECT *
    EXCEPT (nearest_centroids_distance)
    FROM
    ML.PREDICT(
      MODEL `bqml_tutorial.london_station_clusters`,
      (
        SELECT *
        FROM
          stationstats
        WHERE
          REGEXP_CONTAINS(station_name, 'Kennington')
      ));

结果应类似于以下内容。

ML.PREDICT 结果

BigQuery DataFrame

在尝试此示例之前,请按照《BigQuery 快速入门:使用 BigQuery DataFrames》中的 BigQuery DataFrames 设置说明进行操作。如需了解详情,请参阅 BigQuery DataFrames 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭证。如需了解详情,请参阅为本地开发环境设置 ADC


# Select model you'll use for predictions. `read_gbq_model` loads model
# data from BigQuery, but you could also use the `cluster_model` object
# from previous steps.
cluster_model = bpd.read_gbq_model(
    your_model_id,
    # For example: "bqml_tutorial.london_station_clusters",
)

# Use 'contains' function to filter by stations containing the string
# "Kennington".
stationstats = stationstats.loc[
    stationstats["station_name"].str.contains("Kennington")
]

result = cluster_model.predict(stationstats)

# Expected output results:   >>>results.peek(3)
# CENTROID...	NEAREST...	station_name  isweekday	 duration num_trips dist...
# 	1	[{'CENTROID_ID'...	Borough...	  weekday	  1110	    5749	0.13
# 	2	[{'CENTROID_ID'...	Borough...	  weekend	  2125      1774	0.13
# 	1	[{'CENTROID_ID'...	Webber...	  weekday	  795	    6517	0.16
#   3 rows × 7 columns