diff --git a/packages/bigframes/bigframes/bigquery/_operations/ai.py b/packages/bigframes/bigframes/bigquery/_operations/ai.py index 78b5d81b6744..98f3c44690ad 100644 --- a/packages/bigframes/bigframes/bigquery/_operations/ai.py +++ b/packages/bigframes/bigframes/bigquery/_operations/ai.py @@ -87,7 +87,7 @@ def generate( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an ENDPOINT value, BigQuery ML selects a recent stable version of Gemini to use. @@ -178,7 +178,7 @@ def generate_bool( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an ENDPOINT value, BigQuery ML selects a recent stable version of Gemini to use. @@ -252,7 +252,7 @@ def generate_int( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an ENDPOINT value, BigQuery ML selects a recent stable version of Gemini to use. @@ -326,7 +326,7 @@ def generate_double( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an ENDPOINT value, BigQuery ML selects a recent stable version of Gemini to use. @@ -809,7 +809,7 @@ def if_( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an ENDPOINT value, BigQuery ML dynamically chooses a model based on your query to have the best cost to quality tradeoff for the task. @@ -960,7 +960,7 @@ def score( Specifies the connection to use to communicate with the model. For example, ``myproject.us.myconnection``. If not provided, the query uses your end-user credential. endpoint (str, optional): - Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-2.5-flash"``. You can specify any + Specifies the Vertex AI endpoint to use for the model. For example ``"gemini-3.5-flash"``. You can specify any generally available or preview Gemini model. If you specify the model name, BigQuery ML automatically identifies and uses the full endpoint of the model. If you don't specify an endpoint value, BigQuery ML dynamically chooses a model based on your query to have the best cost to quality tradeoff for the task. diff --git a/packages/bigframes/bigframes/ml/llm.py b/packages/bigframes/bigframes/ml/llm.py index 3887453a2239..666885d5905d 100644 --- a/packages/bigframes/bigframes/ml/llm.py +++ b/packages/bigframes/bigframes/ml/llm.py @@ -59,6 +59,8 @@ _GEMINI_2P5_PRO_ENDPOINT = "gemini-2.5-pro" _GEMINI_2P5_FLASH_ENDPOINT = "gemini-2.5-flash" _GEMINI_2P5_FLASH_LITE_ENDPOINT = "gemini-2.5-flash-lite" +_GEMINI_3P1_FLASH_LITE_ENDPOINT = "gemini-3.1-flash-lite" +_GEMINI_3P5_FLASH_ENDPOINT = "gemini-3.5-flash" _GEMINI_ENDPOINTS = ( _GEMINI_1P5_PRO_PREVIEW_ENDPOINT, @@ -73,6 +75,8 @@ _GEMINI_2P5_PRO_ENDPOINT, _GEMINI_2P5_FLASH_ENDPOINT, _GEMINI_2P5_FLASH_LITE_ENDPOINT, + _GEMINI_3P1_FLASH_LITE_ENDPOINT, + _GEMINI_3P5_FLASH_ENDPOINT, ) _GEMINI_PREVIEW_ENDPOINTS = ( _GEMINI_1P5_PRO_PREVIEW_ENDPOINT, @@ -96,6 +100,8 @@ _GEMINI_2P5_PRO_ENDPOINT, _GEMINI_2P5_FLASH_ENDPOINT, _GEMINI_2P5_FLASH_LITE_ENDPOINT, + _GEMINI_3P1_FLASH_LITE_ENDPOINT, + _GEMINI_3P5_FLASH_ENDPOINT, ) _CLAUDE_3_SONNET_ENDPOINT = "claude-3-sonnet" @@ -431,21 +437,22 @@ class GeminiTextGenerator(base.RetriableRemotePredictor): """Gemini text generator LLM model. .. note:: - gemini-1.5-X are going to be deprecated. Use gemini-2.5-X (https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) instead. + gemini-1.5-X are going to be deprecated. Use gemini-3.5-flash (https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) instead. Args: - model_name (str, Default to "gemini-2.0-flash-001"): + model_name (str, Default to "gemini-3.5-flash"): The model for natural language tasks. Accepted values are "gemini-1.5-pro-preview-0514", "gemini-1.5-flash-preview-0514", "gemini-1.5-pro-001", "gemini-1.5-pro-002", "gemini-1.5-flash-001", "gemini-1.5-flash-002", "gemini-2.0-flash-exp", "gemini-2.0-flash-lite-001", "gemini-2.0-flash-001", - "gemini-2.5-pro", "gemini-2.5-flash" and "gemini-2.5-flash-lite". - If no setting is provided, "gemini-2.0-flash-001" will be used by + "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite" and "gemini-3.5-flash". + If no setting is provided, "gemini-3.5-flash" will be used by default and a warning will be issued. .. note:: - "gemini-1.5-X" is going to be deprecated. Please use gemini-2.5-X instead. For example, "gemini-2.5-flash". + "gemini-1.5-X" is going to be deprecated. Please use gemini-3.5-flash instead. "gemini-2.0-flash-exp", "gemini-1.5-pro-preview-0514" and "gemini-1.5-flash-preview-0514" is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of the Service Specific Terms(https://cloud.google.com/terms/service-terms#1). Pre-GA products and features are available "as is" and might have limited support. For more information, see the launch stage descriptions @@ -478,6 +485,8 @@ def __init__( "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ] ] = None, session: Optional[bigframes.Session] = None, @@ -496,7 +505,7 @@ def __init__( warnings.warn(msg, category=exceptions.PreviewWarning) if model_name is None: - model_name = "gemini-2.0-flash-001" + model_name = "gemini-3.5-flash" msg = exceptions.format_message(_REMOVE_DEFAULT_MODEL_WARNING) warnings.warn(msg, category=FutureWarning, stacklevel=2) @@ -522,15 +531,21 @@ def _create_bqml_model(self): ) ) warnings.warn(msg) - if self.model_name.startswith("gemini-1.5"): + if self.model_name.startswith( + ("gemini-2.0", "gemini-1.5") + ) or self.model_name in ( + "gemini-2.5-flash", + "gemini-2.5-flash-lite", + "gemini-2.5-pro-preview-05-06", + ): msg = exceptions.format_message( _MODEL_DEPRECATE_WARNING.format( model_name=self.model_name, - new_model_name="gemini-2.5-X", + new_model_name="gemini-3.5-flash", link="https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator", ) ) - warnings.warn(msg) + warnings.warn(msg, category=exceptions.ApiDeprecationWarning) options = {"endpoint": self.model_name} diff --git a/packages/bigframes/bigframes/ml/loader.py b/packages/bigframes/bigframes/ml/loader.py index 05cf6dde68ad..76975752457a 100644 --- a/packages/bigframes/bigframes/ml/loader.py +++ b/packages/bigframes/bigframes/ml/loader.py @@ -70,6 +70,8 @@ llm._GEMINI_2P5_FLASH_ENDPOINT: llm.GeminiTextGenerator, llm._GEMINI_2P5_FLASH_LITE_ENDPOINT: llm.GeminiTextGenerator, llm._GEMINI_2P5_PRO_ENDPOINT: llm.GeminiTextGenerator, + llm._GEMINI_3P1_FLASH_LITE_ENDPOINT: llm.GeminiTextGenerator, + llm._GEMINI_3P5_FLASH_ENDPOINT: llm.GeminiTextGenerator, llm._CLAUDE_3_HAIKU_ENDPOINT: llm.Claude3TextGenerator, llm._CLAUDE_3_SONNET_ENDPOINT: llm.Claude3TextGenerator, llm._CLAUDE_3_5_SONNET_ENDPOINT: llm.Claude3TextGenerator, diff --git a/packages/bigframes/bigframes/operations/semantics.py b/packages/bigframes/bigframes/operations/semantics.py index 3cad3258e035..d81e584d3545 100644 --- a/packages/bigframes/bigframes/operations/semantics.py +++ b/packages/bigframes/bigframes/operations/semantics.py @@ -57,7 +57,7 @@ def agg( >>> bpd.options.compute.semantic_ops_confirmation_threshold = 25 >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") # doctest: +SKIP + >>> model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash") # doctest: +SKIP >>> df = bpd.DataFrame( ... { @@ -330,7 +330,7 @@ def filter(self, instruction: str, model, ground_with_google_search: bool = Fals >>> bpd.options.compute.semantic_ops_confirmation_threshold = 25 >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") # doctest: +SKIP + >>> model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash") # doctest: +SKIP >>> df = bpd.DataFrame({"country": ["USA", "Germany"], "city": ["Seattle", "Berlin"]}) >>> df.semantics.filter("{city} is the capital of {country}", model) # doctest: +SKIP @@ -443,7 +443,7 @@ def map( >>> bpd.options.compute.semantic_ops_confirmation_threshold = 25 >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") # doctest: +SKIP + >>> model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash") # doctest: +SKIP >>> df = bpd.DataFrame({"ingredient_1": ["Burger Bun", "Soy Bean"], "ingredient_2": ["Beef Patty", "Bittern"]}) >>> df.semantics.map("What is the food made from {ingredient_1} and {ingredient_2}? One word only.", output_column="food", model=model) # doctest: +SKIP @@ -565,7 +565,7 @@ def join( >>> bpd.options.compute.semantic_ops_confirmation_threshold = 25 >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") # doctest: +SKIP + >>> model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash") # doctest: +SKIP >>> cities = bpd.DataFrame({'city': ['Seattle', 'Ottawa', 'Berlin', 'Shanghai', 'New Delhi']}) >>> continents = bpd.DataFrame({'continent': ['North America', 'Africa', 'Asia']}) @@ -805,7 +805,7 @@ def top_k( >>> bpd.options.compute.semantic_ops_confirmation_threshold = 25 >>> import bigframes.ml.llm as llm - >>> model = llm.GeminiTextGenerator(model_name="gemini-2.0-flash-001") # doctest: +SKIP + >>> model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash") # doctest: +SKIP >>> df = bpd.DataFrame( ... { diff --git a/packages/bigframes/notebooks/apps/synthetic_data_generation.ipynb b/packages/bigframes/notebooks/apps/synthetic_data_generation.ipynb index 00d30fc8a8a9..94d24b6069de 100644 --- a/packages/bigframes/notebooks/apps/synthetic_data_generation.ipynb +++ b/packages/bigframes/notebooks/apps/synthetic_data_generation.ipynb @@ -1,370 +1,371 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "# Copyright 2023 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." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# BigQuery DataFrames: Synthetic Data Generation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In addition to BigQuery DataFrames (installing which also installs `pandas` as a dependency) we will use\n", - "`faker` library as a building block for synthetic data generation." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "suoG7eWDZARj", - "outputId": "b5c620a9-8f5b-413f-dd38-93448f941846" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting faker\n", - " Downloading faker-37.1.0-py3-none-any.whl.metadata (15 kB)\n", - "Requirement already satisfied: tzdata in /usr/local/google/home/shuowei/src/python-bigquery-dataframes/venv/lib/python3.10/site-packages (from faker) (2024.2)\n", - "Downloading faker-37.1.0-py3-none-any.whl (1.9 MB)\n", - "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m55.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hInstalling collected packages: faker\n", - "Successfully installed faker-37.1.0\n" - ] - } - ], - "source": [ - "!pip install faker" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "m3q1oeJALhsG" - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'PROJECT_ID' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[3], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mbigframes\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mbpd\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m bpd\u001b[38;5;241m.\u001b[39moptions\u001b[38;5;241m.\u001b[39mbigquery\u001b[38;5;241m.\u001b[39mproject \u001b[38;5;241m=\u001b[39m \u001b[43mPROJECT_ID\u001b[49m\n", - "\u001b[0;31mNameError\u001b[0m: name 'PROJECT_ID' is not defined" - ] - } - ], - "source": [ - "import bigframes.pandas as bpd\n", - "bpd.options.bigquery.project = PROJECT_ID" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's use `GeminiTextGenerator` for our purpose, which is BigQuery DataFrame's state-of-the-art LLM integration at the time of writing this notebook (Apr 16 2024)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 69 - }, - "id": "lIYdn1woOS1n", - "outputId": "be474338-44c2-4ce0-955e-d525b8b9c84b" - }, - "outputs": [], - "source": [ - "from bigframes.ml.llm import GeminiTextGenerator\n", - "\n", - "model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Craft a prompt for the LLM to indicate the schema of the desired data and hints for the code that could generate such data. " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 162 - }, - "id": "SSR-lLScLa95", - "outputId": "cbaec34e-6fa6-45b4-e54a-f11ca06b61e1" - }, - "outputs": [], - "source": [ - "prompt = \"\"\"\\\n", - "Write python code to generate a pandas dataframe based on the requirements:\n", - " Column name: Name, type: string, Description: Latin American Names\n", - " Column name: Age, type: int\n", - " Column name: Gender, type: string, Description: Inclusive\n", - "\n", - "Note:\n", - " - Return the code only, no additional texts or comments\n", - " - Use faker library\n", - " - Generate 100 rows\n", - " - The final dataframe should be named 'result_df'.\n", - "\"\"\"\n", - "\n", - "df_prompt = bpd.DataFrame({\"prompt\" : [prompt]})\n", - "df_prompt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Be accommodating that LLM may not produce a runnable code in the first go and may need some nudging. We will retry by adding the failing code and the exception it throws as additional context in the prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 277 - }, - "id": "miDe3K4GNvOo", - "outputId": "f2039e80-5ad7-4551-f8b2-7ef714a89d63" - }, - "outputs": [], - "source": [ - "max_tries = 5\n", - "for i in range(max_tries):\n", - " # Get LLM generated code\n", - " df_result = model.predict(df_prompt)\n", - " llm_result = df_result['ml_generate_text_llm_result'].iloc[0]\n", - "\n", - " # Python code comes back as a markdown code block,\n", - " # remove the prefix \"```python\" and suffix \"```\"\n", - " code = llm_result[9:-3]\n", - " print(code)\n", - "\n", - " # Check if the generated code is runnable\n", - " try:\n", - " exec(code)\n", - " break\n", - " except Exception as ex:\n", - " print(ex)\n", - " error_context = f\"\"\"\n", - "Previous code:\n", - "{code}\n", - "\n", - "Had this exception:\n", - "{ex}\"\"\"\n", - "\n", - " # Update the prompt to help LLM correct error\n", - " df_prompt[\"prompt\"] += error_context\n", - "\n", - " # If we have exhausted max tries then stop trying\n", - " if i+1 == max_tries:\n", - " raise Exception(\"Failed to generate runnable code\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run the generated code and verify that it produced the desired data." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 424 - }, - "id": "GODcPwX2PBEu", - "outputId": "dec4c872-c464-49e4-cd7f-9442fc977d18" - }, - "outputs": [], - "source": [ - "execution_context = {}\n", - "exec(code, execution_context)\n", - "execution_context.get(\"result_df\")" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright 2023 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." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# BigQuery DataFrames: Synthetic Data Generation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to BigQuery DataFrames (installing which also installs `pandas` as a dependency) we will use\n", + "`faker` library as a building block for synthetic data generation." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "suoG7eWDZARj", + "outputId": "b5c620a9-8f5b-413f-dd38-93448f941846" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We want to run this code at scale to generate since we want to generate large amount of data. Let's deploy a `remote_function` for this purpose." - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting faker\n", + " Downloading faker-37.1.0-py3-none-any.whl.metadata (15 kB)\n", + "Requirement already satisfied: tzdata in /usr/local/google/home/shuowei/src/python-bigquery-dataframes/venv/lib/python3.10/site-packages (from faker) (2024.2)\n", + "Downloading faker-37.1.0-py3-none-any.whl (1.9 MB)\n", + "\u001b[2K \u001b[38;2;114;156;31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m55.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: faker\n", + "Successfully installed faker-37.1.0\n" + ] + } + ], + "source": [ + "!pip install faker" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "m3q1oeJALhsG" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 36 - }, - "id": "n-BsGciNqSwU", - "outputId": "996e5639-a49c-4542-a0dc-ede450e0eb6d" - }, - "outputs": [], - "source": [ - "@bpd.remote_function(packages=['faker', 'pandas'], cloud_function_service_account=\"default\")\n", - "def data_generator(id: int) -> str:\n", - " context = {}\n", - " exec(code, context)\n", - " result_df = context.get(\"result_df\")\n", - " return result_df.to_json(orient=\"records\")\n", - "\n", - "data_generator.bigframes_cloud_function" - ] + "ename": "NameError", + "evalue": "name 'PROJECT_ID' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[3], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mbigframes\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mbpd\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m bpd\u001b[38;5;241m.\u001b[39moptions\u001b[38;5;241m.\u001b[39mbigquery\u001b[38;5;241m.\u001b[39mproject \u001b[38;5;241m=\u001b[39m \u001b[43mPROJECT_ID\u001b[49m\n", + "\u001b[0;31mNameError\u001b[0m: name 'PROJECT_ID' is not defined" + ] + } + ], + "source": [ + "import bigframes.pandas as bpd\n", + "\n", + "bpd.options.bigquery.project = PROJECT_ID" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's use `GeminiTextGenerator` for our purpose, which is BigQuery DataFrame's state-of-the-art LLM integration at the time of writing this notebook (Apr 16 2024)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 69 }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let’s say we want to generate 1 million rows of synthetic data. Since our generated code produces 100 rows in one run, we can initialize an indicator dataframe with 1M/100 = 10K indicator rows. Then we can apply the remote function to produce 100 synthetic data rows for each indicator row." - ] + "id": "lIYdn1woOS1n", + "outputId": "be474338-44c2-4ce0-955e-d525b8b9c84b" + }, + "outputs": [], + "source": [ + "from bigframes.ml.llm import GeminiTextGenerator\n", + "\n", + "model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Craft a prompt for the LLM to indicate the schema of the desired data and hints for the code that could generate such data. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 162 }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "id": "Odkmev9nsYqA", - "outputId": "4aa7a1fd-0c0d-4412-f326-a20e19f583b5" - }, - "outputs": [], - "source": [ - "desired_num_rows = 1_000_000 # 1 million rows\n", - "batch_size = 100 # used in the prompt\n", - "num_batches = int(desired_num_rows/batch_size)\n", - "\n", - "df = bpd.DataFrame({\"row_id\": range(num_batches)})" - ] + "id": "SSR-lLScLa95", + "outputId": "cbaec34e-6fa6-45b4-e54a-f11ca06b61e1" + }, + "outputs": [], + "source": [ + "prompt = \"\"\"\\\n", + "Write python code to generate a pandas dataframe based on the requirements:\n", + " Column name: Name, type: string, Description: Latin American Names\n", + " Column name: Age, type: int\n", + " Column name: Gender, type: string, Description: Inclusive\n", + "\n", + "Note:\n", + " - Return the code only, no additional texts or comments\n", + " - Use faker library\n", + " - Generate 100 rows\n", + " - The final dataframe should be named 'result_df'.\n", + "\"\"\"\n", + "\n", + "df_prompt = bpd.DataFrame({\"prompt\" : [prompt]})\n", + "df_prompt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Be accommodating that LLM may not produce a runnable code in the first go and may need some nudging. We will retry by adding the failing code and the exception it throws as additional context in the prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 277 }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 34 - }, - "id": "UyBhlJFVsmQC", - "outputId": "29748df5-673b-4320-bb1f-53abaace3b81" - }, - "outputs": [], - "source": [ - "df[\"json_data\"] = df[\"row_id\"].apply(data_generator)" - ] + "id": "miDe3K4GNvOo", + "outputId": "f2039e80-5ad7-4551-f8b2-7ef714a89d63" + }, + "outputs": [], + "source": [ + "max_tries = 5\n", + "for i in range(max_tries):\n", + " # Get LLM generated code\n", + " df_result = model.predict(df_prompt)\n", + " llm_result = df_result['ml_generate_text_llm_result'].iloc[0]\n", + "\n", + " # Python code comes back as a markdown code block,\n", + " # remove the prefix \"```python\" and suffix \"```\"\n", + " code = llm_result[9:-3]\n", + " print(code)\n", + "\n", + " # Check if the generated code is runnable\n", + " try:\n", + " exec(code)\n", + " break\n", + " except Exception as ex:\n", + " print(ex)\n", + " error_context = f\"\"\"\n", + "Previous code:\n", + "{code}\n", + "\n", + "Had this exception:\n", + "{ex}\"\"\"\n", + "\n", + " # Update the prompt to help LLM correct error\n", + " df_prompt[\"prompt\"] += error_context\n", + "\n", + " # If we have exhausted max tries then stop trying\n", + " if i+1 == max_tries:\n", + " raise Exception(\"Failed to generate runnable code\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run the generated code and verify that it produced the desired data." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 424 }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "At this point each item in `df[\"json_data\"]` is a json serialized array of 100 records. Let’s flatten that into 1 record per row using a direct SQL." - ] + "id": "GODcPwX2PBEu", + "outputId": "dec4c872-c464-49e4-cd7f-9442fc977d18" + }, + "outputs": [], + "source": [ + "execution_context = {}\n", + "exec(code, execution_context)\n", + "execution_context.get(\"result_df\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We want to run this code at scale to generate since we want to generate large amount of data. Let's deploy a `remote_function` for this purpose." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 36 }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 932 - }, - "id": "6p3eM21qvRvy", - "outputId": "333f4e49-a555-4d2f-b527-02142782b3a7" - }, - "outputs": [], - "source": [ - "sql = f\"\"\"\n", - "WITH T0 AS ({df.sql}),\n", - "T1 AS (\n", - " SELECT PARSE_JSON(json_row) AS json_row\n", - " FROM T0, UNNEST(JSON_EXTRACT_ARRAY(json_data)) AS json_row\n", - ")\n", - "SELECT STRING(json_row.Name) AS Name,\n", - " INT64(json_row.Age) AS Age,\n", - " STRING(json_row.Gender) AS Gender\n", - "FROM T1\n", - "\"\"\"\n", - "df_result = bpd.read_gbq(sql)\n", - "df_result" - ] + "id": "n-BsGciNqSwU", + "outputId": "996e5639-a49c-4542-a0dc-ede450e0eb6d" + }, + "outputs": [], + "source": [ + "@bpd.remote_function(packages=['faker', 'pandas'], cloud_function_service_account=\"default\")\n", + "def data_generator(id: int) -> str:\n", + " context = {}\n", + " exec(code, context)\n", + " result_df = context.get(\"result_df\")\n", + " return result_df.to_json(orient=\"records\")\n", + "\n", + "data_generator.bigframes_cloud_function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let’s say we want to generate 1 million rows of synthetic data. Since our generated code produces 100 rows in one run, we can initialize an indicator dataframe with 1M/100 = 10K indicator rows. Then we can apply the remote function to produce 100 synthetic data rows for each indicator row." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There you have it, 1 million synthetic data rows ready to use, or save them in a BigQuery table for future use." - ] - } - ], - "metadata": { + "id": "Odkmev9nsYqA", + "outputId": "4aa7a1fd-0c0d-4412-f326-a20e19f583b5" + }, + "outputs": [], + "source": [ + "desired_num_rows = 1_000_000 # 1 million rows\n", + "batch_size = 100 # used in the prompt\n", + "num_batches = int(desired_num_rows/batch_size)\n", + "\n", + "df = bpd.DataFrame({\"row_id\": range(num_batches)})" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { "colab": { - "provenance": [] + "base_uri": "https://localhost:8080/", + "height": 34 }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" + "id": "UyBhlJFVsmQC", + "outputId": "29748df5-673b-4320-bb1f-53abaace3b81" + }, + "outputs": [], + "source": [ + "df[\"json_data\"] = df[\"row_id\"].apply(data_generator)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At this point each item in `df[\"json_data\"]` is a json serialized array of 100 records. Let’s flatten that into 1 record per row using a direct SQL." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 932 }, - "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.10.15" - } + "id": "6p3eM21qvRvy", + "outputId": "333f4e49-a555-4d2f-b527-02142782b3a7" + }, + "outputs": [], + "source": [ + "sql = f\"\"\"\n", + "WITH T0 AS ({df.sql}),\n", + "T1 AS (\n", + " SELECT PARSE_JSON(json_row) AS json_row\n", + " FROM T0, UNNEST(JSON_EXTRACT_ARRAY(json_data)) AS json_row\n", + ")\n", + "SELECT STRING(json_row.Name) AS Name,\n", + " INT64(json_row.Age) AS Age,\n", + " STRING(json_row.Gender) AS Gender\n", + "FROM T1\n", + "\"\"\"\n", + "df_result = bpd.read_gbq(sql)\n", + "df_result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There you have it, 1 million synthetic data rows ready to use, or save them in a BigQuery table for future use." + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.10.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/packages/bigframes/notebooks/generative_ai/ai_movie_poster.ipynb b/packages/bigframes/notebooks/generative_ai/ai_movie_poster.ipynb index 8f309fa7c496..16f68f85c94b 100644 --- a/packages/bigframes/notebooks/generative_ai/ai_movie_poster.ipynb +++ b/packages/bigframes/notebooks/generative_ai/ai_movie_poster.ipynb @@ -232,12 +232,14 @@ "MY_CONNECTION = 'bigframes-default-connection' # @param {type:\"string\"}\n", "FULL_CONNECTION_ID = f\"{MY_PROJECT_ID}.{LOCATION}.{MY_CONNECTION}\"\n", "\n", + "import json\n", + "\n", "import gcsfs\n", + "from IPython.display import HTML, display\n", + "\n", "import bigframes\n", - "import bigframes.pandas as bpd\n", "import bigframes.bigquery as bbq\n", - "import json\n", - "from IPython.display import HTML, display\n", + "import bigframes.pandas as bpd\n", "\n", "session = bpd.get_global_session()\n", "\n", diff --git a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb index 527d3c4aaacd..8817dedbbb55 100644 --- a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb +++ b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_code_generation.ipynb @@ -341,9 +341,10 @@ }, "outputs": [], "source": [ - "import bigframes.pandas as bf\n", "from google.cloud import bigquery\n", - "from google.cloud import bigquery_connection_v1 as bq_connection" + "from google.cloud import bigquery_connection_v1 as bq_connection\n", + "\n", + "import bigframes.pandas as bf" ] }, { @@ -430,7 +431,7 @@ "source": [ "from bigframes.ml.llm import GeminiTextGenerator\n", "\n", - "model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")" + "model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")" ] }, { @@ -1091,6 +1092,7 @@ ], "source": [ "import uuid\n", + "\n", "BUCKET_ID = \"code-samples-\" + str(uuid.uuid1())\n", "\n", "!gcloud storage buckets create gs://{BUCKET_ID}" diff --git a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb index 2d5bb46d95ed..7f8d633a794e 100644 --- a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb +++ b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_kmeans.ipynb @@ -1614,7 +1614,7 @@ "source": [ "from bigframes.ml.llm import GeminiTextGenerator\n", "\n", - "q_a_model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")" + "q_a_model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")" ] }, { diff --git a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb index c9fa39926a92..fb6075b47232 100644 --- a/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb +++ b/packages/bigframes/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb @@ -348,25 +348,22 @@ }, "outputs": [], "source": [ - "import bigframes.pandas as bf\n", - "import bigframes.ml as bf_ml\n", + "from google.cloud import bigquery, storage\n", + "\n", "import bigframes.bigquery as bf_bq\n", + "import bigframes.ml as bf_ml\n", "import bigframes.ml.llm as bf_llm\n", - "\n", - "\n", - "from google.cloud import bigquery\n", - "from google.cloud import storage\n", + "import bigframes.pandas as bf\n", "\n", "# Construct a BigQuery client object.\n", "client = bigquery.Client()\n", "\n", - "import pandas as pd\n", - "from IPython.display import Image, display\n", - "from PIL import Image as PILImage\n", "import io\n", - "\n", "import json\n", - "from IPython.display import Markdown\n", + "\n", + "import pandas as pd\n", + "from IPython.display import Image, Markdown, display\n", + "from PIL import Image as PILImage\n", "\n", "# Note: The project option is not required in all environments.\n", "# On BigQuery Studio, the project ID is automatically detected.\n", @@ -1487,7 +1484,7 @@ "source": [ "## gemini model\n", "\n", - "llm_model = bf_llm.GeminiTextGenerator(model_name = \"gemini-2.5-flash\") ## replace with other model as needed" + "llm_model = bf_llm.GeminiTextGenerator(model_name = \"gemini-3.5-flash\") ## replace with other model as needed" ] }, { diff --git a/packages/bigframes/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb b/packages/bigframes/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb index 93ac3f31c140..c30be3d91199 100644 --- a/packages/bigframes/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb +++ b/packages/bigframes/notebooks/generative_ai/bq_dataframes_ml_drug_name_generation.ipynb @@ -1,1590 +1,1591 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "ur8xi4C7S06n" - }, - "outputs": [], - "source": [ - "# Copyright 2023 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." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JAPoU8Sm5E6e" - }, - "source": [ - "# BigQuery DataFrames ML: Drug Name Generation\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - "
\n", - " \n", - " \"Colab Run in Colab\n", - " \n", - " \n", - " \n", - " \"GitHub\n", - " View on GitHub\n", - " \n", - " \n", - " \n", - " \"Vertex\n", - " Open in Vertex AI Workbench\n", - " \n", - " \n", - " \n", - " \"BQ\n", - " Open in BQ Studio\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tvgnzT1CKxrO" - }, - "source": [ - "## Overview\n", - "\n", - "The goal of this notebook is to demonstrate an enterprise generative AI use case. A marketing user can provide information about a new pharmaceutical drug and its generic name, and receive ideas on marketing-oriented brand names for that drug.\n", - "\n", - "Learn more about [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/dataframes-quickstart)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "d975e698c9a4" - }, - "source": [ - "### Objective\n", - "\n", - "In this tutorial, you learn about Generative AI concepts such as prompting and few-shot learning, as well as how to use BigFrames ML for performing these tasks simply using an intuitive dataframe API.\n", - "\n", - "The steps performed include:\n", - "\n", - "1. Ask the user for the generic name and usage for the drug.\n", - "1. Use `bigframes` to query the FDA dataset of over 100,000 drugs, filtered on the brand name, generic name, and indications & usage columns.\n", - "1. Filter this dataset to find prototypical brand names that can be used as examples in prompt tuning.\n", - "1. Create a prompt with the user input, general instructions, examples and counter-examples for the desired brand name.\n", - "1. Use the `bigframes.ml.llm.GeminiTextGenerator` to generate choices of brand names." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "08d289fa873f" - }, - "source": [ - "### Dataset\n", - "\n", - "This notebook uses the [FDA dataset](https://cloud.google.com/blog/topics/healthcare-life-sciences/fda-mystudies-comes-to-google-cloud) available at [`bigquery-public-data.fda_drug`](https://console.cloud.google.com/bigquery?ws=!1m4!1m3!3m2!1sbigquery-public-data!2sfda_drug)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "aed92deeb4a0" - }, - "source": [ - "### Costs\n", - "\n", - "This tutorial uses billable components of Google Cloud:\n", - "\n", - "* BigQuery (compute)\n", - "* BigQuery ML\n", - "\n", - "Learn about [BigQuery compute pricing](https://cloud.google.com/bigquery/pricing#analysis_pricing_models),\n", - "and [BigQuery ML pricing](https://cloud.google.com/bigquery/pricing#bqml),\n", - "and use the [Pricing Calculator](https://cloud.google.com/products/calculator/)\n", - "to generate a cost estimate based on your projected usage." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "i7EUnXsZhAGF" - }, - "source": [ - "## Installation\n", - "\n", - "Install the following packages required to execute this notebook." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "2b4ef9b72d43" - }, - "outputs": [], - "source": [ - "# !pip install -U --quiet bigframes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "58707a750154" - }, - "source": [ - "### Colab only: Uncomment the following cell to restart the kernel." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "f200f10a1da3" - }, - "outputs": [], - "source": [ - "# # Automatically restart kernel after installs so that your environment can access the new packages\n", - "# import IPython\n", - "\n", - "# app = IPython.Application.instance()\n", - "# app.kernel.do_shutdown(True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "960505627ddf" - }, - "source": [ - "### Import libraries" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "id": "PyQmSRbKA8r-" - }, - "outputs": [], - "source": [ - "import bigframes.pandas as bpd\n", - "from bigframes.ml.llm import GeminiTextGenerator\n", - "from IPython.display import Markdown" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "sBCra4QMA2wR" - }, - "source": [ - "### Authenticate your Google Cloud account\n", - "\n", - "Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "74ccc9e52986" - }, - "source": [ - "**1. Vertex AI Workbench**\n", - "* Do nothing as you are already authenticated." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "de775a3773ba" - }, - "source": [ - "**2. Local JupyterLab instance, uncomment and run:**" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "254614fa0c46" - }, - "outputs": [], - "source": [ - "# ! gcloud auth login" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ef21552ccea8" - }, - "source": [ - "**3. Colab, uncomment and run:**" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "id": "603adbbf0532" - }, - "outputs": [], - "source": [ - "# from google.colab import auth\n", - "\n", - "# auth.authenticate_user()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BF1j6f9HApxa" - }, - "source": [ - "## Before you begin\n", - "\n", - "### Set up your Google Cloud project\n", - "\n", - "**The following steps are required, regardless of your notebook environment.**\n", - "\n", - "1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n", - "\n", - "2. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n", - "\n", - "3. [Enable the BigQuery API](https://console.cloud.google.com/flows/enableapi?apiid=bigquery.googleapis.com).\n", - "\n", - "4. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk)." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WReHDGG5g0XY" - }, - "source": [ - "#### Set your project ID\n", - "\n", - "**If you don't know your project ID**, try the following:\n", - "* Run `gcloud config list`.\n", - "* Run `gcloud projects list`.\n", - "* See the support page: [Locate the project ID](https://support.google.com/googleapi/answer/7014113)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "oM1iC_MfAts1" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[1;31mERROR:\u001b[0m (gcloud.config.set) argument VALUE: Must be specified.\n", - "Usage: gcloud config set SECTION/PROPERTY VALUE [optional flags]\n", - " optional flags may be --help | --installation\n", - "\n", - "For detailed information on this command and its flags, run:\n", - " gcloud config set --help\n" - ] - } + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "ur8xi4C7S06n" + }, + "outputs": [], + "source": [ + "# Copyright 2023 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JAPoU8Sm5E6e" + }, + "source": [ + "# BigQuery DataFrames ML: Drug Name Generation\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + " \"Colab Run in Colab\n", + " \n", + " \n", + " \n", + " \"GitHub\n", + " View on GitHub\n", + " \n", + " \n", + " \n", + " \"Vertex\n", + " Open in Vertex AI Workbench\n", + " \n", + " \n", + " \n", + " \"BQ\n", + " Open in BQ Studio\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvgnzT1CKxrO" + }, + "source": [ + "## Overview\n", + "\n", + "The goal of this notebook is to demonstrate an enterprise generative AI use case. A marketing user can provide information about a new pharmaceutical drug and its generic name, and receive ideas on marketing-oriented brand names for that drug.\n", + "\n", + "Learn more about [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/dataframes-quickstart)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "d975e698c9a4" + }, + "source": [ + "### Objective\n", + "\n", + "In this tutorial, you learn about Generative AI concepts such as prompting and few-shot learning, as well as how to use BigFrames ML for performing these tasks simply using an intuitive dataframe API.\n", + "\n", + "The steps performed include:\n", + "\n", + "1. Ask the user for the generic name and usage for the drug.\n", + "1. Use `bigframes` to query the FDA dataset of over 100,000 drugs, filtered on the brand name, generic name, and indications & usage columns.\n", + "1. Filter this dataset to find prototypical brand names that can be used as examples in prompt tuning.\n", + "1. Create a prompt with the user input, general instructions, examples and counter-examples for the desired brand name.\n", + "1. Use the `bigframes.ml.llm.GeminiTextGenerator` to generate choices of brand names." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "08d289fa873f" + }, + "source": [ + "### Dataset\n", + "\n", + "This notebook uses the [FDA dataset](https://cloud.google.com/blog/topics/healthcare-life-sciences/fda-mystudies-comes-to-google-cloud) available at [`bigquery-public-data.fda_drug`](https://console.cloud.google.com/bigquery?ws=!1m4!1m3!3m2!1sbigquery-public-data!2sfda_drug)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aed92deeb4a0" + }, + "source": [ + "### Costs\n", + "\n", + "This tutorial uses billable components of Google Cloud:\n", + "\n", + "* BigQuery (compute)\n", + "* BigQuery ML\n", + "\n", + "Learn about [BigQuery compute pricing](https://cloud.google.com/bigquery/pricing#analysis_pricing_models),\n", + "and [BigQuery ML pricing](https://cloud.google.com/bigquery/pricing#bqml),\n", + "and use the [Pricing Calculator](https://cloud.google.com/products/calculator/)\n", + "to generate a cost estimate based on your projected usage." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "i7EUnXsZhAGF" + }, + "source": [ + "## Installation\n", + "\n", + "Install the following packages required to execute this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "2b4ef9b72d43" + }, + "outputs": [], + "source": [ + "# !pip install -U --quiet bigframes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "58707a750154" + }, + "source": [ + "### Colab only: Uncomment the following cell to restart the kernel." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "f200f10a1da3" + }, + "outputs": [], + "source": [ + "# # Automatically restart kernel after installs so that your environment can access the new packages\n", + "# import IPython\n", + "\n", + "# app = IPython.Application.instance()\n", + "# app.kernel.do_shutdown(True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "960505627ddf" + }, + "source": [ + "### Import libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "PyQmSRbKA8r-" + }, + "outputs": [], + "source": [ + "from IPython.display import Markdown\n", + "\n", + "import bigframes.pandas as bpd\n", + "from bigframes.ml.llm import GeminiTextGenerator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sBCra4QMA2wR" + }, + "source": [ + "### Authenticate your Google Cloud account\n", + "\n", + "Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "74ccc9e52986" + }, + "source": [ + "**1. Vertex AI Workbench**\n", + "* Do nothing as you are already authenticated." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "de775a3773ba" + }, + "source": [ + "**2. Local JupyterLab instance, uncomment and run:**" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "254614fa0c46" + }, + "outputs": [], + "source": [ + "# ! gcloud auth login" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ef21552ccea8" + }, + "source": [ + "**3. Colab, uncomment and run:**" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "603adbbf0532" + }, + "outputs": [], + "source": [ + "# from google.colab import auth\n", + "\n", + "# auth.authenticate_user()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BF1j6f9HApxa" + }, + "source": [ + "## Before you begin\n", + "\n", + "### Set up your Google Cloud project\n", + "\n", + "**The following steps are required, regardless of your notebook environment.**\n", + "\n", + "1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n", + "\n", + "2. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n", + "\n", + "3. [Enable the BigQuery API](https://console.cloud.google.com/flows/enableapi?apiid=bigquery.googleapis.com).\n", + "\n", + "4. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WReHDGG5g0XY" + }, + "source": [ + "#### Set your project ID\n", + "\n", + "**If you don't know your project ID**, try the following:\n", + "* Run `gcloud config list`.\n", + "* Run `gcloud projects list`.\n", + "* See the support page: [Locate the project ID](https://support.google.com/googleapi/answer/7014113)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "oM1iC_MfAts1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1;31mERROR:\u001b[0m (gcloud.config.set) argument VALUE: Must be specified.\n", + "Usage: gcloud config set SECTION/PROPERTY VALUE [optional flags]\n", + " optional flags may be --help | --installation\n", + "\n", + "For detailed information on this command and its flags, run:\n", + " gcloud config set --help\n" + ] + } + ], + "source": [ + "# Please fill in these values.\n", + "PROJECT_ID = \"\" # @param {type:\"string\"}\n", + "\n", + "# Set the project id\n", + "! gcloud config set project {PROJECT_ID}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "evsJaAj5te0X" + }, + "source": [ + "#### BigFrames configuration\n", + "\n", + "Next, we will specify a [BigQuery connection](https://cloud.google.com/bigquery/docs/working-with-connections). If you already have a connection, you can simplify provide the name and skip the following creation steps.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "G1vVsPiMsL2X" + }, + "outputs": [], + "source": [ + "# Please fill in these values.\n", + "LOCATION = \"us\" # @param {type:\"string\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WGS_TzhWlPBN" + }, + "source": [ + "We will now try to use the provided connection, and if it doesn't exist, create a new one. We will also print the service account used." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "init_aip:mbsdk,all" + }, + "source": [ + "### Initialize BigFrames client\n", + "\n", + "Here, we set the project configuration based on the provided parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "OCccLirpkSRz" + }, + "outputs": [], + "source": [ + "# Note: The project option is not required in all environments.\n", + "# On BigQuery Studio, the project ID is automatically detected.\n", + "bpd.options.bigquery.project = PROJECT_ID\n", + "\n", + "# Note: The location option is not required.\n", + "# It defaults to the location of the first table or query\n", + "# passed to read_gbq(). For APIs where a location can't be\n", + "# auto-detected, the location defaults to the \"US\" location.\n", + "bpd.options.bigquery.location = LOCATION" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m8UCEtX9uLn6" + }, + "source": [ + "## Generate a name\n", + "\n", + "Let's start with entering a generic name and description of the drug." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "oxphj2gnuKou" + }, + "outputs": [], + "source": [ + "GENERIC_NAME = \"Entropofloxacin\" # @param {type:\"string\"}\n", + "USAGE = \"Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\" # @param {type:\"string\"}\n", + "NUM_NAMES = 10 # @param {type:\"integer\"}\n", + "TEMPERATURE = 0.5 # @param {type: \"number\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1q-vlbalzu1Q" + }, + "source": [ + "We can now create a prompt string, and populate it with the name and description." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "0knz5ZWMzed-" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Provide 10 unique and modern brand names in Markdown bullet point format. Do not provide any additional explanation.\n", + "\n", + "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", + "\n", + "The generic name is: Entropofloxacin\n", + "\n", + "The indications and usage are: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back..\n" + ] + } + ], + "source": [ + "zero_shot_prompt = f\"\"\"Provide {NUM_NAMES} unique and modern brand names in Markdown bullet point format. Do not provide any additional explanation.\n", + "\n", + "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", + "\n", + "The generic name is: {GENERIC_NAME}\n", + "\n", + "The indications and usage are: {USAGE}.\"\"\"\n", + "\n", + "print(zero_shot_prompt)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, let's create a helper function to predict with our model. It will take a string input, and add it to a temporary BigFrames DataFrame. It will also return the string extracted from the response DataFrame." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def predict(prompt: str, temperature: float = TEMPERATURE) -> str:\n", + " # Create dataframe\n", + " input = bpd.DataFrame(\n", + " {\n", + " \"prompt\": [prompt],\n", + " }\n", + " )\n", + "\n", + " # Return response\n", + " return model.predict(input, temperature=temperature).ml_generate_text_llm_result.iloc[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b1ZapNZsJW2p" + }, + "source": [ + "We can now initialize the model, and get a response to our prompt!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UW2fQ2k5Hsic" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job 25b47284-2b28-4cd9-ac9a-90379f818c84 is DONE. 0 Bytes processed. Open Job" ], - "source": [ - "# Please fill in these values.\n", - "PROJECT_ID = \"\" # @param {type:\"string\"}\n", - "\n", - "# Set the project id\n", - "! gcloud config set project {PROJECT_ID}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "evsJaAj5te0X" - }, - "source": [ - "#### BigFrames configuration\n", - "\n", - "Next, we will specify a [BigQuery connection](https://cloud.google.com/bigquery/docs/working-with-connections). If you already have a connection, you can simplify provide the name and skip the following creation steps.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "G1vVsPiMsL2X" - }, - "outputs": [], - "source": [ - "# Please fill in these values.\n", - "LOCATION = \"us\" # @param {type:\"string\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WGS_TzhWlPBN" - }, - "source": [ - "We will now try to use the provided connection, and if it doesn't exist, create a new one. We will also print the service account used." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "init_aip:mbsdk,all" - }, - "source": [ - "### Initialize BigFrames client\n", - "\n", - "Here, we set the project configuration based on the provided parameters." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "OCccLirpkSRz" - }, - "outputs": [], - "source": [ - "# Note: The project option is not required in all environments.\n", - "# On BigQuery Studio, the project ID is automatically detected.\n", - "bpd.options.bigquery.project = PROJECT_ID\n", - "\n", - "# Note: The location option is not required.\n", - "# It defaults to the location of the first table or query\n", - "# passed to read_gbq(). For APIs where a location can't be\n", - "# auto-detected, the location defaults to the \"US\" location.\n", - "bpd.options.bigquery.location = LOCATION" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "m8UCEtX9uLn6" - }, - "source": [ - "## Generate a name\n", - "\n", - "Let's start with entering a generic name and description of the drug." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "id": "oxphj2gnuKou" - }, - "outputs": [], - "source": [ - "GENERIC_NAME = \"Entropofloxacin\" # @param {type:\"string\"}\n", - "USAGE = \"Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\" # @param {type:\"string\"}\n", - "NUM_NAMES = 10 # @param {type:\"integer\"}\n", - "TEMPERATURE = 0.5 # @param {type: \"number\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1q-vlbalzu1Q" - }, - "source": [ - "We can now create a prompt string, and populate it with the name and description." + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "id": "0knz5ZWMzed-" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Provide 10 unique and modern brand names in Markdown bullet point format. Do not provide any additional explanation.\n", - "\n", - "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", - "\n", - "The generic name is: Entropofloxacin\n", - "\n", - "The indications and usage are: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back..\n" - ] - } + "data": { + "text/html": [ + "Query job 0efa6f42-6569-4274-ac21-667c7eecefc7 is DONE. 0 Bytes processed. Open Job" ], - "source": [ - "zero_shot_prompt = f\"\"\"Provide {NUM_NAMES} unique and modern brand names in Markdown bullet point format. Do not provide any additional explanation.\n", - "\n", - "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", - "\n", - "The generic name is: {GENERIC_NAME}\n", - "\n", - "The indications and usage are: {USAGE}.\"\"\"\n", - "\n", - "print(zero_shot_prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, let's create a helper function to predict with our model. It will take a string input, and add it to a temporary BigFrames DataFrame. It will also return the string extracted from the response DataFrame." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def predict(prompt: str, temperature: float = TEMPERATURE) -> str:\n", - " # Create dataframe\n", - " input = bpd.DataFrame(\n", - " {\n", - " \"prompt\": [prompt],\n", - " }\n", - " )\n", - "\n", - " # Return response\n", - " return model.predict(input, temperature=temperature).ml_generate_text_llm_result.iloc[0]" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "b1ZapNZsJW2p" - }, - "source": [ - "We can now initialize the model, and get a response to our prompt!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "UW2fQ2k5Hsic" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job 25b47284-2b28-4cd9-ac9a-90379f818c84 is DONE. 0 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 0efa6f42-6569-4274-ac21-667c7eecefc7 is DONE. 0 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job c5e98170-7d58-4aa2-a3a3-6680cd9a54c0 is DONE. 8 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 5fd9d5bf-c731-4b21-b7c9-9b6244ffb412 is DONE. 2 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 36f7e8ec-ee42-4f94-8e38-bdf18b371517 is DONE. 118 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "- Etherealox\n", - "- Zenithrox\n", - "- Aureox\n", - "- Lucentrox\n", - "- Aethrox\n", - "- Luminex\n", - "- Elysirox\n", - "- Quasarox\n", - "- Novaflux\n", - "- Arcanox" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "Query job c5e98170-7d58-4aa2-a3a3-6680cd9a54c0 is DONE. 8 Bytes processed. Open Job" ], - "source": [ - "# Define the model\n", - "model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")\n", - "\n", - "# Invoke LLM with prompt\n", - "response = predict(zero_shot_prompt, temperature = TEMPERATURE)\n", - "\n", - "# Print results as Markdown\n", - "Markdown(response)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "o3yIhHV2jsUT" - }, - "source": [ - "We're off to a great start! Let's see if we can refine our response." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mBroUzWS8xOL" - }, - "source": [ - "## Few-shot learning\n", - "\n", - "Let's try using [few-shot learning](https://paperswithcode.com/task/few-shot-learning). We will provide a few examples of what we're looking for along with our prompt.\n", - "\n", - "Our prompt will consist of 3 parts:\n", - "* General instructions (e.g. generate $n$ brand names)\n", - "* Multiple examples\n", - "* Information about the drug we'd like to generate a name for\n", - "\n", - "Let's walk through how to construct this prompt.\n", - "\n", - "Our first step will be to define how many examples we want to provide in the prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "id": "MXdI78SOElyt" - }, - "outputs": [], - "source": [ - "# Specify number of examples to include\n", - "\n", - "NUM_EXAMPLES = 3 # @param {type:\"integer\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "U8w4puVM_892" - }, - "source": [ - "Next, let's define a prefix that will set the overall context." + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 24, - "metadata": { - "id": "aQ2iscnhF2cx" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Provide 10 unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", - "\n", - "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", - "\n", - "First, we will provide 3 examples to help with your thought process.\n", - "\n", - "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", - "\n" - ] - } + "data": { + "text/html": [ + "Query job 5fd9d5bf-c731-4b21-b7c9-9b6244ffb412 is DONE. 2 Bytes processed. Open Job" ], - "source": [ - "prefix_prompt = f\"\"\"Provide {NUM_NAMES} unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", - "\n", - "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", - "\n", - "First, we will provide {NUM_EXAMPLES} examples to help with your thought process.\n", - "\n", - "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", - "\"\"\"\n", - "\n", - "print(prefix_prompt)" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "VI0Spv-axN7d" - }, - "source": [ - "Our next step will be to include examples into the prompt.\n", - "\n", - "We will start out by retrieving the raw data for the examples, by querying the BigQuery public dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "metadata": { - "id": "IoO_Bp8wA07N" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job 542b0ce1-9d56-456f-bcd3-d24a6f0c825a is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 2405ba41-b263-46d3-a0e5-3b5e7ecef6ab is DONE. 0 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job b24663ec-8d81-4295-84df-ffb65a6a0f1b is DONE. 3.1 kB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
openfda_generic_nameopenfda_brand_nameindications_and_usage
0BENZALKONIUM CHLORIDEmeijer kidsUse - hand washing to decrease bacteria on skin
3OCTINOXATE, TITANIUM DIOXIDECD DIORSKIN STAR Studio Makeup Spectacular Bri...Uses Helps prevent sunburn. If used as directe...
4TRIAMCINOLONE ACETONIDETriamcinolone AcetonideINDICATIONS AND USAGE Triamcinolone Acetonide ...
5BACITRACIN ZINC, NEOMYCIN SULFATE, POLYMYXIN B...Triple AntibioticFirst aid to help prevent infection in minor c...
6RISPERIDONERisperidone1. INDICATIONS AND USAGE Risperidone is an aty...
\n", - "

5 rows × 3 columns

\n", - "
[5 rows x 3 columns in total]" - ], - "text/plain": [ - " openfda_generic_name \\\n", - "0 BENZALKONIUM CHLORIDE \n", - "3 OCTINOXATE, TITANIUM DIOXIDE \n", - "4 TRIAMCINOLONE ACETONIDE \n", - "5 BACITRACIN ZINC, NEOMYCIN SULFATE, POLYMYXIN B... \n", - "6 RISPERIDONE \n", - "\n", - " openfda_brand_name \\\n", - "0 meijer kids \n", - "3 CD DIORSKIN STAR Studio Makeup Spectacular Bri... \n", - "4 Triamcinolone Acetonide \n", - "5 Triple Antibiotic \n", - "6 Risperidone \n", - "\n", - " indications_and_usage \n", - "0 Use - hand washing to decrease bacteria on skin \n", - "3 Uses Helps prevent sunburn. If used as directe... \n", - "4 INDICATIONS AND USAGE Triamcinolone Acetonide ... \n", - "5 First aid to help prevent infection in minor c... \n", - "6 1. INDICATIONS AND USAGE Risperidone is an aty... \n", - "\n", - "[5 rows x 3 columns]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "Query job 36f7e8ec-ee42-4f94-8e38-bdf18b371517 is DONE. 118 Bytes processed. Open Job" ], - "source": [ - "# Query 3 columns of interest from drug label dataset\n", - "df = bpd.read_gbq(\"bigquery-public-data.fda_drug.drug_label\",\n", - " columns=[\"openfda_generic_name\", \"openfda_brand_name\", \"indications_and_usage\"])\n", - "\n", - "# Exclude any rows with missing data\n", - "df = df.dropna()\n", - "\n", - "# Drop duplicate rows\n", - "df = df.drop_duplicates()\n", - "\n", - "# Print values\n", - "df.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "W5kOtbNGBTI2" - }, - "source": [ - "Let's now filter the results to remove atypical names." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "id": "95WDe2eCCeLx" - }, - "outputs": [], - "source": [ - "# Remove names with spaces\n", - "df = df[df[\"openfda_brand_name\"].str.find(\" \") == -1]\n", - "\n", - "# Remove names with 5 or fewer characters\n", - "df = df[df[\"openfda_brand_name\"].str.len() > 5]\n", - "\n", - "# Remove names where the generic and brand name match (case-insensitive)\n", - "df = df[df[\"openfda_generic_name\"].str.lower() != df[\"openfda_brand_name\"].str.lower()]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "FZD89ep4EyYc" - }, - "source": [ - "Let's take `NUM_EXAMPLES` samples to include in the prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "id": "2ohZYg7QEyJV" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job 293c90e0-7fdf-4769-9d8e-f222f35d368e is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
openfda_generic_nameopenfda_brand_nameindications_and_usage
81748AMPICILLIN SODIUMAmpicillinINDICATIONS AND USAGE Ampicillin for Injection...
730AZTREONAMCayston1 INDICATIONS AND USAGE CAYSTON® is indicated ...
71763TERAZOSIN HYDROCHLORIDETerazosinINDICATIONS AND USAGE Terazosin capsules are i...
\n", - "
" - ], - "text/plain": [ - " openfda_generic_name openfda_brand_name \\\n", - "81748 AMPICILLIN SODIUM Ampicillin \n", - "730 AZTREONAM Cayston \n", - "71763 TERAZOSIN HYDROCHLORIDE Terazosin \n", - "\n", - " indications_and_usage \n", - "81748 INDICATIONS AND USAGE Ampicillin for Injection... \n", - "730 1 INDICATIONS AND USAGE CAYSTON® is indicated ... \n", - "71763 INDICATIONS AND USAGE Terazosin capsules are i... " - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "- Etherealox\n", + "- Zenithrox\n", + "- Aureox\n", + "- Lucentrox\n", + "- Aethrox\n", + "- Luminex\n", + "- Elysirox\n", + "- Quasarox\n", + "- Novaflux\n", + "- Arcanox" ], - "source": [ - "# Take a sample and convert to a Pandas dataframe for local usage.\n", - "df_examples = df.sample(NUM_EXAMPLES, random_state=3).to_pandas()\n", - "\n", - "df_examples" + "text/plain": [ + "" ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "J-Qa1_SCImXy" - }, - "source": [ - "Let's now convert the data to a JSON structure, to enable embedding into a prompt. For consistency, we'll capitalize each example brand name." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "id": "PcJdSaw0EGcW" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[{'brand_name': 'Ampicillin', 'generic_name': 'AMPICILLIN SODIUM', 'usage': 'INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.'}, {'brand_name': 'Cayston', 'generic_name': 'AZTREONAM', 'usage': '1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)'}, {'brand_name': 'Terazosin', 'generic_name': 'TERAZOSIN HYDROCHLORIDE', 'usage': 'INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.'}]\n" - ] - } + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Define the model\n", + "model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")\n", + "\n", + "# Invoke LLM with prompt\n", + "response = predict(zero_shot_prompt, temperature = TEMPERATURE)\n", + "\n", + "# Print results as Markdown\n", + "Markdown(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "o3yIhHV2jsUT" + }, + "source": [ + "We're off to a great start! Let's see if we can refine our response." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mBroUzWS8xOL" + }, + "source": [ + "## Few-shot learning\n", + "\n", + "Let's try using [few-shot learning](https://paperswithcode.com/task/few-shot-learning). We will provide a few examples of what we're looking for along with our prompt.\n", + "\n", + "Our prompt will consist of 3 parts:\n", + "* General instructions (e.g. generate $n$ brand names)\n", + "* Multiple examples\n", + "* Information about the drug we'd like to generate a name for\n", + "\n", + "Let's walk through how to construct this prompt.\n", + "\n", + "Our first step will be to define how many examples we want to provide in the prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "id": "MXdI78SOElyt" + }, + "outputs": [], + "source": [ + "# Specify number of examples to include\n", + "\n", + "NUM_EXAMPLES = 3 # @param {type:\"integer\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "U8w4puVM_892" + }, + "source": [ + "Next, let's define a prefix that will set the overall context." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "id": "aQ2iscnhF2cx" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Provide 10 unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", + "\n", + "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", + "\n", + "First, we will provide 3 examples to help with your thought process.\n", + "\n", + "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", + "\n" + ] + } + ], + "source": [ + "prefix_prompt = f\"\"\"Provide {NUM_NAMES} unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", + "\n", + "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", + "\n", + "First, we will provide {NUM_EXAMPLES} examples to help with your thought process.\n", + "\n", + "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", + "\"\"\"\n", + "\n", + "print(prefix_prompt)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VI0Spv-axN7d" + }, + "source": [ + "Our next step will be to include examples into the prompt.\n", + "\n", + "We will start out by retrieving the raw data for the examples, by querying the BigQuery public dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "id": "IoO_Bp8wA07N" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job 542b0ce1-9d56-456f-bcd3-d24a6f0c825a is DONE. 84.4 MB processed. Open Job" ], - "source": [ - "examples = [\n", - " {\n", - " \"brand_name\": brand_name.capitalize(),\n", - " \"generic_name\": generic_name,\n", - " \"usage\": usage,\n", - " }\n", - " for brand_name, generic_name, usage in zip(\n", - " df_examples[\"openfda_brand_name\"],\n", - " df_examples[\"openfda_generic_name\"],\n", - " df_examples[\"indications_and_usage\"],\n", - " )\n", - "]\n", - "\n", - "print(examples)" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "oU4mb1Dwgq64" - }, - "source": [ - "We'll create a prompt template for each example, and view the first one." - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "id": "kzAVsF6wJ93S" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Generic name: AMPICILLIN SODIUM\\nUsage: INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.\\nBrand name: Ampicillin\\n\\nGeneric name: AZTREONAM\\nUsage: 1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)\\nBrand name: Cayston\\n\\nGeneric name: TERAZOSIN HYDROCHLORIDE\\nUsage: INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.\\nBrand name: Terazosin\\n\\n'" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "Query job 2405ba41-b263-46d3-a0e5-3b5e7ecef6ab is DONE. 0 Bytes processed. Open Job" ], - "source": [ - "example_prompt = \"\"\n", - "for example in examples:\n", - " example_prompt += f\"Generic name: {example['generic_name']}\\nUsage: {example['usage']}\\nBrand name: {example['brand_name']}\\n\\n\"\n", - "\n", - "example_prompt" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "kbV2X1CXAyLV" - }, - "source": [ - "Finally, we can create a suffix to our prompt. This will contain the generic name of the drug, its usage, ending with a request for brand names." - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": { - "id": "OYp6W_XfHTlo" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generic name: Entropofloxacin\n", - "Usage: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\n", - "Brand names:\n" - ] - } + "data": { + "text/html": [ + "Query job b24663ec-8d81-4295-84df-ffb65a6a0f1b is DONE. 3.1 kB processed. Open Job" ], - "source": [ - "suffix_prompt = f\"\"\"Generic name: {GENERIC_NAME}\n", - "Usage: {USAGE}\n", - "Brand names:\"\"\"\n", - "\n", - "print(suffix_prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RiaisW1nihJP" - }, - "source": [ - "Let's pull it altogether into a few shot prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "metadata": { - "id": "99xdU7l8C1h8" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Provide 10 unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", - "\n", - "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", - "\n", - "First, we will provide 3 examples to help with your thought process.\n", - "\n", - "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", - "Generic name: AMPICILLIN SODIUM\n", - "Usage: INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.\n", - "Brand name: Ampicillin\n", - "\n", - "Generic name: AZTREONAM\n", - "Usage: 1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)\n", - "Brand name: Cayston\n", - "\n", - "Generic name: TERAZOSIN HYDROCHLORIDE\n", - "Usage: INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.\n", - "Brand name: Terazosin\n", - "\n", - "Generic name: Entropofloxacin\n", - "Usage: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\n", - "Brand names:\n" - ] - } + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openfda_generic_nameopenfda_brand_nameindications_and_usage
0BENZALKONIUM CHLORIDEmeijer kidsUse - hand washing to decrease bacteria on skin
3OCTINOXATE, TITANIUM DIOXIDECD DIORSKIN STAR Studio Makeup Spectacular Bri...Uses Helps prevent sunburn. If used as directe...
4TRIAMCINOLONE ACETONIDETriamcinolone AcetonideINDICATIONS AND USAGE Triamcinolone Acetonide ...
5BACITRACIN ZINC, NEOMYCIN SULFATE, POLYMYXIN B...Triple AntibioticFirst aid to help prevent infection in minor c...
6RISPERIDONERisperidone1. INDICATIONS AND USAGE Risperidone is an aty...
\n", + "

5 rows × 3 columns

\n", + "
[5 rows x 3 columns in total]" + ], + "text/plain": [ + " openfda_generic_name \\\n", + "0 BENZALKONIUM CHLORIDE \n", + "3 OCTINOXATE, TITANIUM DIOXIDE \n", + "4 TRIAMCINOLONE ACETONIDE \n", + "5 BACITRACIN ZINC, NEOMYCIN SULFATE, POLYMYXIN B... \n", + "6 RISPERIDONE \n", + "\n", + " openfda_brand_name \\\n", + "0 meijer kids \n", + "3 CD DIORSKIN STAR Studio Makeup Spectacular Bri... \n", + "4 Triamcinolone Acetonide \n", + "5 Triple Antibiotic \n", + "6 Risperidone \n", + "\n", + " indications_and_usage \n", + "0 Use - hand washing to decrease bacteria on skin \n", + "3 Uses Helps prevent sunburn. If used as directe... \n", + "4 INDICATIONS AND USAGE Triamcinolone Acetonide ... \n", + "5 First aid to help prevent infection in minor c... \n", + "6 1. INDICATIONS AND USAGE Risperidone is an aty... \n", + "\n", + "[5 rows x 3 columns]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Query 3 columns of interest from drug label dataset\n", + "df = bpd.read_gbq(\"bigquery-public-data.fda_drug.drug_label\",\n", + " columns=[\"openfda_generic_name\", \"openfda_brand_name\", \"indications_and_usage\"])\n", + "\n", + "# Exclude any rows with missing data\n", + "df = df.dropna()\n", + "\n", + "# Drop duplicate rows\n", + "df = df.drop_duplicates()\n", + "\n", + "# Print values\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W5kOtbNGBTI2" + }, + "source": [ + "Let's now filter the results to remove atypical names." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "id": "95WDe2eCCeLx" + }, + "outputs": [], + "source": [ + "# Remove names with spaces\n", + "df = df[df[\"openfda_brand_name\"].str.find(\" \") == -1]\n", + "\n", + "# Remove names with 5 or fewer characters\n", + "df = df[df[\"openfda_brand_name\"].str.len() > 5]\n", + "\n", + "# Remove names where the generic and brand name match (case-insensitive)\n", + "df = df[df[\"openfda_generic_name\"].str.lower() != df[\"openfda_brand_name\"].str.lower()]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FZD89ep4EyYc" + }, + "source": [ + "Let's take `NUM_EXAMPLES` samples to include in the prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "id": "2ohZYg7QEyJV" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job 293c90e0-7fdf-4769-9d8e-f222f35d368e is DONE. 84.4 MB processed. Open Job" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openfda_generic_nameopenfda_brand_nameindications_and_usage
81748AMPICILLIN SODIUMAmpicillinINDICATIONS AND USAGE Ampicillin for Injection...
730AZTREONAMCayston1 INDICATIONS AND USAGE CAYSTON® is indicated ...
71763TERAZOSIN HYDROCHLORIDETerazosinINDICATIONS AND USAGE Terazosin capsules are i...
\n", + "
" + ], + "text/plain": [ + " openfda_generic_name openfda_brand_name \\\n", + "81748 AMPICILLIN SODIUM Ampicillin \n", + "730 AZTREONAM Cayston \n", + "71763 TERAZOSIN HYDROCHLORIDE Terazosin \n", + "\n", + " indications_and_usage \n", + "81748 INDICATIONS AND USAGE Ampicillin for Injection... \n", + "730 1 INDICATIONS AND USAGE CAYSTON® is indicated ... \n", + "71763 INDICATIONS AND USAGE Terazosin capsules are i... " + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Take a sample and convert to a Pandas dataframe for local usage.\n", + "df_examples = df.sample(NUM_EXAMPLES, random_state=3).to_pandas()\n", + "\n", + "df_examples" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J-Qa1_SCImXy" + }, + "source": [ + "Let's now convert the data to a JSON structure, to enable embedding into a prompt. For consistency, we'll capitalize each example brand name." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "id": "PcJdSaw0EGcW" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'brand_name': 'Ampicillin', 'generic_name': 'AMPICILLIN SODIUM', 'usage': 'INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.'}, {'brand_name': 'Cayston', 'generic_name': 'AZTREONAM', 'usage': '1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)'}, {'brand_name': 'Terazosin', 'generic_name': 'TERAZOSIN HYDROCHLORIDE', 'usage': 'INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.'}]\n" + ] + } + ], + "source": [ + "examples = [\n", + " {\n", + " \"brand_name\": brand_name.capitalize(),\n", + " \"generic_name\": generic_name,\n", + " \"usage\": usage,\n", + " }\n", + " for brand_name, generic_name, usage in zip(\n", + " df_examples[\"openfda_brand_name\"],\n", + " df_examples[\"openfda_generic_name\"],\n", + " df_examples[\"indications_and_usage\"],\n", + " )\n", + "]\n", + "\n", + "print(examples)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oU4mb1Dwgq64" + }, + "source": [ + "We'll create a prompt template for each example, and view the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "id": "kzAVsF6wJ93S" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Generic name: AMPICILLIN SODIUM\\nUsage: INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.\\nBrand name: Ampicillin\\n\\nGeneric name: AZTREONAM\\nUsage: 1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)\\nBrand name: Cayston\\n\\nGeneric name: TERAZOSIN HYDROCHLORIDE\\nUsage: INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.\\nBrand name: Terazosin\\n\\n'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example_prompt = \"\"\n", + "for example in examples:\n", + " example_prompt += f\"Generic name: {example['generic_name']}\\nUsage: {example['usage']}\\nBrand name: {example['brand_name']}\\n\\n\"\n", + "\n", + "example_prompt" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kbV2X1CXAyLV" + }, + "source": [ + "Finally, we can create a suffix to our prompt. This will contain the generic name of the drug, its usage, ending with a request for brand names." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "id": "OYp6W_XfHTlo" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generic name: Entropofloxacin\n", + "Usage: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\n", + "Brand names:\n" + ] + } + ], + "source": [ + "suffix_prompt = f\"\"\"Generic name: {GENERIC_NAME}\n", + "Usage: {USAGE}\n", + "Brand names:\"\"\"\n", + "\n", + "print(suffix_prompt)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RiaisW1nihJP" + }, + "source": [ + "Let's pull it altogether into a few shot prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "id": "99xdU7l8C1h8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Provide 10 unique and modern brand names in Markdown bullet point format, related to the drug at the bottom of this prompt.\n", + "\n", + "Be creative with the brand names. Don't use English words directly; use variants or invented words.\n", + "\n", + "First, we will provide 3 examples to help with your thought process.\n", + "\n", + "Then, we will provide the generic name and usage for the drug we'd like you to generate brand names for.\n", + "Generic name: AMPICILLIN SODIUM\n", + "Usage: INDICATIONS AND USAGE Ampicillin for Injection, USP is indicated in the treatment of infections caused by susceptible strains of the designated organisms in the following conditions: Respiratory Tract Infections caused by Streptococcus pneumoniae. Staphylococcus aureus (penicillinase and nonpenicillinase-producing), H. influenzae, and Group A beta-hemolytic streptococci. Bacterial Meningitis caused by E. coli, Group B streptococci, and other Gram-negative bacteria (Listeria monocytogenes, N. meningitidis). The addition of an aminoglycoside with ampicillin may increase its effectiveness against Gram-negative bacteria. Septicemia and Endocarditis caused by susceptible Gram-positive organisms including Streptococcus spp., penicillin G-susceptible staphylococci, and enterococci. Gram-negative sepsis caused by E. coli, Proteus mirabilis and Salmonella spp. responds to ampicillin. Endocarditis due to enterococcal strains usually respond to intravenous therapy. The addition of an aminoglycoside may enhance the effectiveness of ampicillin when treating streptococcal endocarditis. Urinary Tract Infections caused by sensitive strains of E. coli and Proteus mirabilis. Gastrointestinal Infections caused by Salmonella typhi (typhoid fever), other Salmonella spp., and Shigella spp. (dysentery) usually respond to oral or intravenous therapy. Bacteriology studies to determine the causative organisms and their susceptibility to ampicillin should be performed. Therapy may be instituted prior to obtaining results of susceptibility testing. It is advisable to reserve the parenteral form of this drug for moderately severe and severe infections and for patients who are unable to take the oral forms. A change to oral ampicillin may be made as soon as appropriate. To reduce the development of drug-resistant bacteria and maintain the effectiveness of Ampicillin for Injection, USP and other antibacterial drugs, Ampicillin for Injection, USP should be used only to treat or prevent infections that are proven or strongly suspected to be caused by susceptible bacteria. When culture and susceptibility information are available, they should be considered in selecting or modifying antibacterial therapy. In the absence of such data, local epidemiology and susceptibility patterns may contribute to the empiric selection of therapy. Indicated surgical procedures should be performed.\n", + "Brand name: Ampicillin\n", + "\n", + "Generic name: AZTREONAM\n", + "Usage: 1 INDICATIONS AND USAGE CAYSTON® is indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia [see Clinical Studies (14) ]. To reduce the development of drug-resistant bacteria and maintain the effectiveness of CAYSTON and other antibacterial drugs, CAYSTON should be used only to treat patients with CF known to have Pseudomonas aeruginosa in the lungs. CAYSTON is a monobactam antibacterial indicated to improve respiratory symptoms in cystic fibrosis (CF) patients with Pseudomonas aeruginosa. Safety and effectiveness have not been established in pediatric patients below the age of 7 years, patients with FEV1 <25% or >75% predicted, or patients colonized with Burkholderia cepacia. (1)\n", + "Brand name: Cayston\n", + "\n", + "Generic name: TERAZOSIN HYDROCHLORIDE\n", + "Usage: INDICATIONS AND USAGE Terazosin capsules are indicated for the treatment of symptomatic benign prostatic hyperplasia (BPH). There is a rapid response, with approximately 70% of patients experiencing an increase in urinary flow and improvement in symptoms of BPH when treated with terazosin capsules. The long-term effects of terazosin capsules on the incidence of surgery, acute urinary obstruction or other complications of BPH are yet to be determined. Terazosin capsules are also indicated for the treatment of hypertension. Terazosin capsules can be used alone or in combination with other antihypertensive agents such as diuretics or beta-adrenergic blocking agents.\n", + "Brand name: Terazosin\n", + "\n", + "Generic name: Entropofloxacin\n", + "Usage: Entropofloxacin is a fluoroquinolone antibiotic that is used to treat a variety of bacterial infections, including: pneumonia, streptococcus infections, salmonella infections, escherichia coli infections, and pseudomonas aeruginosa infections It is taken by mouth or by injection. The dosage and frequency of administration will vary depending on the type of infection being treated. It should be taken for the full course of treatment, even if symptoms improve after a few days. Stopping the medication early may increase the risk of the infection coming back.\n", + "Brand names:\n" + ] + } + ], + "source": [ + "# Define the prompt\n", + "few_shot_prompt = prefix_prompt + example_prompt + suffix_prompt\n", + "\n", + "# Print the prompt\n", + "print(few_shot_prompt)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nbUWdHtfitWn" + }, + "source": [ + "Now, let's pass our prompt to the LLM, and get a response!" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "id": "d4ODRJdvLhlQ" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job 5c6c3b79-812c-4a6e-876e-ca1ff6230a6e is DONE. 0 Bytes processed. Open Job" ], - "source": [ - "# Define the prompt\n", - "few_shot_prompt = prefix_prompt + example_prompt + suffix_prompt\n", - "\n", - "# Print the prompt\n", - "print(few_shot_prompt)" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "nbUWdHtfitWn" - }, - "source": [ - "Now, let's pass our prompt to the LLM, and get a response!" + "data": { + "text/html": [ + "Query job 168d5859-5edb-4702-8192-838ac2c7bc17 is DONE. 8 Bytes processed. Open Job" + ], + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "id": "d4ODRJdvLhlQ" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job 5c6c3b79-812c-4a6e-876e-ca1ff6230a6e is DONE. 0 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 168d5859-5edb-4702-8192-838ac2c7bc17 is DONE. 8 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 72f07348-4bcd-4042-84ca-396e7651ad03 is DONE. 2 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 70863a3b-8c63-423c-84cd-2804139daf5f is DONE. 679 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/markdown": [ - "- **Aerion:** (Derived from \"aer\" meaning air)\n", - "- **Aquazone:** (Combining \"aqua\" for water and \"zone\" for area)\n", - "- **Biosphere:** (Inspired by the concept of a self-contained ecosystem)\n", - "- **Celestial:** (Evoking the vastness and healing power of the universe)\n", - "- **Ethereal:** (Conveying a sense of lightness and transcendence)\n", - "- **Luminary:** (From \"lumen\" meaning light, symbolizing hope and healing)\n", - "- **Quasar:** (Inspired by the powerful and distant cosmic objects)\n", - "- **Sanctuary:** (Creating a sense of safety and refuge)\n", - "- **Zenith:** (Reaching the highest point or peak)\n", - "- **Zephyr:** (Named after the gentle west wind, representing a calming and soothing effect)" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "Query job 72f07348-4bcd-4042-84ca-396e7651ad03 is DONE. 2 Bytes processed. Open Job" ], - "source": [ - "response = predict(few_shot_prompt)\n", - "\n", - "Markdown(response)" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "pFakjrTElOBs" - }, - "source": [ - "# Bulk generation\n", - "\n", - "Let's take these experiments to the next level by generating many names in bulk. We'll see how to leverage BigFrames at scale!\n", - "\n", - "We can start by finding drugs that are missing brand names. There are approximately 4,000 drugs that meet this criteria. We'll put a limit of 100 in this notebook." + "data": { + "text/html": [ + "Query job 70863a3b-8c63-423c-84cd-2804139daf5f is DONE. 679 Bytes processed. Open Job" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "- **Aerion:** (Derived from \"aer\" meaning air)\n", + "- **Aquazone:** (Combining \"aqua\" for water and \"zone\" for area)\n", + "- **Biosphere:** (Inspired by the concept of a self-contained ecosystem)\n", + "- **Celestial:** (Evoking the vastness and healing power of the universe)\n", + "- **Ethereal:** (Conveying a sense of lightness and transcendence)\n", + "- **Luminary:** (From \"lumen\" meaning light, symbolizing hope and healing)\n", + "- **Quasar:** (Inspired by the powerful and distant cosmic objects)\n", + "- **Sanctuary:** (Creating a sense of safety and refuge)\n", + "- **Zenith:** (Reaching the highest point or peak)\n", + "- **Zephyr:** (Named after the gentle west wind, representing a calming and soothing effect)" + ], + "text/plain": [ + "" ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": { - "id": "8eAutS41mx6U" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job b73f92bb-0e58-4fe4-adfb-b948fc5f4647 is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 392dae36-aacb-4753-b28c-dad8291cb153 is DONE. 0 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 7c6ff6ee-db64-4629-a417-846dcecac127 is DONE. 6.3 kB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
openfda_generic_nameopenfda_brand_nameindications_and_usage
89MEPHITIS MEPHITICAMEPHITIS MEPHITICAINDICATIONS Condition listed above or as direc...
105ONDANSETRONONDANSETRON1 INDICATIONS AND USAGE Ondansetron Injection,...
124CLOFARABINECLOFARABINE1 INDICATIONS AND USAGE Clofarabine injection ...
273ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDEACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDEUses Temporary relief of occasional headaches ...
284OFLOXACINOFLOXACININDICATIONS AND USAGE To reduce the developmen...
\n", - "

5 rows × 3 columns

\n", - "
[5 rows x 3 columns in total]" - ], - "text/plain": [ - " openfda_generic_name \\\n", - "89 MEPHITIS MEPHITICA \n", - "105 ONDANSETRON \n", - "124 CLOFARABINE \n", - "273 ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDE \n", - "284 OFLOXACIN \n", - "\n", - " openfda_brand_name \\\n", - "89 MEPHITIS MEPHITICA \n", - "105 ONDANSETRON \n", - "124 CLOFARABINE \n", - "273 ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDE \n", - "284 OFLOXACIN \n", - "\n", - " indications_and_usage \n", - "89 INDICATIONS Condition listed above or as direc... \n", - "105 1 INDICATIONS AND USAGE Ondansetron Injection,... \n", - "124 1 INDICATIONS AND USAGE Clofarabine injection ... \n", - "273 Uses Temporary relief of occasional headaches ... \n", - "284 INDICATIONS AND USAGE To reduce the developmen... \n", - "\n", - "[5 rows x 3 columns]" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = predict(few_shot_prompt)\n", + "\n", + "Markdown(response)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pFakjrTElOBs" + }, + "source": [ + "# Bulk generation\n", + "\n", + "Let's take these experiments to the next level by generating many names in bulk. We'll see how to leverage BigFrames at scale!\n", + "\n", + "We can start by finding drugs that are missing brand names. There are approximately 4,000 drugs that meet this criteria. We'll put a limit of 100 in this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "id": "8eAutS41mx6U" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job b73f92bb-0e58-4fe4-adfb-b948fc5f4647 is DONE. 84.4 MB processed. Open Job" ], - "source": [ - "# Query 3 columns of interest from drug label dataset\n", - "df_missing = bpd.read_gbq(\"bigquery-public-data.fda_drug.drug_label\",\n", - " columns=[\"openfda_generic_name\", \"openfda_brand_name\", \"indications_and_usage\"])\n", - "\n", - "# Exclude any rows with missing data\n", - "df_missing = df_missing.dropna()\n", - "\n", - "# Include rows in which openfda_brand_name equals openfda_generic_name\n", - "df_missing = df_missing[df_missing[\"openfda_generic_name\"] == df_missing[\"openfda_brand_name\"]]\n", - "\n", - "# Limit the number of rows for demonstration purposes\n", - "df_missing = df_missing.head(100)\n", - "\n", - "# Print values\n", - "df_missing.head()" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "Fm6L8S7eVnCI" - }, - "source": [ - "We will create a column `prompt` with a customized prompt for each row." + "data": { + "text/html": [ + "Query job 392dae36-aacb-4753-b28c-dad8291cb153 is DONE. 0 Bytes processed. Open Job" + ], + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "id": "19TvGN1PVmVX" - }, - "outputs": [], - "source": [ - "df_missing[\"prompt\"] = (\n", - " \"Provide a unique and modern brand name related to this pharmaceutical drug.\"\n", - " + \"Don't use English words directly; use variants or invented words. The generic name is: \"\n", - " + df_missing[\"openfda_generic_name\"]\n", - " + \". The indications and usage are: \"\n", - " + df_missing[\"indications_and_usage\"]\n", - " + \".\"\n", - ")" + "data": { + "text/html": [ + "Query job 7c6ff6ee-db64-4629-a417-846dcecac127 is DONE. 6.3 kB processed. Open Job" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openfda_generic_nameopenfda_brand_nameindications_and_usage
89MEPHITIS MEPHITICAMEPHITIS MEPHITICAINDICATIONS Condition listed above or as direc...
105ONDANSETRONONDANSETRON1 INDICATIONS AND USAGE Ondansetron Injection,...
124CLOFARABINECLOFARABINE1 INDICATIONS AND USAGE Clofarabine injection ...
273ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDEACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDEUses Temporary relief of occasional headaches ...
284OFLOXACINOFLOXACININDICATIONS AND USAGE To reduce the developmen...
\n", + "

5 rows × 3 columns

\n", + "
[5 rows x 3 columns in total]" + ], + "text/plain": [ + " openfda_generic_name \\\n", + "89 MEPHITIS MEPHITICA \n", + "105 ONDANSETRON \n", + "124 CLOFARABINE \n", + "273 ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDE \n", + "284 OFLOXACIN \n", + "\n", + " openfda_brand_name \\\n", + "89 MEPHITIS MEPHITICA \n", + "105 ONDANSETRON \n", + "124 CLOFARABINE \n", + "273 ACETAMINOPHEN AND DIPHENHYDRAMINE HYDROCHLORIDE \n", + "284 OFLOXACIN \n", + "\n", + " indications_and_usage \n", + "89 INDICATIONS Condition listed above or as direc... \n", + "105 1 INDICATIONS AND USAGE Ondansetron Injection,... \n", + "124 1 INDICATIONS AND USAGE Clofarabine injection ... \n", + "273 Uses Temporary relief of occasional headaches ... \n", + "284 INDICATIONS AND USAGE To reduce the developmen... \n", + "\n", + "[5 rows x 3 columns]" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Query 3 columns of interest from drug label dataset\n", + "df_missing = bpd.read_gbq(\"bigquery-public-data.fda_drug.drug_label\",\n", + " columns=[\"openfda_generic_name\", \"openfda_brand_name\", \"indications_and_usage\"])\n", + "\n", + "# Exclude any rows with missing data\n", + "df_missing = df_missing.dropna()\n", + "\n", + "# Include rows in which openfda_brand_name equals openfda_generic_name\n", + "df_missing = df_missing[df_missing[\"openfda_generic_name\"] == df_missing[\"openfda_brand_name\"]]\n", + "\n", + "# Limit the number of rows for demonstration purposes\n", + "df_missing = df_missing.head(100)\n", + "\n", + "# Print values\n", + "df_missing.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Fm6L8S7eVnCI" + }, + "source": [ + "We will create a column `prompt` with a customized prompt for each row." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "id": "19TvGN1PVmVX" + }, + "outputs": [], + "source": [ + "df_missing[\"prompt\"] = (\n", + " \"Provide a unique and modern brand name related to this pharmaceutical drug.\"\n", + " + \"Don't use English words directly; use variants or invented words. The generic name is: \"\n", + " + df_missing[\"openfda_generic_name\"]\n", + " + \". The indications and usage are: \"\n", + " + df_missing[\"indications_and_usage\"]\n", + " + \".\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "njxwBvCKgMPE" + }, + "source": [ + "We'll create a new helper method, `batch_predict()` and query the LLM. The job may take a couple minutes to execute." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "id": "tiSHa5B4aFhw" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job d216bea6-9b9c-4918-9194-40de2745beca is DONE. 84.4 MB processed. Open Job" + ], + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "njxwBvCKgMPE" - }, - "source": [ - "We'll create a new helper method, `batch_predict()` and query the LLM. The job may take a couple minutes to execute." + "data": { + "text/html": [ + "Query job 37d88636-b1fb-44da-9504-44144af9624d is DONE. 800 Bytes processed. Open Job" + ], + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 46, - "metadata": { - "id": "tiSHa5B4aFhw" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job d216bea6-9b9c-4918-9194-40de2745beca is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 37d88636-b1fb-44da-9504-44144af9624d is DONE. 800 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 0b35db83-5bac-47b4-8a2c-b46a816c0e3e is DONE. 200 Bytes processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } + "data": { + "text/html": [ + "Query job 0b35db83-5bac-47b4-8a2c-b46a816c0e3e is DONE. 200 Bytes processed. Open Job" ], - "source": [ - "def batch_predict(\n", - " input: bpd.DataFrame, temperature: float = TEMPERATURE\n", - ") -> bpd.DataFrame:\n", - " return model.predict(input, temperature=temperature).ml_generate_text_llm_result\n", - "\n", - "\n", - "response = batch_predict(df_missing[\"prompt\"])" + "text/plain": [ + "" ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "K5a2nHdLgZEj" - }, - "source": [ - "Let's check the results for one of our responses!" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def batch_predict(\n", + " input: bpd.DataFrame, temperature: float = TEMPERATURE\n", + ") -> bpd.DataFrame:\n", + " return model.predict(input, temperature=temperature).ml_generate_text_llm_result\n", + "\n", + "\n", + "response = batch_predict(df_missing[\"prompt\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K5a2nHdLgZEj" + }, + "source": [ + "Let's check the results for one of our responses!" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "id": "TnizdeqBdbZj" + }, + "outputs": [ + { + "data": { + "text/html": [ + "Query job 4397b5f3-5058-409c-a361-c9fa715e46ee is DONE. 84.4 MB processed. Open Job" + ], + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "id": "TnizdeqBdbZj" - }, - "outputs": [ - { - "data": { - "text/html": [ - "Query job 4397b5f3-5058-409c-a361-c9fa715e46ee is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 147ea301-e249-49fb-8280-d61948d5df7f is DONE. 84.4 MB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "Query job 067a2a73-0f36-42a6-973e-074ab8be631a is DONE. 56.7 kB processed. Open Job" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generic name: MEPHITIS MEPHITICA\n", - "Brand name: INDICATIONS Condition listed above or as directed by the physician\n", - "Response: **Ephemeral** (Latin root: \"ephemerus,\" meaning \"lasting for a day\")\n", - "\n", - "**Aetheria** (Greek root: \"aither,\" meaning \"upper air, sky\")\n", - "\n", - "**Zenithar** (Combination of \"zenith\" and \"pharma\")\n", - "\n", - "**Celestian** (Latin root: \"celestial,\" meaning \"heavenly\")\n", - "\n", - "**Astralux** (Combination of \"astral\" and \"lux,\" meaning \"light\")\n" - ] - } + "data": { + "text/html": [ + "Query job 147ea301-e249-49fb-8280-d61948d5df7f is DONE. 84.4 MB processed. Open Job" ], - "source": [ - "# Pick a sample\n", - "k = 0\n", - "\n", - "# Gather the prompt and response details\n", - "prompt_generic = df_missing[\"openfda_generic_name\"].iloc[k]\n", - "prompt_usage = df_missing[\"indications_and_usage\"].iloc[k]\n", - "response_str = response.iloc[k]\n", - "\n", - "# Print details\n", - "print(f\"Generic name: {prompt_generic}\")\n", - "print(f\"Brand name: {prompt_usage}\")\n", - "print(f\"Response: {response_str}\")" + "text/plain": [ + "" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "metadata": { - "id": "W4MviwyMI-Qh" - }, - "source": [ - "Congratulations! You have learned how to use generative AI to jumpstart the creative process.\n", - "\n", - "You've also seen how BigFrames can manage each step of the process, including gathering data, data manipulation, and querying the LLM." - ] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "venv", - "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.1" + "data": { + "text/html": [ + "Query job 067a2a73-0f36-42a6-973e-074ab8be631a is DONE. 56.7 kB processed. Open Job" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generic name: MEPHITIS MEPHITICA\n", + "Brand name: INDICATIONS Condition listed above or as directed by the physician\n", + "Response: **Ephemeral** (Latin root: \"ephemerus,\" meaning \"lasting for a day\")\n", + "\n", + "**Aetheria** (Greek root: \"aither,\" meaning \"upper air, sky\")\n", + "\n", + "**Zenithar** (Combination of \"zenith\" and \"pharma\")\n", + "\n", + "**Celestian** (Latin root: \"celestial,\" meaning \"heavenly\")\n", + "\n", + "**Astralux** (Combination of \"astral\" and \"lux,\" meaning \"light\")\n" + ] } + ], + "source": [ + "# Pick a sample\n", + "k = 0\n", + "\n", + "# Gather the prompt and response details\n", + "prompt_generic = df_missing[\"openfda_generic_name\"].iloc[k]\n", + "prompt_usage = df_missing[\"indications_and_usage\"].iloc[k]\n", + "response_str = response.iloc[k]\n", + "\n", + "# Print details\n", + "print(f\"Generic name: {prompt_generic}\")\n", + "print(f\"Brand name: {prompt_usage}\")\n", + "print(f\"Response: {response_str}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W4MviwyMI-Qh" + }, + "source": [ + "Congratulations! You have learned how to use generative AI to jumpstart the creative process.\n", + "\n", + "You've also seen how BigFrames can manage each step of the process, including gathering data, data manipulation, and querying the LLM." + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.1" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/packages/bigframes/notebooks/generative_ai/large_language_models.ipynb b/packages/bigframes/notebooks/generative_ai/large_language_models.ipynb index 4ff9a9d3d237..ecaa1ab50425 100644 --- a/packages/bigframes/notebooks/generative_ai/large_language_models.ipynb +++ b/packages/bigframes/notebooks/generative_ai/large_language_models.ipynb @@ -6,8 +6,9 @@ "metadata": {}, "outputs": [], "source": [ - "import bigframes.pandas\n", "import pandas as pd\n", + "\n", + "import bigframes.pandas\n", "from bigframes.ml.llm import GeminiTextGenerator" ] }, @@ -60,7 +61,7 @@ } ], "source": [ - "model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")" + "model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")" ] }, { diff --git a/packages/bigframes/notebooks/getting_started/bq_dataframes_template.ipynb b/packages/bigframes/notebooks/getting_started/bq_dataframes_template.ipynb index 664a3a68d33a..4e7a2999b20c 100644 --- a/packages/bigframes/notebooks/getting_started/bq_dataframes_template.ipynb +++ b/packages/bigframes/notebooks/getting_started/bq_dataframes_template.ipynb @@ -1,1407 +1,1406 @@ { - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "ur8xi4C7S06n" - }, - "outputs": [], - "source": [ - "# Copyright 2023 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." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JAPoU8Sm5E6e" - }, - "source": [ - "# Get started with BigQuery DataFrames\n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - "\n", - "
\n", - " \n", - " \"Colab Run in Colab\n", - " \n", - " \n", - " \n", - " \"GitHub\n", - " View on GitHub\n", - " \n", - " \n", - " \n", - " \"Vertex\n", - " Open in Vertex AI Workbench\n", - " \n", - " \n", - " \n", - " \"BQ\n", - " Open in BQ Studio\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "24743cf4a1e1" - }, - "source": [ - "**_NOTE_**: This notebook has been tested in the following environment:\n", - "\n", - "* Python version = 3.10" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tvgnzT1CKxrO" - }, - "source": [ - "## Overview\n", - "\n", - "BigQuery DataFrames (also known as BigFrames) provides a Pythonic DataFrame and machine learning (ML) API powered by the BigQuery engine.\n", - "\n", - "* `bigframes.pandas` provides a pandas API for analytics. Many workloads can be\n", - " migrated from pandas to bigframes by just changing a few imports.\n", - "* `bigframes.ml` provides a scikit-learn-like API for ML.\n", - "* `bigframes.ml.llm` provides API for large language models including Gemini.\n", - "\n", - "You can learn more about [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/bigquery-dataframes-introduction) and its [API reference](https://cloud.google.com/python/docs/reference/bigframes/latest).\n", - "\n", - "For any issues or feedback please reach out to bigframes-feedback@google.com." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BF1j6f9HApxa" - }, - "source": [ - "## Before you begin\n", - "\n", - "Complete the tasks in this section to set up your environment." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Yq7zKYWelRQP" - }, - "source": [ - "### Install the python package\n", - "\n", - "You need the [bigframes](https://pypi.org/project/bigframes/) python package to be installed. If you don't have that, uncomment and run the following cell and *restart the kernel*." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "#%pip install --upgrade bigframes" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "WReHDGG5g0XY" - }, - "source": [ - "### Set your project id and location\n", - "\n", - "Following are some quick references:\n", - "\n", - "* Google Cloud Project: https://cloud.google.com/resource-manager/docs/creating-managing-projects.\n", - "* BigQuery Location: https://cloud.google.com/bigquery/docs/locations." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "oM1iC_MfAts1" - }, - "outputs": [], - "source": [ - "PROJECT_ID = \"bigframes-dev\" # @param {type: \"string\"}\n", - "LOCATION = \"US\" # @param {type: \"string\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "960505627ddf" - }, - "source": [ - "### Import library" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "id": "PyQmSRbKA8r-" - }, - "outputs": [], - "source": [ - "import bigframes.pandas as bpd" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "init_aip:mbsdk,all" - }, - "source": [ - "\n", - "### Set BigQuery DataFrames options" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "NPPMuw2PXGeo" - }, - "outputs": [], - "source": [ - "# Note: The project option is not required in all environments.\n", - "# For example, In BigQuery Studio, the project ID is automatically detected,\n", - "# But in Google Colab it must be set by the user.\n", - "bpd.options.bigquery.project = PROJECT_ID\n", - "\n", - "# Note: The location option is not required.\n", - "# It defaults to the location of the first table or query\n", - "# passed to read_gbq(). For APIs where a location can't be\n", - "# auto-detected, the location defaults to the \"US\" location.\n", - "bpd.options.bigquery.location = LOCATION\n", - "\n", - "# Note: BigQuery DataFrames objects are by default fully ordered like Pandas.\n", - "# If ordering is not important for you, you can uncomment the following\n", - "# expression to run BigQuery DataFrames in partial ordering mode.\n", - "bpd.options.bigquery.ordering_mode = \"partial\"\n", - "\n", - "# Note: By default BigQuery DataFrames emits out BigQuery job metadata via a\n", - "# progress bar. But in this notebook let's disable the progress bar to keep the\n", - "# experience less verbose. If you would like the default behavior, please\n", - "# comment out the following expression. \n", - "bpd.options.display.progress_bar = None" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "pDfrKwMKE_dK" - }, - "source": [ - "If you want to reset the project and/or location of the created DataFrame or Series objects, reset the session by executing `bpd.close_session()`. After that, you can redo the above steps." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "9EMAqR37AfLS" - }, - "source": [ - "## Create a BigQuery DataFrames DataFrame\n", - "\n", - "You can create a BigQuery DataFrames DataFrame by reading data from any of the\n", - "following locations:\n", - "\n", - "* A local data file\n", - "* Data stored in a BigQuery table\n", - "* A data file stored in Cloud Storage\n", - "* An in-memory pandas DataFrame\n", - "\n", - "Note that the DataFrame does not copy the data to the local memory, instead\n", - "keeps the underlying data in a BigQuery table during read and analysis. That's\n", - "how it can handle really large size of data (at BigQuery Scale) independent of\n", - "the local memory.\n", - "\n", - "For simplicity, speed and cost efficiency, this tutorial uses the\n", - "[`penguins`](https://pantheon.corp.google.com/bigquery?ws=!1m5!1m4!4m3!1sbigquery-public-data!2sml_datasets!3spenguins)\n", - "table from BigQuery public data, which contains 27 KB data about a set of\n", - "penguins - species, island of residence, culmen length and depth, flipper length\n", - "and sex. There is a version of this data in the Cloud Storage\n", - "[cloud samples data](https://pantheon.corp.google.com/storage/browser/_details/cloud-samples-data/vertex-ai/bigframe/penguins.csv)\n", - "as well." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "id": "Vyex9BQI-BNa" - }, - "outputs": [], - "source": [ - "# This is how you read a BigQuery table\n", - "df = bpd.read_gbq(\"bigquery-public-data.ml_datasets.penguins\")\n", - "\n", - "# This is how you would read a csv from the Cloud Storage\n", - "#df = bpd.read_csv(\"gs://cloud-samples-data/vertex-ai/bigframe/penguins.csv\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use `peek` to preview a few rows (selected arbitrarily) from the dataframes:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
speciesislandculmen_length_mmculmen_depth_mmflipper_length_mmbody_mass_gsex
0Adelie Penguin (Pygoscelis adeliae)Dream36.618.4184.03475.0FEMALE
1Adelie Penguin (Pygoscelis adeliae)Dream39.819.1184.04650.0MALE
2Adelie Penguin (Pygoscelis adeliae)Dream40.918.9184.03900.0MALE
3Chinstrap penguin (Pygoscelis antarctica)Dream46.517.9192.03500.0FEMALE
4Adelie Penguin (Pygoscelis adeliae)Dream37.316.8192.03000.0FEMALE
\n", - "
" - ], - "text/plain": [ - " species island culmen_length_mm \\\n", - "0 Adelie Penguin (Pygoscelis adeliae) Dream 36.6 \n", - "1 Adelie Penguin (Pygoscelis adeliae) Dream 39.8 \n", - "2 Adelie Penguin (Pygoscelis adeliae) Dream 40.9 \n", - "3 Chinstrap penguin (Pygoscelis antarctica) Dream 46.5 \n", - "4 Adelie Penguin (Pygoscelis adeliae) Dream 37.3 \n", - "\n", - " culmen_depth_mm flipper_length_mm body_mass_g sex \n", - "0 18.4 184.0 3475.0 FEMALE \n", - "1 19.1 184.0 4650.0 MALE \n", - "2 18.9 184.0 3900.0 MALE \n", - "3 17.9 192.0 3500.0 FEMALE \n", - "4 16.8 192.0 3000.0 FEMALE " - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.peek()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gE6CEALjDZZV" - }, - "source": [ - "We just created a DataFrame, `df`, refering to the entirety of the source table data, without downloading it to the local machine." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rwPLjqW2Ajzh" - }, - "source": [ - "## Inspect and manipulate data in BigQuery DataFrames" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bExmYlL_ELtV" - }, - "source": [ - "### Using pandas API\n", - "\n", - "You can use pandas API on the BigQuery DataFrames DataFrame as you normally would in Pandas, but computation happens in the BigQuery query engine instead of your local environment." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "EJIZJaNXFQzh" - }, - "source": [ - "Let's compute the mean of the `body_mass_g` series:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "YKwCW7Nsavap" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "average_body_mass: 4201.754385964914\n" - ] - } - ], - "source": [ - "average_body_mass = df[\"body_mass_g\"].mean()\n", - "print(f\"average_body_mass: {average_body_mass}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "DSs1cnca-MOU" - }, - "source": [ - "Calculate the mean `body_mass_g` by `species` using the `groupby` operation:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "4PyKMR61-Mjy" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
body_mass_g
species
Adelie Penguin (Pygoscelis adeliae)3700.662252
Chinstrap penguin (Pygoscelis antarctica)3733.088235
Gentoo penguin (Pygoscelis papua)5076.01626
\n", - "

3 rows × 1 columns

\n", - "
[3 rows x 1 columns in total]" - ], - "text/plain": [ - " body_mass_g\n", - "species \n", - "Adelie Penguin (Pygoscelis adeliae) 3700.662252\n", - "Chinstrap penguin (Pygoscelis antarctica) 3733.088235\n", - "Gentoo penguin (Pygoscelis papua) 5076.01626\n", - "\n", - "[3 rows x 1 columns]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df[[\"species\", \"body_mass_g\"]].groupby(by=df[\"species\"]).mean(numeric_only=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6sf9kZ2C9Ixe" - }, - "source": [ - "You can confirm that the calculations were run in BigQuery by clicking \"Open job\" from the previous cells' output. This takes you to the BigQuery console to view the SQL statement and job details." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using SQL functions\n", - "\n", - "The [bigframes.bigquery module](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.bigquery) provides many [BigQuery SQL functions](https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-all) which may not have a pandas-equivalent." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "import bigframes.bigquery" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `bigframes.bigquery.struct()` function creates a new STRUCT Series with subfields for each column in a DataFrames." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 {'culmen_length_mm': 36.6, 'culmen_depth_mm': ...\n", - "1 {'culmen_length_mm': 39.8, 'culmen_depth_mm': ...\n", - "2 {'culmen_length_mm': 40.9, 'culmen_depth_mm': ...\n", - "3 {'culmen_length_mm': 46.5, 'culmen_depth_mm': ...\n", - "4 {'culmen_length_mm': 37.3, 'culmen_depth_mm': ...\n", - "dtype: struct[pyarrow]" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "lengths = bigframes.bigquery.struct(\n", - " df[[\"culmen_length_mm\", \"culmen_depth_mm\", \"flipper_length_mm\"]]\n", - ")\n", - "lengths.peek()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use the `bigframes.bigquery.sql_scalar()` function to access arbitrary SQL syntax representing a single column expression." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0 18.4\n", - "1 19.1\n", - "2 18.9\n", - "3 17.9\n", - "4 16.8\n", - "dtype: Float64" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "shortest = bigframes.bigquery.sql_scalar(\n", - " \"LEAST({0}, {1}, {2})\",\n", - " columns=[df['culmen_depth_mm'], df['culmen_length_mm'], df['flipper_length_mm']],\n", - ")\n", - "shortest.peek()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize data" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### First party visualizations\n", - "\n", - "BigQuery DataFrames provides a number of visualizations via the `plot` method and [accessor](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.operations.plotting.PlotAccessor) on the DataFrame and Series objects." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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yMhI7r33Xi5cuEDjxo3566+/2LlzJ48//jj9+/enc+fOANx33324uLiwdu1avLy8+Pzzz+nXrx9nz56lVq1a6npXrlzJ77//Tnp6OiNHjmT27NmlTsYVMWnSJE6ePMkPP/xA7dq1WbFiBYMGDeLYsWM0atQIgLy8PN5//32WLl2KVqvlkUce4fnnn+e7774D4J133uG7775j8eLFREREMHfuXFauXMltt90GwNy5czl79iwtWrTgjTfeAMDf318NVF5++WU++OAD/P39efLJJ3n88cfZsWPHDTnG1sD17QULeOell5jz2We8NH06Dz30EA0aNGDatGmEhoby+OOPM2nSJNauXVvpY3o9JEgRQghRruzsbObOncv8+fN57LHHAGjYsCE9evSwyyxUhtlsZtGiRXh4eNCsWTNuu+02zpw5w5o1a9BqtTRp0oR33nmHzZs307lzZ7Zv387evXtJTk7GyckJgPfff5+VK1eyfPlyJkyYoK53yZIleHh4APDoo4+yadOmSgcpsbGxLF68mNjYWGrXrg3A888/z7p161i8eDFvvfUWYJmg77PPPqNhw4aAJbCxBhsA8+bNY9q0aQwfPhyA+fPns2bNGvV5Ly8vHB0dcXV1JSgoqFQ7Zs2aRe/evQF46aWXGDJkCAUFBRWaJO1qx1gDNAwI4J233mLjihW09vICs9lyzIYN44ExY9B7ezN16lS6du3Kq6++ysCBAwF47rnnGDNmTKWO599xywQpCxYsYMGCBZhMpupuihBC/G0uDjpOvjGw2rZdUadOncJgMNCvX78q235YWJgaSAAEBgai0+nsJgILDAwkOTkZgCNHjpCTk4Ovr6/devLz8+26HkquNzg4WF1HZRw7dgyTyUTjxo3tHjcYDHZtcHV1VQOUktvLzMwkKSmJTp06qc/rdDrat2+PuTgguJZWrVrZrRsgOTmZ0NDQa742LCwMd3d3FKMRxWjE38sLbcOGmJKTMefkYC4owN/Li6RLl1CMRvV1rTt0QOflBVjeA4CWLVuqzwcGBlJQUEBWVhaenp4V2o+/45YJUiZOnMjEiRPJysrCq/gACiHErUqj0VSqy6W6XG0Kf2tQYVvjUpE7PDs4ONj9rtFoynzMejLPyckhODi4zMyN7fDmq62jMnJyctDpdBw4cACdzj6gc3d3v+r2qvLmkbbrt85UbLs/itls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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "(\n", - " df\n", - " .sort_values(\"body_mass_g\")\n", - " .reset_index(drop=True)\n", - " .plot(title=\"Numeric features\", logy=True)\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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GGzevLncHZKePHky0tLSFF9JSUnv3bZojMO7d0QmqmiKPt8c00NEFdEnHYGJjo7GkydPlO4fk5+fj9OnT2Pp0qU4cuQIcnJykJqaqnQU5vHjx7CwsAAAWFhYIDIyUmm/RVcpvbvN369cevz4MeRy+XsnVdPW1oa2tvZHvQ91dXUYGRkp7sejp6fHycuowigsLERmZiaePHkCIyMjqKurCx2JiKjEfVKBadu2rcoU7UOGDEHNmjURGBgIa2traGpqIiwsTHGH4bi4OCQmJipuGujp6Ym5c+fiyZMnivEUoaGhkMvliinvPT09cfDgQaXXCQ0NVbnx4H9RVJbevakgUUViZGSk+JwTEVU0n1RgDAwMULt2baVllSpVgomJiWK5n58fxo8fj8qVK0Mul2PUqFHw9PREkyZNAADt27eHq6srBg4ciKCgIKSkpGDKlCkYOXKk4gjK8OHDsXTpUkyaNAm+vr44fvw4tm/fjgMHDpTEewbw9oaClpaWMDMz443/qMLR1NTkkRciqtBK/FYCixYtgpqaGnr16oXs7Gx4e3tj+fLlivXq6urYv38/RowYAU9PT1SqVAmDBg3CrFmzFNvY29vjwIEDGDduHBYvXoxq1aphzZo18Pb2Lum4UFdX5w96IiIikZHkrQSIiIjEgPPAlNKtBIiIiIiEwAJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREolPiN3MkIhKrkr7vTHm/5wyRmPEIDBEREYkOj8CQIKR4h1UiIio5PAJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RE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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "means = df.groupby(\"species\").mean(numeric_only=True)\n", - "means.plot.bar()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Integration with open source visualizations\n", - "\n", - "BigQuery Dataframes is also compatible with several open source visualization packages, such as `matplotlib`." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyUAAAGFCAYAAADjF1xYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAaqdJREFUeJzt3XdYU2f/BvA7CXvvoaKIIOIAcY/WhQquVl9brVqr1bpn3f5srVZttY5W62hrrVrrfm2rtdZdfRUnKrgQEcEJIlN2IMnvD2pqZAgynoTcn+vi0uScPOc+IUC+ecaRqFQqFYiIiIiIiASRig5ARERERET6jUUJEREREREJxaKEiIiIiIiEYlFCRERERERCsSghIiIiIiKhWJQQEREREZFQLEqIiIiIiEgoFiVERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERCzZs3D40bNy7x/jExMZBIJAgNDQUAnDhxAhKJBCkpKRWST9t06NABkydPLnM7iYmJcHJyQkxMTJnb0iUSiQS///47gIKvpcrwOsd8+Xvu7u6Ob775plxztWrVCnv27CnXNolKg0UJERGVq7Nnz0Imk6FHjx6Vcrw2bdogNjYW1tbWr93Gpk2bIJFIIJFIIJVKUaNGDXz44YeIj48vx6Tl49dff8WCBQvK3M6iRYvw9ttvw93dHcC/b5aff9nb26Nr1664cuVKmY+lrdzc3BAbG4uGDRuKjlIqFy9exMiRI8u1zU8++QSzZs2CUqks13aJSopFCRERlasNGzZgwoQJ+N///ofHjx9X+PGMjIzg4uICiURSpnasrKwQGxuLhw8fYv369fjrr78wePDgckpZfuzs7GBpaVmmNjIzM7FhwwYMHz68wLajR48iNjYWhw4dQnp6Orp161Zle6FkMhlcXFxgYGAgOkqpODo6wszMrFzb7NatG9LS0vDXX3+Va7tEJcWihIiIyk16ejp27tyJMWPGoEePHti0aVOBfRYvXgxnZ2dYWlpi+PDhyM7OLrDPjz/+CB8fH5iYmKBevXpYu3ZtkccsbPjW6dOn8eabb8LU1BRubm6YOHEiMjIyis0ukUjg4uKCatWqoVu3bpg4cSKOHj2KrKysV2Z63svw66+/omPHjjAzM4Ofnx/Onj2rcYz169fDzc0NZmZm6NOnD1asWAEbGxv19qFDh6J3794aj5k8eTI6dOigvl3YUJ4vvvgCw4YNg6WlJWrWrIkffvih2HM9cOAAjI2N0apVqwLb7O3t4eLigmbNmmHZsmV48uQJzp8/j88//7zQHoXGjRvj008/BQDk5eVh4sSJsLGxgb29PWbOnIkhQ4ZonFNOTg4mTpwIJycnmJiY4I033sDFixfV25OTkzFo0CA4OjrC1NQUXl5e2Lhxo3r7w4cPMWDAANjZ2cHc3BzNmjXD+fPn1dv37t2LJk2awMTEBB4eHpg/fz7y8vIKfR5eHkr1qmO/7ODBg3jjjTfU59uzZ09ERUVp7HPhwgX4+/vDxMQEzZo1K7Tn6fr16+jWrRssLCzg7OyMwYMHIyEhocjjvjx8a8WKFWjUqBHMzc3h5uaGsWPHIj09XeMxr/qZkMlk6N69O3bs2FHkcYkqEosSIiIqN7t27UK9evXg7e2N999/Hz/99BNUKpXG9nnz5uGLL75ASEgIXF1dCxQcW7duxdy5c7Fo0SKEh4fjiy++wKefforNmzeXKENUVBSCgoLQt29fXL16FTt37sTp06cxfvz4Up2LqakplEol8vLySpxpzpw5mDZtGkJDQ1G3bl0MGDBA/YY4ODgYo0ePxqRJkxAaGoouXbpg0aJFpcpUlOXLl6vf8I4dOxZjxoxBREREkfufOnUKTZs2fWW7pqamAAC5XI5hw4YhPDxco4C4cuUKrl69ig8//BAAsGTJEmzduhUbN25EcHAwnj17pp6/8dyMGTOwZ88ebN68GZcvX4anpycCAwORlJQEAPj0009x8+ZN/PXXXwgPD8e6devg4OAAIL/obd++PR49eoR9+/YhLCwMM2bMUA85OnXqFD744ANMmjQJN2/exPfff49NmzaV+Hku7tiFycjIwJQpUxASEoJjx45BKpWiT58+6jzp6eno2bMn6tevj0uXLmHevHmYNm2aRhspKSno1KkT/P39ERISgoMHD+LJkyfo169fiTIDgFQqxapVq3Djxg1s3rwZx48fx4wZM9TbS/oz0aJFC5w6darExyUqVyoiIqJy0qZNG9U333yjUqlUqtzcXJWDg4Pq77//Vm9v3bq1auzYsRqPadmypcrPz099u06dOqpt27Zp7LNgwQJV69atVSqVShUdHa0CoLpy5YpKpVKp/v77bxUAVXJyskqlUqmGDx+uGjlypMbjT506pZJKpaqsrKxCc2/cuFFlbW2tvn379m1V3bp1Vc2aNStVph9//FG9/caNGyoAqvDwcJVKpVL1799f1aNHD402Bg0apHHcIUOGqN5++22NfSZNmqRq3769+nb79u1VkyZNUt+uVauW6v3331ffViqVKicnJ9W6desKPVeVSqV6++23VcOGDdO47+XnNTk5WdWnTx+VhYWFKi4uTqVSqVTdunVTjRkzRv2YCRMmqDp06KC+7ezsrFq6dKn6dl5enqpmzZrqc0pPT1cZGhqqtm7dqt5HLperqlWrpvrqq69UKpVK1atXL9WHH35YaO7vv/9eZWlpqUpMTCx0e0BAgOqLL77QuG/Lli0qV1dX9W0Aqt9++63Qcy7u2CXx9OlTFQDVtWvX1Hnt7e01Xnfr1q3TOOaCBQtUXbt21WjnwYMHKgCqiIgIlUpV+Pf866+/LjLH7t27Vfb29urbJf2Z2Lt3r0oqlaoUCkWpzpuoPLCnhIiIykVERAQuXLiAAQMGAAAMDAzQv39/bNiwQb1PeHg4WrZsqfG41q1bq/+fkZGBqKgoDB8+HBYWFuqvhQsXFhgWU5SwsDBs2rRJ4/GBgYFQKpWIjo4u8nGpqamwsLCAmZkZvL294ezsjK1bt5Yqk6+vr/r/rq6uAKCeLB8REYEWLVpo7P/y7df14nGfD0MrbpJ+VlYWTExMCt3Wpk0bWFhYwNbWFmFhYdi5cyecnZ0BACNGjMD27duRnZ0NuVyObdu2YdiwYQDyn78nT55onJNMJtPokYmKikJubi7atm2rvs/Q0BAtWrRAeHg4AGDMmDHYsWMHGjdujBkzZuDMmTPqfUNDQ+Hv7w87O7tCs4eFheHzzz/X+D6NGDECsbGxyMzMLPL5eK64YxcmMjISAwYMgIeHB6ysrNSLBty/fx9A/uvd19dX47l+8fX+PPPff/+tkblevXrq56skjh49ioCAAFSvXh2WlpYYPHgwEhMT1edc0p+J572DOTk5JTouUXnSrZldRESktTZs2IC8vDxUq1ZNfZ9KpYKxsTFWr15dotWxno+DX79+fYHiRSaTlShHeno6Ro0ahYkTJxbYVrNmzSIfZ2lpicuXL0MqlcLV1VU9dOnJkyclzmRoaKj+//OJ96VZzUgqlWoMdwOA3NzcVz7uxeM+P3Zxx3VwcEBycnKh23bu3In69evD3t5eY74LAPTq1QvGxsb47bffYGRkhNzcXLzzzjuvzFca3bp1w71793DgwAEcOXIEAQEBGDduHJYtW6b+nhQlPT0d8+fPx3/+858C24oqwkp67ML06tULtWrVwvr161GtWjUolUo0bNgQcrm8ZCf7T+ZevXphyZIlBbY9L2yLExMTg549e2LMmDFYtGgR7OzscPr0aQwfPhxyuRxmZmYl/plISkqCubn5K59noorAooSIiMosLy8PP//8M5YvX46uXbtqbOvduze2b9+O0aNHw8fHB+fPn8cHH3yg3n7u3Dn1/52dnVGtWjXcvXsXgwYNeq0sTZo0wc2bN+Hp6Vmqx0ml0kIfUx6ZAMDb21tjPgaAArcdHR1x/fp1jftCQ0MLFB1l5e/vj19++aXQbW5ubqhTp06h2wwMDDBkyBBs3LgRRkZGeO+999RvYK2treHs7IyLFy+iXbt2AACFQoHLly+rr0NTp04dGBkZITg4GLVq1QKQX3RdvHhRY/K+o6MjhgwZgiFDhuDNN9/E9OnTsWzZMvj6+uLHH39EUlJSob0lTZo0QURERKm/9y8q6tgvS0xMREREBNavX48333wTQP5k8hf5+Phgy5YtyM7OVhdFL77en2fes2cP3N3dX2sVsEuXLkGpVGL58uWQSvMHwOzatavAMUryM3H9+nX4+/uXOgNReWBRQkREZbZ//34kJydj+PDhBXpE+vbtiw0bNqgneQ8dOhTNmjVD27ZtsXXrVty4cQMeHh7q/efPn4+JEyfC2toaQUFByMnJQUhICJKTkzFlypRXZpk5cyZatWqF8ePH46OPPoK5uTlu3ryJI0eOYPXq1a91fmXNBAATJkxAu3btsGLFCvTq1QvHjx/HX3/9pbGUcadOnbB06VL8/PPPaN26NX755ZcKeaMYGBiI2bNnIzk5Gba2tqV67EcffQQfHx8A+ZP3XzRhwgR8+eWX8PT0RL169fDtt98iOTlZfY7m5uYYM2YMpk+fDjs7O9SsWRNfffUVMjMz1csTz507F02bNkWDBg2Qk5OD/fv3q483YMAAfPHFF+jduze+/PJLuLq64sqVK6hWrRpat26NuXPnomfPnqhZsybeeecdSKVShIWF4fr161i4cOErz624Y7/M1tYW9vb2+OGHH+Dq6or79+9j1qxZGvsMHDgQc+bMwYgRIzB79mzExMQUKHDGjRuH9evXY8CAAZgxYwbs7Oxw584d7NixAz/++OMrewg9PT2Rm5uLb7/9Fr169UJwcDC+++47jX1K+jNx6tSpAh8qEFUWzikhIqIy27BhAzp37lzoEK2+ffsiJCQEV69eRf/+/fHpp59ixowZaNq0Ke7du4cxY8Zo7P/RRx/hxx9/xMaNG9GoUSO0b98emzZtQu3atUuUxdfXFydPnsTt27fx5ptvwt/fH3PnztUYVlZaZc0EAG3btsV3332HFStWwM/PDwcPHsTHH3+sMawoMDBQ/fw0b94caWlpGr1K5aVRo0Zo0qRJgU/US8LLywtt2rRBvXr1CgxnmzlzJgYMGIAPPvgArVu3Vs9dePEcFy9ejL59+2Lw4MFo0qQJ7ty5g0OHDqmLIyMjI8yePRu+vr5o164dZDKZeplaIyMjHD58GE5OTujevTsaNWqExYsXq9+4BwYGYv/+/Th8+DCaN2+OVq1a4euvv1b3yrxKccd+mVQqxY4dO3Dp0iU0bNgQH3/8MZYuXaqxj4WFBf744w9cu3YN/v7+mDNnToFhWtWqVUNwcDAUCgW6du2KRo0aYfLkybCxsVH3fBTHz88PK1aswJIlS9CwYUNs3boVX375pcY+JfmZePToEc6cOaNeSY2osklULw9eJSIiokoxYsQI3Lp1S8gyrH/++SemT5+O69evl+jN73MqlQpeXl4YO3bsK3uJlEolfHx80K9fv3K5Cj1VnJkzZyI5OfmV17ghqigcvkVERFRJli1bhi5dusDc3Bx//fUXNm/eXOyFIStSjx49EBkZiUePHsHNza1Ej3n69Cl27NiBuLi4Qj9Rv3fvHg4fPoz27dsjJycHq1evRnR0NAYOHFje8amcOTk5lXgoIlFFYE8JERFRJenXrx9OnDiBtLQ0eHh4YMKECRg9erToWCUmkUjg4OCAlStXFlpoPHjwAO+99x6uX78OlUqFhg0bYvHixeqJ70RERWFRQkREREREQnGiOxERERERCcWihIiIiIiIhGJRQkREREREQnH1LSIiLZadq0BShhxJGXIkZ/7zb4YcSZm5SM2UI0+pglQigVQCSKWSf/8vkUDywv+lkvxJykYGUtibG8HR0hhOliZwtDSGg4URDGT8jIqIiMRhUUJEJEh2rgJ34tMRGZ+GyCfpeJSSpS4+kjNykZQhR1auosJzSCWArVl+ofJiseL0z+1qNqbwcraAlYlhhWchIiL9xNW3iIgqWJY8v/i4/SQNkfHpiPzn34fJmVDq0G9gV2sTeDlboq6TBeq6WMLb2RLeLpYwMZSJjkZERDqORQkRUTlKy87FhegkXIxJxu0nabj9JA2PUrJQVX/TGkgl8HSyQKPq1mhUwxoNq1ujvqsVCxUiIioVFiVERGWQJVcg5F4SzkQl4kxUIq4/SoVCl7o/KoCBVIKG1a3Rrq4j2td1QGM3W8ikEtGxiIhIi7EoISIqBXmeEpfvJ+NsVCLORiUi9EEK5Aql6FhazcrEAG09HdCuriPa1XVEdRtT0ZGIiEjLsCghInqFiLg0HA1/gjNRCbh0LxnZuSxCysLD0RztvBzRvq4jWnnYw9SIQ72IiPQdixIiokI8SMrEvrDH2Bf6GBFP0kTHqbKMDKRo7m6Ljt5OeKtxNThZmoiOREREArAoISL6R0J6Dv68Gou9oY9w+X6K6Dh6RyaVoJ2XA95p6obO9Z1gbMAeFCIifcGihIj0WnpOHg5ej8Pe0Ec4E5Wo95PUtYW1qSF6+bmib5Ma8K9pKzoOERFVMBYlRKR35HlKHL/1BHtDH+P4rXjk5HGOiDar42iOvk1r4D/+NeBizeFdRERVEYsSItIbKZly/HLuHjafvYenaTmi41ApSSVAW08HvNO0BgIbuPBaKEREVQiLEiKq8u4nZmLD6bvYfekhMuUK0XGoHNiaGWJIG3d82KY2rM0MRcchIqIyYlFCRFXWpXvJ+PHUXRy6EQdOFamazI1kGNSqFj56ozacrDi0i4hIV7EoIaIqRalU4fDNOPzwv7tcQUuPGBlI8U7TGhjdrg5q2puJjkNERKXEooSIqoQsuQK7Lz3AT6ejEZOYKToOCSKTStDT1xVjO3jC28VSdBwiIiohFiVEpNNy8hTYfCYG605EITkzV3Qc0hISCRBQzwljO3qiCZcUJiLSeixKiEgnKZUq/HblEVYcuY1HKVmi45AWa+1hjxlB3rzeCRGRFmNRQkQ65++IeCz56xZuxaWJjkI6QiIB/uNfAzO7ecPJkhPiiYi0DYsSItIZt+KeYcH+mwi+kyg6CukoS2MDTAjwxIdta8NQJhUdh4iI/sGihIi0XkqmHMsP38a2C/eh4Nq+VA48HM0xt2d9dPB2Eh2FiIjAooSItJhCqcIv5+7h66O3kcJJ7FQBAuo5YW6v+qhlby46ChGRXmNRQkRaKSQmCXN+u46IJ5w3QhXLyECK4W/UxoROnjAzMhAdh4hIL7EoISKtkpOnwPLDt/Hjqbu8CjtVKhcrE8zqVg+9/auLjkJEpHdYlBCR1rj+KBVTdoXi9pN00VFIjwXUc8KSd3zhYGEsOgoRkd5gUUJEwuUplFjzdxRW/x2JXAV/JZF4DhZG+OodX3Sq5yw6ChGRXmBRQkRC3YlPw5RdYbj6MFV0FKICBreqhTk9fGBiKBMdhYioSmNRQkRCKJUqbDgdjWWHI5CTpxQdh6hInk4WWPleYzSoZi06ChFRlcWihIgq3YOkTEzdHYYL0UmioxCViJFMiqld62LEmx6QSiWi4xARVTksSoioUm2/cB8L999EhlwhOgpRqbX2sMeK/n5wtTYVHYWIqEphUUJElUKep8Sc365h96WHoqMQlYm1qSEW9WmInr7VREchIqoyWJQQUYV7mpaD0b9cwqV7yaKjEJWbAS3c8PnbDWEok4qOQkSk81iUEFGFuvYwFSO3hCA2NVt0FKJy18rDDt+93xQ2ZkaioxAR6TQWJURUYfaGPsLMPVeRncvVtajqqu1gjp+GNkdtB3PRUYiIdBaLEiIqd0qlCksPR2DdiSjRUYgqhY2ZIdYNaorWdexFRyEi0kksSoioXKVl52LyjlAcuxUvOgpRpTKUSbCodyP0a+4mOgoRkc5hUUJE5SYmIQMf/RyCO/HpoqMQCTOqvQdmBdWDRMLrmRARlRSLEiIqF6cjEzBu22WkZuWKjkIkXGADZ3zT3x+mRjLRUYiIdAKLEiIqs7+uxWLijivIVfDXCdFzDatbYcOQ5nC2MhEdhYhI67EoIaIy2Rv6CFN3hSFPyV8lRC9zsTLBxg+bw8fVSnQUIiKtxqKEiF7bnksPMf2/YWA9QlQ0O3MjbBvREvVcWJgQERWFl6Elotey48J9FiREJZCUIceg9edx+0ma6ChERFqLRQkRldqWszGY/ds1FiREJZSYIcfA9ecQycKEiKhQHL5FRKWy4XQ0Fuy/KToGkU5ysDDGjpEt4elkKToKEZFWYVFCRCW27kQUlhy8JToGkU5ztDTG9hGt4OlkIToKEZHWYFFCRCWy8mgkvj56W3QMoirB0dIYO0a2Qh1HFiZERACLEiIqgeWHI/Dt8TuiYxBVKU7/FCYeLEyIiDjRnYiKtzE4mgUJUQWIT8vBgPXnEJ2QIToKEZFwLEqIqEhHbj7hpHaiCvTkWQ4G/HAO9xMzRUchIhKKRQkRFeraw1RM2nGFy/4SVbC4Z9kYuvECUjLloqMQEQnDooSICniUkoVhmy8iU64QHYVIL9xNyMCoLZcgz1OKjkJEJASLEiLSkJadi2EbL+JpWo7oKER65Xx0Emb9elV0DCIiIViUEJFankKJsVsvI4JXnSYS4tfLj7DqWKToGERElY5FCRGpzfntOk5FJoiOQaTXVhy5jb2hj0THICKqVCxKiAgAsObvO9gZ8kB0DCICMHPPVVx/lCo6BhFRpWFRQkTYF/YYyw5HiI5BRP/IzlVi5M8hSEjn3C4i0g+8ojuRngt9kIJ+35/lqj9VROq53Ug5uRmWTd+CXeeRGttUKhXid89DdvQlOPaZA7O6rQttQ6XIQ8qpLciKCkFeahykxuYwqeUHm/ZDYWBpn79PXi4SD65CZuQ5yMxtYdd1LEzdG/+b4/weKJ49hV2X0RV2rvqghbsdto5oCUMZP0MkoqqNv+WI9Fhadi4mbr/CgqSKyIm9jbTQgzB0dC90e1rIXkDy6nZUeTmQx0XBus17cB2yEo69/w+5SY/w9NcF/7YVdhDyuDtweX8ZLPyCkPDHUjz/jCs3JQ7pYYdg0+6D8jgtvXYhJgmf/8ELmBJR1ceihEiPffr7ddxP4pWkqwKlPAsJfyyDfdAESE0sCmyXP7mLZxd+g0O3ya9sS2psDuf3FsLc500Y2teAcfV6sOsyGvK4O8h7Fg8AyE18AFPPljByrAXLJj2gzEyFMusZACDp8FrYdhgKqbFZuZ6jvtpy7h52XrwvOgYRUYViUUKkp369/BC/hz4WHYPKSdKRdTCt01xjCNVzytxsJPyxFHZdx0BmYfta7StzMgFIIDXOL3iMnGoj5+FNKHNzkB19GTILO0hNrZB+429IDIxgVrdNGc6GXjZv301EJ2SIjkFEVGFYlBDpoXuJGZi794boGFROMm6ehDwuCrbthxS6PfnYjzCu7gMzr1av1b4qT46UExthVr+duvfDolEXGDrVxuMNY5F6dhcc3p4JZXY6Uk9vhV3nUUj+3xY8+n4Enuz8FHlpXGa6rLJyFZi8MxR5Cg61JKKqiUUJkZ7JVSgxcfsVpOfkiY5C5SDv2VMkHVsPh17TIDEwKrA9M/I8su+HwTZgxGu1r1Lk4enexQAA+67j1PdLZAaw7zoGNUZvgOuQr2FSowGSj2+AZdNekD+5i6zIs3D98FsYV6uH5KM/vN7JkYawBylY83eU6BhERBWCq28R6ZnFf93Cdyf5xqaqyLx9Fk9/WwRIXviMSaUEIAEkElj6d0fa5T8BiURzu0QK4xr14TJwcZFtPy9I8lLi4DzgC8hMrYrcN/veVSSf3AiX95ch+e+fIJHKYNtxGORP7+HJtllwm7S9HM6WDKQS/Dq2DXxr2IiOQkRUrgxEByCiyhN8JwHf/48FSVViUssPrsNWa9yXeGAlDO1rwKplX8hMrWHROEhje+xP42Hb6SOYerYosl11QZL8GM4Dviy2IFHlyZF0ZF1+b41UBqiU+XURACgVUKk45Ki85ClVmLwzFAcmvgkTQ5noOERE5YbDt4j0RFKGHB/vDAX7RqsWqbEZjBzdNb4khsaQmljCyNEdMgvbAtsBwMDKEYY2Lup2Hq0fjczbZwD8U5D8/iXkcXfg0GsaoFRCkZ4MRXoyVIrcAhlSzuyAqUczGDnXAQAYV6+PzNtnII+PRtrl/TCp7lPxT4Qeufs0A18eCBcdg4ioXLGnhEhPTN8dhvg0Xh2aCpeX9PCfFbYARXoisu6cBwDEbpyosZ/zgC9gUtNXfVv+NAaZt07Bdei36vvM6rVF9oNriNs6E4b21eHQa3olnIF++fncPQT4OKNdXUfRUYiIygXnlBDpgc1nYvDZPq62RVSVOFsZ49DkdrAxK7jAARGRruHwLaIqLjY1C0sO3hIdg4jK2ZNnOfjk9+uiYxARlQsWJURV3MI/w5EpV4iOQUQVYP/VWOwNfSQ6BhFRmbEoIarCgu8k4M+rsaJjEFEFmrv3BpIy5KJjEBGVCYsSoioqV6HE3L0c2kFU1aVm5WLFkQjRMYiIyoRFCVEV9dPpaEQ9zRAdg4gqwfYLDxARlyY6BhHRa2NRQlQFxaVmY9WxSNExiKiSKJQqLNh/U3QMIqLXxqKEqApa+OdNZHByO5FeOX0nAUdvPhEdg4jotbAoIapiztxJwH5ObifSS4sOhCNXoRQdg4io1FiUEFUhuQolL5JIpMeiEzKw+UyM6BhERKXGooSoCtkYHI3I+HTRMYhIoJXHIrlEMBHpHBYlRFVE/LNsrDp2R3QMIhIsLTsPyw9ziWAi0i0sSoiqiLUnopCekyc6BhFpgR0XH+BW3DPRMYiISoxFCVEV8DQtB9sv3Bcdg4i0BJcIJiJdw6KEqApYf+oucvK44g4R/Sv4TiL+jogXHYOIqERYlBDpuKQMOX45d090DCLSQutORImOQERUIixKiHTchtN3kckLJRJRIS5EJ+Hy/WTRMYiIXolFCZEOS83Kxc9n2EtCREX7/iR7S4hI+7EoIdJhm4JjkMYVt4ioGEduPsHdp7x+ERFpNxYlRDoqPScPG89Ei45BRFpOqQJ++N9d0TGIiIrFooRIR205ew8pmbmiYxCRDvj1yiPEp2WLjkFEVCQWJUQ6KEuuwIbT/OSTiEpGnqfET6djRMcgIioSixIiHbTtwn0kpMtFxyAiHbL1/D2kcw4aEWkpFiVEOiZPocSPp9hLQkSlk5adh23nuVofEWknFiVEOubYrXjEpnJsOBGV3k+nYyDPU4qOQURUAIsSIh2z/cJ90RGISEfFPcvG76GPRMcgIiqARQmRDnmYnIn/3X4qOgYR6bCt5/nBBhFpHxYlRDpk58UHUKpEpyAiXRb2IIUXUyQircOihEhHKJQq7Ap5IDoGEVUBv1/hEC4i0i4sSoh0RHrUWQyzvQZTmUJ0FCLScb+FPoJKxW5XItIeLEqIdIT15XUY9WQeblh9jD+8/kRXhyTRkYhIRz1IykLIvWTRMYiI1CQqflRCpP2ykoFldQGF5gUTMx18ccS4CxY/aoTYbCNB4YhIFw1sWRNf9GkkOgYREQAWJUS64eKPwJ9Ti9ysMjDFQ5dO+DnrTfz42A0qlaQSwxGRLrI2NcSFOQEwNpCJjkJExKKESCesDwAehZRo1zwrN1y0DsLS+Ga4nGpZwcGISJd9934TBDV0FR2DiIhFCZHWS4oGVjUu9cNUkCDVpTX2Sjph2YO6SMszKP9sRKTTAhs44/vBzUTHICLiRHcirRdx4LUeJoEKNnFnMCR2Ia6aT8Ahr9/Rxzm+nMMRkS77+9ZTpGTKX70jEVEFY1FCpO0i/ipzE5KcVHg/2IWvUyfjVrUF+M7zPDzMssshHBHpMrlCif1XY0XHICLi8C0irZaVAiytAyjzyr1plcwIT1w6YLu8HdY+qo1cJSfHE+mjZrVs8d8xbUTHICI9x6KESJtd+y+wZ3iFH0Zh7oJQuyB8ndgSp5OsK/x4RKQ9JBIgZE5n2FsYi45CRHqMw7eItNlrzicpLVlGHJo+2IRfMsfgWs0VWOJxFfZGuZVybCISS6UCgqMSRccgIj3HooRIWylygTtHK/2wlvEh6P94MUJMxuK4524MdH1c6RmIqHIFRyaIjkBEeo7Dt4i01d2TwM9viU4BAJDb1EGwZRC+ivVHeLqZ6DhEVM6q25gieFYn0TGISI+xp4RIW5XDqlvlxSglCh0frMEBxSiE1P4Bs2rdhqlMIToWEZWTRylZiE7IEB2DiPQYixIibXVbe4qS5yQqBRxiT2D0k3m4YfUx/vD6E10ckkTHIqJycPoOh3ARkTgcvkWkjRIigdW6c5XlDAc/HDHugiWPGiI220h0HCJ6DUENXPDd4KaiYxCRnjIQHYCICnH/rOgEpWKeEIbeCMPbBqZ46BmAn7PewI+P3aBS8donRLriTFQClEoVpFL+3BJR5ePwLSJt9OC86ASvRZKXBbeH+zEncRYiHWdhm9cJNLFOFx2LiErgWXYerj5KFR2DiPQUixIibfTggugEZWbw7AHaPPgBe+SjccV9DebVDoelQflfmZ6Iyk8w55UQkSCcU0KkbTKTgK88AFS9H02liQ1uOwZi3bPW2PvESXQcInpJKw877BjZWnQMItJDLEqItM3tQ8C2fqJTVLhs+/r427QLljzyQ0yWieg4RATAyECKsLldYWokEx2FiPQMh28RaZv750QnqBQmiTfR7eFK/C0djbN1NmFizbswlPIzEiKR5HlKXHmQLDoGEekhFiVE2qYKzCcpDYlCDtdHhzEl/hPcspuGPV5H0NaWk22JRLkVmyY6AhHpIRYlRNpEkQc8viw6hTCy9Fg0fbARW7PG4GrNr7HY4xrsjXJFxyLSKxFxLEqIqPLxOiVE2iTuKpCbKTqFVrCKv4j3cBH9TSxwt2ZX/JjRBttjq4mORVTl3XrCooSIKh97Soi0ycMQ0Qm0jkSejjoPf8WXydNw22UufvIKRj0LFm5EFSXySRq4Bg4RVTb2lBBpk6e3RCfQakYpd9Ap5Q46Sg2QUPtN7FZ2wLcPPZCl4EpBROUlU67A/aRM1LI3Fx2FiPQIe0qItEnCbdEJdIJEmQfH2L8x9slnuGH9MfZ5HUCAfZLoWERVxi3OKyGiSsaihEibJN4RnUDnSDMT4PvgF2zIGI8bNZbg6zqX4WIsFx2LSKdxsjsRVTYO3yLSFjlpQFqs6BQ6zTwhDH0Qht6Gpnjg1hk/Z72BDY9rQKWSiI5GpFNYlBBRZWNPCZG2SIgUnaDKkORloebDP/BJ4kxEOs7GNq8TaGyVLjoWkc64FfdMdAQi0jMsSoi0BYduVQiDZ/fR5sEP+C13NK64r8Fn7uEwN1CIjkWk1WISM5Gdy58TIqo8LEqItAUnuVcoiUoJ27hgfBi3ANcsJuCg11685RQvOhaRVlIoVbgTz95FIqo8LEqItAWHb1UaaXYK6j3YiVXPJiO8+iKs87wAd9Ns0bGItArnlRBRZeJEdyJtweFbQpgm3kA33ECQzAhxdTpim7wd1j6sBYWKn9mQfnuYnCU6AhHpEf7VJdIGKhWQGCU6hV6TKORwfXQIU5/OwW37Gfiv1xG0tk0VHYtImIT0HNERiEiPsCgh0gaZSUAeP5XUFrL0x2j2YCO2ZY3F1Vrf4AuPa7A1zBMdi6hSJWawKCGiysPhW0TaIDNBdAIqhAQqWD25gIG4gAGmFohyD8SP6W2wI9ZVdDSiCpeQxouQElHlYU8JkTbIYFGi7STydHg+2IPFyVNx22UufvIKRl1z9m5R1ZXAnhIiqkTsKSHSBuwp0SlGKXfQKeUOOkoNkODRDrsU7bHqQR3kKPk5D1UdCWksSoio8vAvKJE2yHgqOgG9BokyD46Pj2Pck88QbvMx9nr9hQD7JNGxiMrFs+w8yPOUomMQkZ5gUUKkDTISRSegMpJmPoXfgy3YkDEeN9y+woo6V+BizDH5pNs42Z2IKguHbxFpAw7fqlLMn4biPwhFHyMz3HfrjM1ZbbHxcQ2oVBLR0YhKJTFdDldrU9ExiEgPsKeESBtwonuVJMnNRK2H+zA3cSZuO/4ftnqdRGOrdNGxiErsKa9VQkSVhD0lRNqAPSVVnuGze2j77Hu0kUiR7N4Gv0k6YsWDusjIk4mORlSkxHQOQSSiysGihEgbcE6J3pColLCLO43hOI0PLWwR4RiINSmtsf+po+hoRAXwqu5EVFk4fItIG2Snik5AAkizk+HzYAdWp01CePVFWOd5ATVNs0XHIlJLymBPCRFVDvaUEGkDZZ7oBCSYaeINdMMNBMmMEVunI7bK2+G7hzWhUPGzIxInO1chOgIR6Qn+tSPSBir+4ad8EkUOqj06iOlP/w8RDjOx2+soWtuyJ43EyFOqREcgIj3BooRIGyhZlFBBBmmP0PzBT9iWNRZhtVbii9rXYGvIXjWqPAoFixIiqhwcvkWkDdhTQsWQQAXrJ+cxEOcxwMwSUU5d8UNaW+yKcxEdjao4hYpFCRFVDvaUEGkDpVJ0AtIRkpw0eD7Yg69SpuC262fY4HUGdc2zRMeiKkrB4VtEVEnYU0KkDdhTQq/BKDkSAcmR6CQ1wFOPdtiV1wHfPvRAjpKfN1H54JwSIqosLEqItAHnlFAZSJR5cHp8HONxHEMda+L9Wj5QgK8pKjsn59YA/EXHICI9wKKESBuoOHyLyodF6n04SOvifOpt0VGoCqjv4Ck6AhHpCfbxE2kDDt+ictQtV3QCqipkEpnoCESkJ1iUEGkD9pRQOeocfQkGUnaEU9nJpCxKiKhysCgh0gaGZqITUBVinZmMVlYcdkNlx54SIqosLEqItIGxlegEVMV0y+aQQCo7qYRvE4iocvC3DZE2MGFRQuWrU/RFGEmNRMcgHWdmwF5cIqocLEqItAF7SqicWWQ/Q1sO4aIysjGxER2BiPQEixIibcCeEqoA3bKyRUcgHWdrbCs6AhHpCRYlRNqAPSVUAdrfvQhTmYnoGKTD2FNCRJWFRQmRNmBPCVUAM3kG3rSqIzoG6TD2lBBRZWFRQqQN2FNCFaRberroCKTD2FNCRJWFRQmRNjCxFp2Aqqg3716EOVdQotfEnhIiqiwsSoi0AXtKqIIY52Wjg6WH6Bikg6QSKayN+YEJEVUOFiVE2sDcQXQCqsK6PUsVHYF0kJWRFS+eSESVhr9tiLSBrbvoBFSFtYm+CEtDC9ExSMfYGNuIjkBEeoRFCZE2YFFCFchQIUeARW3RMUjH2JpwPgkRVR4WJUTawNwBMOIn2VRxuqUkio5AOsbBlMNKiajysCgh0hY2tUQnoCqsRUwIbI04aZlKrpYVfycRUeVhUUKkLWz5BoAqjoEyD53Na4qOQTqktjWH/BFR5WFRQqQtOK+EKli3pHjREUiH1LZiUUJElYdFCZG24PAtqmBN712Co4md6BikI9hTQkSViUUJkbZgTwlVMKlKiS4m1UXHIB3gZOoECy6+QUSViEUJkbbgnBKqBN0SHouOQDqAvSREVNlYlBBpC1t3gFdPpgrm9yAULqaOomOQlnO3dhcdgYj0DN8BEWkLQ1PAzkN0CqriJFAh0NhFdAzScuwpIaLKxqKESJu4+IpOQHqgW/x90RFIy7EoIaLKxqKESJu4siihitfg0TW4mbG3hIrmYc1eWyKqXCxKiLQJe0qokgQacl4JFc7MwAzOZs6iYxCRnmFRQqRNXP1EJyA9ERR3V3QE0lKNHBpBIpGIjkFEeoZFCZE2MXcAbGqKTkF6wDsuHLXNec0SKqiJcxPREYhID7EoIdI21ZuJTkB6IkhmKzoCaSEWJUQkAosSIm1To7noBKQngmJvi45AWsZAYgBfB85tI6LKx6KESNvUYE8JVQ6P+Duoa8HhgvSvenb1YGZoJjoGEekhFiVE2sbVD5AZiU5BeiJIaik6AmkRDt0iIlFYlBBpGwNjoEYL0SlITwQ9vCk6AmkRFiVEJAqLEiJt5BkgOgHpCbfEe2hgxat3EyCBBE2cWJQQkRgsSoi0EYsSqkRBKlPREUgL1LauDVsTrshGRGKwKCHSRi6+gLmT6BSkJ4LuX4cEvFievvN38hcdgYj0GIsSIm0kkQB1OolOQXrCJeUh/Kw8RMcgwZo6NxUdgYj0GIsSIm3FIVxUiYIUXPFNn0klUrSp1kZ0DCLSYyxKiLRVnU4Ah9RQJel6PwxSCf8k6KvGjo1hb2ovOgYR6TH+BSLSVuYO+dcsIaoEjs/i0NSqjugYJEiXWl1ERyAiPceihEibcQgXVaKgXP5J0Feda3UWHYGI9Bz/AhFpM0++UaDK0yXmMgwkBqJjUCVrYN8ALuYuomMQkZ5jUUKkzdxaAhbOolOQnrDNSEQLaw7h0jfsJSEibcCPxIi0mVQGNOwLnFsrOgnpiaAcFc6IDvGCjIgMJBxIQNa9LOSl5KHmhJqwamql3p6Xmoe4XXFIv5EORaYC5nXN4fq+K4xdjIttN+FQApL+TkJuYi5kljJYN7OG8zvOkBrlf1aXciYFcf+NgzJbCds3beE6wFX9WPlTOWKWxaDOvDqQmcoq5sQrUeeaLEqISDz2lBBpu0bvik5AeiQg+iIMpYaiY6gpc5QwqWmCaoOrFdimUqlwb9U9yJ/KUXNiTXjO94ShgyFilsZAmaMsss2Usyl4svsJnN52gtcXXqg+rDpSL6TiyZ4nAIC8tDw82vgIrv1d4T7NHSlnUvAs9Jn68Y+3PIbzu85VoiCpY10H7tbuomMQEbEoIdJ61ZsA9l6iU5CesMpKRRsrT9Ex1Cx9LeHc11mjd+Q5+RM5sqKyUG1INZh5mMHY1RjVPqgGpVyJlHMpRbaZeScTZl5msGltAyNHI1g2tIR1S2tk3c3Kb/epHDJTGaxbWsPMwwzmPubIeZwDAEg5lwKJTALrZtYVcr6VjUO3iEhbsCgh0gW+/UQnID0SmCUXHaFEVLkqAIDE8N/r+UikEkgMJci8nVnk48w8zZAVk4XMu/n7yOPlSL+aDgtfCwCAsbMxlHJl/pCx9DxkRWfBxM0EigwF4n+Nh+v7rkW2rWtYlBCRtuCcEiJd0Ogd4O9FolOQnugUHQJjNxfkKHJERymWsasxDO0N8WT3E1QfWh0SYwkSDyUiLykPeal5RT7OprUNFOkKRC+KhgoqQAHYdbSDUy8nAIDMXIYaI2rg4fqHUMlVsGljA8tGlni44SHsAuyQm5CL+yvvQ6VQwam3E6yb62avSQ2LGqhnV090DCIiACxKiHSDnQdQoznw8KLoJKQHzHPS8KbVGziafEN0lGJJDCSoOaEmHm14hPBx4YAUsKhvkd/joSr6cenh6Xj6x1O4fuAKMw8zyOPliN0ai/i98XB6O78wsWpqpTFkLONWBnIe5qDa+9Vwe+ZtuI12g4G1AaI+j4K5tzkMrHTvz2lvz96iIxARqeneb1EifeXbn0UJVZrA9EwcFR2iBEzdTeG5wBOKTAVUeSoYWOUXCqbupkU+Jv63eNi0sYFdezsAgImbCZQ5Sjza9AiOvRwhkUo09lfmKvH458eoMbIG5PFyqBQqmNczBwAYuxgjMyoTVv4F57xoMwOpAfrW7Ss6BhGRGueUEOmKBv8BpPwcgSpH+5iLMDUo+o29tpGZyWBgZYCcuBxkRWfBsollkfsqc5QF//oV89fw6b6nsGhkAVN3U6iUKuCFhb1UeZq3dUVHt45wMHUQHYOISI1FCZGuMLfnFd6p0pjKM9HBUvyFFBXZCmTdy0LWvX9WxkqQI+teFuSJ+ZPxUy+kIj08HfJ4OZ5dfoaYpTGwamIFy4b/FiUPf3iIuN1x6tuWjS2RdDwJKedSIH8qR/r1dMT/Gg/LxpYFekmyH2Uj9UIqnP+TfxFTY1djQAIknUxCWmgacmJzYOqhO8Xbc/29+4uOQESkgR+7EumSZsOB2wdFpyA9EZj2DH8JzpAVnYWYJTHq23Hb84sLm7Y2qDGiBvJS8xC7IxaKVAUMbAxg08YGjm87arQhT5QDL9QaTm85QSKRIP7XeOQm58LA0gCWjfOXHn6RSqXC402P4TLABVLj/M/wpEZSVP+oOmK3xEKVq4LrYFcY2mrPdV1Kwt3KHS1dW4qOQUSkQaJSqYqZDkhEWkWlAlY3BxIjRScpN+suyrEuRI6YlPwxMA2cZJjbzgjdvP59o3f2QR7mHM/B+UcKyCRAYxcZDr1vBlNDSVHNYs0FOZaeyUFcugp+LlJ8280ULar/e7G7KYeysSlUDnMjCRYHmGCQ77/H230jFz9fzcUfA8wq4Ix1h1xmjA516iAtN110FCpH05tNxwcNPhAdg4hIA4dvEekSiQRoOUp0inJVw0qCxZ2NcWmkOUJGmqOTuwxv78jCjXgFgPyCJGhrJrrWMcCFj8xxcYQ5xrcwgrToegQ7r+diyuFsfNbeGJdHmcPPWYbAXzIQn5Ff+PwRkYtt13JxeLA5vupsgo/+yEJCZv621GwV5hzPwZruJhV+7trOSJGDjha1RcegcmQiM8Hbnm+LjkFEVACLEiJd03ggYGIjOkW56eVtiO5ehvCyl6GuvQyLAkxgYQSce5hflHx8KAcTWxhh1hvGaOAkg7eDDP0aGMLYoOiqZMW5HIxoYogP/Y1Q31GG73qawMxQgp+u5AIAwhOU6OAuQ7NqMgxoZAgrYwmik/M7jWccycaYZoaoac1fjwAQmJokOgKVo67uXWFtrJvXVSGiqo1/dYl0jZE50HSI6BQVQqFUYcf1XGTkAq3dZIjPUOL8IwWczKVosyEDzsvS0H5TBk7fL/rCeHKFCpceK9HZ498pc1KJBJ09DHD2n0LHz1mGkMcKJGepcOmxAlm5KnjaSXH6fh4uxykwsaVRhZ+rrmgdHQJrI91a7paKxgnuRKStWJQQ6aIWI6vU8sDXnihg8cUzGC9Mw+j9WfitvynqO8pwNzl/SNW8k/k9HwcHmaGJiwwBP2ciMlFRaFsJmSooVICzuWZPirO5BHHp+e0FehrgfV9DNF+fjqF7s7C5tynMjYAxf2bjux6mWBeSC+/V6Wj7U4Z6GJm+MlTmorN5LdExqBz42PnA19FXdAwiokKxKCHSRdY1AJ9eolOUG28HKUJHW+D8R+YY08wIQ37Pxs2nCij/WYZjVNP8oVj+rjJ8HWQCb3upeijW65rXwQR3Jlri2hgL9PExxJen5Ohc2wCGMmDh/3Jw+kMzfORviA9+zyqHM9RtQclPRUegcjCg3gDREYiIisSihEhXtRonOkG5MZJJ4GknRdNqMnzZ2QR+zlKsPCeHq0X+r6j6jpq/qnwcpbj/rPAr1jmYSSCTAE8yNBcWfJKhgotF4b/ybiUo8Mu1XCzoZIwTMXloV0sGR3Mp+jUwxOVYJdJy9HuRwuYxl2BnbCs6BpVBdYvq6FWn6nyQQURVD4sSIl3l1hyo3kx0igqhVAE5CsDdRoJqlhJEJGgWILcTlahVxER0I5kETatJcezuv/NOlCoVjt3NQ+sasgL7q1QqjNqfjRVdjWFhJIFCCeT+c7jn/yr0uyaBTKVAF7MaomNQGYzyHQWDKjTkk4iqHhYlRLqs7STRCcps9tFs/O9eHmJSlLj2RIHZR7NxIkaBQY0MIZFIML2NEVZdkOO/N3NxJ0mJT49n41aCEsP9/52MHvBzBlZfkKtvT2lljPWXc7E5VI7wpwqM2Z+NjFwVPmxc8CJ3P17OhaOZBL2887e1rWmA49F5OPcwD1+fzUF9RylsTIpZf1hPdEuIe/VOpJVqWNRgLwkRaT1+bEKky3x6Aa6NgdhQ0UleW3yGCh/8loXYdBWsjSXwdZbi0Ptm6FIn/9fT5FbGyM4DPj6UjaQsFfycZTgy2Ax17P79TCUqSam+zggA9G9oiKeZKsw9kX/xxMYuUhwcZAbnl4ZvPUlXYtGpHJwZbq6+r0V1Gaa2NkaPbVlwMpdgc2/TCn4GdEOT+5fh5OOP+OwE0VGolEb6jmQvCRFpPV7RnUjX3TkK/NJXdArSA0v8e+CXlGuiY1ApuFm6YV/vfSxKiEjrcfgWka7z7AzUekN0CtID3Z4+FB2BSom9JESkK1iUEFUFAXNFJyA94PswDNXNnEXHoBKqaVkTvTw4l4SIdAOLEqKqoGZLoG6Q6BSkB7oaOYmOQCU00nckZNKCK84REWkjFiVEVUWnTwFwlSiqWN3iokVHoBKoZVULPT16io5BRFRiLEqIqgqXhkBDTniniuUTexO1zKuJjkGvMMp3FHtJiEinsCghqko6/h/ASa1UwQIN7EVHoGI0dmzMXhIi0jksSoiqEvs6QJMholNQFdct9o7oCFQEmUSGT1p9AomEQzmJSLewKCGqagI+BcwcRKegKszzSQQ8LdxEx6BC9PfuD287b9ExiIhKjUUJUVVjagsELhKdgqq4QKm16Aj0EgdTB4z3Hy86BhHRa2FRQlQV+b0HuL8pOgVVYd0e3RIdgV4ypekUWBpZio5BRPRaWJQQVVU9vwZkRqJTUBVVK+EufCxriY5B/2jq3BS96vBCiUSku1iUEFVVDl5A28miU1AVFggL0REIgIHEAHNazhEdg4ioTFiUEFVlb04F7DxEp6AqKujhDdERCMBAn4HwsvUSHYOIqExYlBBVZYYmQI/lolNQFVU96T58rVj0iuRk6oSxjceKjkFEVGYsSoiqujqdeKV3qjCBShPREfTajBYzYG5oLjoGEVGZsSgh0geBXwKmdqJTUBUUeP8qJOCF+kToVrsbAt0DRccgIioXLEqI9IGlM/D2GtEpqApyTn0Mf+s6omPoHWczZ3zS6hPRMYiIyg2LEiJ9Ua870Pwj0SmoCgrKMxAdQa9IIMGiNxbByshKdBQionLDooRIn3RdBDj6iE5BVUzXmCuQSWSiY+iNQT6D0NK1pegYRETlikUJkT4xNAHe+Qkw4ORkKj/26U/RjEO4KkVd27qY3HSy6BhEROWORQmRvnGuD3RdKDoFVTFBctEJqj5TA1Msbb8UxjJj0VGIiModixIifdRiBODdXXQKqkK6RF+CgZRzSyrS7Baz4WFdsdeFkUgk+P3334vcfuLECUgkEqSkpFRoDira0KFD0bt37zK3I5fL4enpiTNnzpQ9lA5xd3fHN998o779qte8Ppg3bx4aN25cbu0dPHgQjRs3hlKpLNXjWJQQ6au31wCWrqJTUBVhnZmMVlaeomNUWd1rd0cfrz5laiMuLg4TJkyAh4cHjI2N4ebmhl69euHYsWMlbqNNmzaIjY2FtbV1mbI8V95vhvTBypUrsWnTpjK3891336F27dpo06aN+j6JRKL+sra2Rtu2bXH8+PEyH0ubxcbGolu3bsKOX9mFfmFF2LRp00r1e+BVgoKCYGhoiK1bt5bqcSxKiPSVmR3Q53tAwl8DVD6CshWiI1RJbpZumNt6bpnaiImJQdOmTXH8+HEsXboU165dw8GDB9GxY0eMGzeuxO0YGRnBxcUFEknlXpsmNze3Uo+nzaytrWFjY1OmNlQqFVavXo3hw4cX2LZx40bExsYiODgYDg4O6NmzJ+7evVum42kzFxcXGBtXjSGRr/tzYmFhAXt7+3LNMnToUKxatapUj+G7ESJ95tEeCCjbmx2i5wKiL8JIaiQ6RpViYWiBbzt9W+arto8dOxYSiQQXLlxA3759UbduXTRo0ABTpkzBuXPnNPZNSEhAnz59YGZmBi8vL+zbt0+97eVPdTdt2gQbGxscOnQIPj4+sLCwQFBQEGJjYzUe06JFC5ibm8PGxgZt27bFvXv3sGnTJsyfPx9hYWHqT+ef9wBIJBKsW7cOb731FszNzbFo0SIoFAoMHz4ctWvXhqmpKby9vbFy5UqN7M+HNs2fPx+Ojo6wsrLC6NGjIZcXPenp+Tn8/vvv8PLygomJCQIDA/HgwQON/fbu3YsmTZrAxMQEHh4emD9/PvLy8tTbJRIJfvzxxyKfOwDYt2+f+hgdO3bE5s2bNZ7PwnqOvvnmG7i7uxc4x+c6dOiAiRMnYsaMGbCzs4OLiwvmzZtX5PkCwKVLlxAVFYUePXoU2GZjYwMXFxc0bNgQ69atQ1ZWFo4cOYKff/4Z9vb2yMnJ0di/d+/eGDx4sPr2woUL4eTkBEtLS3z00UeYNWuWxjkplUp8/vnnqFGjBoyNjdG4cWMcPHhQvV0ul2P8+PFwdXWFiYkJatWqhS+//FK9PSUlBaNGjYKzszNMTEzQsGFD7N+/X7399OnTePPNN2Fqago3NzdMnDgRGRkZRT4XL/YcvOrYL7t48SK6dOkCBwcHWFtbo3379rh8+XKB9ot6XcTExKBjx44AAFtbW0gkEgwdOhRA/hCoN954AzY2NrC3t0fPnj0RFRWlbjcmJgYSiQQ7d+5E+/btYWJiou6Z+Omnn9CgQQMYGxvD1dUV48ePBwD166hPnz6QSCTq24W97opqAwBWrFiBRo0awdzcHG5ubhg7dizS09M1Ht+rVy+EhIRoZH4VFiVE+u6NjwG/gaJTUBVgkf0Mba05hKu8yCQyLG2/FHVsyrayWVJSEg4ePIhx48bB3LxgcfPyp+7z589Hv379cPXqVXTv3h2DBg1CUlJSke1nZmZi2bJl2LJlC/73v//h/v37mDZtGgAgLy8PvXv3Rvv27XH16lWcPXsWI0eOhEQiQf/+/TF16lQ0aNAAsbGxiI2NRf/+/dXtzps3D3369MG1a9cwbNgwKJVK1KhRA7t378bNmzcxd+5c/N///R927dqlkefYsWMIDw/HiRMnsH37dvz666+YP39+sc9RZmYmFi1ahJ9//hnBwcFISUnBe++9p95+6tQpfPDBB5g0aRJu3ryJ77//Hps2bcKiRYtK/NxFR0fjnXfeQe/evREWFoZRo0Zhzpw5xeYqqc2bN8Pc3Bznz5/HV199hc8//xxHjhwpcv9Tp06hbt26sLS0LLZdU1NTAPlv1t99910oFAqNQis+Ph5//vknhg0bBgDYunUrFi1ahCVLluDSpUuoWbMm1q1bp9HmypUrsXz5cixbtgxXr15FYGAg3nrrLURGRgIAVq1ahX379mHXrl2IiIjA1q1b1W+elUolunXrhuDgYPzyyy+4efMmFi9eDJksf0nyqKgoBAUFoW/fvrh69Sp27tyJ06dPa7yhLk5xxy5MWloahgwZgtOnT+PcuXPw8vJC9+7dkZaWprFfUa8LNzc37NmzBwAQERGB2NhYdaGdkZGBKVOmICQkBMeOHYNUKkWfPn0KzNOYNWsWJk2ahPDwcAQGBmLdunUYN24cRo4ciWvXrmHfvn3w9Mz/vXzx4kUA//aGPb/9suLaAACpVIpVq1bhxo0b2Lx5M44fP44ZM2ZotFGzZk04Ozvj1KlTJXjm83FWIhEBvVYCSXeBB+devS9RMYIys/G36BBVxPTm0/FG9TfK3M6dO3egUqlQr169Eu0/dOhQDBgwAADwxRdfYNWqVbhw4QKCgoIK3T83Nxffffcd6tTJL57Gjx+Pzz//HADw7NkzpKamomfPnurtPj7/XivJwsICBgYGcHFxKdDuwIED8eGHH2rc92JxUbt2bZw9exa7du1Cv3791PcbGRnhp59+gpmZGRo0aIDPP/8c06dPx4IFCyCVFv5ZbG5uLlavXo2WLfOv/7J582b4+PjgwoULaNGiBebPn49Zs2ZhyJAhAAAPDw8sWLAAM2bMwGeffVai5+7777+Ht7c3li5dCgDw9vbG9evXCxQ2r8PX11edw8vLC6tXr8axY8fQpUuXQve/d+8eqlWrVmybmZmZ+OSTTyCTydC+fXuYmppi4MCB2LhxI959910AwC+//IKaNWuiQ4cOAIBvv/0Ww4cPV3/f5s6di8OHD2t8ir5s2TLMnDlTXfQtWbIEf//9N7755husWbMG9+/fh5eXF9544w1IJBLUqlVL/dijR4/iwoULCA8PR926dQHkfy+e+/LLLzFo0CBMnjxZ/VysWrUK7du3x7p162BiUvxy+MUduzCdOnXSuP3DDz/AxsYGJ0+eRM+ePdX3F/e6sLOzAwA4OTlpfEDQt29fjbZ/+uknODo64ubNm2jYsKH6/smTJ+M///mP+vbChQsxdepUTJo0SX1f8+bNAQCOjo4A/u0NK0pxbTw/5nPu7u5YuHAhRo8ejbVr12q0U61aNdy7d6/I47yMPSVEBBgYAe9tBaxrik5COq7D3YswlfE6OGXVr24/DPIZVC5tqVSqUu3v6+ur/r+5uTmsrKwQHx9f5P5mZmbqggMAXF1d1fvb2dlh6NChCAwMRK9evbBy5UqNoV3FadasWYH71qxZg6ZNm8LR0REWFhb44YcfcP/+fY19/Pz8YGZmpr7dunVrpKenFxiO9SIDAwONN1316tWDjY0NwsPDAQBhYWH4/PPPYWFhof4aMWIEYmNjkZmZqX5ccc9dRESExjEAoEWLFiV5Kl7pxeMCmt+DwmRlZRX5Bn3AgAGwsLCApaUl9uzZgw0bNqjbHzFiBA4fPoxHjx4ByB/6NnToUPUco4iIiALn9OLtZ8+e4fHjx2jbtq3GPm3btlU/10OHDkVoaCi8vb0xceJEHD58WL1faGgoatSooS5IXhYWFoZNmzZpfJ8CAwOhVCoRHR1d5PPxXHHHLsyTJ08wYsQIeHl5wdraGlZWVkhPTy/wmiztzxQAREZGYsCAAfDw8ICVlZW6x+bltl/8OYmPj8fjx48REBDwynMtSknaOHr0KAICAlC9enVYWlpi8ODBSExM1PhZAPJ72l6+rzgsSogon7kDMHAHYFR8dz5RcczkGXjTihdSLItWrq0wu+XscmvPy8sLEokEt27dKtH+hoaGGrclEkmxS3sWtv+LhdDGjRtx9uxZtGnTBjt37kTdunULzGMpzMtDzXbs2IFp06Zh+PDhOHz4MEJDQ/Hhhx8WO1+kvKSnp2P+/PkIDQ1Vf127dg2RkZEab+5L+9y9TCqVFigiSzJ5ubTHdXBwQHJycqHbvv76a4SGhiIuLg5xcXHq3iEA8Pf3h5+fH37++WdcunQJN27cUM+BKC9NmjRBdHQ0FixYgKysLPTr1w/vvPMOgH+HkxUlPT0do0aN0vg+hYWFITIyUqNwfp1jF2bIkCEIDQ3FypUrcebMGYSGhsLe3r7Aa/J1Xhe9evVCUlIS1q9fj/Pnz+P8+fMAUKDtF39OXvX8lMSr2oiJiUHPnj3h6+uLPXv24NKlS1izZk2h2ZKSktS9MyXBooSI/uXcAOj7I1fkojIJemnCI5Wcu5U7lndYXq7XfLGzs0NgYCDWrFlT6ITfyliK1N/fH7Nnz8aZM2fQsGFDbNu2DUD+UCuFomSrtgUHB6NNmzYYO3Ys/P394enpWegk2rCwMGRlZalvnzt3DhYWFnBzcyuy7by8PISEhKhvR0REICUlRT3UrEmTJoiIiICnp2eBr6KGhL3M29tb4xgACozpd3R0RFxcnEZhEhoaWqL2S8Pf3x+3bt0qtBfNxcUFnp6eRb6Z/Oijj7Bp0yZs3LgRnTt31nhevb29C5zTi7etrKxQrVo1BAcHa+wTHByM+vXra+zXv39/rF+/Hjt37sSePXuQlJQEX19fPHz4ELdv3y40W5MmTXDz5s1Cv09GRiVbhKOoYxcmODgYEydORPfu3dWTwhMSEkp0nOee53rx5yAxMRERERH45JNPEBAQAB8fnyKLyBdZWlrC3d292OV9DQ0Ni/2Ze1Ubly5dglKpxPLly9GqVSvUrVsXjx8/LrBfdnY2oqKi4O/v/8rcz/GdBxFp8g4COhc/KZSoOO3uXoS5gdmrdyQN1sbWWBOwBlZGVuXe9po1a6BQKNCiRQvs2bMHkZGRCA8Px6pVq9C6detyP95z0dHRmD17Ns6ePYt79+7h8OHDiIyMVL/Zd3d3R3R0NEJDQ5GQkFBgZacXeXl5ISQkBIcOHcLt27fx6aefFjpRVy6XY/jw4bh58yYOHDiAzz77DOPHjy+2eDA0NMSECRNw/vx5XLp0CUOHDkWrVq3UQ4/mzp2Ln3/+GfPnz8eNGzcQHh6OHTt24JNPPinxczFq1CjcunULM2fOxO3bt7Fr1y6N1caA/JW0nj59iq+++gpRUVFYs2YN/vrrrxIfo6Q6duyI9PR03Lhxo9SPHThwIB4+fIj169erJ7g/N2HCBGzYsAGbN29GZGQkFi5ciKtXr2osIT19+nQsWbIEO3fuREREBGbNmoXQ0FD1/IUVK1Zg+/btuHXrFm7fvo3du3fDxcUFNjY2aN++Pdq1a4e+ffviyJEjiI6Oxl9//aVevWvmzJk4c+YMxo8fj9DQUERGRmLv3r0lnuhe3LEL4+XlhS1btiA8PBznz5/HoEGDSt1bUatWLUgkEuzfvx9Pnz5Feno6bG1tYW9vjx9++AF37tzB8ePHMWXKlBK1N2/ePCxfvhyrVq1CZGQkLl++jG+//Va9/XnBERcXV2ShU1wbnp6eyM3Nxbfffou7d+9iy5Yt+O677wq0ce7cORgbG5fq9wuLEiIqqO1EwP990SlIRxnnZaODZcVeebyqMZAa4OsOX6OmVcXM6/Lw8MDly5fRsWNHTJ06FQ0bNkSXLl1w7NixAqsjlSczMzPcunVLvQzxyJEjMW7cOIwaNQpA/mTeoKAgdOzYEY6Ojti+fXuRbY0aNQr/+c9/0L9/f7Rs2RKJiYkYO3Zsgf0CAgLg5eWFdu3aoX///njrrbdeuUSumZkZZs6ciYEDB6Jt27awsLDAzp071dsDAwOxf/9+HD58GM2bN0erVq3w9ddfv3Ii9Itq166N//73v/j111/h6+uLdevWqVffen6dDB8fH6xduxZr1qyBn58fLly4oF7JrDzZ29ujT58+pb64HZB/nZS+ffvCwsKiwJXlBw0ahNmzZ2PatGnqoVBDhw7VGOI2ceJETJkyBVOnTkWjRo1w8OBB9VLJQP4n9V999RWaNWuG5s2bIyYmBgcOHFAXlXv27EHz5s0xYMAA1K9fHzNmzFB/8u/r64uTJ0/i9u3bePPNN+Hv74+5c+e+clL/c6869ss2bNiA5ORkNGnSBIMHD8bEiRPh5ORUquezevXq6oUUnJ2d1QX0jh07cOnSJTRs2BAff/yxeoGEVxkyZAi++eYbrF27Fg0aNEDPnj3VK5sBwPLly3HkyBG4ubkV2YtRXBt+fn5YsWIFlixZgoYNG2Lr1q2FLpu8fft2DBo0SGN+16tIVKWdAUdE+kGRC+wYCEQWP9GPqDAnPNtigqLoicWk6fM2n5f5iu2UP1E5JSWlwBWri7Np0yZMnjy50q6o/aJFixbhu+++K3YSfkW5evUqunTpgqioKFhYWJTqsQEBAWjQoEGJLo7XpUsXuLi4YMuWLa8blXRMQkKCerhi7dq1S/w4LglMRIWTGQL9fgZ+eQe4d1p0GtIxbaMvwrKOJ9JyOb/kVWa1mMWCRE+sXbsWzZs3h729PYKDg7F06dISDy0qb76+vliyZAmio6PRqFGjEj0mOTkZJ06cwIkTJwos/wrkLyP83XffITAwEDKZDNu3b8fRo0eLvWYKVT0xMTFYu3ZtqQoSgEUJERXH0DR/Ra7NbwGPL796f6J/GCrkCLCojd+Tr4mOotWmNZtWbkv/kvZ7Ps8iKSkJNWvWxNSpUzF7dvmttFZapV05y9/fH8nJyViyZAm8vb0LbJdIJDhw4AAWLVqE7OxseHt7Y8+ePejcuXM5JSZd0KxZs0KX9H4VDt8iolfLTAI29QDib4pOQjok2KMlRqtKdk0KffRx048xrOGwV+9IRKQHONGdiF7NzA74YB/gUPCTMaKitIy5BFsja9ExtNJE/4ksSIiIXsCihIhKxsIRGPIHYO8lOgnpCANlHjqbV8xqUrpsrN9YjPAdIToGEZFWYVFCRCVn6ZxfmNjxit1UMkFJ8aIjaJWRviMxpvEY0TGIiLQOixIiKh0rV2DofsDeU3QS0gHN7l2Co4md6BhaYVjDYZjgP0F0DCIircSiRAfFxMRAIpEgNDS0zG1t2LABXbt2LXsoHbJp0yaNq7POmzcPjRs3FpanMh08eBCNGzeGUqksW0NW1YBhh4HqTcsnGFVZUpUSXUxriI4h3JD6Q/Bx049FxyAi0lqlLkri4uIwadIkeHp6wsTEBM7Ozmjbti3WrVuHzMzMcg3XoUMHTJ48uVzbrArc3NwQGxuLhg0blqmd7OxsfPrpp/jss8/U982bNw8SiQQSiQQGBgZwd3fHxx9/jPT0qnutgWnTpuHYsWOiY1SKoKAgGBoavtZVfAswtweG7Ae89KuopdILevpIdARhJJDg46YfY1rz8r8qNxFRVVKqouTu3bvw9/fH4cOH8cUXX+DKlSs4e/YsZsyYgf379+Po0aMVlZNeIJPJ4OLiAgODsl1m5r///S+srKzQtm1bjfsbNGiA2NhYxMTEYMmSJfjhhx8wderUMh1Lm1lYWMDe3l50jEozdOjQEl2Ft0SMzID3tgONeZ0FKlrjB6FwMXUUHaPSGUmN8FW7r7jKFhFRCZSqKBk7diwMDAwQEhKCfv36wcfHBx4eHnj77bfx559/olevXup9U1JS8NFHH8HR0RFWVlbo1KkTwsLC1NufD5nZsmUL3N3dYW1tjffeew9paWkA8t84nTx5EitXrlR/ch8TEwMAOHnyJFq0aAFjY2O4urpi1qxZyMvLU7edk5ODiRMnwsnJCSYmJnjjjTdw8eLFYs/N3d0dCxYswIABA2Bubo7q1atjzZo1GvuU9ZwAIC0tDYMGDYK5uTlcXV3x9ddfF+gRkkgk+P333zWObWNjg02bNgEoOHzrxIkTkEgkOHbsGJo1awYzMzO0adMGERERxZ7zjh07NL5nzxkYGMDFxQU1atRA//79MWjQIOzbtw8qlQqenp5YtmyZxv6hoaGQSCS4c+cOAODWrVt44403YGJigvr16+Po0aMFzunatWvo1KkTTE1NYW9vj5EjR2r0xpw4cQItWrSAubk5bGxs0LZtW9y7d0+9/Y8//kDz5s1hYmICBwcH9Onz79WQc3JyMG3aNFSvXh3m5uZo2bIlTpw4UeTz8PLwrVcd+0XPvxc7duxAmzZtYGJigoYNG+LkyZPqfRQKBYYPH47atWvD1NQU3t7eWLlypUY7Q4cORe/evTF//nz162v06NGQy+Xqfdzd3fHNN99oPK5x48aYN2+e+vaKFSvQqFEjmJubw83NDWPHji3Qy9WrVy+EhIQgKiqqyOekVGQGQO+1wBtTyqc9qnIkUCHQ2FV0jEplbWyN9V3XI6h2kOgoREQ6ocRFSWJiIg4fPoxx48bB3Ny80H0kEon6/++++y7i4+Px119/4dKlS2jSpAkCAgKQlJSk3icqKgq///479u/fj/379+PkyZNYvHgxAGDlypVo3bo1RowYgdjYWMTGxsLNzQ2PHj1C9+7d0bx5c4SFhWHdunXYsGEDFi5cqG53xowZ2LNnDzZv3ozLly/D09MTgYGBGscuzNKlS+Hn54crV65g1qxZmDRpEo4cOVJu5wQAU6ZMQXBwMPbt24cjR47g1KlTuHy5fK6UPWfOHCxfvhwhISEwMDDAsGHFfzp3+vTpEl1x09TUFHK5HBKJBMOGDcPGjRs1tm/cuBHt2rWDp6cnFAoFevfuDTMzM5w/fx4//PAD5syZo7F/RkYGAgMDYWtri4sXL2L37t04evQoxo8fDwDIy8tD79690b59e1y9ehVnz57FyJEj1a+vP//8E3369EH37t1x5coVHDt2DC1atFC3P378eJw9exY7duzA1atX8e677yIoKAiRkZGvPNdXHbso06dPx9SpU3HlyhW0bt0avXr1QmJiIgBAqVSiRo0a2L17N27evIm5c+fi//7v/7Br1y6NNo4dO4bw8HCcOHEC27dvx6+//or58+e/MvOLpFIpVq1ahRs3bmDz5s04fvw4ZsyYobFPzZo14ezsjFOnTpWq7Vfq/BnQbSkg4VQ1KigovvDCviqqYVEDv3T7BU2cm4iOQkSkM0o8/ufOnTtQqVTw9ta8eJqDgwOys7MBAOPGjcOSJUtw+vRpXLhwAfHx8TA2NgYALFu2DL///jv++9//YuTIkQDy36xt2rQJlpaWAIDBgwfj2LFjWLRoEaytrWFkZAQzMzO4uLioj7d27Vq4ublh9erVkEgkqFevHh4/foyZM2di7ty5yMrKwrp167Bp0yZ069YNALB+/XocOXIEGzZswPTp04s8x7Zt22LWrFkAgLp16yI4OBhff/01unTpUi7nlJaWhs2bN2Pbtm0ICAgAkP+Gvlq1aiX9NhRr0aJFaN++PQBg1qxZ6NGjB7Kzs2FiYlJg35SUFKSmpr7y2JcuXcK2bdvQqVMnAPmf6M+dOxcXLlxAixYtkJubi23btql7T44cOYKoqCicOHFC/X1btGgRunTpom5z27ZtyM7Oxs8//6wucFevXo1evXphyZIlMDQ0RGpqKnr27Ik6dfKXnvXx8dE4z/fee0/jDbufnx8A4P79+9i4cSPu37+vPrdp06bh4MGD2LhxI7744otiz/fZs2fFHrso48ePR9++fQEA69atw8GDB7FhwwbMmDEDhoaGGllr166Ns2fPYteuXejXr5/6fiMjI/z0008wMzNDgwYN8Pnnn2P69OlYsGABpNKSvdF/scfN3d0dCxcuxOjRo7F27VqN/apVq1Zk70+ZtByZfz2TX0cBipzyb590VsNH1+DWoAUeZMaJjlKhfB18sarTKtib6s+QUCKi8lDmjzQvXLiA0NBQNGjQADk5+W9CwsLCkJ6eDnt7e1hYWKi/oqOjNYaMuLu7q9+8A4Crqyvi44tf0z48PBytW7fW+OS6bdu2SE9Px8OHDxEVFYXc3FyNeRKGhoZo0aIFwsPDi227devWBW4/f0x5nNPdu3eRm5ur8am+tbV1gULvdfn6+mocF0CRz2dWVhYAFFqwXLt2DRYWFjA1NUWLFi3QunVrrF69GkD+m9kePXrgp59+ApA/jConJwfvvvsuACAiIgJubm4aheSL5wvkfw/9/Pw0etzatm0LpVKJiIgI2NnZYejQoQgMDESvXr2wcuVKxMbGqvcNDQ1VF3WFZVcoFKhbt67G9+nkyZMlGq70qmMX5cXXjoGBAZo1a6bxeluzZg2aNm0KR0dHWFhY4IcffsD9+/c12vDz84OZmZlGm+np6Xjw4MErj//c0aNHERAQgOrVq8PS0hKDBw9GYmJigUUoTE1Ny31hCrUGfYD39wCmXAaWNAUaVe15JZ3cOmFD4AYWJEREr6HEPSWenp6QSCQF5il4eHgAyH+T81x6ejpcXV0LHcf/4lKshoaGGtskEknZlyqtIJV5ThKJBCqVSuO+3NzcVz7uxWM/L9qKOra9vT0kEgmSk5MLbPP29sa+fftgYGCAatWqwcjISGP7Rx99hMGDB+Prr7/Gxo0b0b9/f4030+Vh48aNmDhxIg4ePIidO3fik08+wZEjR9CqVSuN19rL0tPTIZPJcOnSJchkMo1tFhYWZT7269ixYwemTZuG5cuXo3Xr1rC0tMTSpUtx/vz5UrUjlUqLfV3ExMSgZ8+eGDNmDBYtWgQ7OzucPn0aw4cPh1wu1/geJSUlwdGxAt8g1n4TGHUS2DkYiA2tuOOQTgmKvYsfi/7x1Wnv+7yP6c2nQ8rhi0REr6XEvz3t7e3RpUsXrF69GhkZGcXu26RJE8TFxcHAwACenp4aXw4ODiUOZ2RkBIVCoXGfj48Pzp49q/HmLDg4GJaWlqhRowbq1KkDIyMjBAcHq7fn5ubi4sWLqF+/frHHO3fuXIHbz4fulMc5eXh4wNDQUGPSfWpqKm7fvq2xn6Ojo8an85GRkeX+qbaRkRHq16+PmzdvFrrN09MT7u7uBQoSAOjevTvMzc3Vw5RenLvi7e2NBw8e4MmTJ+r7Xl5kwMfHB2FhYRqvo+DgYEilUo1eI39/f8yePRtnzpxBw4YNsW3bNgD5PUJFLeHr7+8PhUKB+Pj4At+nF3tvXqWoYxflxddOXl4eLl26pH7tBAcHo02bNhg7diz8/f3h6elZaK9NWFiYugfreZsWFhZwc3MDUPB18ezZM0RHR6tvX7p0CUqlEsuXL0erVq1Qt25dPH78uMBxsrOzERUVBX9//xI+G6/JpiYw/DDQ5IOKPQ7pDO+4cNQ2ry46RrkykhphTss5mNliJgsSIqIyKNVv0LVr1yIvLw/NmjXDzp07ER4ejoiICPzyyy+4deuW+pPpzp07o3Xr1ujduzcOHz6MmJgYnDlzBnPmzEFISEiJj+fu7o7z588jJiYGCQkJUCqVGDt2LB48eIAJEybg1q1b2Lt3Lz777DNMmTIFUqkU5ubmGDNmDKZPn46DBw/i5s2bGDFiBDIzMzF8+PBijxccHIyvvvoKt2/fxpo1a7B7925MmjSp3M7J0tISQ4YMwfTp0/H333/jxo0bGD58OKRSqcZwtE6dOmH16tW4cuUKQkJCMHr06AI9MOUhMDAQp0+fLvXjZDIZhg4ditmzZ8PLy0tj6FKXLl1Qp04dDBkyBFevXkVwcDA++eQTAP/23gwaNAgmJiYYMmQIrl+/jr///hsTJkzA4MGD4ezsjOjoaMyePRtnz57FvXv3cPjwYURGRqrf5H/22WfYvn07PvvsM4SHh+PatWtYsmQJgPy5QIMGDcIHH3yAX3/9FdHR0bhw4QK+/PJL/Pnnn688t1cduyhr1qzBb7/9hlu3bmHcuHFITk5WF2teXl4ICQnBoUOHcPv2bXz66aeFrgYnl8sxfPhw3Lx5EwcOHMBnn32G8ePHq+eTdOrUCVu2bMGpU6dw7do1DBkyRKM3yNPTE7m5ufj2229x9+5dbNmyBd99912B45w7dw7GxsYFhitWCANj4K1vgbdWAwYFhwqS/gkyqDrD+mpa1sSW7lvwXr33REchItJ5pSpK6tSpgytXrqBz586YPXs2/Pz80KxZM3z77beYNm0aFixYACD/zeeBAwfQrl07fPjhh6hbty7ee+893Lt3D87OziU+3rRp0yCTyVC/fn04Ojri/v37qF69Og4cOIALFy7Az88Po0ePxvDhw9VvfAFg8eLF6Nu3LwYPHowmTZrgzp07OHToEGxtbYs93tSpUxESEgJ/f38sXLgQK1asQGBgYLme04oVK9C6dWv07NkTnTt3Rtu2beHj46Mxt2P58uVwc3PDm2++iYEDB2LatGnlPjwKAIYPH44DBw4gNTX1tR4rl8vx4Ycfatwvk8nw+++/Iz09Hc2bN8dHH32kXn3r+TmamZnh0KFDSEpKQvPmzfHOO+8gICBAPW/FzMwMt27dQt++fVG3bl2MHDkS48aNw6hRowDkX1Rz9+7d2LdvHxo3boxOnTrhwoUL6gwbN27EBx98gKlTp8Lb2xu9e/fGxYsXUbNmzVee16uOXZTFixdj8eLF8PPzw+nTp7Fv3z51D9qoUaPwn//8B/3790fLli2RmJiIsWPHFmgjICAAXl5eaNeuHfr374+33npLY7nf2bNno3379ujZsyd69OiB3r17qyfjA/lzUlasWIElS5agYcOG2Lp1K7788ssCx9m+fTsGDRpUIa+pIjUZDAw7lN97Qnot6PHtV++kA7q5d8OuXrtQ3774HngiIioZierlQep6yt3dHZMnT670K8hnZGSgevXqWL58+St7cirCu+++iyZNmmD27NmletypU6cQEBCABw8evLIoCw4OxhtvvIE7d+5ovImuCmJiYlC7dm1cuXJF41onpTV06FCkpKQUuD5NeUtISIC3tzdCQkJQu3btCj1WoTKTgF9HAneOvHpfqrL6NnoDt9Pvv3pHLWQiM8HMFjPxTt13REchIqpSynZJcCq1K1eu4NatW2jRogVSU1Px+eefAwDefvttIXmWLl2KP/74o8T75+Tk4OnTp5g3bx7efffdQguS3377DRYWFvDy8sKdO3cwadIktG3btsoVJLooJiYGa9euFVOQAICZHTBwF/C/r4CTSwCVdi5sQRUrSGoFXewvqW1dG8vaL0Nd27qioxARVTmclSfAsmXL4Ofnh86dOyMjIwOnTp0q1QIA5cnd3R0TJkwo8f7bt29HrVq1kJKSgq+++qrQfdLS0jBu3DjUq1cPQ4cORfPmzbF3797yikxl0KxZM/Tv319sCKkU6DALGPIHYOsuNgsJEfSw4AIb2u6tOm9hR48dLEiIiCoIh28RkTjyDODIXODiBgD8VaRP3vNrjxvPol+9o2CmBqaY03IO3vYU05tNRKQv2FNCROIYmQM9lgND9nESvJ4JUlXiQguvqZlzM+zquYsFCRFRJWBPCRFph5x04PAnwKWNopNQJYi1dUOgjRQqLewhsza2xtSmU9Hbs7fGcu1ERFRxWJQQkXaJOg7smwikPhCdhCrYYL+OCH1W8EKiInWv3R0zms+Avam96ChERHqFw7eISLvU6QSMOQM0GQKAn1JXZUFKY9ER1GpY1MB3nb/DknZLWJAQEQnAnhIi0l4PQ4CDs4CHF0UnoQrw1MoFnR1MoBS4NLSBxACDGwzGWL+xMDEwefUDiIioQrAoISLtplIB13YDR+cBzx6JTkPlbFjjAFxMjRRy7EYOjfBZ68/gbect5PhERPQvXjyRiLSbRAL49gPq9QSCV+Z/5WWJTkXlJChPhsruB3MydcLoxqPR16svpBKOYiYi0gbsKSEi3ZL6EDjyGXD9v6KTUDlIMndAgLMV8lR5FX4sG2MbDG84HO/Ve49DtYiItAyLEiLSTQ8uAH/NBB5fFp2EymiUfxecSYmosPbNDMwwuP5gDG0wFBZGFhV2HCIien0sSohId6lUQMQB4H/LWJzosN/qd8bcrNvl3q6R1Aj9vPthhO8I2JnYlXv7RERUfliUEFHVcOdYfnFy/4zoJFRKqaY26FjNHrnK3HJpTyaR4a06b2GM3xi4WriWS5tERFSxWJQQUdUSEwycWpZ/EUbSGeP9A3EyJbxMbRhIDNClVheMaTwGta1rl1MyIiKqDFx9i4iqFve2+V+PLuX3nET8BYCfvWi7wOxcnHzNx9oa2+Kduu+gv3d/OJs7l2suIiKqHOwpIaKq7ckNIHgVcPN3IC9bdBoqQoaxJdq7uSBHkVPix3jbemOQzyB09+gOY5n2XB2eiIhKj0UJEemHrGQgbAdwaRPw9JboNFSIj5t0w9HkG8XuI5PI0NGtIwb5DEIzl2aVlIyIiCoaixIi0j/3z+UXJzd+54UYtchB7/aYLo8udJuVkRX61u2LAd4DOHmdiKgKYlFCRPorKxkI2/lP70nZJllT2WUZmaF9LTdk/VMoyiQytKrWCj1q90DnWp1hamAqOCEREVUUFiVERABw/zxwbRdw608gLVZ0Gr01o0l3PJJJ0d2jO4Lcg2Bvai86EhERVQIWJUREL1KpgIchwK0/gPA/gKS7ohPph2r+QP23kdugDwxt3UWnISKiSsaihIioOE9uAOH78wuUJ9dEp6k6pAZA9WaAT0/A5y3AtpboREREJBCLEiKikkqOyS9O7hzNH+7FSfKl4+gDeHQAPNoDtdoCJlaiExERkZZgUUJE9Dry5MCjECD6FBBzCnh4kddBeZlVjX+LkNrtAUte2JCIiArHooSIqDzkyYHY0Pzlhh+cz//KeCo6VeUxMAGcfACXRvnzQ2q3B+zriE5FREQ6gkUJEVFFSbkPxN/KX274+b9PbwO5GaKTlY2pbX7x4eL7z1cjwKEuIDMQnYyIiHQUixIiosqkUgEp94CnEUB8eP7V5Z9G5C9DnB4PqBSiE+YzcwCsa2h+2dXJL0Bs3ESnIyKiKoZFCRGRtlAqgcxEID0OSH8CpD3J//f5V9oTICcNUMgBRQ6gyAXy/vlXkZN/v0qp2aaBKWBkBhiZA0YW+f8amv37fyNzwML5heLDDbCuDhjyQoVERFR5WJQQEVUlSkV+oaJS5hcfUqnoRERERK/EooSIiIiIiITiR2hERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERERERCsSghIiIiIiKhWJQQEREREZFQLEqIiIiIiEgoFiVERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERERERC/T+jLVh8VAgPvQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "# plotting a histogram\n", - "species_counts = df[\"species\"].value_counts()\n", - "plt.pie(species_counts, labels=species_counts.index, autopct='%1.1f%%')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Pandas interoperability\n", - "\n", - "BigQuery DataFrames can be converted from and to Pandas DataFrame with `to_pandas` and `read_pandas` respectively.\n", - "This could be handy to take advantage of the capabilities of the two systems.\n", - "\n", - "> Note: `to_pandas` converts the BigQuery DataFrame to Pandas DataFrame by bringing all the data in memory, which would be an issue\n", - "for large data, as your machine may not have enough memory to accommodate that." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "We have a dataframe of \n", - "\n", - "\n", - "We have a dataframe of \n", - "\n", - "\n", - "We have a dataframe of \n", - "\n" - ] - } - ], - "source": [ - "def print_type(df):\n", - " print(f\"\\nWe have a dataframe of {type(df)}\\n\")\n", - "\n", - "# The original bigframes dataframe\n", - "cur_df = df\n", - "print_type(cur_df)\n", - "\n", - "# Convert to pandas dataframe\n", - "cur_df = cur_df.to_pandas()\n", - "print_type(cur_df)\n", - "\n", - "# Convert back to bigframes dataframe\n", - "cur_df = bpd.read_pandas(cur_df)\n", - "print_type(cur_df)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Machine Learning with BigQuery DataFrames" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Clean and prepare data\n", - "\n", - "We're are going to start with supervised learning, where a Linear Regression model will learn to predict the body mass (output variable `y`) using input features such as flipper length, sex, species, and more (features `X`)." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "# Drop any rows that has missing (NA) values\n", - "df = df.dropna()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Part of preparing data for a machine learning task is splitting it into subsets for training and testing to ensure that the solution is not overfitting. By default, BQML will automatically manage splitting the data for you. However, BQML also supports manually splitting out your training data.\n", - "\n", - "Performing a manual data split can be done with `bigframes.ml.model_selection.train_test_split` like so:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " df_train shape: (267, 7)\n", - " df_test shape: (67, 7)\n", - "\n" - ] - } - ], - "source": [ - "from bigframes.ml.model_selection import train_test_split\n", - "\n", - "\n", - "# This will split df into test and training sets, with 20% of the rows in the test set,\n", - "# and the rest in the training set\n", - "df_train, df_test = train_test_split(df, test_size=0.2)\n", - "\n", - "# Show the shape of the data after the split\n", - "print(f\"\"\"\n", - " df_train shape: {df_train.shape}\n", - " df_test shape: {df_test.shape}\n", - "\"\"\")" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " X_train shape: (267, 6)\n", - " X_test shape: (67, 7)\n", - " y_train shape: (267, 1)\n", - " y_test shape: (67, 1)\n", - "\n" - ] - } - ], - "source": [ - "# Isolate input features and output variable into DataFrames\n", - "X_train = df_train[[\n", - " 'island',\n", - " 'culmen_length_mm',\n", - " 'culmen_depth_mm',\n", - " 'flipper_length_mm',\n", - " 'sex',\n", - " 'species',\n", - "]]\n", - "y_train = df_train[['body_mass_g']]\n", - "\n", - "X_test = df_test[[\n", - " 'island',\n", - " 'culmen_length_mm',\n", - " 'culmen_depth_mm',\n", - " 'flipper_length_mm',\n", - " 'sex',\n", - " 'species',\n", - " # Include the actual body_mass_g so that we can compare with the predicted\n", - " # without a join.\n", - " 'body_mass_g'\n", - "]]\n", - "y_test = df_test[['body_mass_g']]\n", - "\n", - "# Print the shapes of features and label\n", - "print(f\"\"\"\n", - " X_train shape: {X_train.shape}\n", - " X_test shape: {X_test.shape}\n", - " y_train shape: {y_train.shape}\n", - " y_test shape: {y_test.shape}\n", - "\"\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Define pipeline\n", - "\n", - "This step is subjective to the problem. Although a model can be directly trained on the original data, it is often useful to apply some preprocessing to the original data.\n", - "In this example we want to apply a [`ColumnTransformer`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.compose.ColumnTransformer) in which we apply [`OneHotEncoder`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.preprocessing.OneHotEncoder) to the category features and [`StandardScaler`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.preprocessing.StandardScaler) to the numeric features." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Pipeline(steps=[('preproc',\n", - " ColumnTransformer(transformers=[('onehot', OneHotEncoder(),\n", - " ['island', 'species', 'sex']),\n", - " ('scaler', StandardScaler(),\n", - " ['culmen_depth_mm',\n", - " 'culmen_length_mm',\n", - " 'flipper_length_mm'])])),\n", - " ('linreg', LinearRegression(fit_intercept=False))])" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from bigframes.ml.linear_model import LinearRegression\n", - "from bigframes.ml.pipeline import Pipeline\n", - "from bigframes.ml.compose import ColumnTransformer\n", - "from bigframes.ml.preprocessing import StandardScaler, OneHotEncoder\n", - "\n", - "preprocessing = ColumnTransformer([\n", - " (\"onehot\", OneHotEncoder(), [\"island\", \"species\", \"sex\"]),\n", - " (\"scaler\", StandardScaler(), [\"culmen_depth_mm\", \"culmen_length_mm\", \"flipper_length_mm\"]),\n", - "])\n", - "\n", - "model = LinearRegression(fit_intercept=False)\n", - "\n", - "pipeline = Pipeline([\n", - " ('preproc', preprocessing),\n", - " ('linreg', model)\n", - "])\n", - "\n", - "# View the pipeline\n", - "pipeline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Train and Predict\n", - "\n", - "Supervised learning is when we train a model on input-output pairs, and then ask it to predict the output for new inputs. An example of such a predictor is `bigframes.ml.linear_models.LinearRegression`." - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
predicted_body_mass_gislandculmen_length_mmculmen_depth_mmflipper_length_mmsexspeciesbody_mass_g
03271.548077Biscoe37.918.6172.0FEMALEAdelie Penguin (Pygoscelis adeliae)3150.0
13224.661209Biscoe37.716.0183.0FEMALEAdelie Penguin (Pygoscelis adeliae)3075.0
23395.403541Biscoe34.518.1187.0FEMALEAdelie Penguin (Pygoscelis adeliae)2900.0
33943.436439Biscoe40.118.9188.0MALEAdelie Penguin (Pygoscelis adeliae)4300.0
43986.662895Biscoe41.418.6191.0MALEAdelie Penguin (Pygoscelis adeliae)3700.0
\n", - "
" - ], - "text/plain": [ - " predicted_body_mass_g island culmen_length_mm culmen_depth_mm \\\n", - "0 3271.548077 Biscoe 37.9 18.6 \n", - "1 3224.661209 Biscoe 37.7 16.0 \n", - "2 3395.403541 Biscoe 34.5 18.1 \n", - "3 3943.436439 Biscoe 40.1 18.9 \n", - "4 3986.662895 Biscoe 41.4 18.6 \n", - "\n", - " flipper_length_mm sex species body_mass_g \n", - "0 172.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 3150.0 \n", - "1 183.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 3075.0 \n", - "2 187.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 2900.0 \n", - "3 188.0 MALE Adelie Penguin (Pygoscelis adeliae) 4300.0 \n", - "4 191.0 MALE Adelie Penguin (Pygoscelis adeliae) 3700.0 " - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "ur8xi4C7S06n" + }, + "outputs": [], + "source": [ + "# Copyright 2023 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JAPoU8Sm5E6e" + }, + "source": [ + "# Get started with BigQuery DataFrames\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "\n", + "
\n", + " \n", + " \"Colab Run in Colab\n", + " \n", + " \n", + " \n", + " \"GitHub\n", + " View on GitHub\n", + " \n", + " \n", + " \n", + " \"Vertex\n", + " Open in Vertex AI Workbench\n", + " \n", + " \n", + " \n", + " \"BQ\n", + " Open in BQ Studio\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "24743cf4a1e1" + }, + "source": [ + "**_NOTE_**: This notebook has been tested in the following environment:\n", + "\n", + "* Python version = 3.10" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tvgnzT1CKxrO" + }, + "source": [ + "## Overview\n", + "\n", + "BigQuery DataFrames (also known as BigFrames) provides a Pythonic DataFrame and machine learning (ML) API powered by the BigQuery engine.\n", + "\n", + "* `bigframes.pandas` provides a pandas API for analytics. Many workloads can be\n", + " migrated from pandas to bigframes by just changing a few imports.\n", + "* `bigframes.ml` provides a scikit-learn-like API for ML.\n", + "* `bigframes.ml.llm` provides API for large language models including Gemini.\n", + "\n", + "You can learn more about [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/bigquery-dataframes-introduction) and its [API reference](https://cloud.google.com/python/docs/reference/bigframes/latest).\n", + "\n", + "For any issues or feedback please reach out to bigframes-feedback@google.com." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BF1j6f9HApxa" + }, + "source": [ + "## Before you begin\n", + "\n", + "Complete the tasks in this section to set up your environment." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Yq7zKYWelRQP" + }, + "source": [ + "### Install the python package\n", + "\n", + "You need the [bigframes](https://pypi.org/project/bigframes/) python package to be installed. If you don't have that, uncomment and run the following cell and *restart the kernel*." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "#%pip install --upgrade bigframes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WReHDGG5g0XY" + }, + "source": [ + "### Set your project id and location\n", + "\n", + "Following are some quick references:\n", + "\n", + "* Google Cloud Project: https://cloud.google.com/resource-manager/docs/creating-managing-projects.\n", + "* BigQuery Location: https://cloud.google.com/bigquery/docs/locations." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "oM1iC_MfAts1" + }, + "outputs": [], + "source": [ + "PROJECT_ID = \"bigframes-dev\" # @param {type: \"string\"}\n", + "LOCATION = \"US\" # @param {type: \"string\"}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "960505627ddf" + }, + "source": [ + "### Import library" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "PyQmSRbKA8r-" + }, + "outputs": [], + "source": [ + "import bigframes.pandas as bpd" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "init_aip:mbsdk,all" + }, + "source": [ + "\n", + "### Set BigQuery DataFrames options" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "NPPMuw2PXGeo" + }, + "outputs": [], + "source": [ + "# Note: The project option is not required in all environments.\n", + "# For example, In BigQuery Studio, the project ID is automatically detected,\n", + "# But in Google Colab it must be set by the user.\n", + "bpd.options.bigquery.project = PROJECT_ID\n", + "\n", + "# Note: The location option is not required.\n", + "# It defaults to the location of the first table or query\n", + "# passed to read_gbq(). For APIs where a location can't be\n", + "# auto-detected, the location defaults to the \"US\" location.\n", + "bpd.options.bigquery.location = LOCATION\n", + "\n", + "# Note: BigQuery DataFrames objects are by default fully ordered like Pandas.\n", + "# If ordering is not important for you, you can uncomment the following\n", + "# expression to run BigQuery DataFrames in partial ordering mode.\n", + "bpd.options.bigquery.ordering_mode = \"partial\"\n", + "\n", + "# Note: By default BigQuery DataFrames emits out BigQuery job metadata via a\n", + "# progress bar. But in this notebook let's disable the progress bar to keep the\n", + "# experience less verbose. If you would like the default behavior, please\n", + "# comment out the following expression. \n", + "bpd.options.display.progress_bar = None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pDfrKwMKE_dK" + }, + "source": [ + "If you want to reset the project and/or location of the created DataFrame or Series objects, reset the session by executing `bpd.close_session()`. After that, you can redo the above steps." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9EMAqR37AfLS" + }, + "source": [ + "## Create a BigQuery DataFrames DataFrame\n", + "\n", + "You can create a BigQuery DataFrames DataFrame by reading data from any of the\n", + "following locations:\n", + "\n", + "* A local data file\n", + "* Data stored in a BigQuery table\n", + "* A data file stored in Cloud Storage\n", + "* An in-memory pandas DataFrame\n", + "\n", + "Note that the DataFrame does not copy the data to the local memory, instead\n", + "keeps the underlying data in a BigQuery table during read and analysis. That's\n", + "how it can handle really large size of data (at BigQuery Scale) independent of\n", + "the local memory.\n", + "\n", + "For simplicity, speed and cost efficiency, this tutorial uses the\n", + "[`penguins`](https://pantheon.corp.google.com/bigquery?ws=!1m5!1m4!4m3!1sbigquery-public-data!2sml_datasets!3spenguins)\n", + "table from BigQuery public data, which contains 27 KB data about a set of\n", + "penguins - species, island of residence, culmen length and depth, flipper length\n", + "and sex. There is a version of this data in the Cloud Storage\n", + "[cloud samples data](https://pantheon.corp.google.com/storage/browser/_details/cloud-samples-data/vertex-ai/bigframe/penguins.csv)\n", + "as well." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "Vyex9BQI-BNa" + }, + "outputs": [], + "source": [ + "# This is how you read a BigQuery table\n", + "df = bpd.read_gbq(\"bigquery-public-data.ml_datasets.penguins\")\n", + "\n", + "# This is how you would read a csv from the Cloud Storage\n", + "#df = bpd.read_csv(\"gs://cloud-samples-data/vertex-ai/bigframe/penguins.csv\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use `peek` to preview a few rows (selected arbitrarily) from the dataframes:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
speciesislandculmen_length_mmculmen_depth_mmflipper_length_mmbody_mass_gsex
0Adelie Penguin (Pygoscelis adeliae)Dream36.618.4184.03475.0FEMALE
1Adelie Penguin (Pygoscelis adeliae)Dream39.819.1184.04650.0MALE
2Adelie Penguin (Pygoscelis adeliae)Dream40.918.9184.03900.0MALE
3Chinstrap penguin (Pygoscelis antarctica)Dream46.517.9192.03500.0FEMALE
4Adelie Penguin (Pygoscelis adeliae)Dream37.316.8192.03000.0FEMALE
\n", + "
" ], - "source": [ - "# Learn from the training data how to predict output y\n", - "pipeline.fit(X_train, y_train)\n", - "\n", - "# Predict y for the test data\n", - "y_pred = pipeline.predict(X_test)\n", - "\n", - "# View predictions preview\n", - "y_pred.peek()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Evaluate results\n", - "\n", - "Some models include a convenient `.score(X, y)` method for evaulation with a preset accuracy metric:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
mean_absolute_errormean_squared_errormean_squared_log_errormedian_absolute_errorr2_scoreexplained_variance
0231.91425278873.6004210.005172178.7249850.8905490.890566
\n", - "

1 rows × 6 columns

\n", - "
[1 rows x 6 columns in total]" - ], - "text/plain": [ - " mean_absolute_error mean_squared_error mean_squared_log_error \\\n", - " 231.914252 78873.600421 0.005172 \n", - "\n", - " median_absolute_error r2_score explained_variance \n", - " 178.724985 0.890549 0.890566 \n", - "\n", - "[1 rows x 6 columns]" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " species island culmen_length_mm \\\n", + "0 Adelie Penguin (Pygoscelis adeliae) Dream 36.6 \n", + "1 Adelie Penguin (Pygoscelis adeliae) Dream 39.8 \n", + "2 Adelie Penguin (Pygoscelis adeliae) Dream 40.9 \n", + "3 Chinstrap penguin (Pygoscelis antarctica) Dream 46.5 \n", + "4 Adelie Penguin (Pygoscelis adeliae) Dream 37.3 \n", + "\n", + " culmen_depth_mm flipper_length_mm body_mass_g sex \n", + "0 18.4 184.0 3475.0 FEMALE \n", + "1 19.1 184.0 4650.0 MALE \n", + "2 18.9 184.0 3900.0 MALE \n", + "3 17.9 192.0 3500.0 FEMALE \n", + "4 16.8 192.0 3000.0 FEMALE " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.peek()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gE6CEALjDZZV" + }, + "source": [ + "We just created a DataFrame, `df`, refering to the entirety of the source table data, without downloading it to the local machine." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rwPLjqW2Ajzh" + }, + "source": [ + "## Inspect and manipulate data in BigQuery DataFrames" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bExmYlL_ELtV" + }, + "source": [ + "### Using pandas API\n", + "\n", + "You can use pandas API on the BigQuery DataFrames DataFrame as you normally would in Pandas, but computation happens in the BigQuery query engine instead of your local environment." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "EJIZJaNXFQzh" + }, + "source": [ + "Let's compute the mean of the `body_mass_g` series:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "YKwCW7Nsavap" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "average_body_mass: 4201.754385964914\n" + ] + } + ], + "source": [ + "average_body_mass = df[\"body_mass_g\"].mean()\n", + "print(f\"average_body_mass: {average_body_mass}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DSs1cnca-MOU" + }, + "source": [ + "Calculate the mean `body_mass_g` by `species` using the `groupby` operation:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "4PyKMR61-Mjy" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
body_mass_g
species
Adelie Penguin (Pygoscelis adeliae)3700.662252
Chinstrap penguin (Pygoscelis antarctica)3733.088235
Gentoo penguin (Pygoscelis papua)5076.01626
\n", + "

3 rows × 1 columns

\n", + "
[3 rows x 1 columns in total]" ], - "source": [ - "pipeline.score(X_test.drop(columns=[\"body_mass_g\"]), y_test)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For a more general approach, the library `bigframes.ml.metrics` is provided:" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "np.float64(0.8905492944632485)" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " body_mass_g\n", + "species \n", + "Adelie Penguin (Pygoscelis adeliae) 3700.662252\n", + "Chinstrap penguin (Pygoscelis antarctica) 3733.088235\n", + "Gentoo penguin (Pygoscelis papua) 5076.01626\n", + "\n", + "[3 rows x 1 columns]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df[[\"species\", \"body_mass_g\"]].groupby(by=df[\"species\"]).mean(numeric_only=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6sf9kZ2C9Ixe" + }, + "source": [ + "You can confirm that the calculations were run in BigQuery by clicking \"Open job\" from the previous cells' output. This takes you to the BigQuery console to view the SQL statement and job details." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using SQL functions\n", + "\n", + "The [bigframes.bigquery module](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.bigquery) provides many [BigQuery SQL functions](https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-all) which may not have a pandas-equivalent." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import bigframes.bigquery" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `bigframes.bigquery.struct()` function creates a new STRUCT Series with subfields for each column in a DataFrames." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 {'culmen_length_mm': 36.6, 'culmen_depth_mm': ...\n", + "1 {'culmen_length_mm': 39.8, 'culmen_depth_mm': ...\n", + "2 {'culmen_length_mm': 40.9, 'culmen_depth_mm': ...\n", + "3 {'culmen_length_mm': 46.5, 'culmen_depth_mm': ...\n", + "4 {'culmen_length_mm': 37.3, 'culmen_depth_mm': ...\n", + "dtype: struct[pyarrow]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lengths = bigframes.bigquery.struct(\n", + " df[[\"culmen_length_mm\", \"culmen_depth_mm\", \"flipper_length_mm\"]]\n", + ")\n", + "lengths.peek()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the `bigframes.bigquery.sql_scalar()` function to access arbitrary SQL syntax representing a single column expression." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 18.4\n", + "1 19.1\n", + "2 18.9\n", + "3 17.9\n", + "4 16.8\n", + "dtype: Float64" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "shortest = bigframes.bigquery.sql_scalar(\n", + " \"LEAST({0}, {1}, {2})\",\n", + " columns=[df['culmen_depth_mm'], df['culmen_length_mm'], df['flipper_length_mm']],\n", + ")\n", + "shortest.peek()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### First party visualizations\n", + "\n", + "BigQuery DataFrames provides a number of visualizations via the `plot` method and [accessor](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.operations.plotting.PlotAccessor) on the DataFrame and Series objects." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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yMhI7r33Xi5cuEDjxo3566+/2LlzJ48//jj9+/enc+fOANx33324uLiwdu1avLy8+Pzzz+nXrx9nz56lVq1a6npXrlzJ77//Tnp6OiNHjmT27NmlTsYVMWnSJE6ePMkPP/xA7dq1WbFiBYMGDeLYsWM0atQIgLy8PN5//32WLl2KVqvlkUce4fnnn+e7774D4J133uG7775j8eLFREREMHfuXFauXMltt90GwNy5czl79iwtWrTgjTfeAMDf318NVF5++WU++OAD/P39efLJJ3n88cfZsWPHDTnG1sD17QULeOell5jz2We8NH06Dz30EA0aNGDatGmEhoby+OOPM2nSJNauXVvpY3o9JEgRQghRruzsbObOncv8+fN57LHHAGjYsCE9evSwyyxUhtlsZtGiRXh4eNCsWTNuu+02zpw5w5o1a9BqtTRp0oR33nmHzZs307lzZ7Zv387evXtJTk7GyckJgPfff5+VK1eyfPlyJkyYoK53yZIleHh4APDoo4+yadOmSgcpsbGxLF68mNjYWGrXrg3A888/z7p161i8eDFvvfUWYJmg77PPPqNhw4aAJbCxBhsA8+bNY9q0aQwfPhyA+fPns2bNGvV5Ly8vHB0dcXV1JSgoqFQ7Zs2aRe/evQF46aWXGDJkCAUFBRWaJO1qx1gDNAwI4J233mLjihW09vICs9lyzIYN44ExY9B7ezN16lS6du3Kq6++ysCBAwF47rnnGDNmTKWO599xywQpCxYsYMGCBZhMpupuihBC/G0uDjpOvjGw2rZdUadOncJgMNCvX78q235YWJgaSAAEBgai0+nsJgILDAwkOTkZgCNHjpCTk4Ovr6/devLz8+26HkquNzg4WF1HZRw7dgyTyUTjxo3tHjcYDHZtcHV1VQOUktvLzMwkKSmJTp06qc/rdDrat2+PuTgguJZWrVrZrRsgOTmZ0NDQa742LCwMd3d3FKMRxWjE38sLbcOGmJKTMefkYC4owN/Li6RLl1CMRvV1rTt0QOflBVjeA4CWLVuqzwcGBlJQUEBWVhaenp4V2o+/45YJUiZOnMjEiRPJysrCq/gACiHErUqj0VSqy6W6XG0Kf2tQYVvjUpE7PDs4ONj9rtFoynzMejLPyckhODi4zMyN7fDmq62jMnJyctDpdBw4cACdzj6gc3d3v+r2qvLmkbbrt85UbLs/itls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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "(\n", + " df\n", + " .sort_values(\"body_mass_g\")\n", + " .reset_index(drop=True)\n", + " .plot(title=\"Numeric features\", logy=True)\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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GGzevLncHZKePHky0tLSFF9JSUnv3bZojMO7d0QmqmiKPt8c00NEFdEnHYGJjo7GkydPlO4fk5+fj9OnT2Pp0qU4cuQIcnJykJqaqnQU5vHjx7CwsAAAWFhYIDIyUmm/RVcpvbvN369cevz4MeRy+XsnVdPW1oa2tvZHvQ91dXUYGRkp7sejp6fHycuowigsLERmZiaePHkCIyMjqKurCx2JiKjEfVKBadu2rcoU7UOGDEHNmjURGBgIa2traGpqIiwsTHGH4bi4OCQmJipuGujp6Ym5c+fiyZMnivEUoaGhkMvliinvPT09cfDgQaXXCQ0NVbnx4H9RVJbevakgUUViZGSk+JwTEVU0n1RgDAwMULt2baVllSpVgomJiWK5n58fxo8fj8qVK0Mul2PUqFHw9PREkyZNAADt27eHq6srBg4ciKCgIKSkpGDKlCkYOXKk4gjK8OHDsXTpUkyaNAm+vr44fvw4tm/fjgMHDpTEewbw9oaClpaWMDMz443/qMLR1NTkkRciqtBK/FYCixYtgpqaGnr16oXs7Gx4e3tj+fLlivXq6urYv38/RowYAU9PT1SqVAmDBg3CrFmzFNvY29vjwIEDGDduHBYvXoxq1aphzZo18Pb2Lum4UFdX5w96IiIikZHkrQSIiIjEgPPAlNKtBIiIiIiEwAJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREolPiN3MkIhKrkr7vTHm/5wyRmPEIDBEREYkOj8CQIKR4h1UiIio5PAJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLDAkNERESiwwJDREREosMCQ0RERKLzSQVmxYoVqFOnDuRyOeRyOTw9PXHo0CHF+jdv3mDkyJEwMTGBvr4+evXqhcePHyvtIzExEZ07d4aenh7MzMwwceJE5OXlKW1z8uRJeHh4QFtbG46OjggJCfn375CIiIgqnE8qMNWqVcOCBQsQHR2NqKgotGnTBt26dcO1a9cAAOPGjcO+ffuwY8cOnDp1Co8ePULPnj0Vz8/Pz0fnzp2Rk5OD8PBwbNiwASEhIZg2bZpim4SEBHTu3BmtW7dGTEwMxo4dC39/fxw5cqSE3jIRERGJnaywsLDwv+ygcuXK+N///ocvvvgCpqam2LJlC7744gsAwM2bN+Hi4oKIiAg0adIEhw4dQpcuXfDo0SOYm5sDAFauXInAwEA8ffoUWlpaCAwMxIEDB3D16lXFa/Tt2xepqak4fPjwR+dKT0+HoaEh0tLSIJfL/8tbpFJwo6ZLie/T5eaNEt8nSUtJfy75maT/Soo/Kz/29/e/HgOTn5+PrVu34vXr1/D09ER0dDRyc3Ph5eWl2KZmzZqwsbFBREQEACAiIgJubm6K8gIA3t7eSE9PVxzFiYiIUNpH0TZF+3if7OxspKenK30RERFRxfTJBebKlSvQ19eHtrY2hg8fjt27d8PV1RUpKSnQ0tKCkZGR0vbm5uZISUkBAKSkpCiVl6L1Res+tE16ejqysrLem2v+/PkwNDRUfFlbW3/qWyMiIiKR+OQC4+zsjJiYGJw/fx4jRozAoEGDcP369dLI9kkmT56MtLQ0xVdSUpLQkYiIiKiUaHzqE7S0tODo6AgAqF+/Pi5cuIDFixfjyy+/RE5ODlJTU5WOwjx+/BgWFhYAAAsLC0RGRirtr+gqpXe3+fuVS48fP4ZcLoeuru57c2lra0NbW/tT3w4RERGJ0H+eB6agoADZ2dmoX78+NDU1ERYWplgXFxeHxMREeHp6AgA8PT1x5coVPHnyRLFNaGgo5HI5XF1dFdu8u4+ibYr2QURERPRJR2AmT56Mjh07wsbGBq9evcKWLVtw8uRJHDlyBIaGhvDz88P48eNRuXJlyOVyjBo1Cp6enmjSpAkAoH379nB1dcXAgQMRFBSElJQUTJkyBSNHjlQcPRk+fDiWLl2KSZMmwdfXF8ePH8f27dtx4MCBkn/3REREJEqfVGCePHkCHx8fJCcnw9DQEHXq1MGRI0fQrl07AMCiRYugpqaGXr16ITs7G97e3li+fLni+erq6ti/fz9GjBgBT09PVKpUCYMGDcKsWbMU29jb2+PAgQMYN24cFi9ejGrVqmHNmjXw9vYuobdMREREYvef54EprzgPTPkmxbkNqPzjPDBU3kjxZ2WpzwNDREREJBQWGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEp1PKjDz589Hw4YNYWBgADMzM3Tv3h1xcXFK27x58wYjR46EiYkJ9PX10atXLzx+/Fhpm8TERHTu3Bl6enowMzPDxIkTkZeXp7TNyZMn4eHhAW1tbTg6OiIkJOTfvUMiIiKqcD6pwJw6dQojR47EuXPnEBoaitzcXLRv3x6vX79WbDNu3Djs27cPO3bswKlTp/Do0SP07NlTsT4/Px+dO3dGTk4OwsPDsWHDBoSEhGDatGmKbRISEtC5c2e0bt0aMTExGDt2LPz9/XHkyJESeMtEREQkdrLCwsLCf/vkp0+fwszMDKdOnULLli2RlpYGU1NTbNmyBV988QUA4ObNm3BxcUFERASaNGmCQ4cOoUuXLnj06BHMzc0BACtXrkRgYCCePn0KLS0tBAYG4sCBA7h69aritfr27YvU1FQcPnz4o7Klp6fD0NAQaWlpkMvl//YtUim5UdOlxPfpcvNGie+TpKWkP5f8TNJ/JcWflR/7+/s/jYFJS0sDAFSuXBkAEB0djdzcXHh5eSm2qVmzJmxsbBAREQEAiIiIgJubm6K8AIC3tzfS09Nx7do1xTbv7qNom6J9FCc7Oxvp6elKX0RERFQx/esCU1BQgLFjx6JZs2aoXbs2ACAlJQVaWlowMjJS2tbc3BwpKSmKbd4tL0Xri9Z9aJv09HRkZWUVm2f+/PkwNDRUfFlbW//bt0ZERETl3L8uMCNHjsTVq1exdevWkszzr02ePBlpaWmKr6SkJKEjERERUSnR+DdPCggIwP79+3H69GlUq1ZNsdzCwgI5OTlITU1VOgrz+PFjWFhYKLaJjIxU2l/RVUrvbvP3K5ceP34MuVwOXV3dYjNpa2tDW1v737wdIiIiEplPOgJTWFiIgIAA7N69G8ePH4e9vb3S+vr160NTUxNhYWGKZXFxcUhMTISnpycAwNPTE1euXMGTJ08U24SGhkIul8PV1VWxzbv7KNqmaB9EREQkbZ90BGbkyJHYsmUL/vzzTxgYGCjGrBgaGkJXVxeGhobw8/PD+PHjUblyZcjlcowaNQqenp5o0qQJAKB9+/ZwdXXFwIEDERQUhJSUFEyZMgUjR45UHEEZPnw4li5dikmTJsHX1xfHjx/H9u3bceDAgRJ++0RERCRGn3QEZsWKFUhLS0OrVq1gaWmp+Nq2bZtim0WLFqFLly7o1asXWrZsCQsLC+zatUuxXl1dHfv374e6ujo8PT3x1VdfwcfHB7NmzVJsY29vjwMHDiA0NBR169bFwoULsWbNGnh7e5fAWyYiIiKx+0/zwJRnnAemfJPi3AZU/nEeGCpvpPizskzmgSEiIiISAgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERic4nF5jTp0+ja9eusLKygkwmw549e5TWFxYWYtq0abC0tISuri68vLxw+/ZtpW1evHiBAQMGQC6Xw8jICH5+fsjIyFDa5vLly2jRogV0dHRgbW2NoKCgT393REREVCF9coF5/fo16tati2XLlhW7PigoCEuWLMHKlStx/vx5VKpUCd7e3njz5o1imwEDBuDatWsIDQ3F/v37cfr0aQwbNkyxPj09He3bt4etrS2io6Pxv//9DzNmzMCvv/76L94iERERVTQan/qEjh07omPHjsWuKywsRHBwMKZMmYJu3boBADZu3Ahzc3Ps2bMHffv2xY0bN3D48GFcuHABDRo0AAD88ssv6NSpE3766SdYWVlh8+bNyMnJwbp166ClpYVatWohJiYGP//8s1LRISIiImkq0TEwCQkJSElJgZeXl2KZoaEhGjdujIiICABAREQEjIyMFOUFALy8vKCmpobz588rtmnZsiW0tLQU23h7eyMuLg4vX74s9rWzs7ORnp6u9EVEREQVU4kWmJSUFACAubm50nJzc3PFupSUFJiZmSmt19DQQOXKlZW2KW4f777G382fPx+GhoaKL2tr6//+hoiIiKhcqjBXIU2ePBlpaWmKr6SkJKEjERERUSkp0QJjYWEBAHj8+LHS8sePHyvWWVhY4MmTJ0rr8/Ly8OLFC6VtitvHu6/xd9ra2pDL5UpfREREVDGVaIGxt7eHhYUFwsLCFMvS09Nx/vx5eHp6AgA8PT2RmpqK6OhoxTbHjx9HQUEBGjdurNjm9OnTyM3NVWwTGhoKZ2dnGBsbl2RkIiIiEqFPLjAZGRmIiYlBTEwMgLcDd2NiYpCYmAiZTIaxY8dizpw52Lt3L65cuQIfHx9YWVmhe/fuAAAXFxd06NABQ4cORWRkJM6ePYuAgAD07dsXVlZWAID+/ftDS0sLfn5+uHbtGrZt24bFixdj/PjxJfbGiYiISLw++TLqqKgotG7dWvG4qFQMGjQIISEhmDRpEl6/fo1hw4YhNTUVzZs3x+HDh6Gjo6N4zubNmxEQEIC2bdtCTU0NvXr1wpIlSxTrDQ0NcfToUYwcORL169dHlSpVMG3aNF5CTURERAAAWWFhYaHQIUpDeno6DA0NkZaWxvEw5dCNmi4lvk+XmzdKfJ8kLSX9ueRnkv4rKf6s/Njf3xXmKiQiIiKSDhYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdFhgiIiISHRYYIiIiEh0NoQMQERFVBG4b3Ep8n9tLfI8VBwsMEYkSf1kQSRsLDH2Ukv5lwV8URET0X3AMDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJDgsMERERiQ4LDBEREYkOCwwRERGJTrkuMMuWLYOdnR10dHTQuHFjREZGCh2JiIiIyoFyOxPvtm3bMH78eKxcuRKNGzdGcHAwvL29ERcXBzMzM6HjlRi77w6U+D7vLehc4vskaSnpzyU/k/Rf8Wcl/V25PQLz888/Y+jQoRgyZAhcXV2xcuVK6OnpYd26dUJHIyIiIoGVyyMwOTk5iI6OxuTJkxXL1NTU4OXlhYiIiGKfk52djezsbMXjtLQ0AEB6enrphv2PCrIzS3yf6ZPlJb7PfNtqJbq/jPz8Et0fUP7/W4tJSX8uxfCZBEr+c8nPZMkRw89KMXwmgfL/uSzKV1hY+MHtymWBefbsGfLz82Fubq603NzcHDdv3iz2OfPnz8fMmTNVlltbW5dKxvLMsFT2eqNE99aoRPf2/xmWzjun/04Mn0mgFD6X/EyWayX/X0cEn0lANJ/LV69ewfADWctlgfk3Jk+ejPHjxyseFxQU4MWLFzAxMYFMJhMwmfilp6fD2toaSUlJkMtL/i9pok/FzySVN/xMlpzCwkK8evUKVlZWH9yuXBaYKlWqQF1dHY8fP1Za/vjxY1hYWBT7HG1tbWhraystMzIyKq2IkiSXy/k/JpUr/ExSecPPZMn40JGXIuVyEK+Wlhbq16+PsLAwxbKCggKEhYXB09NTwGRERERUHpTLIzAAMH78eAwaNAgNGjRAo0aNEBwcjNevX2PIkCFCRyMiIiKBldsC8+WXX+Lp06eYNm0aUlJSUK9ePRw+fFhlYC+VPm1tbUyfPl3lFB2RUPiZpPKGn8myJyv8p+uUiIiIiMqZcjkGhoiIiOhDWGCIiIhIdFhgiIiISHRYYIiIiEh0WGCIiIhIdMrtZdRUPmRnZ/OyQBJcQkIC/vrrL9y/fx+ZmZkwNTWFu7s7PD09oaOjI3Q8kiB+JoXHAkNKDh06hK1bt+Kvv/5CUlISCgoKUKlSJbi7u6N9+/YYMmTIP96fgqikbN68GYsXL0ZUVBTMzc1hZWUFXV1dvHjxAvHx8dDR0cGAAQMQGBgIW1tboeOSBPAzWX5wHhgCAOzevRuBgYF49eoVOnXqhEaNGin9j3n16lX89ddfiIiIwODBgzF79myYmpoKHZsqMHd3d2hpaWHQoEHo2rWryp3ls7OzERERga1bt+KPP/7A8uXL0bt3b4HSkhTwM1m+sMAQAMDT0xNTpkxBx44doab2/qFRDx8+xC+//AJzc3OMGzeuDBOS1Bw5cgTe3t4fte3z589x79491K9fv5RTkZTxM1m+sMAQERGR6HAMDL1XTk4OEhISUL16dWho8KNC5cObN2+Qk5OjtEwulwuUhoifSaHwMmpSkZmZCT8/P+jp6aFWrVpITEwEAIwaNQoLFiwQOB1JUWZmJgICAmBmZoZKlSrB2NhY6YuorPEzKTwWGFIxefJkxMbG4uTJk0qXA3p5eWHbtm0CJiOpmjhxIo4fP44VK1ZAW1sba9aswcyZM2FlZYWNGzcKHY8kiJ9J4XEMDKmwtbXFtm3b0KRJExgYGCA2NhYODg64c+cOPDw8kJ6eLnREkhgbGxts3LgRrVq1glwux8WLF+Ho6IhNmzbh999/x8GDB4WOSBLDz6TweASGVDx9+hRmZmYqy1+/fg2ZTCZAIpK6Fy9ewMHBAcDbsQUvXrwAADRv3hynT58WMhpJFD+TwmOBIRUNGjTAgQMHFI+LSsuaNWvg6ekpVCySMAcHByQkJAAAatasie3btwMA9u3bByMjIwGTkVTxMyk8XlpCKubNm4eOHTvi+vXryMvLw+LFi3H9+nWEh4fj1KlTQscjCRoyZAhiY2Px2Wef4bvvvkPXrl2xdOlS5Obm4ueffxY6HkkQP5PC4xgYKlZ8fDwWLFiA2NhYZGRkwMPDA4GBgXBzcxM6GhHu37+P6OhoODo6ok6dOkLHIeJnUgAsMERERCQ6PIVEH8QJmqg8GD16NBwdHTF69Gil5UuXLsWdO3cQHBwsTDCSrFmzZn1w/bRp08ooiXTxCAypyMzMxKRJk7B9+3Y8f/5cZX1+fr4AqUjKqlatir1796rcV+bixYv4/PPP8eDBA4GSkVS5u7srPc7NzUVCQgI0NDRQvXp1XLx4UaBk0sEjMKRi4sSJOHHiBFasWIGBAwdi2bJlePjwIVatWsWZeEkQz58/h6GhocpyuVyOZ8+eCZCIpO7SpUsqy9LT0zF48GD06NFDgETSw8uoScW+ffuwfPly9OrVCxoaGmjRogWmTJmCefPmYfPmzULHIwlydHTE4cOHVZYfOnRIMRcHkdDkcjlmzpyJqVOnCh1FEngEhlR8aIKmESNGCBmNJGr8+PEICAjA06dP0aZNGwBAWFgYFi5cyPEvVK6kpaUhLS1N6BiSwAJDKoomaLKxsVFM0NSoUSNO0ESC8fX1RXZ2NubOnYvZs2cDAOzs7LBixQr4+PgInI6kaMmSJUqPCwsLkZycjE2bNqFjx44CpZIWDuIlFYsWLYK6ujpGjx6NY8eOoWvXrigsLFRM0DRmzBihI5KEPX36FLq6utDX1xc6CkmYvb290mM1NTWYmpqiTZs2mDx5MgwMDARKJh0sMPSPOEETERGVNywwRFQueXh4ICwsDMbGxnB3d//gjUR5ySoJKSkpCQBgbW0tcBJp4RgYAvD2fO6wYcOgo6Ojcm737/4+mRhRaejWrRu0tbUV/+ad0Kk8ycvLw8yZM7FkyRJkZGQAAPT19TFq1ChMnz4dmpqaAies+HgEhgC8PZ8bFRUFExMTlXO775LJZLh7924ZJiMiKn9GjBiBXbt2YdasWfD09AQAREREYMaMGejevTtWrFghcMKKjwWGiMo9BwcHXLhwASYmJkrLU1NT4eHhwVJNZc7Q0BBbt25VueLo4MGD6NevHy+lLgOcyI6Iyr179+4VewuL7Oxs3kaABKGtrQ07OzuV5fb29tDS0ir7QBLEMTAE4O1EYR/r559/LsUkRP9n7969in8fOXJE6XYC+fn5CAsL++ApT6LSEhAQgNmzZ2P9+vWKsVpFcxUFBAQInE4aeAqJAACtW7f+qO1kMhmOHz9eymmI3lJTe3uQWCaT4e8/qjQ1NWFnZ4eFCxeiS5cuQsQjCevRowfCwsKgra2NunXrAgBiY2ORk5ODtm3bKm27a9cuISJWeDwCQwCAEydOCB2BSEVBQQGAt4flL1y4gCpVqgiciOgtIyMj9OrVS2kZL6MuWzwCQ+91584dxMfHo2XLltDV1UVhYSEvZSUionKBg3hJxfPnz9G2bVvUqFEDnTp1QnJyMgDAz88P3377rcDpSIpGjx5d7PxES5cuxdixY8s+EBEJjgWGVIwbNw6amppITEyEnp6eYvmXX36Jw4cPC5iMpOqPP/5As2bNVJY3bdoUO3fuFCAREbBz50706dMHTZo0gYeHh9IXlT4WGFJx9OhR/Pjjj6hWrZrScicnJ9y/f1+gVCRlz58/V7oCqYhcLsezZ88ESERSt2TJEgwZMgTm5ua4dOkSGjVqBBMTE9y9e5d3oy4jLDCk4vXr10pHXoq8ePFCcbkgUVlydHQs9ujfoUOH4ODgIEAikrrly5fj119/xS+//AItLS1MmjQJoaGhGD16NCexKyO8ColUtGjRAhs3bsTs2bMBvL2EtaCgAEFBQR99uTVRSRo/fjwCAgLw9OlTtGnTBgAQFhaGhQsXIjg4WNhwJEmJiYlo2rQpAEBXVxevXr0CAAwcOBBNmjTB0qVLhYwnCSwwpCIoKAht27ZFVFQUcnJyMGnSJFy7dg0vXrzA2bNnhY5HEuTr66uYJKyoWNvZ2WHFihXw8fEROB1JkYWFBV68eAFbW1vY2Njg3LlzqFu3LhISElTmLKLSwVNIpKJ27dq4desWmjdvjm7duuH169fo2bMnLl26hOrVqwsdjyQmLy8PGzduRM+ePfHgwQM8fvwY6enpuHv3LssLCaZNmzaKmaKHDBmCcePGoV27dvjyyy/Ro0cPgdNJA+eBIaJyT09PDzdu3ICtra3QUYgAvJ1ksaCgABoab09kbN26FeHh4XBycsLXX3/N+yGVARYYAgBcvnz5o7etU6dOKSYhUtWqVSuMHTsW3bt3FzoKEZUTHANDAIB69eop7jfz7my7Rf323WXF3RWYqDR98803+Pbbb/HgwQPUr18flSpVUlrPUk1CePnyJdauXYsbN24AAFxdXTFkyBBUrlxZ4GTSwCMwBABK87tcunQJEyZMwMSJE+Hp6QkAiIiIwMKFCxEUFMS/gqnMFd3U8V3vFm6Waiprp0+fxueffw65XI4GDRoAAKKjo5Gamop9+/ahZcuWAies+FhgSEWjRo0wY8YMdOrUSWn5wYMHMXXqVERHRwuUjKTqnyZQ5NgYKmtubm7w9PTEihUroK6uDuDt0elvvvkG4eHhuHLlisAJKz4WGFKhq6uLixcvwsXFRWn5jRs34OHhgaysLIGSERGVD7q6uoiJiYGzs7PS8ri4ONSrV48/J8sAx8CQChcXF8yfPx9r1qxRjKTPycnB/PnzVUoNUVm6fv06EhMTkZOTo7T8888/FygRSZWHhwdu3LihUmBu3LiBunXrCpRKWlhgSMXKlSvRtWtXVKtWTTE48vLly5DJZNi3b5/A6UiK7t69ix49euDKlSuKsS/A/w0u5xgYKmujR4/GmDFjcOfOHTRp0gQAcO7cOSxbtgwLFixQurKTg8xLB08hUbFev36NzZs34+bNmwDeHpXp37+/ytUfRGWha9euUFdXx5o1a2Bvb4/IyEg8f/4c3377LX766Se0aNFC6IgkMcUNLH8XB5mXPhYYIir3qlSpguPHj6NOnTowNDREZGQknJ2dcfz4cXz77be4dOmS0BFJYv5pYPm7OMi8dPAUEr0XxxtQeZGfnw8DAwMAb8vMo0eP4OzsDFtbW8TFxQmcjqSIpUR4LDCkguMNqLypXbs2YmNjYW9vj8aNGyMoKAhaWlr49ddf4eDgIHQ8IhIAb+ZIKsaMGQN7e3s8efIEenp6uHbtGk6fPo0GDRrg5MmTQscjCZoyZQoKCgoAALNmzUJCQgJatGiBgwcPYvHixQKnIyIhcAwMqeB4AxKDFy9ewNjYWOk2F0QkHTwCQyqKG28AgOMNSDC+vr549eqV0rLKlSsjMzMTvr6+AqUiIiGxwJCKovEGABTjDc6ePYtZs2ZxvAEJYsOGDcXObJqVlYWNGzcKkIikLikpCQ8ePFA8joyMxNixY/Hrr78KmEpaWGBIxYfGGyxZskTgdCQl6enpSEtLQ2FhIV69eoX09HTF18uXL3Hw4EGYmZkJHZMkqH///jhx4gQAICUlBe3atUNkZCR++OEHzJo1S+B00sAxMPRRON6AhKCmpvbBz5xMJsPMmTPxww8/lGEqIsDY2Bjnzp2Ds7MzlixZgm3btuHs2bM4evQohg8fjrt37wodscLjZdT0USpXrix0BJKgEydOoLCwEG3atMEff/yh9DnU0tKCra0trKysBExIUpWbmwttbW0AwLFjxxTzY9WsWRPJyclCRpMMFhgiKrc+++wzAEBCQgKsra3/cfp2orJSq1YtrFy5Ep07d0ZoaChmz54NAHj06BFMTEwETicNPIVERKKQmpqKyMhIPHnyRDFGq4iPj49AqUiqTp48iR49eiA9PR2DBg3CunXrAADff/89bt68iV27dgmcsOJjgSGicm/fvn0YMGAAMjIyIJfLlcbFyGQyvHjxQsB0JFX5+flIT0+HsbGxYtm9e/egp6fHweVlgAWGiMq9GjVqoFOnTpg3bx709PSEjkNE5QALDKnYsGEDqlSpgs6dOwMAJk2ahF9//RWurq74/fffeRMzKnOVKlXClStXOA8RCcrDwwNhYWEwNjaGu7v7B6+Qu3jxYhkmkyYO4iUV8+bNw4oVKwAAERERWLZsGRYtWoT9+/dj3LhxPLdLZc7b2xtRUVEsMCSobt26Ka486t69u7BhiEdgSJWenh5u3rwJGxsbBAYGIjk5GRs3bsS1a9fQqlUrPH36VOiIJDFr167FrFmzMGTIELi5uUFTU1NpfdElrEQkHTwCQyr09fXx/Plz2NjY4OjRoxg/fjwAQEdHp9jp3IlK29ChQwGg2BlOZTIZ8vPzyzoSEQmMBYZUtGvXDv7+/nB3d8etW7fQqVMnAMC1a9dgZ2cnbDiSpL9fNk0khE+ZjZxXxpU+FhhSsWzZMkyZMgVJSUn4448/FJMyRUdHo1+/fgKnIyISRnBwsNAR6B0cA0NEovD69WucOnUKiYmJyMnJUVo3evRogVIRkVBYYAgAcPnyZdSuXRtqamq4fPnyB7etU6dOGaUieuvSpUvo1KkTMjMz8fr1a1SuXBnPnj1TTBjGG+eREOLj47F+/XrEx8dj8eLFMDMzw6FDh2BjY4NatWoJHa/CY4EhAG/v+puSkgIzMzPFHYDf/WgUPeaASRJCq1atUKNGDaxcuRKGhoaIjY2FpqYmvvrqK4wZMwY9e/YUOiJJzKlTp9CxY0c0a9YMp0+fxo0bN+Dg4IAFCxYgKioKO3fuFDpihccCQwCA+/fvw8bGBjKZDPfv3//gtpzIjsqakZERzp8/D2dnZxgZGSEiIgIuLi44f/48Bg0ahJs3bwodkSTG09MTvXv3xvjx42FgYIDY2Fg4ODggMjISPXv2xIMHD4SOWOFxEC8BUC4lLChU3mhqairuRG1mZobExES4uLjA0NAQSUlJAqcjKbpy5Qq2bNmistzMzAzPnj0TIJH0sMAQAGDv3r0fvS0nDaOy5u7ujgsXLsDJyQmfffYZpk2bhmfPnmHTpk2oXbu20PFIgoyMjJCcnAx7e3ul5ZcuXULVqlUFSiUtPIVEAKD46/afcAwMCSEqKgqvXr1C69at8eTJE/j4+CA8PBxOTk5Yt24d6tatK3REkpgJEybg/Pnz2LFjB2rUqIGLFy/i8ePH8PHxgY+PD6ZPny50xAqPBYaIiOgT5eTkYOTIkQgJCUF+fj40NDSQn5+P/v37IyQkBOrq6kJHrPBYYOiD3rx5Ax0dHaFjEBGVS0lJSbhy5QoyMjLg7u4OJycnoSNJBgsMqcjPz8e8efOwcuVKPH78GLdu3YKDgwOmTp0KOzs7+Pn5CR2RiIgk7uMGPpCkzJ07FyEhIQgKCoKWlpZiee3atbFmzRoBkxERlQ+9evXCjz/+qLI8KCgIvXv3FiCR9LDAkIqNGzfi119/xYABA5TO49atW5fzbRARATh9+rTiRrfv6tixI06fPi1AIulhgSEVDx8+hKOjo8rygoIC5ObmCpCISFVqaqrQEUjCMjIylI5QF9HU1ER6eroAiaSHBYZUuLq64q+//lJZvnPnTri7uwuQiKTuxx9/xLZt2xSP+/TpAxMTE1StWhWxsbECJiOpcnNzU/pMFtm6dStcXV0FSCQ9nMiOVEybNg2DBg3Cw4cPUVBQgF27diEuLg4bN27E/v37hY5HErRy5Ups3rwZABAaGorQ0FAcOnQI27dvx8SJE3H06FGBE5LUTJ06FT179kR8fDzatGkDAAgLC8Pvv/+OHTt2CJxOGngVEhXrr7/+wqxZsxAbG4uMjAx4eHhg2rRpaN++vdDRSIJ0dXVx69YtWFtbY8yYMXjz5g1WrVqFW7duoXHjxnj58qXQEUmCDhw4gHnz5iEmJga6urqoU6cOpk+fjs8++0zoaJLAAkNE5Z6VlRV27tyJpk2bwtnZGXPmzEHv3r0RFxeHhg0bcswBkQTxFBKpuHDhAgoKCtC4cWOl5efPn4e6ujoaNGggUDKSqp49e6J///5wcnLC8+fP0bFjRwBv7ztT3IBzotKWlJQEmUyGatWqAQAiIyOxZcsWuLq6YtiwYQKnkwYO4iUVI0eOLPYOvw8fPsTIkSMFSERSt2jRIgQEBMDV1RWhoaHQ19cHACQnJ+Obb74ROB1JUf/+/XHixAkAQEpKCry8vBAZGYkffvgBs2bNEjidNPAUEqnQ19fH5cuX4eDgoLQ8ISEBderUwatXrwRKRkRUPhgbG+PcuXNwdnbGkiVLsG3bNpw9exZHjx7F8OHDcffuXaEjVng8hUQqtLW18fjxY5UCk5ycDA0NfmSobOzduxcdO3aEpqYm9u7d+8FtP//88zJKRfRWbm4utLW1AQDHjh1TfAZr1qyJ5ORkIaNJBo/AkIp+/fohOTkZf/75JwwNDQG8nTSse/fuMDMzw/bt2wVOSFKgpqaGlJQUmJmZQU3t/We7ZTIZ8vPzyzAZEdC4cWO0bt0anTt3Rvv27XHu3DnUrVsX586dwxdffIEHDx4IHbHCY4EhFQ8fPkTLli3x/PlzxcR1MTExMDc3R2hoKKytrQVOSEQkrJMnT6JHjx5IT0/HoEGDsG7dOgDA999/j5s3b2LXrl0CJ6z4WGCoWK9fv8bmzZsRGxurmN+gX79+0NTUFDoaEVG5kJ+fj/T0dBgbGyuW3bt3D3p6ejAzMxMwmTSwwBBRubRkyZKP3nb06NGlmITo/Z4+fYq4uDgAgLOzM0xNTQVOJB0sMKRiw4YNqFKlCjp37gwAmDRpEn799Ve4urri999/h62trcAJSQrs7e0/ajuZTMYrPqjMvX79GqNGjcLGjRtRUFAAAFBXV4ePjw9++eUX6OnpCZyw4mOBIRXOzs5YsWIF2rRpg4iICLRt2xbBwcHYv38/NDQ0eG6XiCTv66+/xrFjx7B06VI0a9YMAHDmzBmMHj0a7dq1w4oVKwROWPGxwJAKPT093Lx5EzY2NggMDERycjI2btyIa9euoVWrVnj69KnQEUmicnJykJCQgOrVq/OSfhJUlSpVsHPnTrRq1Upp+YkTJ9CnTx/+nCwDnImXVOjr6+P58+cAgKNHj6Jdu3YAAB0dHWRlZQkZjSQqMzMTfn5+0NPTQ61atZCYmAgAGDVqFBYsWCBwOpKizMxMmJubqyw3MzNDZmamAImkhwWGVLRr1w7+/v7w9/fHrVu30KlTJwDAtWvXYGdnJ2w4kqTJkycjNjYWJ0+ehI6OjmK5l5cXtm3bJmAykipPT09Mnz4db968USzLysrCzJkz4enpKWAy6eAxWFKxbNkyTJkyBUlJSfjjjz9gYmICAIiOjka/fv0ETkdStGfPHmzbtg1NmjSBTCZTLK9Vqxbi4+MFTEZStXjxYnh7e6NatWqoW7cuACA2NhY6Ojo4cuSIwOmkgWNgiKjc09PTw9WrV+Hg4AADAwPExsbCwcEBsbGxaNmyJdLS0oSOSBKUmZmJzZs34+bNmwAAFxcXDBgwALq6ugInkwYegaFipaamYu3atbhx4waAt3/p+vr6Km4tQFSWGjRogAMHDmDUqFEAoDgKs2bNGh6uJ8Ho6elh6NChQseQLB6BIRVRUVHw9vaGrq4uGjVqBAC4cOECsrKycPToUXh4eAickKTmzJkz6NixI7766iuEhITg66+/xvXr1xEeHo5Tp06hfv36QkckiXnfDUZlMhl0dHTg6Oj40XMZ0b/DAkMqWrRoAUdHR6xevVpxqWpeXh78/f1x9+5dnD59WuCEJEXx8fFYsGABYmNjkZGRAQ8PDwQGBsLNzU3oaCRBampqkMlk+Puv0KJlMpkMzZs3x549e5RuNUAlhwWGVOjq6uLSpUuoWbOm0vLr16+jQYMGvESQiCQvLCwMP/zwA+bOnas4Uh0ZGYmpU6diypQpMDQ0xNdff43GjRtj7dq1AqetmDgGhlTI5XIkJiaqFJikpCQYGBgIlIqk7ODBg1BXV4e3t7fS8iNHjqCgoAAdO3YUKBlJ1ZgxY/Drr7+iadOmimVt27aFjo4Ohg0bhmvXriE4OBi+vr4CpqzYOA8Mqfjyyy/h5+eHbdu2ISkpCUlJSdi6dSv8/f15GTUJ4rvvvkN+fr7K8sLCQnz33XcCJCKpi4+Ph1wuV1kul8sV9+ZycnLCs2fPyjqaZPAIDKn46aefIJPJ4OPjg7y8PACApqYmRowYwVlPSRC3b9+Gq6uryvKaNWvizp07AiQiqatfvz4mTpyIjRs3Ku5A/fTpU0yaNAkNGzYE8PZza21tLWTMCo0FhlRoaWlh8eLFmD9/vmKSsOrVq/PuqiQYQ0ND3L17V2Um6Dt37qBSpUrChCJJW7t2Lbp164Zq1aopSkpSUhIcHBzw559/AgAyMjIwZcoUIWNWaBzES0Tl3tdff42IiAjs3r0b1atXB/C2vPTq1QsNGzbEmjVrBE5IUlRQUICjR4/i1q1bAABnZ2e0a9cOamocnVEWWGBIRY8ePZSmay/y7vwG/fv3h7OzswDpSIrS0tLQoUMHREVFoVq1agCABw8eoEWLFti1axeMjIyEDUiSc/fuXTg4OAgdQ9JYYEjF4MGDsWfPHhgZGSkmCLt48SJSU1PRvn17xMbG4t69ewgLC0OzZs0ETktSUVhYiNDQUMTGxkJXVxd16tRBy5YthY5FEqWmpobPPvsMfn5++OKLL5RuMkplgwWGVHz33XdIT0/H0qVLFYdCCwoKMGbMGBgYGGDu3LkYPnw4rl27hjNnzgiclqQqNTWVR15IMDExMVi/fj1+//135OTk4Msvv4Svry8aN24sdDTJYIEhFaampjh79ixq1KihtPzWrVto2rQpnj17hitXrqBFixZITU0VJiRJyo8//gg7Ozt8+eWXAIA+ffrgjz/+gIWFBQ4ePKi4GzBRWcvLy8PevXsREhKCw4cPo0aNGvD19cXAgQMVVydR6eBII1KRl5enuLvqu27evKmYi0NHR6fYcTJEpWHlypWKKz1CQ0MRGhqKQ4cOoWPHjpg4caLA6UjKNDQ00LNnT+zYsQM//vgj7ty5gwkTJsDa2ho+Pj5ITk4WOmKFxcuoScXAgQPh5+eH77//XjGfwYULFzBv3jz4+PgAAE6dOoVatWoJGZMkJCUlRVFg9u/fjz59+qB9+/aws7PjIXsSVFRUFNatW4etW7eiUqVKmDBhAvz8/PDgwQPMnDkT3bp1Q2RkpNAxKyQWGFKxaNEimJubIygoCI8fPwYAmJubY9y4cQgMDAQAtG/fHh06dBAyJkmIsbExkpKSYG1tjcOHD2POnDkA3g7sLW6GXqLS9vPPP2P9+vWIi4tDp06dsHHjRnTq1EkxbtDe3h4hISEqcxdRyeEYGPqg9PR0ACh2ymyishIQEID9+/fDyckJly5dwr1796Cvr4+tW7ciKCgIFy9eFDoiSYyTkxN8fX0xePBgWFpaFrtNTk4Ofv/9dwwaNKiM00kDCwypmD59Onx9fWFrayt0FCIAQG5uLhYvXoykpCQMHjwY7u7uAN4eLTQwMIC/v7/ACYmorLHAkIp69erh6tWrijkOevXqBW1tbaFjEREJ7vXr15gwYQL27t2LnJwctG3bFr/88guvOBIACwwV69KlS4o5DvLy8tC3b1/4+voqBvUSlbX4+HgEBwfjxo0bAABXV1eMHTuWs6FSmRo/fjx+/fVXDBgwADo6Ovj999/RrFkz7N69W+hoksMCQx+Um5uLffv2Yf369Thy5Ahq1qwJPz8/DB48GIaGhkLHI4k4cuQIPv/8c9SrV08x+/PZs2cRGxuLffv2oV27dgInJKmwt7dHUFAQevfuDQCIjo5GkyZNkJWVBQ0NXhdTllhg6INycnKwe/durFu3DsePH0fTpk3x6NEjPH78GKtXr1ZMLEZUmtzd3eHt7Y0FCxYoLf/uu+9w9OhRDuKlMqOpqYn79+/DyspKsUxPTw83b96EjY2NgMmkhxPZUbGio6MREBAAS0tLjBs3Du7u7rhx4wZOnTqF27dvY+7cuRg9erTQMUkibty4AT8/P5Xlvr6+uH79ugCJSKoKCgqgqamptExDQ4OX8wuAx7tIhZubG27evIn27dtj7dq16Nq1K9TV1ZW26devH8aMGSNQQpIaU1NTxMTEwMnJSWl5TEwMzMzMBEpFUlRYWIi2bdsqnS7KzMxE165doaWlpVjGo4KljwWGVPTp0we+vr6oWrXqe7epUqUKCgoKyjAVSdnQoUMxbNgw3L17F02bNgXwdgzMjz/+iPHjxwucjqRk+vTpKsu6desmQBLiGBhSkp6ejvPnzyMnJweNGjXipYFULhQWFiI4OBgLFy7Eo0ePAABWVlaYOHEiRo8ezftyEUkQCwwpxMTEoFOnTnj8+DEKCwthYGCA7du3w9vbW+hoRAqvXr0CABgYGAichIiExEG8pBAYGAh7e3ucOXMG0dHRaNu2LQICAoSORaTEwMCA5YUE0aFDB5w7d+4ft3v16hV+/PFHLFu2rAxSSRePwJBClSpVcPToUXh4eAAAUlNTUblyZaSmpvJeSCQod3f3Yk8TyWQy6OjowNHREYMHD0br1q0FSEdSsXbtWkybNg2Ghobo2rUrGjRoACsrK+jo6ODly5e4fv06zpw5g4MHD6Jz58743//+x0urSxELDCmoqakhJSVF6aoOAwMDXL58Gfb29gImI6mbPHkyVqxYATc3NzRq1AgAcOHCBVy+fBmDBw/G9evXERYWhl27dnFAJZWq7Oxs7NixA9u2bcOZM2eQlpYG4G2ZdnV1hbe3N/z8/ODi4iJw0oqPBYYU1NTUcPz4cVSuXFmxrGnTpti+fTuqVaumWFanTh0h4pGEDR06FDY2Npg6darS8jlz5uD+/ftYvXo1pk+fjgMHDiAqKkqglCRFaWlpyMrKgomJicr8MFS6WGBIQU1NDTKZDMV9JIqWy2QyTthEZc7Q0BDR0dFwdHRUWn7nzh3Ur18faWlpuHnzJho2bKgY5EtEFRvngSGFhIQEoSMQFUtHRwfh4eEqBSY8PBw6OjoA3s6QWvRvIqr4WGBIwdbWVugIRMUaNWoUhg8fjujoaMUd0S9cuIA1a9bg+++/B/D2ho/16tUTMCURlSWeQiIAQGJi4ieNln/48OEHZ+olKmmbN2/G0qVLERcXBwBwdnbGqFGj0L9/fwBAVlaW4qokIqr4WGAIAGBubo7u3bvD399f8Rfu36WlpWH79u1YvHgxhg0bxps5EhGRYHgKiQAA169fx9y5c9GuXTvo6Oigfv36KvMbXLt2DR4eHggKCkKnTp2EjkwSMmjQIPj5+aFly5ZCRyFSkpOTgydPnqjcG47zv5Q+HoEhJVlZWThw4ADOnDmD+/fvIysrC1WqVIG7uzu8vb1Ru3ZtoSOSBHXv3h0HDx6Era0thgwZgkGDBvEUJgnq9u3b8PX1RXh4uNJyXq1ZdlhgiEgUnj59ik2bNmHDhg24fv06vLy84Ofnh27dunH+DSpzzZo1g4aGBr777jtYWlqqzBRdt25dgZJJBwsMEYnOxYsXsX79eqxZswb6+vr46quv8M0338DJyUnoaCQRlSpVQnR0NGrWrCl0FMnizRyJSFSSk5MRGhqK0NBQqKuro1OnTrhy5QpcXV2xaNEioeORRLi6uuLZs2dCx5A0HoEhonIvNzcXe/fuxfr163H06FHUqVMH/v7+6N+/v+JGo7t374avry9evnwpcFqSguPHj2PKlCmYN28e3NzcVE5j8ga4pY8FhojKvSpVqqCgoAD9+vXD0KFDi52wLjU1Fe7u7pxRmsqEmtrbExh/H/vCQbxlhwWGiMq9TZs2oXfv3pykjsqNU6dOfXD9Z599VkZJpIsFhop1+/ZtnDhxotj5DaZNmyZQKpKie/fuITQ0FLm5ufjss89Qq1YtoSMRUTnAAkMqVq9ejREjRqBKlSqwsLBQOkQqk8lw8eJFAdORlJw4cQJdunRBVlYWAEBDQwPr1q3DV199JXAykqLLly+jdu3aUFNTw+XLlz+4bZ06dcoolXSxwJAKW1tbfPPNNwgMDBQ6Cklc8+bNUaVKFaxYsQI6OjqYMmUKdu/ejUePHgkdjSRITU0NKSkpMDMzg5qaGmQyGYr7FcoxMGWDBYZUyOVyxMTEwMHBQegoJHFGRkYIDw+Hq6srACAzMxNyuRyPHz+GiYmJwOlIau7fvw8bGxvIZDLcv3//g9va2tqWUSrpYoEhFX5+fmjYsCGGDx8udBSSuHf/4i1iYGCA2NhYFmwiiePNHEmFo6Mjpk6dinPnzhU7vwHvQk1l6ciRIzA0NFQ8LigoQFhYGK5evapY9vnnnwsRjSRs48aNH1zv4+NTRkmki0dgSIW9vf1718lkMty9e7cM05CUFc218SEcb0BCMDY2Vnqcm5uLzMxMaGlpQU9PDy9evBAomXTwCAyp4ERgVF78/RJ+ovKiuBmfb9++jREjRmDixIkCJJIeHoEhIiIqIVFRUfjqq69w8+ZNoaNUeDwCQwCA8ePHY/bs2ahUqRLGjx//wW1//vnnMkpFUnbu3Dk0adLko7bNzMxEQkICJ7kjwWloaPAy/zLCAkMAgEuXLiE3N1fx7/f5+30/iErLwIED4eDgAH9/f3Tq1AmVKlVS2eb69ev47bffsH79evz4448sMFRm9u7dq/S4sLAQycnJWLp0KZo1ayZQKmnhKSQiKpdyc3OxYsUKLFu2DHfv3kWNGjVgZWUFHR0dvHz5Ejdv3kRGRgZ69OiB77//Hm5ubkJHJgn5+wBzmUwGU1NTtGnTBgsXLoSlpaVAyaSDBYaIyr2oqCicOXMG9+/fR1ZWFqpUqQJ3d3e0bt0alStXFjoeEQmABYZUtG7d+oOnio4fP16GaYiIiFRxDAypqFevntLj3NxcxMTE4OrVqxg0aJAwoYiIypH3Xewgk8mgo6MDR0dHdOvWjUcISxGPwNBHmzFjBjIyMvDTTz8JHYWISFCtW7fGxYsXkZ+fD2dnZwDArVu3oK6ujpo1ayIuLg4ymQxnzpxR3MuLShYLDH20O3fuoFGjRpxhkogkLzg4GH/99RfWr18PuVwOAEhLS4O/vz+aN2+OoUOHon///sjKysKRI0cETlsxscDQR9u0aRMCAwM5xwERSV7VqlURGhqqcnTl2rVraN++PR4+fIiLFy+iffv2ePbsmUApKzaOgSEVPXv2VHpcNL9BVFQUpk6dKlAqIqLyIy0tDU+ePFEpME+fPkV6ejoAwMjICDk5OULEkwQWGFLx7p1/gbfzHTg7O2PWrFlo3769QKlI6sLCwhAWFoYnT56o3CNp3bp1AqUiqerWrRt8fX2xcOFCNGzYEABw4cIFTJgwAd27dwcAREZGokaNGgKmrNh4ComIyr2ZM2di1qxZaNCgASwtLVUu89+9e7dAyUiqMjIyMG7cOGzcuBF5eXkA3t5GYNCgQVi0aBEqVaqEmJgYAKpXdlLJYIEhonLP0tISQUFBGDhwoNBRiJRkZGTg7t27AAAHBwfo6+sLnEg6WGBIhbGxcbET2b07v8HgwYMxZMgQAdKRFJmYmCAyMhLVq1cXOgoRlRMcA0Mqpk2bhrlz56Jjx45o1KgRgLfncg8fPoyRI0ciISEBI0aMQF5eHoYOHSpwWpICf39/bNmyhYPIqdx4/fo1FixY8N5xWUVHZaj0sMCQijNnzmDOnDkYPny40vJVq1bh6NGj+OOPP1CnTh0sWbKEBYbKxJs3b/Drr7/i2LFjqFOnDjQ1NZXW//zzzwIlI6ny9/fHqVOnMHDgwGLHZVHp4ykkUqGvr4+YmBg4OjoqLb9z5w7q1auHjIwMxMfHo06dOnj9+rVAKUlKWrdu/d51MpmM9+eiMmdkZIQDBw6gWbNmQkeRLB6BIRWVK1fGvn37MG7cOKXl+/btU9zX4/Xr1zAwMBAiHknQiRMnhI5ApMTY2Jj3ORIYCwypmDp1KkaMGIETJ04oxsBcuHABBw8exMqVKwEAoaGh+Oyzz4SMSUQkmNmzZ2PatGnYsGED9PT0hI4jSTyFRMU6e/Ysli5diri4OACAs7MzRo0ahaZNmwqcjKSiZ8+eCAkJgVwuV5kd+u927dpVRqmI3nJ3d0d8fDwKCwthZ2enMi7r4sWLAiWTDh6BoWI1a9aM53ZJUIaGhoqBkX+fHZpIaEWz7ZJweASGilVQUIA7d+4Ue3lgy5YtBUpFRET0Fo/AkIpz586hf//+uH//Pv7eb2UyGfLz8wVKRkRUfqSmpmLnzp2Ij4/HxIkTUblyZVy8eBHm5uaoWrWq0PEqPB6BIRX16tVDjRo1MHPmzGLnN+DhfCpr9vb2H5xng5OGUVm7fPkyvLy8YGhoiHv37iEuLg4ODg6YMmUKEhMTsXHjRqEjVng8AkMqbt++jZ07d6rMA0MklLFjxyo9zs3NxaVLl3D48GFMnDhRmFAkaePHj8fgwYMRFBSkNKVEp06d0L9/fwGTSQcLDKlo3Lgx7ty5wwJD5caYMWOKXb5s2TJERUWVcRqit1NLrFq1SmV51apVkZKSIkAi6WGBIRWjRo3Ct99+i5SUFLi5ualcHlinTh2BkhEp69ixIyZPnoz169cLHYUkRltbG+np6SrLb926BVNTUwESSQ/HwJAKNTU1lWUymQyFhYUcxEvlSlBQEJYvX4579+4JHYUkxt/fH8+fP8f27dtRuXJlXL58Gerq6ujevTtatmyJ4OBgoSNWeCwwpOL+/fsfXG9ra1tGSYjecnd3VxrEW1hYiJSUFDx9+hTLly/HsGHDBExHUpSWloYvvvgCUVFRePXqFaysrJCSkgJPT08cPHgQlSpVEjpihccCQ0Tl3syZM5Ueq6mpwdTUFK1atULNmjUFSkUEnDlzBpcvX0ZGRgY8PDzg5eUldCTJYIGhYm3atAkrV65EQkICIiIiYGtri+DgYNjb26Nbt25CxyMiIolTHexAkrdixQqMHz8enTp1QmpqqmLMi5GREc/rkiDS09OL/Xr16hVycnKEjkcSFRYWhi5duqB69eqoXr06unTpgmPHjgkdSzJYYEjFL7/8gtWrV+OHH36Aurq6YnmDBg1w5coVAZORVBkZGcHY2Fjly8jICLq6urC1tcX06dNVbntBVFqWL1+ODh06wMDAAGPGjMGYMWMgl8vRqVMnLFu2TOh4ksDLqElFQkIC3N3dVZZra2vj9evXAiQiqQsJCcEPP/yAwYMHo1GjRgCAyMhIbNiwAVOmTMHTp0/x008/QVtbG99//73AaUkK5s2bh0WLFiEgIECxbPTo0WjWrBnmzZuHkSNHCphOGlhgSIW9vT1iYmJUrjY6fPgwXFxcBEpFUrZhwwYsXLgQffr0USzr2rUr3NzcsGrVKoSFhcHGxgZz585lgaEykZqaig4dOqgsb9++PQIDAwVIJD08hUQqxo8fj5EjR2Lbtm0oLCxEZGQk5s6di8mTJ2PSpElCxyMJCg8PL/aooLu7OyIiIgAAzZs3R2JiYllHI4n6/PPPsXv3bpXlf/75J7p06SJAIunhERhS4e/vD11dXUyZMgWZmZno378/rKyssHjxYvTt21foeCRB1tbWWLt2LRYsWKC0fO3atbC2tgYAPH/+HMbGxkLEIwlydXXF3LlzcfLkSXh6egIAzp07h7Nnz+Lbb7/FkiVLFNuOHj1aqJgVGi+jJhXZ2dnIy8tDpUqVkJmZiYyMDJiZmQkdiyRs79696N27N2rWrImGDRsCAKKionDz5k3s3LkTXbp0wYoVK3D79m38/PPPAqclKbC3t/+o7WQyGe+WXkpYYEjh6dOn8PHxwbFjx1BQUICGDRti8+bNqF69utDRiJCQkIBVq1bh1q1bAABnZ2d8/fXXsLOzEzYYEQmCBYYUfH19cejQIYwePRo6OjpYtWoVLC0tceLECaGjERERKWGBIQVra2usWbMG3t7eAIDbt2/DxcUFr1+/hra2tsDpSOpSU1MRGRmJJ0+eqMz34uPjI1AqIhIKCwwpqKur4+HDh7CwsFAsq1SpEq5du8bD9CSoffv2YcCAAcjIyIBcLle6saNMJsOLFy8ETEdEQuBl1KTk3Zl3ix6z45LQvv32W/j6+iIjIwOpqal4+fKl4ovlhUiaeASGFNTU1GBoaKj0121qairkcjnU1P6v6/IXBpW1SpUq4cqVK3BwcBA6ChGVE5wHhhTWr18vdASiYnl7eyMqKooFhsqV1NRUrF27Fjdu3AAA1KpVC76+vjA0NBQ4mTTwCAwRlXtr167FrFmzMGTIELi5uUFTU1Np/eeffy5QMpKqqKgoeHt7Q1dXV3F/rgsXLiArKwtHjx6Fh4eHwAkrPhYYIir33j2F+XcymQz5+fllmIYIaNGiBRwdHbF69WpoaLw9mZGXlwd/f3/cvXsXp0+fFjhhxccCQ0RE9Il0dXVx6dIl1KxZU2n59evX0aBBA2RmZgqUTDp4FRIRicqbN2+EjkAEuVxe7M1Dk5KSYGBgIEAi6WGBIaJyLz8/H7Nnz0bVqlWhr6+vuLfM1KlTsXbtWoHTkRR9+eWX8PPzw7Zt25CUlISkpCRs3boV/v7+6Nevn9DxJIEFht4rJycHcXFxyMvLEzoKSdzcuXMREhKCoKAgaGlpKZbXrl0ba9asETAZSdVPP/2Enj17wsfHB3Z2drCzs8PgwYPxxRdf4McffxQ6niRwDAypyMzMxKhRo7BhwwYAwK1bt+Dg4IBRo0ahatWq+O677wROSFLj6OiIVatWoW3btjAwMEBsbCwcHBxw8+ZNeHp64uXLl0JHJInKzMxEfHw8AKB69erQ09MTOJF08AgMqZg8eTJiY2Nx8uRJ6OjoKJZ7eXlh27ZtAiYjqXr48CEcHR1VlhcUFCA3N1eARERv6enpwdjYGMbGxiwvZYwFhlTs2bMHS5cuRfPmzZVm5a1Vq5biLw2isuTq6oq//vpLZfnOnTvh7u4uQCKSuoKCAsyaNQuGhoawtbWFra0tjIyMMHv2bJWbjVLp4Ey8pOLp06cwMzNTWf769WulQkNUVqZNm4ZBgwbh4cOHKCgowK5duxAXF4eNGzdi//79QscjCfrhhx+wdu1aLFiwAM2aNQMAnDlzBjNmzMCbN28wd+5cgRNWfBwDQypatmyJ3r17Y9SoUTAwMMDly5dhb2+PUaNG4fbt2zh8+LDQEUmC/vrrL8yaNQuxsbHIyMiAh4cHpk2bhvbt2wsdjSTIysoKK1euVJkF+s8//8Q333yDhw8fCpRMOngEhlTMmzcPHTt2xPXr15GXl4fFixfj+vXrCA8Px6lTp4SORxLVokULhIaGCh2DCMDbm9r+fRI7AKhZsyZveFtGOAaGVDRv3hwxMTHIy8uDm5sbjh49CjMzM0RERKB+/fpCxyMJi4qKwqZNm7Bp0yZER0cLHYckrG7duli6dKnK8qVLl6Ju3boCJJIenkIionLvwYMH6NevH86ePQsjIyMAb+8E3LRpU2zduhXVqlUTNiBJzqlTp9C5c2fY2NjA09MTABAREYGkpCQcPHgQLVq0EDhhxccjMAQASE9PV/r3h76Iypq/vz9yc3Nx48YNvHjxAi9evMCNGzdQUFAAf39/oeORBH322We4desWevTogdTUVKSmpqJnz56Ii4tjeSkjPAJDAAB1dXUkJyfDzMwMampqxV5tVFhYyDv/kiB0dXURHh6ucsl0dHQ0WrRowRvnUZlLTEyEtbV1sT8rExMTYWNjI0AqaeEgXgIAHD9+HJUrVwYAnDhxQuA0RMqsra2LnbAuPz8fVlZWAiQiqbO3t1f80feu58+fw97enn/olQEWGALw9nBocf8mKg/+97//YdSoUVi2bBkaNGgA4O2A3jFjxuCnn34SOB1JUdER6b/LyMhQmsGcSg9PIREA4PLlyx+9bZ06dUoxCZEqY2NjZGZmIi8vDxoab//uKvp3pUqVlLblJaxUmsaPHw8AWLx4MYYOHap0+4D8/HycP38e6urqOHv2rFARJYNHYAgAUK9ePchkMvxTn+UYGBJCcHCw0BGIAACXLl0C8PYIzJUrV5Tujq6lpYW6detiwoQJQsWTFB6BIQDA/fv3P3pbW1vbUkxCRFT+DRkyBIsXL4ZcLhc6imSxwBAREZHocB4YKtamTZvQrFkzWFlZKY7OBAcH488//xQ4GRGR8F6/fo2pU6eiadOmcHR0hIODg9IXlT6OgSEVK1aswLRp0zB27FjMnTtXMebFyMgIwcHB6Natm8AJiYiE5e/vj1OnTmHgwIGwtLQs9ookKl08hUQqXF1dMW/ePHTv3h0GBgaIjY2Fg4MDrl69ilatWuHZs2dCRyQiEpSRkREOHDiAZs2aCR1FsngKiVQkJCSozHgKANra2nj9+rUAiYj+T1JSEpKSkoSOQRJnbGysmPyThMECQyrs7e0RExOjsvzw4cNwcXEp+0AkeXl5eZg6dSoMDQ1hZ2cHOzs7GBoaYsqUKcXO0EtU2mbPno1p06bxNhYC4hgYUjF+/HiMHDkSb968QWFhISIjI/H7779j/vz5WLNmjdDxSIJGjRqFXbt2ISgoSOnOvzNmzMDz58+xYsUKgROS1CxcuBDx8fEwNzeHnZ0dNDU1ldZfvHhRoGTSwTEwVKzNmzdjxowZiI+PBwBYWVlh5syZ8PPzEzgZSZGhoSG2bt2Kjh07Ki0/ePAg+vXrh7S0NIGSkVTNnDnzg+unT59eRkmkiwWGPigzMxMZGRkqNywjKktmZmY4deqUyinMGzduoGXLlnj69KlAyYhIKBwDQx+kp6fH8kKCCwgIwOzZs5Gdna1Ylp2djblz5yIgIEDAZCRlqampWLNmDSZPnqy4B9fFixfx8OFDgZNJA4/AEADA3d39o+cx4LldKms9evRAWFgYtLW1UbduXQBAbGwscnJy0LZtW6Vtd+3aJUREkpjLly/Dy8sLhoaGuHfvHuLi4uDg4IApU6YgMTERGzduFDpihcdBvAQA6N69u+Lfb968wfLly+Hq6qoYMHnu3Dlcu3YN33zzjUAJScqMjIzQq1cvpWXW1tYCpSF6e7HD4MGDERQUBAMDA8XyTp06oX///gImkw4egSEV/v7+sLS0xOzZs5WWT58+HUlJSVi3bp1AyYiIygdDQ0NcvHgR1atXV5rw8/79+3B2dsabN2+EjljhcQwMqdixYwd8fHxUln/11Vf4448/BEhERFS+aGtrIz09XWX5rVu3YGpqKkAi6eEpJFKhq6uLs2fPwsnJSWn52bNnoaOjI1AqkrqdO3di+/btSExMRE5OjtI6jsuisvb5559j1qxZ2L59OwBAJpMhMTERgYGBKqc7qXTwCAypGDt2LEaMGIHRo0fjt99+w2+//YZRo0Zh5MiRGDdunNDxSIKWLFmCIUOGwNzcHJcuXUKjRo1gYmKCu3fvqswNQ1QWFi5cqJhiIisrC5999hkcHR1hYGCAuXPnCh1PEjgGhoq1fft2LF68GDdu3AAAuLi4YMyYMejTp4/AyUiKatasienTp6Nfv35K4w2mTZuGFy9eYOnSpUJHJIk6c+YMLl++jIyMDHh4eMDLy0voSJLBAkOf5OrVq6hdu7bQMUhi9PT0cOPGDdja2sLMzAyhoaGoW7cubt++jSZNmuD58+dCRySiMsYxMPSPXr16hd9//x1r1qxBdHQ08vPzhY5EEmNhYYEXL17A1tYWNjY2OHfuHOrWrYuEhATwbzAqS1lZWQgLC0OXLl0AAJMnT1aaYFFdXR2zZ8/meMEywAJD73X69GmsWbMGu3btgpWVFXr27Illy5YJHYskqE2bNti7dy/c3d0xZMgQjBs3Djt37kRUVBR69uwpdDySkA0bNuDAgQOKArN06VLUqlULurq6AICbN2/CysqK4wXLAE8hkZKUlBSEhIRg7dq1SE9PR58+fbBy5UrExsbC1dVV6HgkUQUFBSgoKICGxtu/ubZu3Yrw8HA4OTnh66+/hpaWlsAJSSpatGiBSZMmoWvXrgCgNCYLAH777TcsW7YMERERQsaUBBYYUujatStOnz6Nzp07Y8CAAejQoQPU1dWhqanJAkOCycvLw7x58+Dr64tq1aoJHYckztLSEhEREbCzswMAmJqa4sKFC4rHt27dQsOGDXmH9DLAy6hJ4dChQ/Dz88PMmTPRuXNnqKurCx2JCBoaGggKCkJeXp7QUYiQmpqqNObl6dOnivICvD1a+O56Kj0sMKRw5swZvHr1CvXr10fjxo2xdOlSPHv2TOhYRGjbti1OnToldAwiVKtWDVevXn3v+suXL/NIYRnhKSRS8fr1a2zbtg3r1q1DZGQk8vPz8fPPP8PX11fppmVEZWXlypWYOXMmBgwYgPr166NSpUpK6z///HOBkpHUjBkzBseOHUN0dLTKlUZZWVlo0KABvLy8sHjxYoESSgcLDH1QXFwc1q5di02bNiE1NRXt2rXD3r17hY5FEqOm9v6DxTKZjJf2U5l5/Pgx6tWrBy0tLQQEBKBGjRoA3v6sXLp0KfLy8nDp0iWYm5sLnLTiY4Ghj5Kfn499+/Zh3bp1LDBEJGkJCQkYMWIEQkNDFfMQyWQytGvXDsuXL1dckUSliwWGiMq9jRs34ssvv4S2trbS8pycHGzdurXYu6cTlbYXL17gzp07AABHR0dUrlxZ4ETSwgJDROWeuro6kpOTYWZmprT8+fPnMDMz4ykkIgniVUhEVO4VFhZCJpOpLH/w4AEMDQ0FSEREQuOtBIio3HJ3d4dMJoNMJkPbtm0VM/ECb8dlJSQkoEOHDgImJCKhsMAQUbnVvXt3AEBMTAy8vb2hr6+vWKelpQU7Ozv06tVLoHREJCSOgSGicm/Dhg3o27evyiBeIpIuFhgiKveSkpIgk8kUM5xGRkZiy5YtcHV1xbBhwwROR0RC4CBeIir3+vfvjxMnTgB4e8d0Ly8vREZG4ocffsCsWbMETkdEQmCBIaJy7+rVq2jUqBEAYPv27XBzc0N4eDg2b96MkJAQYcMRkSBYYIio3MvNzVWMfzl27Jji3kc1a9ZEcnKykNGISCAsMERU7tWqVQsrV67EX3/9hdDQUMWl048ePYKJiYnA6YhICCwwRFTu/fjjj1i1ahVatWqFfv36oW7dugCAvXv3Kk4tEZG08CokIhKF/Px8pKenw9jYWLHs3r170NPTU7nFABFVfCwwREREJDo8hURE5d7jx48xcOBAWFlZQUNDA+rq6kpfRCQ9vJUAEZV7gwcPRmJiIqZOnQpLS8tib+xIRNLCU0hEVO4ZGBjgr7/+Qr169YSOQkTlBE8hEVG5Z21tDf6tRUTvYoEhonIvODgY3333He7duyd0FCIqJ3gKiYjKPWNjY2RmZiIvLw96enrQ1NRUWv/ixQuBkhGRUDiIl4jKveDgYKEjEFE5wyMwREREJDo8AkNE5VJ6ejrkcrni3x9StB0RSQePwBBRuaSuro7k5GSYmZlBTU2t2LlfCgsLIZPJkJ+fL0BCIhISj8AQUbl0/PhxVK5cGQBw4sQJgdMQUXnDIzBEREQkOjwCQ0SikJqaisjISDx58gQFBQVK63x8fARKRURC4REYIir39u3bhwEDBiAjIwNyuVxpPIxMJuM8MEQSxAJDROVejRo10KlTJ8ybNw96enpCxyGicoAFhojKvUqVKuHKlStwcHAQOgoRlRO8FxIRlXve3t6IiooSOgYRlSMcxEtE5dLevXsV/+7cuTMmTpyI69evw83NTeVeSJ9//nlZxyMigfEUEhGVS2pqH3eAmBPZEUkTCwwRERGJDsfAEBERkeiwwBBRuXX8+HG4uroWezPHtLQ01KpVC6dPnxYgGREJjQWGiMqt4OBgDB06tNi7TRsaGuLrr7/GokWLBEhGREJjgSGicis2NhYdOnR47/r27dsjOjq6DBMRUXnBAkNE5dbjx49VLpl+l4aGBp4+fVqGiYiovGCBIaJyq2rVqrh69ep711++fBmWlpZlmIiIygsWGCIqtzp16oSpU6fizZs3KuuysrIwffp0dOnSRYBkRCQ0zgNDROXW48eP4eHhAXV1dQQEBMDZ2RkAcPPmTSxbtgz5+fm4ePEizM3NBU5KRGWNBYaIyrX79+9jxIgROHLkCIp+XMlkMnh7e2PZsmWwt7cXOCERCYEFhohE4eXLl7hz5w4KCwvh5OQEY2NjoSMRkYBYYIiIiEh0OIiXiIiIRIcFhoiIiESHBYaIiIhEhwWGiIiIRIcFhogqnMGDB6N79+5CxyCiUsSrkIiowklLS0NhYSGMjIyEjkJEpYQFhoiIiESHp5CIqFTs3LkTbm5u0NXVhYmJCby8vPD69WvF6Z2ZM2fC1NQUcrkcw4cPR05OjuK5BQUFmD9/Puzt7aGrq4u6deti586dSvu/du0aunTpArlcDgMDA7Ro0QLx8fEAVE8h/dP+Xr58iQEDBsDU1BS6urpwcnLC+vXrS/cbRET/iYbQAYio4klOTka/fv0QFBSEHj164NWrV/jrr78UtwIICwuDjo4OTp48iXv37mHIkCEwMTHB3LlzAQDz58/Hb7/9hpUrV8LJyQmnT5/GV199BVNTU3z22Wd4+PAhWrZsiVatWuH48eOQy+U4e/Ys8vLyis3zT/ubOnUqrl+/jkOHDqFKlSq4c+cOsrKyyuz7RUSfjqeQiKjEXbx4EfXr18e9e/dga2urtG7w4MHYt28fkpKSoKenBwBYuXIlJk6ciLS0NOTm5qJy5co4duwYPD09Fc/z9/dHZmYmtmzZgu+//x5bt25FXFwcNDU1VV5/8ODBSE1NxZ49e5Cdnf2P+/v8889RpUoVrFu3rpS+I0RU0ngEhohKXN26ddG2bVu4ubnB29sb7du3xxdffKG4f1HdunUV5QUAPD09kZGRgaSkJGRkZCAzMxPt2rVT2mdOTg7c3d0BADExMWjRokWx5eXv7ty584/7GzFiBHr16oWLFy+iffv26N69O5o2bfqfvgdEVLpYYIioxKmrqyM0NBTh4eE4evQofvnlF/zwww84f/78Pz43IyMDAHDgwAFUrVpVaZ22tjYAQFdX96OzfMz+OnbsiPv37+PgwYMIDQ1F27ZtMXLkSPz0008f/TpEVLZYYIioVMhkMjRr1gzNmjXDtGnTYGtri927dwMAYmNjkZWVpSgi586dg76+PqytrVG5cmVoa2sjMTERn332WbH7rlOnDjZs2IDc3Nx/PArj6ur6j/sDAFNTUwwaNAiDBg1CixYtMHHiRBYYonKMBYaIStz58+cRFhaG9u3bw8zMDOfPn8fTp0/h4uKCy5cvIycnB35+fpgyZQru3buH6dOnIyAgAGpqajAwMMCECf+vnftVUS0IADD+gSBYjoKYTSJiEQWL/4LJN9DkC1iENRyDCAaTIPgS2s0GQfApxFfQdorcdtmFC3dZdu9l4Pv1GYZJHzPMvDGdTnm9XrTbbR6PB5fLhSiKGI/HTCYTdrsdw+GQOI7JZrNcr1eazSblcvnDWj4z32KxoNFoUK1WSZKE4/FIpVL5T7sn6TMMGEnfLooizucz2+2W5/NJsVhks9kwGAw4HA70+31KpRLdbpckSRiNRiyXy9/jV6sVhUKB9XrN7XYjl8tRr9eZz+cA5PN5TqcTs9mMXq9HKpWiVqvRarX+uJ6/zZdOp4njmPv9TiaTodPpsN/vf3yfJH2dr5Ak/VPvXwhJ0lf5kZ0kSQqOASNJkoLjFZIkSQqOJzCSJCk4BowkSQqOASNJkoJjwEiSpOAYMJIkKTgGjCRJCo4BI0mSgmPASJKk4PwCwpjOvTnIzUoAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "means = df.groupby(\"species\").mean(numeric_only=True)\n", + "means.plot.bar()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Integration with open source visualizations\n", + "\n", + "BigQuery Dataframes is also compatible with several open source visualization packages, such as `matplotlib`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyUAAAGFCAYAAADjF1xYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAaqdJREFUeJzt3XdYU2f/BvA7CXvvoaKIIOIAcY/WhQquVl9brVqr1bpn3f5srVZttY5W62hrrVrrfm2rtdZdfRUnKrgQEcEJIlN2IMnvD2pqZAgynoTcn+vi0uScPOc+IUC+ecaRqFQqFYiIiIiIiASRig5ARERERET6jUUJEREREREJxaKEiIiIiIiEYlFCRERERERCsSghIiIiIiKhWJQQEREREZFQLEqIiIiIiEgoFiVERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERCzZs3D40bNy7x/jExMZBIJAgNDQUAnDhxAhKJBCkpKRWST9t06NABkydPLnM7iYmJcHJyQkxMTJnb0iUSiQS///47gIKvpcrwOsd8+Xvu7u6Ob775plxztWrVCnv27CnXNolKg0UJERGVq7Nnz0Imk6FHjx6Vcrw2bdogNjYW1tbWr93Gpk2bIJFIIJFIIJVKUaNGDXz44YeIj48vx6Tl49dff8WCBQvK3M6iRYvw9ttvw93dHcC/b5aff9nb26Nr1664cuVKmY+lrdzc3BAbG4uGDRuKjlIqFy9exMiRI8u1zU8++QSzZs2CUqks13aJSopFCRERlasNGzZgwoQJ+N///ofHjx9X+PGMjIzg4uICiURSpnasrKwQGxuLhw8fYv369fjrr78wePDgckpZfuzs7GBpaVmmNjIzM7FhwwYMHz68wLajR48iNjYWhw4dQnp6Orp161Zle6FkMhlcXFxgYGAgOkqpODo6wszMrFzb7NatG9LS0vDXX3+Va7tEJcWihIiIyk16ejp27tyJMWPGoEePHti0aVOBfRYv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EVUlSkV+oaJS5hcfUqnoRERERK/EooSIiIiIiITiR2hERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERERERCsSghIiIiIiKhWJQQEREREZFQLEqIiIiIiEgoFiVERERERCQUixIiIiIiIhKKRQkREREREQnFooSIiIiIiIRiUUJEREREREKxKCEiIiIiIqFYlBARERERkVAsSoiIiIiISCgWJUREREREJBSLEiIiIiIiEopFCRERERERCcWihIiIiIiIhGJRQkREREREQrEoISIiIiIioViUEBERERGRUCxKiIiIiIhIKBYlREREREQkFIsSIiIiIiISikUJEREREREJxaKEiIiIiIiEYlFCRERERERC/T+jLVh8VAgPvQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# plotting a histogram\n", + "species_counts = df[\"species\"].value_counts()\n", + "plt.pie(species_counts, labels=species_counts.index, autopct='%1.1f%%')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pandas interoperability\n", + "\n", + "BigQuery DataFrames can be converted from and to Pandas DataFrame with `to_pandas` and `read_pandas` respectively.\n", + "This could be handy to take advantage of the capabilities of the two systems.\n", + "\n", + "> Note: `to_pandas` converts the BigQuery DataFrame to Pandas DataFrame by bringing all the data in memory, which would be an issue\n", + "for large data, as your machine may not have enough memory to accommodate that." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "We have a dataframe of \n", + "\n", + "\n", + "We have a dataframe of \n", + "\n", + "\n", + "We have a dataframe of \n", + "\n" + ] + } + ], + "source": [ + "def print_type(df):\n", + " print(f\"\\nWe have a dataframe of {type(df)}\\n\")\n", + "\n", + "# The original bigframes dataframe\n", + "cur_df = df\n", + "print_type(cur_df)\n", + "\n", + "# Convert to pandas dataframe\n", + "cur_df = cur_df.to_pandas()\n", + "print_type(cur_df)\n", + "\n", + "# Convert back to bigframes dataframe\n", + "cur_df = bpd.read_pandas(cur_df)\n", + "print_type(cur_df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Machine Learning with BigQuery DataFrames" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Clean and prepare data\n", + "\n", + "We're are going to start with supervised learning, where a Linear Regression model will learn to predict the body mass (output variable `y`) using input features such as flipper length, sex, species, and more (features `X`)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# Drop any rows that has missing (NA) values\n", + "df = df.dropna()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Part of preparing data for a machine learning task is splitting it into subsets for training and testing to ensure that the solution is not overfitting. By default, BQML will automatically manage splitting the data for you. However, BQML also supports manually splitting out your training data.\n", + "\n", + "Performing a manual data split can be done with `bigframes.ml.model_selection.train_test_split` like so:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " df_train shape: (267, 7)\n", + " df_test shape: (67, 7)\n", + "\n" + ] + } + ], + "source": [ + "from bigframes.ml.model_selection import train_test_split\n", + "\n", + "# This will split df into test and training sets, with 20% of the rows in the test set,\n", + "# and the rest in the training set\n", + "df_train, df_test = train_test_split(df, test_size=0.2)\n", + "\n", + "# Show the shape of the data after the split\n", + "print(f\"\"\"\n", + " df_train shape: {df_train.shape}\n", + " df_test shape: {df_test.shape}\n", + "\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " X_train shape: (267, 6)\n", + " X_test shape: (67, 7)\n", + " y_train shape: (267, 1)\n", + " y_test shape: (67, 1)\n", + "\n" + ] + } + ], + "source": [ + "# Isolate input features and output variable into DataFrames\n", + "X_train = df_train[[\n", + " 'island',\n", + " 'culmen_length_mm',\n", + " 'culmen_depth_mm',\n", + " 'flipper_length_mm',\n", + " 'sex',\n", + " 'species',\n", + "]]\n", + "y_train = df_train[['body_mass_g']]\n", + "\n", + "X_test = df_test[[\n", + " 'island',\n", + " 'culmen_length_mm',\n", + " 'culmen_depth_mm',\n", + " 'flipper_length_mm',\n", + " 'sex',\n", + " 'species',\n", + " # Include the actual body_mass_g so that we can compare with the predicted\n", + " # without a join.\n", + " 'body_mass_g'\n", + "]]\n", + "y_test = df_test[['body_mass_g']]\n", + "\n", + "# Print the shapes of features and label\n", + "print(f\"\"\"\n", + " X_train shape: {X_train.shape}\n", + " X_test shape: {X_test.shape}\n", + " y_train shape: {y_train.shape}\n", + " y_test shape: {y_test.shape}\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define pipeline\n", + "\n", + "This step is subjective to the problem. Although a model can be directly trained on the original data, it is often useful to apply some preprocessing to the original data.\n", + "In this example we want to apply a [`ColumnTransformer`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.compose.ColumnTransformer) in which we apply [`OneHotEncoder`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.preprocessing.OneHotEncoder) to the category features and [`StandardScaler`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.preprocessing.StandardScaler) to the numeric features." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Pipeline(steps=[('preproc',\n", + " ColumnTransformer(transformers=[('onehot', OneHotEncoder(),\n", + " ['island', 'species', 'sex']),\n", + " ('scaler', StandardScaler(),\n", + " ['culmen_depth_mm',\n", + " 'culmen_length_mm',\n", + " 'flipper_length_mm'])])),\n", + " ('linreg', LinearRegression(fit_intercept=False))])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from bigframes.ml.compose import ColumnTransformer\n", + "from bigframes.ml.linear_model import LinearRegression\n", + "from bigframes.ml.pipeline import Pipeline\n", + "from bigframes.ml.preprocessing import OneHotEncoder, StandardScaler\n", + "\n", + "preprocessing = ColumnTransformer([\n", + " (\"onehot\", OneHotEncoder(), [\"island\", \"species\", \"sex\"]),\n", + " (\"scaler\", StandardScaler(), [\"culmen_depth_mm\", \"culmen_length_mm\", \"flipper_length_mm\"]),\n", + "])\n", + "\n", + "model = LinearRegression(fit_intercept=False)\n", + "\n", + "pipeline = Pipeline([\n", + " ('preproc', preprocessing),\n", + " ('linreg', model)\n", + "])\n", + "\n", + "# View the pipeline\n", + "pipeline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Train and Predict\n", + "\n", + "Supervised learning is when we train a model on input-output pairs, and then ask it to predict the output for new inputs. An example of such a predictor is `bigframes.ml.linear_models.LinearRegression`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
predicted_body_mass_gislandculmen_length_mmculmen_depth_mmflipper_length_mmsexspeciesbody_mass_g
03271.548077Biscoe37.918.6172.0FEMALEAdelie Penguin (Pygoscelis adeliae)3150.0
13224.661209Biscoe37.716.0183.0FEMALEAdelie Penguin (Pygoscelis adeliae)3075.0
23395.403541Biscoe34.518.1187.0FEMALEAdelie Penguin (Pygoscelis adeliae)2900.0
33943.436439Biscoe40.118.9188.0MALEAdelie Penguin (Pygoscelis adeliae)4300.0
43986.662895Biscoe41.418.6191.0MALEAdelie Penguin (Pygoscelis adeliae)3700.0
\n", + "
" ], - "source": [ - "from bigframes.ml.metrics import r2_score\n", - "\n", - "r2_score(y_pred['body_mass_g'], y_pred[\"predicted_body_mass_g\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Generative AI with BigQuery DataFrames\n", - "\n", - "BigQuery DataFrames integration with the Large Language Models (LLM) supported by BigQuery ML. Check out the [`bigframes.ml.llm`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm) module for all the available models.\n", - "\n", - "To use this feature you would need to have a few additional APIs enabled and IAM roles configured. Please make sure of that by following [this documentation](https://cloud.google.com/bigquery/docs/use-bigquery-dataframes#remote-models) and then uncomment the code in the following cells to try out the integration with Gemini." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create prompts\n", - "\n", - "A \"prompt\" text column can be initialized either directly or via the pandas APIs. For simplicity let's use a direct initialization here." - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
prompt
0What is BigQuery?
1What is BQML?
2What is BigQuery DataFrames?
\n", - "

3 rows × 1 columns

\n", - "
[3 rows x 1 columns in total]" - ], - "text/plain": [ - " prompt\n", - "0 What is BigQuery?\n", - "1 What is BQML?\n", - "2 What is BigQuery DataFrames?\n", - "\n", - "[3 rows x 1 columns]" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " predicted_body_mass_g island culmen_length_mm culmen_depth_mm \\\n", + "0 3271.548077 Biscoe 37.9 18.6 \n", + "1 3224.661209 Biscoe 37.7 16.0 \n", + "2 3395.403541 Biscoe 34.5 18.1 \n", + "3 3943.436439 Biscoe 40.1 18.9 \n", + "4 3986.662895 Biscoe 41.4 18.6 \n", + "\n", + " flipper_length_mm sex species body_mass_g \n", + "0 172.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 3150.0 \n", + "1 183.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 3075.0 \n", + "2 187.0 FEMALE Adelie Penguin (Pygoscelis adeliae) 2900.0 \n", + "3 188.0 MALE Adelie Penguin (Pygoscelis adeliae) 4300.0 \n", + "4 191.0 MALE Adelie Penguin (Pygoscelis adeliae) 3700.0 " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Learn from the training data how to predict output y\n", + "pipeline.fit(X_train, y_train)\n", + "\n", + "# Predict y for the test data\n", + "y_pred = pipeline.predict(X_test)\n", + "\n", + "# View predictions preview\n", + "y_pred.peek()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Evaluate results\n", + "\n", + "Some models include a convenient `.score(X, y)` method for evaulation with a preset accuracy metric:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mean_absolute_errormean_squared_errormean_squared_log_errormedian_absolute_errorr2_scoreexplained_variance
0231.91425278873.6004210.005172178.7249850.8905490.890566
\n", + "

1 rows × 6 columns

\n", + "
[1 rows x 6 columns in total]" ], - "source": [ - "df = bpd.DataFrame(\n", - " {\n", - " \"prompt\": [\"What is BigQuery?\", \"What is BQML?\", \"What is BigQuery DataFrames?\"],\n", - " })\n", - "df" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Generate responses\n", - "\n", - "Here we will use the [`GeminiTextGenerator`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) LLM to answer the questions. Read the [GeminiTextGenerator API documentation](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) for all the model versions supported via the `model_name` param." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# from bigframes.ml.llm import GeminiTextGenerator\n", - "\n", - "# model = GeminiTextGenerator(model_name=\"gemini-2.5-flash\")\n", - "\n", - "# pred = model.predict(df)\n", - "# pred" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's print the full text response for the question \"What is BigQuery DataFrames?\"." - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": {}, - "outputs": [], - "source": [ - "# import IPython.display\n", - "\n", - "# IPython.display.Markdown(pred.loc[2][\"ml_generate_text_llm_result\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TpV-iwP9qw9c" - }, - "source": [ - "## Cleaning up\n", - "\n", - "To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud\n", - "project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n", - "\n", - "To remove any temporary cloud artifacts (inclusing BQ tables) created in the current BigQuery DataFrames session, simply call `close_session`." - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": {}, - "outputs": [], - "source": [ - "# Delete the temporary cloud artifacts created during the bigframes session \n", - "bpd.close_session()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "wCsmt0IwFkDy" - }, - "source": [ - "## Summary and next steps\n", - "\n", - "1. You created BigQuery DataFrames objects, and inspected and manipulated data with pandas APIs at BigQuery scale and speed.\n", - "\n", - "1. You also created ML model from a DataFrame and used them to run predictions on another DataFrame.\n", - "\n", - "1. You got access to Google's state-of-the-art Gemini LLM through simple pythonic API.\n", - "\n", - "Learn more about BigQuery DataFrames in the documentation [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/bigquery-dataframes-introduction) and its [API reference](https://cloud.google.com/python/docs/reference/bigframes/latest).\n", - "\n", - "Also, find more sample notebooks in the [GitHub repo](https://github.com/googleapis/python-bigquery-dataframes/tree/main/notebooks), including the [pypi.ipynb](https://github.com/googleapis/python-bigquery-dataframes/blob/main/notebooks/dataframes/pypi.ipynb) that processes 400+ TB data at the cost and efficiency close to direct SQL by taking advantage of the [partial ordering](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes._config.bigquery_options.BigQueryOptions#bigframes__config_bigquery_options_BigQueryOptions_ordering_mode) mode." - ] + "text/plain": [ + " mean_absolute_error mean_squared_error mean_squared_log_error \\\n", + " 231.914252 78873.600421 0.005172 \n", + "\n", + " median_absolute_error r2_score explained_variance \n", + " 178.724985 0.890549 0.890566 \n", + "\n", + "[1 rows x 6 columns]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" } - ], - "metadata": { - "colab": { - "provenance": [], - "toc_visible": true - }, - "kernelspec": { - "display_name": "venv", - "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.10.16" + ], + "source": [ + "pipeline.score(X_test.drop(columns=[\"body_mass_g\"]), y_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For a more general approach, the library `bigframes.ml.metrics` is provided:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(0.8905492944632485)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from bigframes.ml.metrics import r2_score\n", + "\n", + "r2_score(y_pred['body_mass_g'], y_pred[\"predicted_body_mass_g\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generative AI with BigQuery DataFrames\n", + "\n", + "BigQuery DataFrames integration with the Large Language Models (LLM) supported by BigQuery ML. Check out the [`bigframes.ml.llm`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm) module for all the available models.\n", + "\n", + "To use this feature you would need to have a few additional APIs enabled and IAM roles configured. Please make sure of that by following [this documentation](https://cloud.google.com/bigquery/docs/use-bigquery-dataframes#remote-models) and then uncomment the code in the following cells to try out the integration with Gemini." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create prompts\n", + "\n", + "A \"prompt\" text column can be initialized either directly or via the pandas APIs. For simplicity let's use a direct initialization here." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
prompt
0What is BigQuery?
1What is BQML?
2What is BigQuery DataFrames?
\n", + "

3 rows × 1 columns

\n", + "
[3 rows x 1 columns in total]" + ], + "text/plain": [ + " prompt\n", + "0 What is BigQuery?\n", + "1 What is BQML?\n", + "2 What is BigQuery DataFrames?\n", + "\n", + "[3 rows x 1 columns]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" } + ], + "source": [ + "df = bpd.DataFrame(\n", + " {\n", + " \"prompt\": [\"What is BigQuery?\", \"What is BQML?\", \"What is BigQuery DataFrames?\"],\n", + " })\n", + "df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generate responses\n", + "\n", + "Here we will use the [`GeminiTextGenerator`](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) LLM to answer the questions. Read the [GeminiTextGenerator API documentation](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes.ml.llm.GeminiTextGenerator) for all the model versions supported via the `model_name` param." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# from bigframes.ml.llm import GeminiTextGenerator\n", + "\n", + "# model = GeminiTextGenerator(model_name=\"gemini-3.5-flash\")\n", + "\n", + "# pred = model.predict(df)\n", + "# pred" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's print the full text response for the question \"What is BigQuery DataFrames?\"." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "# import IPython.display\n", + "\n", + "# IPython.display.Markdown(pred.loc[2][\"ml_generate_text_llm_result\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TpV-iwP9qw9c" + }, + "source": [ + "## Cleaning up\n", + "\n", + "To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud\n", + "project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n", + "\n", + "To remove any temporary cloud artifacts (inclusing BQ tables) created in the current BigQuery DataFrames session, simply call `close_session`." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "# Delete the temporary cloud artifacts created during the bigframes session \n", + "bpd.close_session()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wCsmt0IwFkDy" + }, + "source": [ + "## Summary and next steps\n", + "\n", + "1. You created BigQuery DataFrames objects, and inspected and manipulated data with pandas APIs at BigQuery scale and speed.\n", + "\n", + "1. You also created ML model from a DataFrame and used them to run predictions on another DataFrame.\n", + "\n", + "1. You got access to Google's state-of-the-art Gemini LLM through simple pythonic API.\n", + "\n", + "Learn more about BigQuery DataFrames in the documentation [BigQuery DataFrames](https://cloud.google.com/bigquery/docs/bigquery-dataframes-introduction) and its [API reference](https://cloud.google.com/python/docs/reference/bigframes/latest).\n", + "\n", + "Also, find more sample notebooks in the [GitHub repo](https://github.com/googleapis/python-bigquery-dataframes/tree/main/notebooks), including the [pypi.ipynb](https://github.com/googleapis/python-bigquery-dataframes/blob/main/notebooks/dataframes/pypi.ipynb) that processes 400+ TB data at the cost and efficiency close to direct SQL by taking advantage of the [partial ordering](https://cloud.google.com/python/docs/reference/bigframes/latest/bigframes._config.bigquery_options.BigQueryOptions#bigframes__config_bigquery_options_BigQueryOptions_ordering_mode) mode." + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.10.16" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/packages/bigframes/notebooks/kaggle/vector-search-with-bigframes-over-national-jukebox.ipynb b/packages/bigframes/notebooks/kaggle/vector-search-with-bigframes-over-national-jukebox.ipynb index 317ba0f1adba..6b689488e57b 100644 --- a/packages/bigframes/notebooks/kaggle/vector-search-with-bigframes-over-national-jukebox.ipynb +++ b/packages/bigframes/notebooks/kaggle/vector-search-with-bigframes-over-national-jukebox.ipynb @@ -1,8 +1,24 @@ { "cells": [ { - "id": "f4ece66a", "cell_type": "markdown", + "id": "f4ece66a", + "metadata": { + "@deathbeds/jupyterlab-fonts": { + "styles": { + "": { + "body[data-jp-deck-mode='presenting'] &": { + "zoom": "194%" + } + } + } + }, + "editable": true, + "slideshow": { + "slide_type": "subslide" + }, + "tags": [] + }, "source": [ "# Creating a searchable index of the National Jukebox\n", "\n", @@ -20,42 +36,44 @@ "To follow along, you'll need a Google Cloud project\n", "\n", "* Go to https://cloud.google.com/free to start a free trial." - ], + ] + }, + { + "cell_type": "markdown", + "id": "bc01a1d3", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { "": { "body[data-jp-deck-mode='presenting'] &": { - "zoom": "194%" + "z-index": "0", + "zoom": "216%" } } } }, - "editable": true, "slideshow": { - "slide_type": "subslide" - }, - "tags": [] + "slide_type": "slide" + } }, - "execution_count": null - }, - { - "id": "bc01a1d3", - "cell_type": "markdown", "source": [ "The National Jukebox is a project of the USA Library of Congress to provide access to thousands of acoustic sound recordings from the very earliest days of the commercial record industry.\n", "\n", "* Learn more at https://www.loc.gov/collections/national-jukebox/about-this-collection/\n", "\n", "\"recording" - ], + ] + }, + { + "cell_type": "markdown", + "id": "4fc7c468", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { "": { "body[data-jp-deck-mode='presenting'] &": { "z-index": "0", - "zoom": "216%" + "zoom": "181%" } } } @@ -64,11 +82,6 @@ "slide_type": "slide" } }, - "execution_count": null - }, - { - "id": "4fc7c468", - "cell_type": "markdown", "source": [ "\n", "To search the National Jukebox, we combine powerful features of BigQuery:\n", @@ -86,32 +99,11 @@ "3. BigQuery DataFrames to use Python instead of SQL.\n", "\n", " https://cloud.google.com/bigquery/docs/bigquery-dataframes-introduction" - ], - "metadata": { - "@deathbeds/jupyterlab-fonts": { - "styles": { - "": { - "body[data-jp-deck-mode='presenting'] &": { - "z-index": "0", - "zoom": "181%" - } - } - } - }, - "slideshow": { - "slide_type": "slide" - } - }, - "execution_count": null + ] }, { - "id": "90f2e543", "cell_type": "markdown", - "source": [ - "## Getting started with BigQuery DataFrames (bigframes)\n", - "\n", - "Install the bigframes package." - ], + "id": "90f2e543", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -126,14 +118,16 @@ "slide_type": "slide" } }, - "execution_count": null + "source": [ + "## Getting started with BigQuery DataFrames (bigframes)\n", + "\n", + "Install the bigframes package." + ] }, { - "id": "56694cb4", "cell_type": "code", - "source": [ - "%pip install --upgrade bigframes google-cloud-automl google-cloud-translate google-ai-generativelanguage tensorflow " - ], + "execution_count": null, + "id": "56694cb4", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -153,17 +147,14 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "%pip install --upgrade bigframes google-cloud-automl google-cloud-translate google-ai-generativelanguage tensorflow " + ] }, { - "id": "fa84ad03", "cell_type": "markdown", - "source": [ - "**Important:** restart the kernel by going to \"Run -> Restart & clear cell outputs\" before continuing.\n", - "\n", - "Configure bigframes to use your GCP project. First, go to \"Add-ons -> Google Cloud SDK\" and click the \"Attach\" button. Then," - ], + "id": "fa84ad03", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -176,17 +167,16 @@ } } }, - "execution_count": null + "source": [ + "**Important:** restart the kernel by going to \"Run -> Restart & clear cell outputs\" before continuing.\n", + "\n", + "Configure bigframes to use your GCP project. First, go to \"Add-ons -> Google Cloud SDK\" and click the \"Attach\" button. Then," + ] }, { - "id": "1fbd4f9e", "cell_type": "code", - "source": [ - "from kaggle_secrets import UserSecretsClient\n", - "user_secrets = UserSecretsClient()\n", - "user_credential = user_secrets.get_gcloud_credential()\n", - "user_secrets.set_tensorflow_credential(user_credential)" - ], + "execution_count": null, + "id": "1fbd4f9e", "metadata": { "execution": { "iopub.execute_input": "2025-08-14T15:53:08.494636Z", @@ -197,22 +187,19 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "from kaggle_secrets import UserSecretsClient\n", + "\n", + "user_secrets = UserSecretsClient()\n", + "user_credential = user_secrets.get_gcloud_credential()\n", + "user_secrets.set_tensorflow_credential(user_credential)" + ] }, { - "id": "0b0b1cd8", "cell_type": "code", - "source": [ - "import bigframes._config\n", - "import bigframes.pandas as bpd\n", - "\n", - "PROJECT_ID = \"your-project-id\" # @param {type:\"string\"}\n", - "bpd.options.bigquery.location = \"US\"\n", - "\n", - "# Set to your GCP project ID.\n", - "bpd.options.bigquery.project = PROJECT_ID" - ], + "execution_count": null, + "id": "0b0b1cd8", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -232,17 +219,21 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "import bigframes._config\n", + "import bigframes.pandas as bpd\n", + "\n", + "PROJECT_ID = \"your-project-id\" # @param {type:\"string\"}\n", + "bpd.options.bigquery.location = \"US\"\n", + "\n", + "# Set to your GCP project ID.\n", + "bpd.options.bigquery.project = PROJECT_ID" + ] }, { - "id": "32e58a7f", "cell_type": "markdown", - "source": [ - "## Reading data\n", - "\n", - "BigQuery DataFrames can read data from BigQuery, GCS, or even local sources. With `engine=\"bigquery\"`, BigQuery's distributed processing reads the file without it ever having to reach your local Python environment." - ], + "id": "32e58a7f", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -257,19 +248,16 @@ "slide_type": "slide" } }, - "execution_count": null + "source": [ + "## Reading data\n", + "\n", + "BigQuery DataFrames can read data from BigQuery, GCS, or even local sources. With `engine=\"bigquery\"`, BigQuery's distributed processing reads the file without it ever having to reach your local Python environment." + ] }, { - "id": "e52aa9e8", "cell_type": "code", - "source": [ - "df = bpd.read_json(\n", - " \"gs://cloud-samples-data/third-party/usa-loc-national-jukebox/jukebox.jsonl\",\n", - " engine=\"bigquery\",\n", - " orient=\"records\",\n", - " lines=True,\n", - ")" - ], + "execution_count": null, + "id": "e52aa9e8", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -289,16 +277,20 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "df = bpd.read_json(\n", + " \"gs://cloud-samples-data/third-party/usa-loc-national-jukebox/jukebox.jsonl\",\n", + " engine=\"bigquery\",\n", + " orient=\"records\",\n", + " lines=True,\n", + ")" + ] }, { - "id": "0c1fca97", "cell_type": "code", - "source": [ - "# Use `peek()` instead of `head()` to see arbitrary rows rather than the \"first\" rows.\n", - "df.peek()" - ], + "execution_count": null, + "id": "0c1fca97", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -321,15 +313,16 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# Use `peek()` instead of `head()` to see arbitrary rows rather than the \"first\" rows.\n", + "df.peek()" + ] }, { - "id": "4a13e789", "cell_type": "code", - "source": [ - "df.shape" - ], + "execution_count": null, + "id": "4a13e789", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -349,18 +342,15 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "df.shape" + ] }, { - "id": "26b8baba", "cell_type": "code", - "source": [ - "# For the purposes of a demo, select only a subset of rows.\n", - "df = df.sample(n=250)\n", - "df.cache()\n", - "df.shape" - ], + "execution_count": null, + "id": "26b8baba", "metadata": { "execution": { "iopub.execute_input": "2025-08-14T15:55:55.448664Z", @@ -371,32 +361,18 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# For the purposes of a demo, select only a subset of rows.\n", + "df = df.sample(n=250)\n", + "df.cache()\n", + "df.shape" + ] }, { - "id": "af84cb21", "cell_type": "code", - "source": [ - "# As a side effect of how I extracted the song information from the HTML DOM,\n", - "# we ended up with lists in places where we only expect one item.\n", - "#\n", - "# We can \"explode\" to flatten these lists.\n", - "flattened = df.explode([\n", - " \"Recording Repository\",\n", - " \"Recording Label\",\n", - " \"Recording Take Number\",\n", - " \"Recording Date\",\n", - " \"Recording Matrix Number\",\n", - " \"Recording Catalog Number\",\n", - " \"Media Size\",\n", - " \"Recording Location\",\n", - " \"Summary\",\n", - " \"Rights Advisory\",\n", - " \"Title\",\n", - "])\n", - "flattened.peek()" - ], + "execution_count": null, + "id": "af84cb21", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -419,15 +395,32 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# As a side effect of how I extracted the song information from the HTML DOM,\n", + "# we ended up with lists in places where we only expect one item.\n", + "#\n", + "# We can \"explode\" to flatten these lists.\n", + "flattened = df.explode([\n", + " \"Recording Repository\",\n", + " \"Recording Label\",\n", + " \"Recording Take Number\",\n", + " \"Recording Date\",\n", + " \"Recording Matrix Number\",\n", + " \"Recording Catalog Number\",\n", + " \"Media Size\",\n", + " \"Recording Location\",\n", + " \"Summary\",\n", + " \"Rights Advisory\",\n", + " \"Title\",\n", + "])\n", + "flattened.peek()" + ] }, { - "id": "085deffd", "cell_type": "code", - "source": [ - "flattened.shape" - ], + "execution_count": null, + "id": "085deffd", "metadata": { "execution": { "iopub.execute_input": "2025-08-14T15:56:06.546531Z", @@ -438,15 +431,14 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "flattened.shape" + ] }, { - "id": "f8e653ee", "cell_type": "markdown", - "source": [ - "To access unstructured data from BigQuery, create a URI pointing to a file in Google Cloud Storage (GCS). Then, construct a \"blob\" (also known as an \"Object Ref\" in BigQuery terms) so that BigQuery can read from GCS." - ], + "id": "f8e653ee", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -463,14 +455,14 @@ }, "tags": [] }, - "execution_count": null + "source": [ + "To access unstructured data from BigQuery, create a URI pointing to a file in Google Cloud Storage (GCS). Then, construct a \"blob\" (also known as an \"Object Ref\" in BigQuery terms) so that BigQuery can read from GCS." + ] }, { - "id": "dbd1a844", "cell_type": "code", - "source": [ - "flattened = flattened.assign(**{\\n \"GCS Prefix\": \"gs://cloud-samples-data/third-party/usa-loc-national-jukebox/\",\\n \"GCS Stub\": flattened['URL'].str.extract(r'/(jukebox-[0-9]+)/'),\\n})\\nflattened[\"GCS URI\"] = flattened[\"GCS Prefix\"] + flattened[\"GCS Stub\"] + \".mp3\"" - ], + "execution_count": null, + "id": "dbd1a844", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -495,15 +487,18 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "flattened = flattened.assign(**{\n", + " \"GCS Prefix\": \"gs://cloud-samples-data/third-party/usa-loc-national-jukebox/\",\n", + " \"GCS Stub\": flattened['URL'].str.extract(r'/(jukebox-[0-9]+)/'),\n", + "})\n", + "flattened[\"GCS URI\"] = flattened[\"GCS Prefix\"] + flattened[\"GCS Stub\"] + \".mp3\"" + ] }, { - "id": "fae13ec5", "cell_type": "markdown", - "source": [ - "BigQuery (and BigQuery DataFrames) provide access to powerful models and multimodal capabilities. Here, we transcribe audio to text." - ], + "id": "fae13ec5", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -520,11 +515,30 @@ }, "tags": [] }, - "execution_count": null + "source": [ + "BigQuery (and BigQuery DataFrames) provide access to powerful models and multimodal capabilities. Here, we transcribe audio to text." + ] }, { - "id": "f08f92b1", "cell_type": "code", + "execution_count": null, + "id": "f08f92b1", + "metadata": { + "editable": true, + "execution": { + "iopub.execute_input": "2025-08-14T15:56:20.908198Z", + "iopub.status.busy": "2025-08-14T15:56:20.907791Z", + "iopub.status.idle": "2025-08-14T15:58:45.909086Z", + "shell.execute_reply": "2025-08-14T15:58:45.908060Z", + "shell.execute_reply.started": "2025-08-14T15:56:20.908170Z" + }, + "slideshow": { + "slide_type": "" + }, + "tags": [], + "trusted": true + }, + "outputs": [], "source": [ "import bigframes.bigquery as bbq\n", "\n", @@ -539,7 +553,7 @@ "# Call GenAI model to perform audio transcription\n", "raw_results = bbq.ai.generate(\n", " prompt=(\"Transcribe the provided audio.\", audio_runtime),\n", - " endpoint=\"gemini-2.5-flash\"\n", + " endpoint=\"gemini-3.5-flash\"\n", ")\n", "\n", "# Package result struct to contain 'content' and 'status' expected by downstream cells\n", @@ -548,31 +562,11 @@ " \"status\": raw_results.struct.field(\"status\")\n", "})\n", "flattened[\"Transcription\"] = bbq.struct(transcription_df)" - ], - "metadata": { - "editable": true, - "execution": { - "iopub.execute_input": "2025-08-14T15:56:20.908198Z", - "iopub.status.busy": "2025-08-14T15:56:20.907791Z", - "iopub.status.idle": "2025-08-14T15:58:45.909086Z", - "shell.execute_reply": "2025-08-14T15:58:45.908060Z", - "shell.execute_reply.started": "2025-08-14T15:56:20.908170Z" - }, - "slideshow": { - "slide_type": "" - }, - "tags": [], - "trusted": true - }, - "execution_count": null, - "outputs": [] + ] }, { - "id": "30969ae1", "cell_type": "markdown", - "source": [ - "Sometimes the model has transient errors. Check the status column to see if there are errors." - ], + "id": "30969ae1", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -587,16 +581,14 @@ "slide_type": "slide" } }, - "execution_count": null + "source": [ + "Sometimes the model has transient errors. Check the status column to see if there are errors." + ] }, { - "id": "7d0dbc38", "cell_type": "code", - "source": [ - "print(f\"Successful rows: {(flattened['Transcription'].struct.field('status') == '').sum()}\")\n", - "print(f\"Failed rows: {(flattened['Transcription'].struct.field('status') != '').sum()}\")\n", - "flattened.shape" - ], + "execution_count": null, + "id": "7d0dbc38", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -621,16 +613,17 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "print(f\"Successful rows: {(flattened['Transcription'].struct.field('status') == '').sum()}\")\n", + "print(f\"Failed rows: {(flattened['Transcription'].struct.field('status') != '').sum()}\")\n", + "flattened.shape" + ] }, { - "id": "6cddf53b", "cell_type": "code", - "source": [ - "# Show transcribed lyrics.\n", - "flattened[\"Transcription\"].struct.field(\"content\")" - ], + "execution_count": null, + "id": "6cddf53b", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -650,19 +643,16 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] - }, - { - "id": "ba0386cc", - "cell_type": "code", + "outputs": [], "source": [ - "# Find all instrumentatal songs\n", - "instrumental = flattened[flattened[\"Transcription\"].struct.field(\"content\") == \"\"]\n", - "print(instrumental.shape)\n", - "song = instrumental.peek(1)\n", - "song" - ], + "# Show transcribed lyrics.\n", + "flattened[\"Transcription\"].struct.field(\"content\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba0386cc", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -685,22 +675,19 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# Find all instrumentatal songs\n", + "instrumental = flattened[flattened[\"Transcription\"].struct.field(\"content\") == \"\"]\n", + "print(instrumental.shape)\n", + "song = instrumental.peek(1)\n", + "song" + ] }, { - "id": "61a883b2", "cell_type": "code", - "source": [ - "import gcsfs\n", - "import IPython.display\n", - "\n", - "fs = gcsfs.GCSFileSystem(project='bigframes-dev')\n", - "with fs.open(song[\"GCS URI\"].iloc[0]) as song_file:\n", - " song_bytes = song_file.read()\n", - "\n", - "IPython.display.Audio(song_bytes)" - ], + "execution_count": null, + "id": "61a883b2", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -725,19 +712,21 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] - }, - { - "id": "e8a25c46", - "cell_type": "markdown", + "outputs": [], "source": [ - "## Creating a searchable index\n", + "import gcsfs\n", + "import IPython.display\n", "\n", - "To be able to search by semantics rather than just text, generate embeddings and then create an index to efficiently search these.\n", + "fs = gcsfs.GCSFileSystem(project='bigframes-dev')\n", + "with fs.open(song[\"GCS URI\"].iloc[0]) as song_file:\n", + " song_bytes = song_file.read()\n", "\n", - "See also, this example: https://github.com/googleapis/python-bigquery-dataframes/blob/main/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb" - ], + "IPython.display.Audio(song_bytes)" + ] + }, + { + "cell_type": "markdown", + "id": "e8a25c46", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -752,16 +741,18 @@ "slide_type": "slide" } }, - "execution_count": null + "source": [ + "## Creating a searchable index\n", + "\n", + "To be able to search by semantics rather than just text, generate embeddings and then create an index to efficiently search these.\n", + "\n", + "See also, this example: https://github.com/googleapis/python-bigquery-dataframes/blob/main/notebooks/generative_ai/bq_dataframes_llm_vector_search.ipynb" + ] }, { - "id": "ead0fa8c", "cell_type": "code", - "source": [ - "from bigframes.ml.llm import TextEmbeddingGenerator\n", - "\n", - "text_model = TextEmbeddingGenerator(model_name=\"text-multilingual-embedding-002\")" - ], + "execution_count": null, + "id": "ead0fa8c", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -781,21 +772,17 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "from bigframes.ml.llm import TextEmbeddingGenerator\n", + "\n", + "text_model = TextEmbeddingGenerator(model_name=\"text-multilingual-embedding-002\")" + ] }, { - "id": "5ed7776d", "cell_type": "code", - "source": [ - "df_to_index = (\n", - " flattened\n", - " .assign(content=flattened[\"Transcription\"].struct.field(\"content\"))\n", - " [flattened[\"Transcription\"].struct.field(\"content\") != \"\"]\n", - ")\n", - "embedding = text_model.predict(df_to_index)\n", - "embedding.peek(1)" - ], + "execution_count": null, + "id": "5ed7776d", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -815,18 +802,21 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "df_to_index = (\n", + " flattened\n", + " .assign(content=flattened[\"Transcription\"].struct.field(\"content\"))\n", + " [flattened[\"Transcription\"].struct.field(\"content\") != \"\"]\n", + ")\n", + "embedding = text_model.predict(df_to_index)\n", + "embedding.peek(1)" + ] }, { - "id": "c96e9832", "cell_type": "code", - "source": [ - "# Check the status column to look for errors.\n", - "print(f\"Successful rows: {(embedding['ml_generate_embedding_status'] == '').sum()}\")\n", - "print(f\"Failed rows: {(embedding['ml_generate_embedding_status'] != '').sum()}\")\n", - "embedding.shape" - ], + "execution_count": null, + "id": "c96e9832", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -851,15 +841,17 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# Check the status column to look for errors.\n", + "print(f\"Successful rows: {(embedding['ml_generate_embedding_status'] == '').sum()}\")\n", + "print(f\"Failed rows: {(embedding['ml_generate_embedding_status'] != '').sum()}\")\n", + "embedding.shape" + ] }, { - "id": "0e2a5d7b", "cell_type": "markdown", - "source": [ - "We're now ready to save this to a table." - ], + "id": "0e2a5d7b", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -871,15 +863,14 @@ } } }, - "execution_count": null + "source": [ + "We're now ready to save this to a table." + ] }, { - "id": "51819a0c", "cell_type": "code", - "source": [ - "embedding_table_id = f\"{bpd.options.bigquery.project}.kaggle.national_jukebox\"\n", - "embedding.to_gbq(embedding_table_id, if_exists=\"replace\")" - ], + "execution_count": null, + "id": "51819a0c", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -899,20 +890,15 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "embedding_table_id = f\"{bpd.options.bigquery.project}.kaggle.national_jukebox\"\n", + "embedding.to_gbq(embedding_table_id, if_exists=\"replace\")" + ] }, { - "id": "5e16fb14", "cell_type": "markdown", - "source": [ - "## Searching the database\n", - "\n", - "To search by semantics, we:\n", - "\n", - "1. Turn our search string into an embedding using the same model as our index.\n", - "2. Find the closest matches to the search string." - ], + "id": "5e16fb14", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -927,17 +913,19 @@ "slide_type": "slide" } }, - "execution_count": null + "source": [ + "## Searching the database\n", + "\n", + "To search by semantics, we:\n", + "\n", + "1. Turn our search string into an embedding using the same model as our index.\n", + "2. Find the closest matches to the search string." + ] }, { - "id": "1bad3317", "cell_type": "code", - "source": [ - "import bigframes.pandas as bpd\n", - "\n", - "df_written = bpd.read_gbq(embedding_table_id)\n", - "df_written.peek(1)" - ], + "execution_count": null, + "id": "1bad3317", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -960,22 +948,18 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "import bigframes.pandas as bpd\n", + "\n", + "df_written = bpd.read_gbq(embedding_table_id)\n", + "df_written.peek(1)" + ] }, { - "id": "8aaaef1f", "cell_type": "code", - "source": [ - "from bigframes.ml.llm import TextEmbeddingGenerator\n", - "\n", - "search_string = \"walking home\"\n", - "\n", - "text_model = TextEmbeddingGenerator(model_name=\"text-multilingual-embedding-002\")\n", - "search_df = bpd.DataFrame([search_string], columns=['search_string'])\n", - "search_embedding = text_model.predict(search_df)\n", - "search_embedding" - ], + "execution_count": null, + "id": "8aaaef1f", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -995,24 +979,22 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "from bigframes.ml.llm import TextEmbeddingGenerator\n", + "\n", + "search_string = \"walking home\"\n", + "\n", + "text_model = TextEmbeddingGenerator(model_name=\"text-multilingual-embedding-002\")\n", + "search_df = bpd.DataFrame([search_string], columns=['search_string'])\n", + "search_embedding = text_model.predict(search_df)\n", + "search_embedding" + ] }, { - "id": "908a2340", "cell_type": "code", - "source": [ - "import bigframes.bigquery as bbq\n", - "\n", - "vector_search_results = bbq.vector_search(\n", - " base_table=embedding_table_id,\n", - " column_to_search=\"ml_generate_embedding_result\",\n", - " query=search_embedding,\n", - " distance_type=\"COSINE\",\n", - " query_column_to_search=\"ml_generate_embedding_result\",\n", - " top_k=5,\n", - ")" - ], + "execution_count": null, + "id": "908a2340", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -1037,15 +1019,24 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "import bigframes.bigquery as bbq\n", + "\n", + "vector_search_results = bbq.vector_search(\n", + " base_table=embedding_table_id,\n", + " column_to_search=\"ml_generate_embedding_result\",\n", + " query=search_embedding,\n", + " distance_type=\"COSINE\",\n", + " query_column_to_search=\"ml_generate_embedding_result\",\n", + " top_k=5,\n", + ")" + ] }, { - "id": "f84ebe70", "cell_type": "code", - "source": [ - "vector_search_results.dtypes" - ], + "execution_count": null, + "id": "f84ebe70", "metadata": { "execution": { "iopub.execute_input": "2025-08-14T16:05:50.566930Z", @@ -1056,16 +1047,15 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "vector_search_results.dtypes" + ] }, { - "id": "eeff1c72", "cell_type": "code", - "source": [ - "results = vector_search_results[[\"Title\", \"Summary\", \"Names\", \"GCS URI\", \"Transcription\", \"distance\"]].sort_values(\"distance\").to_pandas()\n", - "results" - ], + "execution_count": null, + "id": "eeff1c72", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -1088,15 +1078,16 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "results = vector_search_results[[\"Title\", \"Summary\", \"Names\", \"GCS URI\", \"Transcription\", \"distance\"]].sort_values(\"distance\").to_pandas()\n", + "results" + ] }, { - "id": "7ec53675", "cell_type": "code", - "source": [ - "print(results[\"Transcription\"].struct.field(\"content\").iloc[0])" - ], + "execution_count": null, + "id": "7ec53675", "metadata": { "@deathbeds/jupyterlab-fonts": { "styles": { @@ -1116,22 +1107,15 @@ }, "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "print(results[\"Transcription\"].struct.field(\"content\").iloc[0])" + ] }, { - "id": "a96552fb", "cell_type": "code", - "source": [ - "import gcsfs\n", - "import IPython.display\n", - "\n", - "fs = gcsfs.GCSFileSystem(project='bigframes-dev')\n", - "with fs.open(results[\"GCS URI\"].iloc[0]) as song_file:\n", - " song_bytes = song_file.read()\n", - "\n", - "IPython.display.Audio(song_bytes)" - ], + "execution_count": null, + "id": "a96552fb", "metadata": { "editable": true, "execution": { @@ -1148,18 +1132,27 @@ "tags": [], "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "import gcsfs\n", + "import IPython.display\n", + "\n", + "fs = gcsfs.GCSFileSystem(project='bigframes-dev')\n", + "with fs.open(results[\"GCS URI\"].iloc[0]) as song_file:\n", + " song_bytes = song_file.read()\n", + "\n", + "IPython.display.Audio(song_bytes)" + ] }, { - "id": "72af7c7f", "cell_type": "code", - "source": [], + "execution_count": null, + "id": "72af7c7f", "metadata": { "trusted": true }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [] } ], "metadata": { @@ -1196,6 +1189,6 @@ "version": "3.11.13" } }, - "nbformat_minor": 4, - "nbformat": 4 -} \ No newline at end of file + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/packages/bigframes/notebooks/multimodal/multimodal_dataframe.ipynb b/packages/bigframes/notebooks/multimodal/multimodal_dataframe.ipynb index cd363db6f362..6c172ce43481 100644 --- a/packages/bigframes/notebooks/multimodal/multimodal_dataframe.ipynb +++ b/packages/bigframes/notebooks/multimodal/multimodal_dataframe.ipynb @@ -125,6 +125,7 @@ "FULL_CONNECTION_ID = f\"{PROJECT}.{LOCATION}.bigframes-default-connection\"\n", "\n", "import bigframes\n", + "\n", "# Setup project\n", "bigframes.options.bigquery.project = PROJECT\n", "bigframes.options.bigquery.location = LOCATION\n", @@ -133,8 +134,8 @@ "bigframes.options.display.blob_display_width = 300\n", "bigframes.options.display.progress_bar = None\n", "\n", - "import bigframes.pandas as bpd\n", - "import bigframes.bigquery as bbq" + "import bigframes.bigquery as bbq\n", + "import bigframes.pandas as bpd" ] }, { @@ -146,6 +147,7 @@ "source": [ "import bigframes.bigquery as bbq\n", "\n", + "\n", "def get_runtime_json_str(series, mode=\"R\", with_metadata=False):\n", " \"\"\"\n", " Get the runtime (contains signed URL to access gcs data) and apply the\n", @@ -185,13 +187,15 @@ "\n", "from IPython.display import HTML, display\n", "\n", + "\n", "def render_images(df):\n", " \"\"\"Helper to display BigFrames DataFrame with rendered image previews.\"\"\"\n", - " import bigframes.pandas as bpd\n", - " import bigframes.bigquery as bbq\n", + " import json\n", + "\n", " import bigframes\n", + " import bigframes.bigquery as bbq\n", + " import bigframes.pandas as bpd\n", " from bigframes import dtypes\n", - " import json\n", " \n", " if isinstance(df, bpd.Series):\n", " df = df.to_frame()\n", @@ -275,6 +279,7 @@ "outputs": [], "source": [ "import gcsfs\n", + "\n", "import bigframes.bigquery as bbq\n", "\n", "# List files using gcsfs (public bucket)\n", @@ -582,11 +587,12 @@ " packages=[\"opencv-python-headless\", \"numpy\", \"requests\"],\n", ")\n", "def image_blur(src_rt: str, dst_rt: str, kx: int, ky: int) -> str:\n", + " import base64\n", " import json\n", + "\n", " import cv2 as cv\n", " import numpy as np\n", " import requests\n", - " import base64\n", "\n", " src_obj = json.loads(src_rt)\n", " if \"access_urls\" not in src_obj:\n", @@ -682,6 +688,7 @@ "outputs": [], "source": [ "from bigframes.ml import llm\n", + "\n", "gemini = llm.GeminiTextGenerator()" ] }, @@ -794,8 +801,9 @@ "def pdf_extract(src_obj_ref_rt: str) -> str:\n", " import io\n", " import json\n", - " from pypdf import PdfReader\n", + "\n", " import requests\n", + " from pypdf import PdfReader\n", " src_obj_ref_rt_json = json.loads(src_obj_ref_rt)\n", " src_url = src_obj_ref_rt_json[\"access_urls\"][\"read_url\"]\n", " response = requests.get(src_url, timeout=30, stream=True)\n", @@ -821,8 +829,9 @@ "def pdf_chunk(src_obj_ref_rt: str, chunk_size: int, overlap_size: int) -> list[str]:\n", " import io\n", " import json\n", - " from pypdf import PdfReader\n", + "\n", " import requests\n", + " from pypdf import PdfReader\n", " src_obj_ref_rt_json = json.loads(src_obj_ref_rt)\n", " src_url = src_obj_ref_rt_json[\"access_urls\"][\"read_url\"]\n", " response = requests.get(src_url, timeout=30, stream=True)\n", @@ -857,6 +866,7 @@ "outputs": [], "source": [ "import gcsfs\n", + "\n", "import bigframes.bigquery as bbq\n", "\n", "# List files using gcsfs\n", @@ -913,6 +923,7 @@ "outputs": [], "source": [ "import gcsfs\n", + "\n", "import bigframes.bigquery as bbq\n", "\n", "audio_gcs_path = \"gs://bigframes_blob_test/audio/*\"\n", @@ -962,7 +973,7 @@ "\n", "transcribed_results = bbq.ai.generate(\n", " prompt=(prompt_text, audio_runtime),\n", - " endpoint=\"gemini-2.5-flash\",\n", + " endpoint=\"gemini-3.5-flash\",\n", " model_params={\"generationConfig\": {\"temperature\": 0.0}},\n", ")\n", "\n", @@ -1032,8 +1043,9 @@ "def extract_exif(src_obj_ref_rt: str) -> str:\n", " import io\n", " import json\n", - " from PIL import ExifTags, Image\n", + "\n", " import requests\n", + " from PIL import ExifTags, Image\n", " src_obj_ref_rt_json = json.loads(src_obj_ref_rt)\n", " src_url = src_obj_ref_rt_json[\"access_urls\"][\"read_url\"]\n", " response = requests.get(src_url, timeout=30)\n", @@ -1056,6 +1068,7 @@ "outputs": [], "source": [ "import gcsfs\n", + "\n", "import bigframes.bigquery as bbq\n", "\n", "# Create a Multimodal DataFrame from the sample image URIs\n", diff --git a/packages/bigframes/tests/system/large/bigquery/test_ai.py b/packages/bigframes/tests/system/large/bigquery/test_ai.py index 504fe5aa3897..bc4241a58e29 100644 --- a/packages/bigframes/tests/system/large/bigquery/test_ai.py +++ b/packages/bigframes/tests/system/large/bigquery/test_ai.py @@ -33,7 +33,7 @@ def text_model(bq_connection, dataset_id): model_name = f"{dataset_id}.text_model" return ml.create_model( model_name=model_name, - options={"endpoint": "gemini-2.5-flash"}, + options={"endpoint": "gemini-3.5-flash"}, connection_name=bq_connection, ) diff --git a/packages/bigframes/tests/system/large/ml/test_llm.py b/packages/bigframes/tests/system/large/ml/test_llm.py index 638e151ca141..2f11883818dc 100644 --- a/packages/bigframes/tests/system/large/ml/test_llm.py +++ b/packages/bigframes/tests/system/large/ml/test_llm.py @@ -30,6 +30,8 @@ "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) @pytest.mark.flaky(retries=2) @@ -57,6 +59,8 @@ def test_create_load_gemini_text_generator_model( "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) @pytest.mark.flaky(retries=2) @@ -78,6 +82,8 @@ def test_gemini_text_generator_predict_default_params_success( "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) @pytest.mark.flaky(retries=2) @@ -101,6 +107,8 @@ def test_gemini_text_generator_predict_with_params_success( "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) @pytest.mark.flaky(retries=2) @@ -126,6 +134,8 @@ def test_gemini_text_generator_multi_cols_predict_success( "gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) @pytest.mark.flaky(retries=2) @@ -168,6 +178,8 @@ def test_gemini_text_generator_predict_output_schema_success( ( "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) def test_llm_gemini_score(llm_fine_tune_df_default_index, model_name): @@ -197,6 +209,8 @@ def test_llm_gemini_score(llm_fine_tune_df_default_index, model_name): ( "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) def test_llm_gemini_pro_score_params(llm_fine_tune_df_default_index, model_name): diff --git a/packages/bigframes/tests/system/large/operations/conftest.py b/packages/bigframes/tests/system/large/operations/conftest.py index 0122c860ca74..7ae35d12213b 100644 --- a/packages/bigframes/tests/system/large/operations/conftest.py +++ b/packages/bigframes/tests/system/large/operations/conftest.py @@ -22,7 +22,7 @@ def gemini_flash_model(session, bq_connection) -> llm.GeminiTextGenerator: return llm.GeminiTextGenerator( session=session, connection_name=bq_connection, - model_name="gemini-2.5-flash", + model_name="gemini-3.5-flash", ) diff --git a/packages/bigframes/tests/system/load/test_llm.py b/packages/bigframes/tests/system/load/test_llm.py index eec76cf9b679..1570287c07ce 100644 --- a/packages/bigframes/tests/system/load/test_llm.py +++ b/packages/bigframes/tests/system/load/test_llm.py @@ -43,6 +43,8 @@ def llm_remote_text_df(session, llm_remote_text_pandas_df): ( "gemini-2.5-flash", "gemini-2.5-flash-lite", + "gemini-3.1-flash-lite", + "gemini-3.5-flash", ), ) def test_llm_gemini_configure_fit( @@ -79,7 +81,7 @@ def test_llm_gemini_configure_fit( @pytest.mark.flaky(retries=2) def test_llm_gemini_w_ground_with_google_search(llm_remote_text_df): - model = llm.GeminiTextGenerator(model_name="gemini-2.5-flash", max_iterations=1) + model = llm.GeminiTextGenerator(model_name="gemini-3.5-flash", max_iterations=1) df = model.predict( llm_remote_text_df["prompt"], ground_with_google_search=True, diff --git a/packages/bigframes/tests/system/small/bigquery/test_ai.py b/packages/bigframes/tests/system/small/bigquery/test_ai.py index f3c94edd1969..4cc2f40b5d9b 100644 --- a/packages/bigframes/tests/system/small/bigquery/test_ai.py +++ b/packages/bigframes/tests/system/small/bigquery/test_ai.py @@ -60,7 +60,7 @@ def test_ai_function_pandas_input(session): s2 = bpd.Series(["fruit", "tree"], session=session) prompt = (s1, " is a ", s2) - result = bbq.ai.generate_bool(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate_bool(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( @@ -81,7 +81,7 @@ def test_ai_function_string_input(session): mock_get_session.return_value = session prompt = "Is apple a fruit?" - result = bbq.ai.generate_bool(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate_bool(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( @@ -102,7 +102,7 @@ def test_ai_function_compile_model_params(session): model_params = {"generation_config": {"thinking_config": {"thinking_budget": 0}}} result = bbq.ai.generate_bool( - prompt, endpoint="gemini-2.5-flash", model_params=model_params + prompt, endpoint="gemini-3.5-flash", model_params=model_params ) assert _contains_no_nulls(result) @@ -121,7 +121,7 @@ def test_ai_generate(session): country = bpd.Series(["Japan", "Canada"], session=session) prompt = ("What's the capital city of ", country, "? one word only") - result = bbq.ai.generate(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( @@ -141,7 +141,7 @@ def test_ai_generate_with_output_schema(session): result = bbq.ai.generate( prompt, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", output_schema={"population": "INT64", "is_in_north_america": "bool"}, ) @@ -165,7 +165,7 @@ def test_ai_generate_with_invalid_output_schema_raise_error(session): with pytest.raises(ValueError): bbq.ai.generate( prompt, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", output_schema={"population": "INT64", "is_in_north_america": "JSON"}, ) @@ -175,7 +175,7 @@ def test_ai_generate_bool(session): s2 = bpd.Series(["fruit", "tree"], session=session) prompt = (s1, " is a ", s2) - result = bbq.ai.generate_bool(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate_bool(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( @@ -216,7 +216,7 @@ def test_ai_generate_int(session): s = bpd.Series(["Cat"], session=session) prompt = ("How many legs does a ", s, " have?") - result = bbq.ai.generate_int(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate_int(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( @@ -259,7 +259,7 @@ def test_ai_generate_double(session): s = bpd.Series(["Cat"], session=session) prompt = ("How many legs does a ", s, " have?") - result = bbq.ai.generate_double(prompt, endpoint="gemini-2.5-flash") + result = bbq.ai.generate_double(prompt, endpoint="gemini-3.5-flash") assert _contains_no_nulls(result) assert result.dtype == pd.ArrowDtype( diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_classify_with_params/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_classify_with_params/out.sql index 30542740a2dc..d4f17709e6c8 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_classify_with_params/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_classify_with_params/out.sql @@ -3,7 +3,7 @@ SELECT input => STRUCT(`string_col`), categories => ['greeting', 'rejection'], examples => [('hi', 'greeting'), ('bye', 'rejection')], - endpoint => 'gemini-2.5-flash', + endpoint => 'gemini-3.5-flash', max_error_ratio => 0.1 ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate/out.sql index 622782fa7d65..b4df722e3e5a 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate/out.sql @@ -1,7 +1,7 @@ SELECT AI.GENERATE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash', + endpoint => 'gemini-3.5-flash', request_type => 'SHARED' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool/out.sql index a71bce037a5d..bedca1dd2053 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool/out.sql @@ -1,6 +1,6 @@ SELECT AI.GENERATE_BOOL( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool_with_connection_id/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool_with_connection_id/out.sql index db1ec378aaf9..a4632308e981 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool_with_connection_id/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_bool_with_connection_id/out.sql @@ -2,6 +2,6 @@ SELECT AI.GENERATE_BOOL( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), connection_id => 'bigframes-dev.us.bigframes-default-connection', - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double/out.sql index 1cef75687988..ff939f543d75 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double/out.sql @@ -1,6 +1,6 @@ SELECT AI.GENERATE_DOUBLE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double_with_connection_id/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double_with_connection_id/out.sql index d0088721e386..f73852c10542 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double_with_connection_id/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_double_with_connection_id/out.sql @@ -2,6 +2,6 @@ SELECT AI.GENERATE_DOUBLE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), connection_id => 'bigframes-dev.us.bigframes-default-connection', - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int/out.sql index 9ef143c8b9e4..e8f96741c7f8 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int/out.sql @@ -1,6 +1,6 @@ SELECT AI.GENERATE_INT( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int_with_connection_id/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int_with_connection_id/out.sql index 3fa3e8cc05e1..b3af20bfc859 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int_with_connection_id/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_int_with_connection_id/out.sql @@ -2,6 +2,6 @@ SELECT AI.GENERATE_INT( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), connection_id => 'bigframes-dev.us.bigframes-default-connection', - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_connection_id/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_connection_id/out.sql index 14604cfc8dfd..76f7762f3c3b 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_connection_id/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_connection_id/out.sql @@ -2,6 +2,6 @@ SELECT AI.GENERATE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), connection_id => 'bigframes-dev.us.bigframes-default-connection', - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_output_schema/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_output_schema/out.sql index 31c179e7b01a..60d5b7e1785c 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_output_schema/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_generate_with_output_schema/out.sql @@ -1,7 +1,7 @@ SELECT AI.GENERATE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash', + endpoint => 'gemini-3.5-flash', output_schema => 'x INT64, y FLOAT64' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_if_with_endpoint/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_if_with_endpoint/out.sql index 4dd910528a41..5b919e526584 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_if_with_endpoint/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_if_with_endpoint/out.sql @@ -1,6 +1,6 @@ SELECT AI.IF( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash' + endpoint => 'gemini-3.5-flash' ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_score_with_endpoint_and_max_error_ratio/out.sql b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_score_with_endpoint_and_max_error_ratio/out.sql index a802e5a396bf..93189eb04451 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_score_with_endpoint_and_max_error_ratio/out.sql +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/snapshots/test_ai_ops/test_ai_score_with_endpoint_and_max_error_ratio/out.sql @@ -1,7 +1,7 @@ SELECT AI.SCORE( prompt => STRUCT(`string_col`, ' is the same as ', `string_col`), - endpoint => 'gemini-2.5-flash', + endpoint => 'gemini-3.5-flash', max_error_ratio => 0.5 ) AS `result` FROM `bigframes-dev`.`sqlglot_test`.`scalar_types` AS `bft_0` \ No newline at end of file diff --git a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/test_ai_ops.py b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/test_ai_ops.py index 57c524908607..0b50f655e09c 100644 --- a/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/test_ai_ops.py +++ b/packages/bigframes/tests/unit/core/compile/sqlglot/expressions/test_ai_ops.py @@ -30,7 +30,7 @@ def test_ai_generate(scalar_types_df: dataframe.DataFrame, snapshot): op = ops.AIGenerate( prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", request_type="SHARED", ) @@ -47,7 +47,7 @@ def test_ai_generate_with_connection_id(scalar_types_df: dataframe.DataFrame, sn op = ops.AIGenerate( prompt_context=(None, " is the same as ", None), connection_id=CONNECTION_ID, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -63,7 +63,7 @@ def test_ai_generate_with_output_schema(scalar_types_df: dataframe.DataFrame, sn op = ops.AIGenerate( prompt_context=(None, " is the same as ", None), connection_id=None, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", output_schema="x INT64, y FLOAT64", ) @@ -94,7 +94,7 @@ def test_ai_generate_bool(scalar_types_df: dataframe.DataFrame, snapshot): op = ops.AIGenerateBool( prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -112,7 +112,7 @@ def test_ai_generate_bool_with_connection_id( op = ops.AIGenerateBool( prompt_context=(None, " is the same as ", None), connection_id=CONNECTION_ID, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -145,7 +145,7 @@ def test_ai_generate_int(scalar_types_df: dataframe.DataFrame, snapshot): op = ops.AIGenerateInt( # The prompt does not make semantic sense but we only care about syntax correctness. prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -164,7 +164,7 @@ def test_ai_generate_int_with_connection_id( # The prompt does not make semantic sense but we only care about syntax correctness. prompt_context=(None, " is the same as ", None), connection_id=CONNECTION_ID, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -198,7 +198,7 @@ def test_ai_generate_double(scalar_types_df: dataframe.DataFrame, snapshot): op = ops.AIGenerateDouble( # The prompt does not make semantic sense but we only care about syntax correctness. prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -217,7 +217,7 @@ def test_ai_generate_double_with_connection_id( # The prompt does not make semantic sense but we only care about syntax correctness. prompt_context=(None, " is the same as ", None), connection_id=CONNECTION_ID, - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -322,7 +322,7 @@ def test_ai_if_with_endpoint(scalar_types_df: dataframe.DataFrame, snapshot): op = ops.AIIf( prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", ) sql = utils._apply_ops_to_sql( @@ -354,7 +354,7 @@ def test_ai_classify_with_params(scalar_types_df: dataframe.DataFrame, snapshot) prompt_context=(None,), categories=("greeting", "rejection"), examples=(("hi", "greeting"), ("bye", "rejection")), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", max_error_ratio=0.1, ) @@ -421,7 +421,7 @@ def test_ai_score_with_endpoint_and_max_error_ratio( op = ops.AIScore( prompt_context=(None, " is the same as ", None), - endpoint="gemini-2.5-flash", + endpoint="gemini-3.5-flash", max_error_ratio=0.5, )