Crear un modelo k-medias para agrupar el conjunto de datos de alquileres de bicicletas de Londres

En este tutorial se explica cómo usar un modelo de k-means en BigQuery ML para identificar clústeres en un conjunto de datos.

El algoritmo k-medias que agrupa tus datos en clústeres es una forma de aprendizaje automático no supervisado. A diferencia del aprendizaje automático supervisado, que se centra en el análisis predictivo, el aprendizaje automático no supervisado se centra en el análisis descriptivo. El aprendizaje automático no supervisado puede ayudarte a comprender tus datos para que puedas tomar decisiones basadas en ellos.

Las consultas de este tutorial usan funciones de geografía disponibles en las analíticas geoespaciales. Para obtener más información, consulta la introducción a las analíticas geoespaciales.

En este tutorial se usa el conjunto de datos público de alquiler de bicicletas de Londres. Los datos incluyen marcas de tiempo de inicio y finalización, nombres de las estaciones y duración del trayecto.

Crear conjunto de datos

Crea un conjunto de datos de BigQuery para almacenar tu modelo de k-means:

  1. En la Google Cloud consola, ve a la página BigQuery.

    Ir a la página de BigQuery

  2. En el panel de la izquierda, haz clic en Explorador:

    Botón destacado del panel Explorador.

    Si no ves el panel de la izquierda, haz clic en Ampliar panel de la izquierda para abrirlo.

  3. En el panel Explorador, haz clic en el nombre de tu proyecto.

  4. Haz clic en Ver acciones > Crear conjunto de datos.

    Crea un conjunto de datos.

  5. En la página Crear conjunto de datos, haz lo siguiente:

    • En ID del conjunto de datos, introduce bqml_tutorial.

    • En Tipo de ubicación, selecciona Multirregional y, a continuación, UE (varias regiones de la Unión Europea).

      El conjunto de datos público London Bicycle Hires se almacena en la EU multirregión. El conjunto de datos debe estar en la misma ubicación.

    • Deje el resto de los ajustes predeterminados como están y haga clic en Crear conjunto de datos.

      Página de creación de conjuntos de datos.

Examinar los datos de entrenamiento

Examina los datos que usarás para entrenar tu modelo de k-means. En este tutorial, agruparás las estaciones de bicicletas en clústeres en función de los siguientes atributos:

  • Duración de los alquileres
  • Número de viajes al día
  • Distancia del centro de la ciudad

SQL

Esta consulta extrae datos sobre alquileres de bicicletas, incluidas las columnas start_station_name y duration, y combina estos datos con información de las estaciones. Esto incluye la creación de una columna calculada que contenga la distancia de la estación al centro de la ciudad. A continuación, calcula los atributos de la estación en una columna stationstats, incluida la duración media de los trayectos y el número de viajes, así como la columna distance_from_city_center calculada.

Sigue estos pasos para examinar los datos de entrenamiento:

  1. En la Google Cloud consola, ve a la página BigQuery.

    Ir a BigQuery

  2. En el editor de consultas, pega la siguiente consulta y haz clic en Ejecutar:

    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;

Los resultados deberían ser similares a los siguientes:

Resultados de la consulta

BigQuery DataFrames

Antes de probar este ejemplo, sigue las instrucciones de configuración de BigQuery DataFrames que se indican en la guía de inicio rápido de BigQuery con BigQuery DataFrames. Para obtener más información, consulta la documentación de referencia de los DataFrames de BigQuery.

Para autenticarte en BigQuery, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configurar ADC en un entorno de desarrollo local.

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

Crear un modelo k-medias

Crea un modelo k-medias con los datos de entrenamiento de alquileres de bicicletas de Londres.

SQL

En la siguiente consulta, la instrucción CREATE MODEL especifica el número de clústeres que se van a usar (cuatro). En la instrucción SELECT, la cláusula EXCEPT excluye la columna station_name porque no contiene ninguna característica. La consulta crea una fila única por station_name y solo se mencionan las características en la instrucción SELECT.

Para crear un modelo k-medias, sigue estos pasos:

  1. En la Google Cloud consola, ve a la página BigQuery.

    Ir a BigQuery

  2. En el editor de consultas, pega la siguiente consulta y haz clic en Ejecutar:

    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 DataFrames

Antes de probar este ejemplo, sigue las instrucciones de configuración de BigQuery DataFrames que se indican en la guía de inicio rápido de BigQuery con BigQuery DataFrames. Para obtener más información, consulta la documentación de referencia de los DataFrames de BigQuery.

Para autenticarte en BigQuery, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configurar ADC en un entorno de desarrollo local.


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,
)

Interpretar los clústeres de datos

La información de la pestaña Evaluación de los modelos puede ayudarte a interpretar los clústeres que genera el modelo.

Sigue estos pasos para ver la información de evaluación del modelo:

  1. En la Google Cloud consola, ve a la página BigQuery.

    Ir a BigQuery

  2. En el panel de la izquierda, haz clic en Explorador:

    Botón destacado del panel Explorador.

  3. En el panel Explorador, expande tu proyecto y haz clic en Conjuntos de datos.

  4. Haga clic en el conjunto de datos bqml_tutorial y, a continuación, vaya a la pestaña Modelos.

  5. Selecciona el modelo london_station_clusters.

  6. Selecciona la pestaña Evaluación. En esta pestaña se muestran visualizaciones de los clústeres identificados por el modelo de k-means. En la sección Funciones numéricas, los gráficos de barras muestran los valores de las funciones numéricas más importantes de cada centroide. Cada centroide representa un clúster de datos determinado. Puedes seleccionar las funciones que quieras visualizar en el menú desplegable.

    Gráficos de funciones numéricas

    Este modelo crea los siguientes centroides:

    • El centroide 1 muestra una estación de la ciudad menos concurrida, con alquileres de menor duración.
    • El centroide 2 muestra la segunda estación de la ciudad, que tiene menos actividad y se usa para alquileres de mayor duración.
    • El centroide 3 muestra una estación de una ciudad con mucho movimiento que está cerca del centro.
    • El centroide 4 muestra una estación de cercanías con trayectos más largos.

    Si tuvieras una empresa de alquiler de bicicletas, podrías usar esta información para tomar decisiones empresariales. Por ejemplo:

    • Supongamos que quieres probar un nuevo tipo de bloqueo. ¿Qué clúster de estaciones deberías elegir como sujeto para este experimento? Las estaciones de los centroides 1, 2 o 4 parecen opciones lógicas porque no son las más concurridas.

    • Supongamos que quieres incluir bicicletas de carreras en algunas estaciones. ¿Qué emisoras deberías elegir? El centroide 4 es el grupo de estaciones que están lejos del centro de la ciudad y que tienen los trayectos más largos. Es probable que sean candidatas para bicicletas de carreras.

Usar la función ML.PREDICT para predecir el clúster de una estación

Identifica el clúster al que pertenece una estación concreta mediante la ML.PREDICTfunción SQL o la predictfunción DataFrames de BigQuery.

SQL

En la siguiente consulta se usa la función REGEXP_CONTAINS para buscar todas las entradas de la columna station_name que contengan la cadena Kennington. La función ML.PREDICT usa esos valores para predecir qué clústeres pueden contener esas estaciones.

Sigue estos pasos para predecir el clúster de cada estación que tenga la cadena Kennington en su nombre:

  1. En la Google Cloud consola, ve a la página BigQuery.

    Ir a BigQuery

  2. En el editor de consultas, pega la siguiente consulta y haz clic en Ejecutar:

    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')
      ));

Los resultados deberían ser similares a los siguientes.

Resultados de ML.PREDICT

BigQuery DataFrames

Antes de probar este ejemplo, sigue las instrucciones de configuración de BigQuery DataFrames que se indican en la guía de inicio rápido de BigQuery con BigQuery DataFrames. Para obtener más información, consulta la documentación de referencia de los DataFrames de BigQuery.

Para autenticarte en BigQuery, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configurar ADC en un entorno de desarrollo local.


# 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