This is the official Python library for the Myst Platform.
Project description
Myst Python Library
This is the official Python client library for the Myst Platform.
Requirements
- Python 3.7+
Installation
To install the package from PyPI:
$ pip install --upgrade myst-alpha
Authentication
The Myst API uses JSON Web Tokens (JWTs) to authenticate requests.
The Myst Python library handles the sending of JWTs to the API automatically and currently supports two ways to authenticate to obtain a JWT: through your Google user account or a Myst service account.
Authenticating using your user account
If you don't yet have a Google account, you can create one on the Google Account Signup page.
Once you have access to a Google account, send an email to support@myst.ai
with your email so we can authorize you to
use the Myst Platform.
Use the following code snippet to authenticate using your user account:
import myst
myst.authenticate()
The first time you run this, you'll be presented with a web browser and asked to authorize the Myst Python library to
make requests on behalf of your Google user account. If you'd like to re-authorize (for example with a different
account), pass use_cache=False
to be presented with the web browser authorization once again.
Authenticating using a service account
You can also authenticate using a Myst service account. To request a service account, email support@myst.ai
.
To authenticate using a service account, set the MYST_APPLICATION_CREDENTIALS
environment variable to the path to your
service account key file:
$ export MYST_APPLICATION_CREDENTIALS=</path/to/key/file.json>
Then, go through the service account authentication flow:
import myst
myst.authenticate_with_service_account()
Alternatively, you can explicitly pass the path to your service account key:
from pathlib import Path
import myst
myst.authenticate_with_service_account(key_file_path=Path("/path/to/key/file.json"))
Connecting to a different environment
Contributors may want to connect to a non-production environment that they are authorized to develop in. In that case, you can set the client with the API host you'd like to connect to.
import myst
myst.set_client(myst.Client(api_host="https://petstore.api"))
myst.authenticate()
Working with projects and graphs
A project is a workspace for setting up a graph of sources, models, operations, and time series to achieve a particular objective. The sources, model, operations, and time series therein are nodes of the graph, and they are connected by different types of edges.
For more of a conceptual overview, see the platform documentation. The following assumes some familiarity with those concepts and focuses instead on demonstrating how to use the Myst client library to interact with the platform.
Projects
Create a project
import myst
project = myst.Project.create(title="SF Electricity Load")
List projects
import myst
projects = myst.Project.list()
Retrieve a project
import myst
project = myst.Project.get(uuid="f89d7fbe-a051-4d0c-aa60-d6838b7e64a0")
Update a project
import myst
project = myst.Project.get(uuid="f89d7fbe-a051-4d0c-aa60-d6838b7e64a0")
project = project.update(title="My Project")
Delete a project
import myst
project = myst.Project.get(uuid="f89d7fbe-a051-4d0c-aa60-d6838b7e64a0")
project.delete()
Filter a project by title
import myst
projects = myst.Project.filter(title="My title")
Nodes (Sources, Models, Operations, Time Series)
A node (source, model, operation, or time series) is always associated with a project, and there are multiple patterns in the client library API by which you can list or create them.
Create a node
For example, suppose you want to create a temperature time series to be used as a feature in your model. Assuming that
you have the variable project: Project
in scope, you can write the following to create a new time series:
ksfo_temperature_time_series = project.create_time_series(
title="Temperature (KSFO)",
sample_period=myst.TimeDelta("PT1H"), # Sample period of one hour. "PT1H" is an ISO 8601 duration string.
)
Or, to exactly the same effect:
import myst
ksfo_temperature_time_series = myst.TimeSeries.create(
project=project, # Notice that project must be specified in this formulation.
title="Temperature (KSFO)",
sample_period=myst.TimeDelta("PT1H"),
)
This is true for the other types of nodes, too. In all, the client library offers the following equivalent ways to create the different types of nodes:
project.create_source(...)
<=>Source.create(project=project, ...)
project.create_operation(...)
<=>Operation.create(project=project, ...)
project.create_model(...)
<=>Model.create(project=project, ...)
project.create_time_series(...)
<=>TimeSeries.create(project=project, ...)
Create a node with connector
For nodes that are powered by connectors, you must specify the parameters of that connector during construction. For example, suppose you wanted to create a source node based on The Weather Company's Cleaned Observations API, to be used as the source of temperature data in the time series we created above. To do so, you would write:
from myst.connectors.source_connectors import cleaned_observations
ksfo_cleaned_observations = project.create_source(
title="Cleaned Weather (KSFO)",
connector=cleaned_observations.CleanedObservations(
latitude=37.619,
longitude=-122.374,
fields=[
cleaned_observations.Field.SURFACE_TEMPERATURE_CELSIUS,
],
),
)
Model
and Operation
nodes are constructed similarly. As another example, if we wanted to take the 3-hour rolling
mean of the temperature, we could create an operation as follows:
import myst
from myst.connectors.operation_connectors import time_transformations
rolling_mean_ksfo_temperature = project.create_operation(
title="Temperature (KSFO) - 3H Rolling Mean",
connector=time_transformations.TimeTransformations(
rolling_window_parameters=time_transformations.RollingWindowParameters(
window_period=myst.TimeDelta("PT3H"),
min_periods=1,
centered=False,
aggregation_function=time_transformations.AggregationFunction.MEAN,
)
),
)
We will see how to connect an input to this operation in a following step.
List nodes in a project
nodes = project.list_nodes()
Retrieve a node
import myst
model = myst.Model.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
Similar for myst.Source.get
, myst.Operation.get
, and myst.TimeSeries.get
.
Update a node
import myst
model = myst.Model.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
model = model.update(title="My Model")
Similar for updating Source
, Operation
, and TimeSeries
instances.
Edges (Inputs, Layers)
Create a layer
A layer is an edge that feeds into a time series. You can create a layer into a time series with either:
import myst
from myst.connectors.source_connectors import cleaned_observations
layer = ksfo_temperature_time_series.create_layer(
node=ksfo_cleaned_observations,
order=0,
end_timing=myst.TimeDelta("-PT23H"),
label_indexer=cleaned_observations.Field.SURFACE_TEMPERATURE_CELSIUS.value,
)
or:
import myst
from myst.connectors.source_connectors import cleaned_observations
layer = myst.Layer.create(
time_series=ksfo_temperature_time_series,
node=ksfo_cleaned_observations,
order=0,
end_timing=myst.TimeDelta("-PT23H"),
label_indexer=cleaned_observations.Field.SURFACE_TEMPERATURE_CELSIUS.value,
)
Update a layer
import myst
layer = myst.Layer.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
layer = layer.update(order=2)
Create an input
An input is an edge that feeds into a model or an operation. To connect the temperature time series into the operation we constructed before, we would write:
from myst.connectors.operation_connectors import time_transformations
operation_input = rolling_mean_ksfo_temperature.create_input(
time_series=ksfo_temperature_time_series,
group_name=time_transformations.GroupName.OPERANDS,
)
As always, this creation method is also available as:
import myst
from myst.connectors.operation_connectors import time_transformations
operation_input = myst.Input.create(
time_series=rolling_mean_ksfo_temperature,
node=ksfo_temperature_time_series,
group_name=time_transformations.GroupName.OPERANDS,
)
Update an input
import myst
input_ = myst.Input.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
input_ = input_.update(group_name="My Group Name")
List time series layers
layers = ksfo_temperature_time_series.list_layers()
List model/operation inputs
inputs = rolling_mean_ksfo_temperature.list_inputs()
Working with time series
Time series are at the core of Myst's API. In addition to the functionality offered by a generic node, time series also support querying and inserting data.
First, retrieve a time series:
import myst
time_series = myst.TimeSeries.get(
project="40bcb171-1c51-4497-9524-914630818aeb",
uuid="ca2a63d1-3515-47b4-afc7-13c6656dd744",
)
To insert a TimeArray
of random scalar data into the time series:
import myst
import numpy as np
time_array = myst.TimeArray(
sample_period="PT1H",
start_time="2021-07-01T00:00:00Z",
end_time="2021-07-08T00:00:00Z",
as_of_time="2021-07-01T00:00:00Z",
values=np.random.randn(168),
)
time_series.insert_time_array(time_array=time_array)
You can also query a time series for a given as of time and natural time range. In this example, the query will return the data we just inserted:
import myst
returned_time_array = time_series.query_time_array(
start_time=myst.Time("2021-07-01T00:00:00Z"),
end_time=myst.Time("2021-07-08T00:00:00Z"),
as_of_time=myst.Time("2021-07-01T00:00:00Z"),
)
assert returned_time_array == time_array
You are also able to update a time series.
import myst
time_series = myst.TimeSeries.get(
project="40bcb171-1c51-4497-9524-914630818aeb",
uuid="ca2a63d1-3515-47b4-afc7-13c6656dd744",
)
time_series = time_series.update(title="My Time Series")
You are also able to create an ad-hoc time series run job.
import myst
time_series = myst.TimeSeries.get(
project="40bcb171-1c51-4497-9524-914630818aeb",
uuid="ca2a63d1-3515-47b4-afc7-13c6656dd744",
)
time_series_run_job = time_series.run(
start_timing=myst.Time("2022-07-28T00:00:00Z"),
end_timing=myst.Time("2022-08-10T00:00:00Z")
)
time_series_run_job = time_series_run_job.wait_until_completed()
result = time_series.get_run_result(uuid=time_series_run_job.result)
Working with models
Models are at the core of Myst's API. In addition to the functionality offered by a generic node, models also support creating an ad-hoc fit job.
You are able to create an ad-hoc model fit job.
import myst
model = myst.Model.get(
project="40bcb171-1c51-4497-9524-914630818aeb",
uuid="ca2a63d1-3515-47b4-afc7-13c6656dd744",
)
model_fit_job = model.fit(
start_timing=myst.Time("2022-07-28T00:00:00Z"),
end_timing=myst.Time("2022-08-10T00:00:00Z")
)
model_fit_job = model_fit_job.wait_until_completed()
result = model.get_fit_result(uuid=model_fit_job.result)
Working with policies
A policy is the way to specify the schedule on which a particular target will be fit or run, as well as the parameters around the target time range.
At the time of this writing, the Myst Platform supports two types of policies: time series run policies and model fit policies.
Time series run policies
Create a time series run policy
import myst
ksfo_temp_run_policy = ksfo_temperature_time_series.create_run_policy(
schedule_timing=myst.TimeDelta("PT1H"), # Run every hour.
start_timing=myst.TimeDelta("PT1H"), # Run on data starting from an hour from now (inclusive)...
end_timing=myst.TimeDelta("P7D"), # ...up to 7 days from now (exclusive).
)
Update a time series run policy
import myst
time_series_run_policy = myst.TimeSeriesRunPolicy.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
time_series_run_policy = time_series_run_policy.update(active=False)
Model fit policies
Suppose we have a variable xgboost_model
that contains a value of type Model
.
Create a model fit policy
import myst
xgboost_model_fit_policy = xgboost_model.create_fit_policy(
schedule_timing=myst.TimeDelta("PT24H"), # Run every 24 hours.
start_timing=myst.Time("2021-01-01T00:00:00Z"), # Fit on data from the beginning of 2021 (UTC)...
end_timing=myst.TimeDelta("-PT1H"), # ...up to an hour ago (exclusive).
)
Update a model fit policy
import myst
model_fit_policy = myst.ModelFitPolicy.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
model_fit_policy = model_fit_policy.update(active=False)
Deploying
In order for the graph to be executed, it must first be deployed. The Python client library does not currently support this functionality; we recommend using the Myst Platform UI to deploy a project.
Backtesting
In order to run a backtest, make sure that you have created and deployed a project and graph with a model you want to backtest.
Listing backtests
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# List all backtests associated with the project.
backtests = myst.Backtest.list(project=project)
Creating and running backtest
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# Use an existing deployed model within the project.
model = myst.Model.get(project=project, uuid="<uuid>")
# Create a backtest.
backtest = myst.Backtest.create(
project=project,
title="My Backtest",
model=model,
test_start_time=myst.Time("2021-07-01T00:00:00Z"),
test_end_time=myst.Time("2022-01-01T00:00:00Z"),
fit_start_timing=myst.TimeDelta("-P1M"),
fit_end_timing=myst.TimeDelta("-PT24H"),
fit_reference_timing=myst.CronTiming(cron_expression="0 0 * * 1"),
predict_start_timing=myst.TimeDelta("PT1H"),
predict_end_timing=myst.TimeDelta("PT24H"),
predict_reference_timing=myst.CronTiming(cron_expression="0 0 * * *"),
)
# Run the backtest.
backtest.run()
Analyze backtest results
To extract the result of a backtest in the client library, you can use the following code:
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# Use an existing backtest within the project.
backtest = myst.Backtest.get(project=project, uuid="<uuid>")
# Wait until the backtest is complete.
backtest.wait_until_completed()
# Get the result of the completed backtest.
backtest_result = backtest.get_result()
Once you have extracted your backtest result, you can use the following code to generate metrics:
# Get `mape`, `mae`, and `rmse` from the result object.
metrics_dictionary = backtest_result.metrics
# To calculate custom metrics, map the backtest result to a pandas data frame.
result_data_frame = backtest_result.to_pandas_data_frame()
# Compute some metrics for the backtest result.
absolute_error_series = (result_data_frame["targets"] - result_data_frame["predictions"]).abs()
absolute_percentage_error_series = absolute_error_series / result_data_frame["targets"].abs()
# Create an index with the prediction horizons.
horizon_index = (
result_data_frame.index.get_level_values("time") -
result_data_frame.index.get_level_values("reference_time")
)
# Print the MAE and MAPE for each prediction horizon.
print(absolute_error_series.groupby(horizon_index).mean())
print(absolute_percentage_error_series.groupby(horizon_index).mean())
update a backtest
import myst
backtest = myst.Backtest.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
backtest = backtest.update(title="My backtest")
HPO
In order to run an HPO, make sure that you have created a project and graph with a model you want to optimize.
Listing HPOs
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# List all HPOs associated with the project.
hpos = myst.HPO.list(project=project)
Creating and running HPO
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# Use an existing deployed model within the project.
model = myst.Model.get(project=project, uuid="<uuid>")
# Create an hpo.
hpo = myst.HPO.create(
project=project,
title="My HPO",
model=model,
search_space={
"num_boost_round": myst.hpo.LogUniform(lower=100, upper=1000),
"max_depth": myst.hpo.QUniform(lower=1, upper=12, q=1),
"learning_rate": myst.hpo.LogUniform(lower=0.005, upper=0.2),
"min_child_weight": myst.hpo.QUniform(lower=0, upper=100, q=5),
},
search_algorithm=myst.hpo.Hyperopt(num_trials=10, max_concurrent_trials=5),
test_start_time=myst.Time("2021-07-01T00:00:00Z"),
test_end_time=myst.Time("2022-01-01T00:00:00Z"),
fit_start_timing=myst.TimeDelta("-P1M"),
fit_end_timing=myst.TimeDelta("-PT24H"),
fit_reference_timing=myst.CronTiming(cron_expression="0 0 * * 1"),
predict_start_timing=myst.TimeDelta("PT1H"),
predict_end_timing=myst.TimeDelta("PT24H"),
predict_reference_timing=myst.CronTiming(cron_expression="0 0 * * *"),
)
# Run the HPO.
hpo.run()
Analyze HPO results
To extract the result of an HPO in the client library, you can use the following code:
import myst
# Use an existing project.
project = myst.Project.get(uuid="<uuid>")
# Use an existing HPO within the project.
hpo = myst.HPO.get(project=project, uuid="<uuid>")
# Wait until the HPO is complete.
hpo.wait_until_completed()
# Get the result of the completed HPO.
hpo_result = hpo.get_result()
Once you have extracted your HPO result, you can use the following code to get the optimize parameters and the metrics from the best_trial:
# Get the optimized parameters for the best trial.
parameters = hpo_result.best_trial.parameters
# Get the pre-computed metrics for the best trial.
metrics = hpo_result.best_trial.metrics
update an HPO
import myst
hpo = myst.HPO.get(
project="05703aea-7319-4623-810d-b92b58692906",
uuid="a5ba722c-6750-4796-8b43-230b5e0f4c4a",
)
hpo = hpo.update(title="My HPO")
Further Examples
For more full-featured usage examples of the Myst Platform Python client library, see the
/examples
folder.
Support
For questions or just to say hi, reach out to support@myst.ai
.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file myst-alpha-0.21.1.tar.gz
.
File metadata
- Download URL: myst-alpha-0.21.1.tar.gz
- Upload date:
- Size: 159.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.28.1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 4088219563e2b9f58daee01e9c0d20fe6dba1ac85cf9662ea8c73533e1ad125d |
|
MD5 | 4bbcdd4e0c1585e9de08bd7b118373b5 |
|
BLAKE2b-256 | 2208b59e4dae423725b5be9b48e346c9b929c4a0016d55bc3cc76f8015abb9fc |
File details
Details for the file myst_alpha-0.21.1-py3-none-any.whl
.
File metadata
- Download URL: myst_alpha-0.21.1-py3-none-any.whl
- Upload date:
- Size: 273.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: python-requests/2.28.1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 99570fb1ede3a119eae99b2578d58845040ac763604228abb7e3a2c3d7ef137c |
|
MD5 | 4b48e64c005aafdb5188466486198efd |
|
BLAKE2b-256 | dd5cc4c5529440731a4cb535078593a65b9c0ed126a4ca6d428a4ac7c0af93de |