Skip to content
2 changes: 2 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Modules
list_datasets
list_qualities
status_update
edit_dataset
fork_dataset

:mod:`openml.evaluations`: Evaluation Functions
-----------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/progress.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Changelog

0.11.0
~~~~~~
* ADD #929: Add data edit API
* ADD #929: Add data edit API and Fork API
Comment thread
PGijsbers marked this conversation as resolved.
Outdated
* FIX #873: Fixes an issue which resulted in incorrect URLs when printing OpenML objects after
switching the server.
* FIX #885: Logger no longer registered by default. Added utility functions to easily register
Expand Down
13 changes: 12 additions & 1 deletion examples/30_extended/datasets_tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import openml
import pandas as pd
from openml.datasets.functions import edit_dataset, get_dataset
from openml.datasets import edit_dataset, fork_dataset, get_dataset

############################################################################
# Exercise 0
Expand Down Expand Up @@ -146,4 +146,15 @@
data_id = edit_dataset(564, default_target_attribute="y")
print(f"Edited dataset ID: {data_id}")


############################################################################
# Fork dataset
# Used to create a copy of the dataset with you as the owner.
# Use this API only if you are unable to edit the critical fields (default_target_attribute, ignore_attribute,
# row_id_attribute) of a dataset through the edit_dataset API.
# After the dataset is forked, you can edit the new version of the dataset using edit_dataset.

data_id = fork_dataset(564)
print(f"Forked dataset ID: {data_id}")

openml.config.stop_using_configuration_for_example()
4 changes: 4 additions & 0 deletions openml/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
list_datasets,
status_update,
list_qualities,
edit_dataset,
fork_dataset,
)
from .dataset import OpenMLDataset
from .data_feature import OpenMLDataFeature
Expand All @@ -24,4 +26,6 @@
"OpenMLDataFeature",
"status_update",
"list_qualities",
"edit_dataset",
"fork_dataset",
]
58 changes: 52 additions & 6 deletions openml/datasets/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -815,12 +815,19 @@ def edit_dataset(
) -> int:
"""
Edits an OpenMLDataset.
Specify atleast one field to edit, apart from data_id
- For certain fields, a new dataset version is created : attributes, data,
default_target_attribute, ignore_attribute, row_id_attribute.
In addition to providing the dataset id of the dataset to edit (through data_id),
you must specify a value for at least one of the optional function arguments,
i.e. one value for a field to edit.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The i.e. appears to be indented too far


- For other fields, the uploader can edit the exisiting version.
Noone except the uploader can edit the exisitng version.
This function allows editing of both non-critical and critical fields.
Critical fields are default_target_attribute, ignore_attribute, row_id_attribute.

- Editing non-critical data fields is allowed for all authenticated users.
- Editing critical fields is allowed only for the owner, provided there are no tasks
associated with this dataset.

If dataset has tasks or if the user is not the owner, the only way
to edit critical fields is to use fork_dataset followed by edit_dataset.

Parameters
----------
Expand Down Expand Up @@ -862,7 +869,7 @@ def edit_dataset(

Returns
-------
data_id of the existing edited version or the new version created and published"""
Dataset id """
Comment thread
PGijsbers marked this conversation as resolved.
Outdated
if not isinstance(data_id, int):
raise TypeError("`data_id` must be of type `int`, not {}.".format(type(data_id)))

Expand Down Expand Up @@ -897,6 +904,45 @@ def edit_dataset(
return int(data_id)


def fork_dataset(data_id: int) -> int:
"""
Creates a new dataset version, with the authenticated user as the new owner.
The forked dataset can have distinct dataset meta-data,
but the actual data itself is shared with the original version.

This API is intended for use when a user is unable to edit the critical fields of a dataset
through the edit_dataset API.
(Critical fields are default_target_attribute, ignore_attribute, row_id_attribute.)

Specifically, this happens when the user is:
1. Not the owner of the dataset.
2. User is the owner of the dataset, but the dataset has tasks.

In these two cases the only way to edit critical fields is:
1. STEP 1: Fork the dataset using fork_dataset API
2. STEP 2: Call edit_dataset API on the forked version.


Parameters
----------
data_id : int
id of the dataset to be forked

Returns
-------
Dataset id of the forked dataset

"""
if not isinstance(data_id, int):
raise TypeError("`data_id` must be of type `int`, not {}.".format(type(data_id)))
# compose data fork parameters
form_data = {"data_id": data_id}
result_xml = openml._api_calls._perform_api_call("data/fork", "post", data=form_data)
result = xmltodict.parse(result_xml)
data_id = result["oml:data_fork"]["oml:id"]
return int(data_id)


def _get_dataset_description(did_cache_dir, dataset_id):
"""Get the dataset description as xml dictionary.

Expand Down
15 changes: 12 additions & 3 deletions tests/test_datasets/test_dataset_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from openml.utils import _tag_entity, _create_cache_directory_for_id
from openml.datasets.functions import (
create_dataset,
edit_dataset,
attributes_arff_from_df,
_get_cached_dataset,
_get_cached_dataset_features,
Expand All @@ -40,6 +39,7 @@
_get_online_dataset_format,
DATASETS_CACHE_DIR_NAME,
)
from openml.datasets import fork_dataset, edit_dataset


class TestOpenMLDataset(TestBase):
Expand Down Expand Up @@ -1389,7 +1389,7 @@ def test_data_edit_errors(self):
data_id=100000,
description="xor operation dataset",
)
# Check server exception when owner/admin edits critical features of dataset with tasks
# Check server exception when owner/admin edits critical fields of dataset with tasks
self.assertRaisesRegex(
OpenMLServerException,
"Critical features default_target_attribute, row_id_attribute and ignore_attribute "
Expand All @@ -1398,7 +1398,7 @@ def test_data_edit_errors(self):
data_id=223,
default_target_attribute="y",
)
# Check server exception when a non-owner or non-admin tries to edit critical features
# Check server exception when a non-owner or non-admin tries to edit critical fields
self.assertRaisesRegex(
OpenMLServerException,
"Critical features default_target_attribute, row_id_attribute and ignore_attribute "
Expand All @@ -1407,3 +1407,12 @@ def test_data_edit_errors(self):
data_id=128,
default_target_attribute="y",
)

def test_data_fork(self):
did = 1
result = fork_dataset(did)
self.assertNotEqual(did, result)
# Check server exception when unknown dataset is provided
self.assertRaisesRegex(
OpenMLServerException, "Unknown dataset", fork_dataset, data_id=100000,
Comment thread
PGijsbers marked this conversation as resolved.
Outdated
)