{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "apache-license-cell", "metadata": {}, "outputs": [], "source": [ "# Copyright 2025 Google LLC\n", "#\n", "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License.\n" ] }, { "cell_type": "markdown", "id": "077f0740-c394-4f49-8a5e-14125dacd340", "metadata": { "id": "077f0740-c394-4f49-8a5e-14125dacd340" }, "source": [ "# Accelerated Data Analytics with Google Cloud and NVIDIA" ] }, { "cell_type": "markdown", "id": "546nxPZMk0UW", "metadata": { "id": "546nxPZMk0UW" }, "source": [ "| Authors |\n", "| --- |\n", "| Jeff Nelson, Will Hill |" ] }, { "cell_type": "markdown", "id": "pO1iOPohk2rd", "metadata": { "id": "pO1iOPohk2rd" }, "source": [ "## Overview\n", "\n", "In this codelab, you will learn how to accelerate your data analytics workflows on large datasets using NVIDIA GPUs and open-source libraries on Google Cloud. You will start by optimizing your infrastructure and then explore how to apply GPU acceleration with zero code changes.\n", "\n", "You will focus on `pandas`, a popular data manipulation library, and learn how to accelerate it using NVIDIA's `cuDF` library. The best part is you can get this GPU acceleration without changing your existing `pandas` and `scikit-learn` code." ] }, { "cell_type": "markdown", "id": "41d1dd05-fe46-451b-9e9c-23a239539b84", "metadata": { "id": "41d1dd05-fe46-451b-9e9c-23a239539b84" }, "source": [ "### Objectives\n", "\n", "* Understand Colab Enterprise on Google Cloud.\n", "* Customize a Colab runtime environment with specific GPU, CPU, and memory configurations.\n", "* Accelerate `pandas` with zero code changes using NVIDIA `cuDF`.\n", "* Profile your code to identify and optimize performance bottlenecks." ] }, { "cell_type": "markdown", "id": "0a28c0e6", "metadata": {}, "source": [ "**Note:** This notebook is intended to run in Colab Enterprise with a [GPU-enabled runtime](https://docs.cloud.google.com/colab/docs/default-runtimes-with-gpus)." ] }, { "cell_type": "markdown", "id": "dWVpCu9qlBxi", "metadata": { "id": "dWVpCu9qlBxi" }, "source": [ "### Services and Costs\n", "\n", "This tutorial uses the following billable components of Google Cloud:\n", "\n", "* **Colab Enterprise**: [Pricing](https://cloud.google.com/colab/pricing)\n", "\n", "* **Google Cloud Storage**: [Pricing](https://cloud.google.com/storage/pricing)\n", "\n", "\n", "You can use the [Pricing Calculator](https://cloud.google.com/products/calculator) to generate a cost estimate based on your projected usage." ] }, { "cell_type": "markdown", "id": "new-setup-section-id", "metadata": {}, "source": [ "## Setup\n", "\n", "**Note:** This notebook is intended to run in Colab Enterprise with a [GPU-enabled runtime](https://docs.cloud.google.com/colab/docs/default-runtimes-with-gpus). If you run this notebook elsewhere, you must ensure you have a compatible NVIDIA GPU and have installed the necessary RAPIDS libraries (`cuDF`, `cuML`). Please refer to the [RAPIDS installation guide](https://docs.rapids.ai/install) for environment-specific instructions." ] }, { "cell_type": "markdown", "id": "ZdiJgBVtpMWV", "metadata": { "id": "ZdiJgBVtpMWV" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Prepare the NYC taxi dataset\n", "\n", "This codelab uses the [NYC Taxi & Limousine Commission (TLC) Trip Record Data](https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page).\n", "\n", "The dataset contains individual trip records from yellow taxis in New York City, and includes fields like:\n", "* Pick-up and drop-off dates, times, and locations\n", "* Trip distances\n", "* Itemized fare amounts\n", "* Passenger counts" ] }, { "cell_type": "markdown", "id": "X4EHora4pYGz", "metadata": { "id": "X4EHora4pYGz" }, "source": [ "### Download the data\n", "\n", "Next, download the trip data for all of 2024. The data is stored in the Parquet file format.\n", "\n", "The following code block performs these steps:\n", "1. Defines the range of data to download.\n", "2. Creates a local directory named `nyc_taxi_data` to store the files.\n", "3. Loops through each month, downloads the corresponding Parquet file if it doesn't already exist, and saves it to the directory." ] }, { "cell_type": "code", "execution_count": null, "id": "K8TNoHuZOcQu", "metadata": { "id": "K8TNoHuZOcQu" }, "outputs": [], "source": [ "from tqdm import tqdm\n", "import requests\n", "import time\n", "import os\n", "\n", "YEAR = 2024\n", "DATA_DIR = \"nyc_taxi_data\"\n", "\n", "os.makedirs(DATA_DIR, exist_ok=True)\n", "print(f\"Checking/Downloading files for {YEAR}...\")\n", "\n", "\n", "for month in tqdm(range(1, 13), unit=\"file\"):\n", "\n", " # Define standardized filename for both local path and URL\n", " file_name = f\"yellow_tripdata_{YEAR}-{month:02d}.parquet\"\n", " local_path = os.path.join(DATA_DIR, file_name)\n", " url = f\"https://d37ci6vzurychx.cloudfront.net/trip-data/{file_name}\"\n", "\n", " if not os.path.exists(local_path):\n", " try:\n", " with requests.get(url, stream=True) as response:\n", " response.raise_for_status()\n", " with open(local_path, 'wb') as f:\n", " for chunk in response.iter_content(chunk_size=8192):\n", " f.write(chunk)\n", " time.sleep(1)\n", " except requests.exceptions.HTTPError as e:\n", "\n", " print(f\"\\nSkipping {file_name}: {e}\")\n", "\n", " if os.path.exists(local_path):\n", " os.remove(local_path)\n", "\n", "print(\"\\nDownload complete.\")" ] }, { "cell_type": "markdown", "id": "IJ9BI8mInul8", "metadata": { "id": "IJ9BI8mInul8" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Explore the taxi trip data\n", "\n", "Now that you've downloaded the dataset, it's time to perform an initial exploratory data analysis (EDA). The goal of EDA is to understand the data's structure, find anomalies, and uncover potential patterns." ] }, { "cell_type": "markdown", "id": "jnwZt0OnrcbZ", "metadata": { "id": "jnwZt0OnrcbZ" }, "source": [ "### Load a single month of data\n", "\n", "Begin by loading a single month's worth of data. This provides a large enough sample (over 3 million rows) to be meaningful while keeping memory usage manageable for interactive analysis." ] }, { "cell_type": "code", "execution_count": null, "id": "mARNbcmQriTA", "metadata": { "id": "mARNbcmQriTA" }, "outputs": [], "source": [ "import pandas as pd\n", "\n", "# Load the last month of the downloaded data\n", "df = pd.read_parquet(\"nyc_taxi_data/yellow_tripdata_2024-12.parquet\")\n", "df.head()" ] }, { "cell_type": "markdown", "id": "Tngv8LWdroVi", "metadata": { "id": "Tngv8LWdroVi" }, "source": [ "### Get summary statistics\n", "\n", "Use the `.describe()` method to generate high-level summary statistics for the numerical columns. This is a great first step to spot potential data quality issues, such as unexpected minimum or maximum values." ] }, { "cell_type": "code", "execution_count": null, "id": "HTNt8dMerni0", "metadata": { "id": "HTNt8dMerni0" }, "outputs": [], "source": [ "df.describe().round(2)" ] }, { "cell_type": "markdown", "id": "QzwVZFTxr-NL", "metadata": { "id": "QzwVZFTxr-NL" }, "source": [ "### Investigate data quality\n", "\n", "The output from `.describe()` immediately reveals an issue. Notice that the `min` value for `tpep_pickup_datetime` and `tpep_dropoff_datetime` is in the year 2008, which doesn't make sense for a 2024 dataset.\n", "\n", "This is an example of why to always inspect your data. You can investigate this further by sorting the DataFrame to find the exact rows that contain these outlier dates." ] }, { "cell_type": "code", "execution_count": null, "id": "iI-MCr4xr3Qd", "metadata": { "id": "iI-MCr4xr3Qd" }, "outputs": [], "source": [ "# Sort by the dropoff datetime to see the oldest records\n", "df.sort_values(\"tpep_pickup_datetime\").head()" ] }, { "cell_type": "markdown", "id": "LYdmYx0-sOkn", "metadata": { "id": "LYdmYx0-sOkn" }, "source": [ "### Visualize data distributions\n", "\n", "Next, you can create histograms of the numerical columns to visualize their distributions. This helps you understand the spread and skew of features like `trip_distance` and `fare_amount`. The `.hist()` function is a quick way to plot histograms for all numerical columns in a DataFrame." ] }, { "cell_type": "code", "execution_count": null, "id": "BpP8FR6gsORu", "metadata": { "id": "BpP8FR6gsORu" }, "outputs": [], "source": [ "_ = df.hist(figsize=(20, 20))" ] }, { "cell_type": "markdown", "id": "fq-i-btDsoc7", "metadata": { "id": "fq-i-btDsoc7" }, "source": [ "Finally, generate a scatter matrix to visualize the relationships between a few key columns. Because plotting millions of points is slow and can obscure patterns, use `.sample()` to create the plot from a random sample of 100,000 rows." ] }, { "cell_type": "code", "execution_count": null, "id": "6HhQ1peRsrNM", "metadata": { "id": "6HhQ1peRsrNM" }, "outputs": [], "source": [ "_ = pd.plotting.scatter_matrix(\n", " df[['passenger_count', 'trip_distance', 'tip_amount', 'total_amount']].sample(100_000),\n", " diagonal=\"kde\",\n", " figsize=(15, 15)\n", ")" ] }, { "cell_type": "markdown", "id": "QXqON139ncyE", "metadata": { "id": "QXqON139ncyE" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Why use the Parquet file format?\n", "\n", "The NYC taxi dataset is provided in [Apache Parquet](https://parquet.apache.org/) format. This is a deliberate choice made for large-scale analytics. Parquet offers several advantages over file types like CSV:\n", "\n", "* **Efficient and Fast:** As a columnar format, Parquet is highly efficient to store and read. It supports modern compression methods that result in smaller file sizes and significantly faster I/O, especially on GPUs.\n", "* **Preserves the Schema:** Parquet stores data types in the file's metadata. You never have to guess data types when you read the file.\n", "* **Enables Selective Reading:** The columnar structure allows you to read only the specific columns you need for an analysis. This can dramatically reduce the amount of data you have to load into memory." ] }, { "cell_type": "markdown", "id": "AEK3QiQXtGGn", "metadata": { "id": "AEK3QiQXtGGn" }, "source": [ "### Inspect metadata without loading the full dataset\n", "\n", "While you can't view a Parquet file in a standard text editor, you can easily inspect its schema and metadata without loading any data into memory. This is useful for quickly understanding the structure of a file." ] }, { "cell_type": "code", "execution_count": null, "id": "eN3-P7bstzeD", "metadata": { "id": "eN3-P7bstzeD" }, "outputs": [], "source": [ "import pyarrow as pa\n", "from pyarrow.parquet import ParquetFile\n", "\n", "pf = ParquetFile('nyc_taxi_data/yellow_tripdata_2024-12.parquet')\n", "\n", "# Print the schema\n", "print(\"File Schema:\")\n", "print(pf.schema)\n", "\n", "# Print the file metadata\n", "print(\"\\nFile Metadata:\")\n", "print(pf.metadata)" ] }, { "cell_type": "markdown", "id": "4dTPizIRt98o", "metadata": { "id": "4dTPizIRt98o" }, "source": [ "### Read only the columns you need\n", "\n", "Imagine you only need to analyze trip distance and fare amounts. With Parquet, you can load just those columns, which is much faster and more memory-efficient than loading the entire DataFrame." ] }, { "cell_type": "code", "execution_count": null, "id": "NKzOtbnvuAxd", "metadata": { "id": "NKzOtbnvuAxd" }, "outputs": [], "source": [ "import pandas as pd\n", "\n", "# Read only four specific columns from the Parquet file\n", "df_subset = pd.read_parquet(\n", " 'nyc_taxi_data/yellow_tripdata_2024-12.parquet',\n", " columns=['passenger_count', 'trip_distance', 'tip_amount', 'total_amount']\n", ")\n", "\n", "df_subset.head()" ] }, { "cell_type": "markdown", "id": "K6AGXHzAu4iV", "metadata": { "id": "K6AGXHzAu4iV" }, "source": [ "\n", "---\n", "\n", "\n", "## Accelerate pandas with NVIDIA cuDF\n", "\n", "[NVIDIA CUDA for DataFrames (cuDF)](https://docs.rapids.ai/api/cudf/stable/) is an open-source, GPU-accelerated library that allows you to interact with DataFrames. cuDF lets you to perform common data operations like filtering, joining, and grouping on the GPU with massive parallelism.\n", "\n", "The key feature you use in this codelab is the `cudf.pandas` accelerator mode. When you enable it, your standard `pandas` code is automatically redirected to use GPU-powered `cuDF` kernels under the hood, all without requiring you to change your code." ] }, { "cell_type": "markdown", "id": "alsYWAcsnEbO", "metadata": { "id": "alsYWAcsnEbO" }, "source": [ "### Enable GPU acceleration\n", "\n", "To use NVIDIA cuDF in a Colab Enterprise notebook, you load its magic extension before you import `pandas`.\n", "\n", "First, inspect the standard `pandas` library. Notice the output shows the path to the default `pandas` installation." ] }, { "cell_type": "code", "execution_count": null, "id": "GhZXJ3bKvGk5", "metadata": { "id": "GhZXJ3bKvGk5" }, "outputs": [], "source": [ "import pandas as pd\n", "pd # Note the output for the standard pandas library" ] }, { "cell_type": "markdown", "id": "g4BqVfwuvJ5Z", "metadata": { "id": "g4BqVfwuvJ5Z" }, "source": [ "Now, load the `cudf.pandas` extension and import `pandas` again. Watch how the output for the `pd` module changes - this confirms that the GPU-accelerated version is now active." ] }, { "cell_type": "code", "execution_count": null, "id": "rNBf_eV5vMVq", "metadata": { "id": "rNBf_eV5vMVq" }, "outputs": [], "source": [ "%load_ext cudf.pandas\n", "import pandas as pd\n", "pd # Note the new output, indicating cudf.pandas is active" ] }, { "cell_type": "markdown", "id": "markdown-before-shutdown-1", "metadata": {}, "source": [ "### Restarting the Kernel\n", "\n", "To ensure a clean environment for the next steps and to properly activate `cudf.pandas`, it is often necessary to restart the kernel after loading the extension. This step ensures that all subsequent `pandas` imports correctly use the GPU-accelerated `cuDF` backend. When using \"Run All\", execution will stop here, and you will need to continue manually after the restart." ] }, { "cell_type": "code", "execution_count": null, "id": "tg7lk0jW2-n9", "metadata": { "id": "tg7lk0jW2-n9" }, "outputs": [], "source": [ "import IPython\n", "IPython.Application.instance().kernel.do_shutdown(True)" ] }, { "cell_type": "markdown", "id": "CHZHwXAbnL6C", "metadata": { "id": "CHZHwXAbnL6C" }, "source": [ "**Note:** If you receive a warning like `UserWarning: Failed to dlopen libcuda.so.1...` and you were unable to load the extension, check that your runtime has a GPU attached and re-try." ] }, { "cell_type": "markdown", "id": "Ln3_PId3yewx", "metadata": { "id": "Ln3_PId3yewx" }, "source": [ "### Other ways to enable `cudf.pandas`\n", "\n", "While the magic command (`%load_ext`) is the easiest method in a notebook, you can also enable the accelerator in other environments:\n", "\n", "* **In Python scripts:** Call `import cudf.pandas` and `cudf.pandas.install()` before your `pandas` import.\n", "* **From non-notebook environments:** Run your script using `python -m cudf.pandas your_script.py`." ] }, { "cell_type": "markdown", "id": "CvVKX1LxzWY7", "metadata": { "id": "CvVKX1LxzWY7" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Compare CPU vs. GPU performance\n", "\n", "Now for the most important part: comparing the performance of standard `pandas` on a CPU with `cudf.pandas` on a GPU.\n", "\n", "To ensure a completely fair baseline for the CPU, you must first reset the Colab runtime. This clears any GPU accelerators you might have enabled in the previous sections. You can restart runtime by running the following cell, or selecting **Restart session** from the **Runtime** menu." ] }, { "cell_type": "code", "execution_count": null, "id": "PkKx7k_t4MS1", "metadata": { "id": "PkKx7k_t4MS1" }, "outputs": [], "source": [ "import IPython\n", "\n", "print(\"Restarting the kernel to ensure a clean CPU baseline...\")\n", "print(\"If you are using 'Run All', execution will stop here. Please continue manually after the restart.\")\n", "\n", "IPython.Application.instance().kernel.do_shutdown(True)" ] }, { "cell_type": "markdown", "id": "UbLO6QDQ4Tvy", "metadata": { "id": "UbLO6QDQ4Tvy" }, "source": [ "### Define the analytics pipeline\n", "\n", "Now that the environment is clean, you will define the benchmarking function. This function allows you to run the exact same pipeline - loading, sorting, and summarizing - using whichever `pandas` module you pass to it." ] }, { "cell_type": "code", "execution_count": null, "id": "cerlcQBHzhOg", "metadata": { "id": "cerlcQBHzhOg" }, "outputs": [], "source": [ "import time\n", "import glob\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "\n", "def run_analytics_pipeline(pd_module):\n", " \"\"\"Loads, sorts, and summarizes data using the provided pandas module.\"\"\"\n", " timings = {}\n", "\n", " # 1. Load all 2024 Parquet files from the directory\n", " t0 = time.time()\n", " df = pd_module.concat(\n", " [pd_module.read_parquet(f) for f in glob.glob(\"nyc_taxi_data/*_2024*.parquet\")],\n", " ignore_index=True\n", " )\n", " timings[\"load\"] = time.time() - t0\n", "\n", " # 2. Sort the data by multiple columns\n", " t0 = time.time()\n", " df = df.sort_values(\n", " ['tpep_pickup_datetime', 'trip_distance', 'passenger_count'],\n", " ascending=[False, True, False]\n", " )\n", " timings[\"sort\"] = time.time() - t0\n", "\n", " # 3. Perform a groupby and aggregation\n", " t0 = time.time()\n", " df['tpep_pickup_datetime'] = pd_module.to_datetime(df['tpep_pickup_datetime'])\n", " _ = (\n", " df.loc[df.tpep_pickup_datetime > '2024-11-01']\n", " .groupby(['VendorID', 'tpep_pickup_datetime'])\n", " [['passenger_count', 'fare_amount']]\n", " .agg(['min', 'mean', 'max'])\n", " )\n", " timings[\"summarize\"] = time.time() - t0\n", "\n", " return timings" ] }, { "cell_type": "markdown", "id": "x1ZERZwyzofN", "metadata": { "id": "x1ZERZwyzofN" }, "source": [ "### Run the comparison\n", "\n", "First, you will run the pipeline using standard `pandas` on the CPU. Then, you enable `cudf.pandas` and run it again on the GPU." ] }, { "cell_type": "code", "execution_count": null, "id": "IvwmhT9ezqe4", "metadata": { "id": "IvwmhT9ezqe4" }, "outputs": [], "source": [ "# --- Run on CPU ---\n", "print(\"Running analytics pipeline on CPU...\")\n", "# Ensure we are using standard pandas\n", "import pandas as pd\n", "assert \"cudf\" not in str(pd), \"Error: cuDF is still active. Please restart the kernel.\"\n", "\n", "cpu_times = run_analytics_pipeline(pd)\n", "print(f\"CPU times: {cpu_times}\")\n", "\n", "# --- Run on GPU ---\n", "print(\"\\nEnabling cudf.pandas and running on GPU...\")\n", "# Load the extension\n", "%load_ext cudf.pandas\n", "import pandas as gpu_pd\n", "\n", "gpu_times = run_analytics_pipeline(gpu_pd)\n", "print(f\"GPU times: {gpu_times}\")" ] }, { "cell_type": "markdown", "id": "9JMHOzhCztwk", "metadata": { "id": "9JMHOzhCztwk" }, "source": [ "### Visualize the results\n", "\n", "Finally, visualize the difference. The following code calculates the speedup for each operation and plots them side-by-side." ] }, { "cell_type": "code", "execution_count": null, "id": "Qi_K-gxNzw58", "metadata": { "id": "Qi_K-gxNzw58" }, "outputs": [], "source": [ "# Create a DataFrame for plotting\n", "results_df = pd.DataFrame([cpu_times, gpu_times], index=[\"CPU\", \"GPU\"]).T\n", "total_cpu_time = results_df['CPU'].sum()\n", "total_gpu_time = results_df['GPU'].sum()\n", "speedup = total_cpu_time / total_gpu_time\n", "\n", "print(\"--- Performance Results ---\")\n", "print(results_df)\n", "print(f\"\\nTotal CPU Time: {total_cpu_time:.2f} seconds\")\n", "print(f\"Total GPU Time: {total_gpu_time:.2f} seconds\")\n", "print(f\"Overall Speedup: {speedup:.2f}x\")\n", "\n", "# Plot the results\n", "fig, ax = plt.subplots(figsize=(10, 6))\n", "results_df.plot(kind='bar', ax=ax, color={\"CPU\": \"tab:blue\", \"GPU\": \"#76B900\"})\n", "\n", "ax.set_ylabel(\"Time (seconds)\")\n", "ax.set_title(f\"CPU vs. GPU Runtimes (Overall Speedup: {speedup:.2f}x)\", fontsize=14)\n", "ax.tick_params(axis='x', rotation=0)\n", "\n", "# Add numerical labels to the bars\n", "for container in ax.containers:\n", " ax.bar_label(container, fmt=\"%.2f\", padding=3)\n", "\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "Xb329PmQme9d", "metadata": { "id": "Xb329PmQme9d" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Profile your code to find bottlenecks\n", "\n", "Even with GPU acceleration, some `pandas` operations might fall back to the CPU if they are not yet supported by `cuDF`. These \"CPU fallbacks\" can become performance bottlenecks.\n", "\n", "To help you identify these areas, [`cudf.pandas`](https://docs.rapids.ai/api/cudf/stable/cudf_pandas/usage/) includes two built-in profilers. You can use them to see exactly which parts of your code are running on the GPU and which are falling back to the CPU.\n", "\n", "* `%%cudf.pandas.profile`: Use this for a high-level, function-by-function summary of your code. It's best for getting a quick overview of which operations are running on which device.\n", "* `%%cudf.pandas.line_profile`: Use this for a detailed, line-by-line analysis. It's the best tool for pinpointing the exact lines in your code that are causing a fallback to the CPU.\n", "\n", "Use these profilers as \"cell magics\" at the top of a notebook cell." ] }, { "cell_type": "markdown", "id": "7k7XnDov59Es", "metadata": { "id": "7k7XnDov59Es" }, "source": [ "### Function-level profiling with `%%cudf.pandas.profile`\n", "\n", "First, run the function-level profiler on the same analytics pipeline from the previous section. The output shows a table of every function called, the device it ran on (GPU or CPU), and how many times it was called.\n" ] }, { "cell_type": "markdown", "id": "markdown-before-setup-profiler", "metadata": {}, "source": [ "#### Re-enable `cudf.pandas` and imports for profiling\n", "\n", "Since the kernel was restarted for the CPU vs. GPU comparison, `cudf.pandas` needs to be re-enabled before running the profilers. This cell also imports `glob` which is used in the profiling examples. A dummy `pd.DataFrame` creation ensures `cudf.pandas` is fully active." ] }, { "cell_type": "code", "execution_count": null, "id": "iwj3_bae6Xpx", "metadata": { "id": "iwj3_bae6Xpx" }, "outputs": [], "source": [ "%load_ext cudf.pandas\n", "import pandas as pd\n", "\n", "# Ensure cudf.pandas is active before profiling\n", "pd.DataFrame({\"a\": [1]})" ] }, { "cell_type": "markdown", "id": "_5lL4pK0mbHS", "metadata": { "id": "_5lL4pK0mbHS" }, "source": [ "After ensuring cudf.pandas is active, you can run a profile." ] }, { "cell_type": "code", "execution_count": null, "id": "bltCs_Xy6jI5", "metadata": { "id": "bltCs_Xy6jI5" }, "outputs": [], "source": [ "%%cudf.pandas.profile\n", "\n", "df = pd.concat([pd.read_parquet(f) for f in glob.glob(\"nyc_taxi_data/*2024-1*.parquet\")], ignore_index=True)\n", "\n", "df = df.sort_values(['tpep_pickup_datetime', 'trip_distance', 'passenger_count'], ascending=[False, True, False])\n", "\n", "summary = (\n", " df\n", " .loc[(df.tpep_pickup_datetime > '2024-11-01')]\n", " .groupby(['VendorID','tpep_pickup_datetime'])\n", " [['passenger_count', 'fare_amount']]\n", " .agg(['min', 'mean', 'max'])\n", ")" ] }, { "cell_type": "markdown", "id": "7JFmpnZm6pRU", "metadata": { "id": "7JFmpnZm6pRU" }, "source": [ "### Line-by-line profiling with `%%cudf.pandas.line_profile`\n", "\n", "Next, run the line-level profiler. This gives you a much more granular view, showing the portion of time each line of code spent executing on the GPU versus the CPU. This is the most effective way to find specific bottlenecks to optimize." ] }, { "cell_type": "code", "execution_count": null, "id": "Jy4zvDHj6oTU", "metadata": { "id": "Jy4zvDHj6oTU" }, "outputs": [], "source": [ "%%cudf.pandas.line_profile\n", "\n", "df = pd.concat([pd.read_parquet(f) for f in glob.glob(\"nyc_taxi_data/*2024-1*.parquet\")], ignore_index=True)\n", "\n", "df = df.sort_values(['tpep_pickup_datetime', 'trip_distance', 'passenger_count'], ascending=[False, True, False])\n", "\n", "summary = (\n", " df\n", " .loc[(df.tpep_pickup_datetime > '2024-11-01')]\n", " .groupby(['VendorID','tpep_pickup_datetime'])\n", " [['passenger_count', 'fare_amount']]\n", " .agg(['min', 'mean', 'max'])\n", ")" ] }, { "cell_type": "markdown", "id": "jkKXC7aRmOGr", "metadata": { "id": "jkKXC7aRmOGr" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Integrate with Google Cloud Storage\n", "\n", "[Google Cloud Storage (GCS)](https://docs.cloud.google.com/storage/docs/introduction) is a scalable and durable object storage service. When you use Colab Enterprise, GCS is a great place to store your datasets, model checkpoints, and other artifacts.\n", "\n", "Your Colab Enterprise runtime has the necessary permissions to read and write data directly to GCS buckets, and these operations are GPU-accelerated for maximum performance." ] }, { "cell_type": "markdown", "id": "QnatmcFs7-2V", "metadata": { "id": "QnatmcFs7-2V" }, "source": [ "### Create a GCS bucket\n", "\n", "First, create a new GCS bucket. GCS bucket names are globally unique, so append the a UUID to its name." ] }, { "cell_type": "code", "execution_count": null, "id": "-D7s5f599MlN", "metadata": { "id": "-D7s5f599MlN" }, "outputs": [], "source": [ "from google.cloud import storage\n", "import uuid\n", "\n", "unique_suffix = uuid.uuid4().hex[:12]\n", "bucket_name = f'nyc-taxi-codelab-{unique_suffix}'\n", "project_id = storage.Client().project\n", "\n", "client = storage.Client()\n", "\n", "try:\n", " bucket = client.create_bucket(bucket_name)\n", " print(f\"Successfully created bucket: gs://{bucket.name}\")\n", "except Exception as e:\n", " print(f\"Bucket creation failed. You may already own it or the name is taken: {e}\")" ] }, { "cell_type": "markdown", "id": "O7Jwcpx-8Nbg", "metadata": { "id": "O7Jwcpx-8Nbg" }, "source": [ "### Write data directly to GCS\n", "\n", "Now, save a DataFrame directly to your new GCS bucket. If the `df` variable isn't available from the previous sections, the code first loads a single month of data." ] }, { "cell_type": "code", "execution_count": null, "id": "2Th441rJ8Q-v", "metadata": { "id": "2Th441rJ8Q-v" }, "outputs": [], "source": [ "%%cudf.pandas.line_profile\n", "\n", "# Ensure the DataFrame exists before saving to GCS\n", "if 'df' not in locals():\n", " print(\"DataFrame not found, loading a sample file...\")\n", " df = pd.read_parquet('nyc_taxi_data/yellow_tripdata_2024-12.parquet')\n", "\n", "print(f\"Writing data to gs://{bucket_name}/nyc_taxi_data.parquet...\")\n", "df.to_parquet(f\"gs://{bucket_name}/nyc_taxi_data.parquet\", index=False)\n", "print(\"Write operation complete.\")" ] }, { "cell_type": "markdown", "id": "OSHA46Hu9oB9", "metadata": { "id": "OSHA46Hu9oB9" }, "source": [ "### Verify the file in GCS\n", "\n", "You can verify the data is in GCS by visiting the bucket. The following code creates a clickable link." ] }, { "cell_type": "code", "execution_count": null, "id": "veDqH83t9vFH", "metadata": { "id": "veDqH83t9vFH" }, "outputs": [], "source": [ "import IPython\n", "from IPython.display import Markdown\n", "\n", "# Construct the URL and display it as a clickable link\n", "gcs_url = f\"https://console.cloud.google.com/storage/browser/{bucket_name}?project={project_id}\"\n", "Markdown(f'**[Click here to view your GCS bucket in the Google Cloud Console]({gcs_url})**')" ] }, { "cell_type": "markdown", "id": "BZQ46G2r8n1g", "metadata": { "id": "BZQ46G2r8n1g" }, "source": [ "### Read data directly from GCS\n", "\n", "Finally, read data directly from a GCS path into a DataFrame. This operation is also GPU-accelerated, allowing you to load large datasets from cloud storage at high speed." ] }, { "cell_type": "code", "execution_count": null, "id": "tDBOI29m8nUz", "metadata": { "id": "tDBOI29m8nUz" }, "outputs": [], "source": [ "%%cudf.pandas.line_profile\n", "\n", "print(f\"Reading data from gs://{bucket_name}/nyc_taxi_data.parquet...\")\n", "df_from_gcs = pd.read_parquet(f\"gs://{bucket_name}/nyc_taxi_data.parquet\")\n", "\n", "df_from_gcs.head()" ] }, { "cell_type": "markdown", "id": "OnMKuSMS-OeL", "metadata": { "id": "OnMKuSMS-OeL" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Clean Up\n", "\n", "To avoid incurring unexpected charges to your Google Cloud account, you need to clean up the resources you created." ] }, { "cell_type": "markdown", "id": "delete-resources-md-id", "metadata": {}, "source": [ "### Delete resources\n", "\n", "The following code permanently deletes the GCS bucket and the locally downloaded NYC taxi dataset." ] }, { "cell_type": "code", "execution_count": null, "id": "RJ_57PPv-Qfb", "metadata": { "id": "RJ_57PPv-Qfb" }, "outputs": [], "source": [ "\n", "# Permanately delete the GCS bucket\n", "print(f\"Deleting GCS bucket: gs://{bucket_name}...\")\n", "!gsutil rm -r -f gs://{bucket_name}\n", "print(\"Bucket deleted.\")\n", "\n", "# Remove NYC taxi dataset on the Colab runtime\n", "print(\"Deleting local 'nyc_taxi_data' directory...\")\n", "!rm -rf nyc_taxi_data\n", "print(\"Local files deleted.\")" ] }, { "cell_type": "markdown", "id": "KqO0y01y-7aa", "metadata": { "id": "KqO0y01y-7aa" }, "source": [ "### Shut down your Colab runtime\n", "\n", "* In the Google Cloud console, go to the Colab Enterprise **Runtimes** page.\n", "* In the **Region** menu, select the region that contains your runtime.\n", "* Select the runtime you want to delete.\n", "* Click **Delete**.\n", "* Click **Confirm**." ] }, { "cell_type": "markdown", "id": "delete-notebook-md-id", "metadata": {}, "source": [ "### Delete your Notebook\n", "\n", "* In the Google Cloud console, go to the Colab Enterprise **My Notebooks** page.\n", "* In the **Region** menu, select the region that contains your notebook.\n", "* Select the notebook you want to delete.\n", "* Click **Delete**.\n", "* Click **Confirm**." ] }, { "cell_type": "markdown", "id": "e02OwTy-n5SI", "metadata": { "id": "e02OwTy-n5SI" }, "source": [ "\n", "\n", "---\n", "\n", "\n", "## Recap\n", "\n", "Congratulations! You've successfully accelerated a pandas analytics workflow using NVIDIA cuDF on Colab Enterprise. You learned how to configure GPU-enabled runtimes, enable `cudf.pandas` for zero-code-change acceleration, profile code for bottlenecks, and integrate with Google Cloud Storage.\n", "\n", "### Reference docs\n", "\n", "* [Colab Enterprise Documentation](https://cloud.google.com/colab/docs/introduction)\n", "* [NVIDIA cuDF Documentation](https://docs.rapids.ai/api/cudf/stable/)" ] } ], "metadata": { "accelerator": "GPU", "colab": { "gpuType": "L4", "name": "Accelerated_Analytics_Full.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.9" } }, "nbformat": 4, "nbformat_minor": 5 }