diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index b693a8792..a03acac46 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -14,15 +14,15 @@ jobs:
strategy:
matrix:
include:
- - python-version: 3.6
+ - python-version: 3.7
prod-key: LABELBOX_API_KEY
staging-key: STAGING_LABELBOX_API_KEY
da-test-key: DA_GCP_LABELBOX_API_KEY
- - python-version: 3.7
+ - python-version: 3.8
prod-key: PROD_LABELBOX_API_KEY_2
staging-key: STAGING_LABELBOX_API_KEY_2
da-test-key: DA_GCP_LABELBOX_API_KEY
- - python-version: 3.8
+ - python-version: 3.9
prod-key: PROD_LABELBOX_API_KEY_3
staging-key: STAGING_LABELBOX_API_KEY_3
da-test-key: DA_GCP_LABELBOX_API_KEY
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af2f8e8cc..f8500c6d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+# Version 3.23.0 (2022-06-08)
+## Added
+* `Task` objects now have the following properties:
+ * `errors` - fetch information about why the task failed
+ * `result` - fetch the result of the task
+ * These are currently only compatible with data row import tasks.
+* Officially added support for python 3.9
+
+## Removed
+* python 3.6 is no longer officially supported
+
# Version 3.22.1 (2022-05-23)
## Updated
* Renamed `custom_metadata` to `metadata_fields` in DataRow
diff --git a/README.md b/README.md
index c4a465233..0bbf41892 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ The Labelbox Python API offers a simple, user-friendly way to interact with the
## Requirements
-- Use Python 3.6, 3.7 or 3.8
+- Use Python 3.7, 3.8 or 3.9
- [Create an account](http://app.labelbox.com/)
- [Generate an API key](https://labelbox.com/docs/api/getting-started#create_api_key)
diff --git a/examples/basics/data_row_metadata.ipynb b/examples/basics/data_row_metadata.ipynb
index 7c56245c7..1dbb26828 100644
--- a/examples/basics/data_row_metadata.ipynb
+++ b/examples/basics/data_row_metadata.ipynb
@@ -1,649 +1,804 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "id": "db768cda",
- "metadata": {},
- "source": [
- "
\n",
- " \n",
- " | "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "cb5611d0",
- "metadata": {},
- "source": [
- "\n",
- " \n",
- " | \n",
- "\n",
- "\n",
- " \n",
- " | "
- ]
- },
- {
- "cell_type": "markdown",
- "id": "8465cef2",
- "metadata": {
- "id": "8465cef2"
- },
- "source": [
- "# Data Row Metadata\n",
- "\n",
- "Metadata is useful to be better understand data on the platform to help with labeling review, model diagnostics, and data selection. This **should not be confused with attachments**. Attachments provide additional context for labelers but is not searchable within Catalog."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "f61dSEA0o4zY",
- "metadata": {
- "id": "f61dSEA0o4zY"
- },
- "source": [
- "### Installation"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "id": "OL0N3xLRNKK7",
- "metadata": {
- "id": "OL0N3xLRNKK7"
- },
- "outputs": [],
- "source": [
- "!pip install -q --upgrade tensorflow-hub \\\n",
- " scikit-learn \\\n",
- " seaborn \\\n",
- " \"labelbox[data]\""
- ]
- },
- {
- "cell_type": "markdown",
- "id": "SF251uzCGyMx",
- "metadata": {
- "id": "SF251uzCGyMx"
- },
- "source": [
- "## Setup"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "id": "e3472f7c",
- "metadata": {
- "id": "e3472f7c"
- },
- "outputs": [],
- "source": [
- "import labelbox\n",
- "import random\n",
- "import numpy as np\n",
- "from labelbox import Client\n",
- "from labelbox.schema.data_row_metadata import (\n",
- " DataRowMetadata,\n",
- " DataRowMetadataField,\n",
- " DeleteDataRowMetadata,\n",
- ")\n",
- "from sklearn.random_projection import GaussianRandomProjection\n",
- "import tensorflow as tf\n",
- "import seaborn as sns\n",
- "import tensorflow_hub as hub\n",
- "from datetime import datetime\n",
- "from tqdm.notebook import tqdm\n",
- "import requests\n",
- "from pprint import pprint"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "014d3d1b",
- "metadata": {},
- "source": [
- "# API Key and Client\n",
- "Provide a valid api key below in order to properly connect to the Labelbox Client."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "id": "ogVqVzGlNXBY",
- "metadata": {
- "id": "ogVqVzGlNXBY"
- },
- "outputs": [],
- "source": [
- "# Add your api key\n",
- "API_KEY = None\n",
- "client = Client(api_key=API_KEY)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "c5ca6b13",
- "metadata": {
- "id": "c5ca6b13"
- },
- "source": [
- "## Metadata ontology\n",
- "\n",
- "We use a similar system for managing metadata as we do feature schemas. Metadata schemas are strongly typed to ensure we can provide the best experience in the App. Each metadata field can be uniquely accessed by id. Names are unique within the kind of metadata, reserved or custom. A DataRow can have a maximum of 5 metadata fields at a time.\n",
- "\n",
- "### Metadata kinds\n",
- "\n",
- "* **Enum**: A classification with options, only one option can be selected at a time\n",
- "* **DateTime**: A utc ISO datetime \n",
- "* **Embedding**: 128 float 32 vector used for similarity\n",
- "* **String**: A string of less than 500 characters\n",
- "\n",
- "### Reserved fields\n",
- "\n",
- "* **tag**: a free text field\n",
- "* **split**: enum of train-valid-test\n",
- "* **captureDateTime**: ISO 8601 datetime field. All times must be in UTC\n",
- "* **embedding**: A 128 length list 32 bit floats used for similarity search. All datarows share the same similarity index.\n",
- "\n",
- "### Custom fields\n",
- "\n",
- "You can create your own fields from within the app by navigating to the [metadata schema page](https://app.labelbox.com/schema/metadata)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "id": "6b611f62",
- "metadata": {
- "id": "6b611f62"
- },
- "outputs": [],
- "source": [
- "mdo = client.get_data_row_metadata_ontology()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "7dbbb519",
- "metadata": {
- "id": "7dbbb519"
- },
- "outputs": [],
- "source": [
- "# dictionary access with id\n",
- "pprint(mdo.fields_by_id, indent=2)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "id": "sHsS5Q1XpuUp",
- "metadata": {
- "id": "sHsS5Q1XpuUp"
- },
- "outputs": [],
- "source": [
- "# access by name\n",
- "split_field = mdo.reserved_by_name[\"split\"]\n",
- "train_field = mdo.reserved_by_name[\"split\"][\"train\"]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "id": "tGOtekYIq4xt",
- "metadata": {
- "id": "tGOtekYIq4xt"
- },
- "outputs": [],
- "source": [
- "tag_field = mdo.reserved_by_name[\"tag\"]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "id": "6oZD8PMzq8yg",
- "metadata": {
- "id": "6oZD8PMzq8yg"
- },
- "outputs": [],
- "source": [
- "tag_field"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "D0sTgB5QrKET",
- "metadata": {
- "id": "D0sTgB5QrKET"
- },
- "source": [
- "## Setup embeddings/similarity\n",
- "\n",
- "The quality of embeddings is based on how well a machine learning model overlaps with your task. An off the shelf model will perform much worse then a model you have trained yourself.\n",
- "\n",
- "Here we use a [TensorfFlow Hub](https://tfhub.dev) model trained on Imagenet to create embeddings and then run a dimensionality reduction step to match the Labelbox requirements"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "id": "uRWOGD9T5m1a",
- "metadata": {
- "id": "uRWOGD9T5m1a"
- },
- "outputs": [],
- "source": [
- "TFHUB_MODEL = \"https://tfhub.dev/google/imagenet/efficientnet_v2_imagenet21k_b0/feature_vector/2\" #@param {type:\"string\"}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "id": "aRLXG52U7VEe",
- "metadata": {
- "id": "aRLXG52U7VEe"
- },
- "outputs": [],
- "source": [
- "def make_file_processor(height, width):\n",
- "\n",
- " def decode_image(img):\n",
- " # convert the compressed string to a 3D uint8 tensor\n",
- " img = tf.image.decode_jpeg(img, channels=3)\n",
- " img = tf.image.convert_image_dtype(img, dtype=tf.float32)\n",
- " return img\n",
- "\n",
- " def process_file(bytez):\n",
- " img = decode_image(bytez)\n",
- " img = tf.image.resize(img, [height, width])\n",
- " return img[tf.newaxis, ...]\n",
- "\n",
- " return process_file\n",
- "\n",
- "\n",
- "INPUT_HEIGHT, INPUT_WIDTH = 224, 224\n",
- "SIMILARITY_DIMENSION = 128\n",
- "model = hub.KerasLayer(TFHUB_MODEL)\n",
- "processor = make_file_processor(INPUT_HEIGHT, INPUT_WIDTH)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "sw90ea9PyBkC",
- "metadata": {
- "id": "sw90ea9PyBkC"
- },
- "source": [
- "## Uploading metadata\n",
- "\n",
- "As mentioned previously metadata is strongly typed. To upload metadata for a datarow you construct a `DataRowMetadata` object which contains the fields `DataRowMetadataField` to put on the datarow. To construct a metadata field you must provide the schema id for the field and value that will be uploaded. Metadata will overwrite on a per field basis. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "id": "5A8JMD8O8Jmq",
- "metadata": {
- "id": "5A8JMD8O8Jmq"
- },
- "outputs": [],
- "source": [
- "field = DataRowMetadataField(\n",
- " schema_id=mdo.reserved_by_name[\"captureDateTime\"].\n",
- " uid, # specify the schema id\n",
- " value=datetime.now(), # typed inputs\n",
- ")\n",
- "# Completed object ready for upload\n",
- "DataRowMetadata(data_row_id=\"my-datarow-id\", fields=[field])"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "p-s_PFHfNNZB",
- "metadata": {
- "id": "p-s_PFHfNNZB"
- },
- "source": [
- "### Building up uploads"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "id": "XVfT1vzMrv71",
- "metadata": {
- "id": "XVfT1vzMrv71"
- },
- "outputs": [],
- "source": [
- "# Select a dataset to use\n",
- "dataset_id = None\n",
- "dataset = client.get_dataset(dataset_id)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "id": "f115e96a",
- "metadata": {
- "id": "f115e96a"
- },
- "outputs": [],
- "source": [
- "# train-valid-test break down\n",
- "test = 0.05\n",
- "valid = 0.05 + test\n",
- "train = 1 - valid"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "id": "a06b5592",
- "metadata": {
- "id": "a06b5592"
- },
- "outputs": [],
- "source": [
- "uploads = []\n",
- "embeddings = []\n",
- "\n",
- "for datarow in tqdm(dataset.export_data_rows(), total=dataset.row_count):\n",
- "\n",
- " response = requests.get(datarow.row_data, stream=True)\n",
- "\n",
- " # assign datarows a split\n",
- " rnd = random.random()\n",
- " if rnd < test:\n",
- " split = mdo.reserved_by_name[\"split\"][\"test\"]\n",
- " elif rnd < valid:\n",
- " split = mdo.reserved_by_name[\"split\"][\"valid\"]\n",
- " else:\n",
- " split = mdo.reserved_by_name[\"split\"][\"train\"]\n",
- "\n",
- " embeddings.append(\n",
- " list(model(processor(response.content), training=False)[0].numpy()))\n",
- " dt = datetime.utcnow()\n",
- " message = \"my-new-message\"\n",
- "\n",
- " uploads.append(\n",
- " DataRowMetadata(\n",
- " data_row_id=datarow.uid,\n",
- " fields=[\n",
- " DataRowMetadataField(\n",
- " schema_id=mdo.reserved_by_name[\"captureDateTime\"].uid,\n",
- " value=dt,\n",
- " ),\n",
- " DataRowMetadataField(schema_id=split.parent, value=split.uid),\n",
- " DataRowMetadataField(schema_id=mdo.reserved_by_name[\"tag\"].uid,\n",
- " value=message),\n",
- " ]))"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "9bWUGSqL4Mlc",
- "metadata": {
- "id": "9bWUGSqL4Mlc"
- },
- "source": [
- "### Reduce dimensionality\n",
- "\n",
- "Labelbox supports dimensions of length 128 so we use a [Gaussian Random Projection](https://scikit-learn.org/stable/modules/random_projection.html#gaussian-random-projection) to project the data from 1024 into a compatible size"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "id": "83LsKyeX0Q8H",
- "metadata": {
- "id": "83LsKyeX0Q8H"
- },
- "outputs": [],
- "source": [
- "gaussian_projected = GaussianRandomProjection(n_components=2).fit_transform(\n",
- " np.vstack(embeddings))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "id": "Pr2Vo7l90pz8",
- "metadata": {
- "id": "Pr2Vo7l90pz8"
- },
- "outputs": [],
- "source": [
- "sns.scatterplot(x=gaussian_projected[:, 0], y=gaussian_projected[:, 1])\n",
- "sns.despine()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "id": "eOuJe1RMy-Dx",
- "metadata": {
- "id": "eOuJe1RMy-Dx"
- },
- "outputs": [],
- "source": [
- "# project to 128 and add to upload\n",
- "projected = GaussianRandomProjection(n_components=SIMILARITY_DIMENSION,\n",
- " random_state=42).fit_transform(\n",
- " np.vstack(embeddings))\n",
- "for md, embd in zip(uploads, projected):\n",
- " md.fields.append(\n",
- " DataRowMetadataField(\n",
- " schema_id=mdo.reserved_by_name[\"embedding\"].uid,\n",
- " value=embd.tolist(), # convert from numpy to list\n",
- " ),)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "id": "-FUkp4CEPuzx",
- "metadata": {
- "id": "-FUkp4CEPuzx",
- "pycharm": {
- "is_executing": true
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "db768cda",
+ "metadata": {
+ "id": "db768cda"
+ },
+ "source": [
+ "\n",
+ " \n",
+ " | "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cb5611d0",
+ "metadata": {
+ "id": "cb5611d0"
+ },
+ "source": [
+ "\n",
+ " \n",
+ " | \n",
+ "\n",
+ "\n",
+ " \n",
+ " | "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8465cef2",
+ "metadata": {
+ "id": "8465cef2"
+ },
+ "source": [
+ "# Data Row Metadata\n",
+ "\n",
+ "Metadata is useful to be better understand data on the platform to help with labeling review, model diagnostics, and data selection. This **should not be confused with attachments**. Attachments provide additional context for labelers but is not searchable within Catalog."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f61dSEA0o4zY",
+ "metadata": {
+ "id": "f61dSEA0o4zY"
+ },
+ "source": [
+ "### Installation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "OL0N3xLRNKK7",
+ "metadata": {
+ "id": "OL0N3xLRNKK7"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install -q --upgrade tensorflow-hub \\\n",
+ " scikit-learn \\\n",
+ " seaborn \\\n",
+ " \"labelbox[data]\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "SF251uzCGyMx",
+ "metadata": {
+ "id": "SF251uzCGyMx"
+ },
+ "source": [
+ "## Setup"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e3472f7c",
+ "metadata": {
+ "id": "e3472f7c"
+ },
+ "outputs": [],
+ "source": [
+ "import labelbox\n",
+ "import random\n",
+ "import numpy as np\n",
+ "from labelbox import Client\n",
+ "from labelbox.schema.data_row_metadata import (\n",
+ " DataRowMetadataField,\n",
+ " DataRowMetadata,\n",
+ " DeleteDataRowMetadata,\n",
+ ")\n",
+ "from sklearn.random_projection import GaussianRandomProjection\n",
+ "import tensorflow as tf\n",
+ "import seaborn as sns\n",
+ "import tensorflow_hub as hub\n",
+ "from datetime import datetime\n",
+ "from tqdm.notebook import tqdm\n",
+ "import requests\n",
+ "from pprint import pprint\n",
+ "from uuid import uuid4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "014d3d1b",
+ "metadata": {
+ "id": "014d3d1b"
+ },
+ "source": [
+ "# API Key and Client\n",
+ "Provide a valid api key below in order to properly connect to the Labelbox Client."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ogVqVzGlNXBY",
+ "metadata": {
+ "id": "ogVqVzGlNXBY"
+ },
+ "outputs": [],
+ "source": [
+ "# Add your api key\n",
+ "API_KEY = None\n",
+ "client = Client(api_key=API_KEY)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c5ca6b13",
+ "metadata": {
+ "id": "c5ca6b13"
+ },
+ "source": [
+ "## Metadata ontology\n",
+ "\n",
+ "We use a similar system for managing metadata as we do feature schemas. Metadata schemas are strongly typed to ensure we can provide the best experience in the App. Each metadata field can be uniquely accessed by id. Names are unique within the kind of metadata, reserved or custom. A DataRow can have a maximum of 5 metadata fields at a time.\n",
+ "\n",
+ "### Metadata kinds\n",
+ "\n",
+ "* **Enum**: A classification with options, only one option can be selected at a time\n",
+ "* **DateTime**: A utc ISO datetime \n",
+ "* **Embedding**: 128 float 32 vector used for similarity\n",
+ "* **String**: A string of less than 500 characters\n",
+ "\n",
+ "### Reserved fields\n",
+ "\n",
+ "* **tag**: a free text field\n",
+ "* **split**: enum of train-valid-test\n",
+ "* **captureDateTime**: ISO 8601 datetime field. All times must be in UTC\n",
+ "* **embedding**: A 128 length list 32 bit floats used for similarity search. All datarows share the same similarity index.\n",
+ "\n",
+ "### Custom fields\n",
+ "\n",
+ "You can create your own fields from within the app by navigating to the [metadata schema page](https://app.labelbox.com/schema/metadata)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6b611f62",
+ "metadata": {
+ "id": "6b611f62"
+ },
+ "outputs": [],
+ "source": [
+ "mdo = client.get_data_row_metadata_ontology()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7dbbb519",
+ "metadata": {
+ "id": "7dbbb519"
+ },
+ "outputs": [],
+ "source": [
+ "# list all your metadata ontology as a dictionary accessable by id \n",
+ "metadata_ontologies = mdo.fields_by_id\n",
+ "pprint(metadata_ontologies, indent=2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "sHsS5Q1XpuUp",
+ "metadata": {
+ "id": "sHsS5Q1XpuUp"
+ },
+ "outputs": [],
+ "source": [
+ "# access by name\n",
+ "split_field = mdo.reserved_by_name[\"split\"]\n",
+ "train_field = mdo.reserved_by_name[\"split\"][\"train\"]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "tGOtekYIq4xt",
+ "metadata": {
+ "id": "tGOtekYIq4xt"
+ },
+ "outputs": [],
+ "source": [
+ "tag_field = mdo.reserved_by_name[\"tag\"]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6oZD8PMzq8yg",
+ "metadata": {
+ "id": "6oZD8PMzq8yg"
+ },
+ "outputs": [],
+ "source": [
+ "tag_field"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "NGRnQ-fSOBMi",
+ "metadata": {
+ "id": "NGRnQ-fSOBMi"
+ },
+ "source": [
+ "## Construct metadata fields\n",
+ "\n",
+ "To construct a metadata field you must provide the Schema Id for the field and the value that will be uploaded. You can either construct a DataRowMetadataField object or specify the Schema Id and value in a dictionary format.\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "Option 1: Specify metadata with a list of DataRowMetadataField. This is the recommended option since it comes with validation for metadata fields."
+ ],
+ "metadata": {
+ "id": "oimnTR0ccLJI"
+ },
+ "id": "oimnTR0ccLJI"
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "jp_tNEGmOBMj",
+ "metadata": {
+ "id": "jp_tNEGmOBMj"
+ },
+ "outputs": [],
+ "source": [
+ "# Construct a metadata field of string kind\n",
+ "tag_metadata_field = DataRowMetadataField(\n",
+ " schema_id=mdo.reserved_by_name[\"tag\"].uid, # specify the schema id\n",
+ " value=\"tag_string\", # typed inputs\n",
+ ")\n",
+ "\n",
+ "# Construct an metadata field of datetime kind\n",
+ "capture_datetime_field = DataRowMetadataField(\n",
+ " schema_id=mdo.reserved_by_name[\"captureDateTime\"].uid, # specify the schema id\n",
+ " value=datetime.utcnow(), # typed inputs\n",
+ ")\n",
+ "\n",
+ "# Construct a metadata field of Enums options\n",
+ "train_schema = mdo.reserved_by_name[\"split\"][\"train\"]\n",
+ "split_metadta_field = DataRowMetadataField(\n",
+ " schema_id=train_schema.parent, # specify the schema id\n",
+ " value=train_schema.uid, # typed inputs\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "Option 2: Alternatively, you can specify the metadata fields with dictionary format without declaring the DataRowMetadataField objects.\n"
+ ],
+ "metadata": {
+ "id": "Vr572V2ScPtY"
+ },
+ "id": "Vr572V2ScPtY"
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "o3Kqq2SKOjTx",
+ "metadata": {
+ "id": "o3Kqq2SKOjTx"
+ },
+ "outputs": [],
+ "source": [
+ "# Construct a dictionary of string metadata\n",
+ "tag_metadata_field_dict = {\n",
+ " \"schema_id\": mdo.reserved_by_name[\"tag\"].uid,\n",
+ " \"value\": \"tag_string\",\n",
+ "}\n",
+ "\n",
+ "# Construct a dictionary of datetime metadata\n",
+ "capture_datetime_field_dict = {\n",
+ " \"schema_id\": mdo.reserved_by_name[\"captureDateTime\"].uid,\n",
+ " \"value\": datetime.utcnow(),\n",
+ "}\n",
+ "\n",
+ "# Construct a dictionary of Enums options metadata\n",
+ "split_metadta_field_dict = {\n",
+ " \"schema_id\": mdo.reserved_by_name[\"split\"][\"train\"].parent,\n",
+ " \"value\": mdo.reserved_by_name[\"split\"][\"train\"].uid,\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "jgMEVgK5Kkcn",
+ "metadata": {
+ "id": "jgMEVgK5Kkcn"
+ },
+ "source": [
+ "# Upload Data Rows together with metadata\n",
+ "\n",
+ "Note: currently, there is a 30k limit on bulk uploading data rows containing metadata.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "VxquoZLRKjh8",
+ "metadata": {
+ "id": "VxquoZLRKjh8"
+ },
+ "outputs": [],
+ "source": [
+ "# A simple example of uploading Data Rows with metadta\n",
+ "dataset = client.create_dataset(name=\"Simple Data Rows import with metadata example\")\n",
+ "\n",
+ "data_row = {\"row_data\": \"https://storage.googleapis.com/labelbox-sample-datasets/Docs/basic.jpg\", \"external_id\": str(uuid4())}\n",
+ "data_row['metadata_fields'] = [tag_metadata_field, capture_datetime_field, split_metadta_field] \n",
+ "# Also works with a list of dictionary as specified in Option 2. Uncomment the line below to try. \n",
+ "# data_row['metadata_fields'] = [tag_metadata_field_dict, capture_datetime_field_dict, split_metadta_field_dict]\n",
+ "\n",
+ "task = dataset.create_data_rows([data_row])\n",
+ "task.wait_till_done()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "xxq3lopcpZp8",
+ "metadata": {
+ "id": "xxq3lopcpZp8"
+ },
+ "source": [
+ "## Accessing Metadata\n",
+ "\n",
+ "You can examine individual Data Row, including its metadata."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "DK5M4kdZQc1i",
+ "metadata": {
+ "id": "DK5M4kdZQc1i"
+ },
+ "outputs": [],
+ "source": [
+ "datarow = next(dataset.data_rows())\n",
+ "for metadata_field in datarow.metadata_fields:\n",
+ " print(metadata_field['name'], \":\", metadata_field['value'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "qK5DExa6SRg7",
+ "metadata": {
+ "id": "qK5DExa6SRg7"
+ },
+ "source": [
+ "You can bulk export metadata given Data Row Ids"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "GCCeJH67SK6g",
+ "metadata": {
+ "id": "GCCeJH67SK6g"
+ },
+ "outputs": [],
+ "source": [
+ "datarows_metadata = mdo.bulk_export([datarow.uid])\n",
+ "len(datarows_metadata)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0O6mHNu5O6aL",
+ "metadata": {
+ "id": "0O6mHNu5O6aL"
+ },
+ "source": [
+ "# Upload/update metadata to existing Data Rows\n",
+ "\n",
+ "Next, the following section will go over how to upload or update metadata to existing data rows. \n",
+ "\n",
+ "It also covers a more complex example of adding custom embeddings metadata for similarity search in Catalog. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "D0sTgB5QrKET",
+ "metadata": {
+ "id": "D0sTgB5QrKET"
+ },
+ "source": [
+ "## Setup custom embeddings/similarity of Data Rows\n",
+ "\n",
+ "Labelbox uses embeddings to power [Similarity Search](https://docs.labelbox.com/docs/similarity). The quality of embeddings is based on how well a machine learning model overlaps with your task. An off the shelf model will perform much worse then a model you have trained yourself.\n",
+ "\n",
+ "Here we use a [TensorfFlow Hub](https://tfhub.dev) model trained on Imagenet to create embeddings and then run a dimensionality reduction step to match the Labelbox requirements. You can create your own embeddings using any model of choice, as long as it is a 12 float 32 vector."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "uRWOGD9T5m1a",
+ "metadata": {
+ "id": "uRWOGD9T5m1a"
+ },
+ "outputs": [],
+ "source": [
+ "TFHUB_MODEL = \"https://tfhub.dev/google/imagenet/efficientnet_v2_imagenet21k_b0/feature_vector/2\" #@param {type:\"string\"}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "aRLXG52U7VEe",
+ "metadata": {
+ "id": "aRLXG52U7VEe"
+ },
+ "outputs": [],
+ "source": [
+ "def make_file_processor(height, width):\n",
+ "\n",
+ " def decode_image(img):\n",
+ " # convert the compressed string to a 3D uint8 tensor\n",
+ " img = tf.image.decode_jpeg(img, channels=3)\n",
+ " img = tf.image.convert_image_dtype(img, dtype=tf.float32)\n",
+ " return img\n",
+ "\n",
+ " def process_file(bytez):\n",
+ " img = decode_image(bytez)\n",
+ " img = tf.image.resize(img, [height, width])\n",
+ " return img[tf.newaxis, ...]\n",
+ "\n",
+ " return process_file\n",
+ "\n",
+ "\n",
+ "INPUT_HEIGHT, INPUT_WIDTH = 224, 224\n",
+ "SIMILARITY_DIMENSION = 128\n",
+ "model = hub.KerasLayer(TFHUB_MODEL)\n",
+ "processor = make_file_processor(INPUT_HEIGHT, INPUT_WIDTH)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "p-s_PFHfNNZB",
+ "metadata": {
+ "id": "p-s_PFHfNNZB"
+ },
+ "source": [
+ "### Building up uploads"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "XVfT1vzMrv71",
+ "metadata": {
+ "id": "XVfT1vzMrv71"
+ },
+ "outputs": [],
+ "source": [
+ "# Select a dataset to use, or you can just use the 1-image dataset created above. \n",
+ "dataset_id = \"cl3ntfr7j7cmh07bmeqz3gfjt\"\n",
+ "dataset = client.get_dataset(dataset_id)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "zg172gOwTUnY",
+ "metadata": {
+ "id": "zg172gOwTUnY"
+ },
+ "outputs": [],
+ "source": [
+ "dataset.row_count"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f115e96a",
+ "metadata": {
+ "id": "f115e96a"
+ },
+ "outputs": [],
+ "source": [
+ "# train-valid-test break down\n",
+ "test = 0.05\n",
+ "valid = 0.05 + test\n",
+ "train = 1 - valid"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a06b5592",
+ "metadata": {
+ "id": "a06b5592"
+ },
+ "outputs": [],
+ "source": [
+ "uploads = []\n",
+ "embeddings = []\n",
+ "\n",
+ "for datarow in tqdm(dataset.export_data_rows(), total=dataset.row_count):\n",
+ "\n",
+ " response = requests.get(datarow.row_data, stream=True)\n",
+ "\n",
+ " # assign datarows a split\n",
+ " rnd = random.random()\n",
+ " if rnd < test:\n",
+ " split = mdo.reserved_by_name[\"split\"][\"test\"]\n",
+ " elif rnd < valid:\n",
+ " split = mdo.reserved_by_name[\"split\"][\"valid\"]\n",
+ " else:\n",
+ " split = mdo.reserved_by_name[\"split\"][\"train\"]\n",
+ "\n",
+ " embeddings.append(\n",
+ " list(model(processor(response.content), training=False)[0].numpy()))\n",
+ " dt = datetime.utcnow()\n",
+ " message = \"my-new-message\"\n",
+ "\n",
+ " uploads.append(\n",
+ " DataRowMetadata(\n",
+ " data_row_id=datarow.uid,\n",
+ " fields=[\n",
+ " DataRowMetadataField(\n",
+ " schema_id=mdo.reserved_by_name[\"captureDateTime\"].uid,\n",
+ " value=dt,\n",
+ " ),\n",
+ " DataRowMetadataField(schema_id=split.parent, value=split.uid),\n",
+ " DataRowMetadataField(schema_id=mdo.reserved_by_name[\"tag\"].uid,\n",
+ " value=message),\n",
+ " ]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9bWUGSqL4Mlc",
+ "metadata": {
+ "id": "9bWUGSqL4Mlc"
+ },
+ "source": [
+ "### Reduce dimensionality\n",
+ "\n",
+ "Labelbox supports dimensions of length 128 so we use a [Gaussian Random Projection](https://scikit-learn.org/stable/modules/random_projection.html#gaussian-random-projection) to project the data from 1024 into a compatible size"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "83LsKyeX0Q8H",
+ "metadata": {
+ "id": "83LsKyeX0Q8H"
+ },
+ "outputs": [],
+ "source": [
+ "gaussian_projected = GaussianRandomProjection(n_components=2).fit_transform(\n",
+ " np.vstack(embeddings))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Pr2Vo7l90pz8",
+ "metadata": {
+ "id": "Pr2Vo7l90pz8"
+ },
+ "outputs": [],
+ "source": [
+ "sns.scatterplot(x=gaussian_projected[:, 0], y=gaussian_projected[:, 1])\n",
+ "sns.despine()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "eOuJe1RMy-Dx",
+ "metadata": {
+ "id": "eOuJe1RMy-Dx"
+ },
+ "outputs": [],
+ "source": [
+ "# project to 128 and add to upload\n",
+ "projected = GaussianRandomProjection(n_components=SIMILARITY_DIMENSION,\n",
+ " random_state=42).fit_transform(\n",
+ " np.vstack(embeddings))\n",
+ "for md, embd in zip(uploads, projected):\n",
+ " md.fields.append(\n",
+ " DataRowMetadataField(\n",
+ " schema_id=mdo.reserved_by_name[\"embedding\"].uid,\n",
+ " value=embd.tolist(), # convert from numpy to list\n",
+ " ),)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "-FUkp4CEPuzx",
+ "metadata": {
+ "id": "-FUkp4CEPuzx",
+ "pycharm": {
+ "is_executing": true
+ }
+ },
+ "outputs": [],
+ "source": [
+ "mdo.bulk_upsert(uploads)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "355d6a09",
+ "metadata": {
+ "id": "355d6a09"
+ },
+ "source": [
+ "### Upload\n",
+ "\n",
+ "Uploads will overwrite the current value for the feature if it is already present."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "lF1jMUoq4txW",
+ "metadata": {
+ "id": "lF1jMUoq4txW"
+ },
+ "source": [
+ "### Similarity\n",
+ "\n",
+ "To access similarity navigate to the datarow page or within a dataset or catalog and toggle the drop down on the left \n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "CWoDE-1w4_mJ",
+ "metadata": {
+ "id": "CWoDE-1w4_mJ"
+ },
+ "outputs": [],
+ "source": [
+ "# datarow page\n",
+ "print(f'https://app.labelbox.com/datarows/{datarow.uid}')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "Pttg3qwq0z8R",
+ "metadata": {
+ "id": "Pttg3qwq0z8R"
+ },
+ "outputs": [],
+ "source": [
+ "metadata = mdo.bulk_export([datarow.uid])[0]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "h3awG-OEGP86",
+ "metadata": {
+ "id": "h3awG-OEGP86"
+ },
+ "source": [
+ "## Delete Metadata\n",
+ "\n",
+ "To delete fields from a datarow you provide the schema ids you want removed \n",
+ "\n",
+ "**Note** for enums you must currently pass the Enum and Option schema ids"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "mATsdVyaGRXB",
+ "metadata": {
+ "id": "mATsdVyaGRXB"
+ },
+ "outputs": [],
+ "source": [
+ "md = uploads[0]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "_6B2sJa_GiZe",
+ "metadata": {
+ "id": "_6B2sJa_GiZe"
+ },
+ "outputs": [],
+ "source": [
+ "fields = []\n",
+ "# iterate through the fields you want to delete\n",
+ "for field in md.fields:\n",
+ " fields.append(field.schema_id)\n",
+ "\n",
+ "deletes = DeleteDataRowMetadata(data_row_id=md.data_row_id, fields=fields)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4zKFfgXaNz7c",
+ "metadata": {
+ "id": "4zKFfgXaNz7c"
+ },
+ "outputs": [],
+ "source": [
+ "len(mdo.bulk_export(deletes.data_row_id)[0].fields)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8ePkScszH7_h",
+ "metadata": {
+ "id": "8ePkScszH7_h"
+ },
+ "outputs": [],
+ "source": [
+ "mdo.bulk_delete([deletes])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "rKdJhVcEIDku",
+ "metadata": {
+ "id": "rKdJhVcEIDku",
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "len(mdo.bulk_export(deletes.data_row_id)[0].fields)"
+ ]
}
- },
- "outputs": [],
- "source": [
- "mdo.bulk_upsert(uploads)"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "xxq3lopcpZp8",
- "metadata": {
- "id": "xxq3lopcpZp8"
- },
- "source": [
- "## Accessing Metadata\n",
- "\n",
- "Currently there is no option for exporting metadata in bulk. We will be working to support that functionality in the near future."
- ]
- },
- {
- "cell_type": "markdown",
- "id": "355d6a09",
- "metadata": {},
- "source": [
- "### Upload\n",
- "\n",
- "Uploads will overwrite the current value for the feature if it is already present."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "id": "F5F18Xdz5P5Y",
- "metadata": {
- "id": "F5F18Xdz5P5Y"
- },
- "outputs": [],
- "source": [
- "datarow = next(dataset.data_rows())"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "lF1jMUoq4txW",
- "metadata": {
- "id": "lF1jMUoq4txW"
- },
- "source": [
- "### Similarity\n",
- "\n",
- "To access similarity navigate to the datarow page or within a dataset or catalog and toggle the drop down on the left \n",
- "\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "id": "CWoDE-1w4_mJ",
- "metadata": {
- "id": "CWoDE-1w4_mJ"
- },
- "outputs": [],
- "source": [
- "# datarow page\n",
- "print(f'https://app.labelbox.com/datarows/{datarow.uid}')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "id": "Pttg3qwq0z8R",
- "metadata": {
- "id": "Pttg3qwq0z8R"
- },
- "outputs": [],
- "source": [
- "metadata = mdo.bulk_export([datarow.uid])[0]"
- ]
- },
- {
- "cell_type": "markdown",
- "id": "h3awG-OEGP86",
- "metadata": {
- "id": "h3awG-OEGP86"
- },
- "source": [
- "## Delete Metadata\n",
- "\n",
- "To delete fields from a datarow you provide the schema ids you want removed \n",
- "\n",
- "**Note** for enums you must currently pass the Enum and Option schema ids"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "id": "mATsdVyaGRXB",
- "metadata": {
- "id": "mATsdVyaGRXB"
- },
- "outputs": [],
- "source": [
- "md = uploads[0]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "id": "_6B2sJa_GiZe",
- "metadata": {
- "id": "_6B2sJa_GiZe"
- },
- "outputs": [],
- "source": [
- "fields = []\n",
- "# iterate through the fields you want to delete\n",
- "for field in md.fields:\n",
- " fields.append(field.schema_id)\n",
- "\n",
- "deletes = DeleteDataRowMetadata(data_row_id=md.data_row_id, fields=fields)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "id": "4zKFfgXaNz7c",
- "metadata": {
- "id": "4zKFfgXaNz7c"
- },
- "outputs": [],
- "source": [
- "len(mdo.bulk_export(deletes.data_row_id)[0].fields)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "id": "8ePkScszH7_h",
- "metadata": {
- "id": "8ePkScszH7_h"
- },
- "outputs": [],
- "source": [
- "mdo.bulk_delete([deletes])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "id": "rKdJhVcEIDku",
- "metadata": {
- "id": "rKdJhVcEIDku",
- "pycharm": {
- "name": "#%%\n"
+ ],
+ "metadata": {
+ "colab": {
+ "collapsed_sections": [],
+ "name": "data_row_metadata.ipynb",
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.12"
}
- },
- "outputs": [],
- "source": [
- "len(mdo.bulk_export(deletes.data_row_id)[0].fields)"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "collapsed_sections": [],
- "name": "DataRow Metadata.ipynb",
- "provenance": []
- },
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 5
-}
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
\ No newline at end of file
diff --git a/examples/basics/data_rows.ipynb b/examples/basics/data_rows.ipynb
index cc98eab7f..2e633c0cc 100644
--- a/examples/basics/data_rows.ipynb
+++ b/examples/basics/data_rows.ipynb
@@ -1,399 +1,495 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "source": [
- "\n",
- " \n",
- " | "
- ],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "\n",
- " \n",
- " | \n",
- "\n",
- "\n",
- " \n",
- " | "
- ],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "# Data rows"
- ],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "* Data rows are the items that are actually being labeled. We currently support the following:\n",
- " * Image\n",
- " * Text\n",
- " * Video\n",
- " * Geospatial / Tiled Imagery\n",
- " * Audio\n",
- " * Documents (Beta)\n",
- " * HTML (Beta)\n",
- " * DICOM (Beta)\n",
- "* A data row is a member of a dataset \n",
- "* A data row cannot exist without belonging to a dataset.\n",
- "* DataRows are added to labeling tasks by first attaching them to datasets and then attaching datasets to projects."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "source": [
- "!pip install labelbox"
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "source": [
- "from labelbox import DataRow, Client\n",
- "import uuid\n",
- "import os"
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "* Set the following cell with your data to run this notebook"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "source": [
- "# Pick a project that has a dataset attached, data has external ids, and there are some labels\n",
- "# This will modify the project so just pick a dummy one that you don't care about\n",
- "PROJECT_ID = \"ckpnfquwy0kyg0y8t9rwb99cz\""
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "# API Key and Client\n",
- "Provide a valid api key below in order to properly connect to the Labelbox Client."
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "source": [
- "# Add your api key\n",
- "API_KEY = None\n",
- "client = Client(api_key=API_KEY)"
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "source": [
- "project = client.get_project(PROJECT_ID)\n",
- "dataset = next(project.datasets())\n",
- "# This is the same as\n",
- "# -> dataset = client.get_dataset(dataset_id)"
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "markdown",
- "source": [
- "### Read"
- ],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "source": [
- "data_rows = dataset.data_rows()\n",
- "data_row = next(data_rows)"
- ],
- "outputs": [],
- "metadata": {}
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "source": [
- "# Url\n",
- "print(\"Associated dataset\", data_row.dataset())\n",
- "print(\"Associated label(s)\", next(data_row.labels()))\n",
- "print(\"External id\", data_row.external_id)"
- ],
- "outputs": [
+ "cells": [
{
- "output_type": "stream",
- "name": "stdout",
- "text": [
- "Associated dataset \n",
- "Associated label(s)