Use experiment.run_nondp to train models and compute R^2 values for nonprivate
OLS, and use experiment.run_private_regression to do the same for the private
OLS algorithms described in the paper.

Example code for fetching and preprocessing a dataset appears below.

# import numpy as np
# import pandas as pd
# from openml import tasks

# # Fetch data from OpenML
# dataset = tasks.get_task(id).get_dataset()
# X, Y, categorical, feature_names = dataset.get_data(
#  target=dataset.default_target_attribute)
# data = pd.DataFrame(X)
# data.columns = feature_names
# n, d = X.shape
# data.insert(d, "label", Y)
# categorical_df = pd.DataFrame(categorical)
# # Drop nans
# data.dropna(inplace = True)
# # Convert labels to numpy and discard from dataframe
# Y = data.iloc[:, -1].to_numpy()
# data = data.iloc[:, :-1]
# categorical_indices = np.nonzero(categorical_df.to_numpy())[0]
# # Add expanded columns
# for category_idx in categorical_indices:
#   expanded = pd.get_dummies(data[data.columns[category_idx]])
#   data = pd.concat([data, expanded], axis=1)
# # Drop columns that got expanded
# data = data.drop(data.columns[categorical_indices], axis=1)
# # Add intercept feature
# X = data.to_numpy()
# X = np.column_stack((X, np.ones(X.shape[0]))).astype(float)
# Slightly perturb data to avoid ties for Kendall
# X = X + np.random.uniform(low=-1e-6, high = 1e-6, size=X.shape)
# Y = Y + np.random.uniform(low=-1e-6, high = 1e-6, size=Y.shape)