Output¶
Reference
Find the reference for the output
module here.
Write the output data after a run is completed. The Output
class is
the main holding place for the decisions made by the decision model. An output
is built through options, a solution, statistics, and other assets.
An output is written to a destination, through the
OutputWriter
class. You may use the write
function to write an output to a destination, or call the .write
method on
the OutputWriter
class.
The most common destination, and the one used by Nextmv Cloud, is either
stdout
or the local filesystem. The
LocalOutputWriter
class is provided for this reason
and it is the default output writer used by the write
function. When writing
locally, the output is written, by default, to this locations:
- File or
stdout
forJSON
outputs. output
directory forCSV_ARCHIVE
outputs.outputs
directory forMULTI_FILE
outputs.
JSON
outputs¶
Work with JSON
outputs. This is the default output format for Nextmv.
import nextmv
output = nextmv.Output(
solution={"foo": "bar"},
statistics=nextmv.Statistics(
result=nextmv.ResultStatistics(
duration=1.0,
value=2.0,
custom={"custom": "result_value"},
),
run=nextmv.RunStatistics(
duration=3.0,
iterations=4,
custom={"custom": "run_value"},
),
),
)
# Write to stdout.
nextmv.write(output)
# Write to a file.
nextmv.write(output, path="output.json")
The .solution
property of the output is a dictionary that represents the
output data. The .statistics
property can be a Statistics
object, or a dictionary.
By default, Output
serializes JSON
using pretty printing. If you want
to change the serialization behavior, you can pass the json_configurations
parameter. The provided values are passed to the underlying json.dumps
method. For example, to get compressed output, you can set:
output = nextmv.Output(
# ...
json_configurations={
"indent": None, # No indentation for compact output
"separators": (",", ":") # Use compact separators
},
# ...
)
CSV_ARCHIVE
outputs¶
Work with one, or multiple, CSV
files. In the .solution
property of the
output, the keys are the filenames and the values are the dataframes,
represented as a list of dictionaries. Each CSV
file must be utf-8
encoded.
By default, the output is written to a directory named output
, and the
filenames are derived from the keys of the .solution
dictionary. If you want
to change the output directory, you can pass the path
parameter to the
write
function.
import nextmv
output = nextmv.Output(
output_format=nextmv.OutputFormat.CSV_ARCHIVE,
solution={
"output": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 40},
],
},
statistics=nextmv.Statistics(
result=nextmv.ResultStatistics(
duration=1.0,
value=2.0,
custom={"custom": "result_value"},
),
run=nextmv.RunStatistics(
duration=3.0,
iterations=4,
custom={"custom": "run_value"},
),
),
)
# Write multiple CSV fiules to a dir named "output".
nextmv.write(output)
# Write multiple CSV files to a custom dir.
nextmv.write(output, "custom_dir")
Similarly to the JSON
output, the .statistics
property can be a
Statistics
object, or a dictionary.
By default, Output
serializes CSV
using ,
as the separator. If you
want to change the serialization behavior, you can pass the csv_configurations
parameter. The provided values are passed to the underlying csv.DictWriter
method. For example, to use ;
as the separator, you can set:
output = nextmv.Output(
# ...
csv_configurations={
"delimiter": ";", # Use semicolon as the separator
},
# ...
)
MULTI_FILE
outputs¶
When you need to work with a diverse set of files, use the MULTI_FILE
output
format. Multi-file supports the following file formats:
.json
- Text (utf-8 encoded text)
.csv
(which must be utf-8 encoded).xlsx
(Excel files)
To work with multi-file outputs, you need to define one or more
SolutionFile
classes, each of which is associated with a
file. You can use the following convenience functions to create these classes:
json_solution_file
: write a.json
file.csv_solution_file
: write a.csv
file.text_solution_file
: write a text file. Any file withutf-8
encoded text can be used, like.mip
, or.lp
files.
By default, the output is written to a directory named outputs
, and the
filenames are derived from the .name
parameter of the
SolutionFile
classes. If you want to change the output
directory, you can pass the path
parameter to the write
function.
In the output
directory, the following sub-directories are used:
outputs/solutions
: for the solution files.outputs/statistics
: for the statistics file. IfMULTI_FILE
is used, the statistics are written to a file namedstatistics.json
.outputs/assets
: for the assets files. IfMULTI_FILE
is used, the assets are written to a file namedassets.json
.
import nextmv
# Define a solution file for a JSON file.
json_file = nextmv.json_solution_file("output.json", {"foo": "bar"})
# Define a solution file for a CSV file.
csv_file = nextmv.csv_solution_file("output.csv", [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 40}])
# Define a solution file for a text file.
text_file = nextmv.text_solution_file("output.txt", "Hello, World!")
output = nextmv.Output(
output_format=nextmv.OutputFormat.MULTI_FILE,
solution_files=[json_file, csv_file, text_file],
statistics=nextmv.Statistics(
result=nextmv.ResultStatistics(
duration=1.0,
value=2.0,
custom={"custom": "result_value"},
),
run=nextmv.RunStatistics(
duration=3.0,
iterations=4,
custom={"custom": "run_value"},
),
),
)
# Write multiple files to a dir named "outputs".
nextmv.write(output)
# Write multiple files to a custom dir.
nextmv.write(output, "custom_dir")
When working with binary files, such as Excel files, you must define your own
SolutionFile
class. The most important parameter of this class is the
.writer
, which is a Callable
(function) that you provide. The signature of
this function is as follows:
The file_path
establishes the location where this data is written to. The
.name
defined in the class is going to be given to this function, with the
correct directory already joined. This .writer
can receive additional
arguments and keyword arguments, which you can define in the SolutionFile
class through the .writer_args
and .writer_kwargs
parameters.
from typing import Any
import nextmv
# Define a custom writer for an Excel file.
def excel_writer(file_path: str, data: Any) -> None:
import pandas as pd
df = pd.DataFrame(data)
df.to_excel(file_path, index=False)
# Define a solution file for an Excel file.
excel_file = nextmv.SolutionFile(
name="output.xlsx",
writer=excel_writer,
writer_args=[], # Optional, you don't need to define this if no args are needed
writer_kwargs={}, # Optional, you don't need to define this if no kwargs are needed
)
# Load the multi-file output with the defined solution files.
multi_file_output = nextmv.Output(
output_format=nextmv.OutputFormat.MULTI_FILE,
solution_files=[excel_file],
statistics=nextmv.Statistics(
result=nextmv.ResultStatistics(
duration=1.0,
value=2.0,
custom={"custom": "result_value"},
),
run=nextmv.RunStatistics(
duration=3.0,
iterations=4,
custom={"custom": "run_value"},
),
),
)
# Write multiple files to a dir named "outputs".
nextmv.write(multi_file_output)
# Write multiple files to a custom dir.
nextmv.write(multi_file_output, "custom_dir")
Assets¶
A run in Nextmv Cloud can include custom assets, such as those used in custom visualization.
You can use the .assets
property of the output to include these assets.
For example, you can create a simple plot, which consists of a Plotly bar chart
with radius and distance for a planet. Consider the following visuals.py
file:
import json
import nextmv
import plotly.graph_objects as go
def create_visuals(name: str, radius: float, distance: float) -> list[nextmv.Asset]:
"""Create a Plotly bar chart with radius and distance for a planet."""
fig = go.Figure()
fig.add_trace(
go.Bar(x=[name], y=[radius], name="Radius (km)", marker_color="red", opacity=0.5),
)
fig.add_trace(
go.Bar(x=[name], y=[distance], name="Distance (Millions km)", marker_color="blue", opacity=0.5),
)
fig.update_layout(
title="Radius and Distance by Planet", xaxis_title="Planet", yaxis_title="Values", barmode="group"
)
fig = fig.to_json()
assets = [
nextmv.Asset(
name="Plotly example",
content_type="json",
visual=nextmv.Visual(
visual_schema=nextmv.VisualSchema.PLOTLY,
visual_type="custom-tab",
label="Charts",
),
content=[json.loads(fig)],
)
]
return assets
This list of assets can be included in the output.
import nextmv
from visuals import create_visuals
# Read the input from stdin.
input = nextmv.load()
name = input.data["name"]
options = nextmv.Options(
nextmv.Option("details", bool, True, "Print details to logs. Default true.", False),
)
##### Insert model here
# Print logs that render in the run view in Nextmv Console.
message = f"Hello, {name}"
nextmv.log(message)
if options.details:
detail = "You are", {input.data["distance"]}, " million km from the sun"
nextmv.log(detail)
assets = create_visuals(name, input.data["radius"], input.data["distance"])
# Write output and statistics.
output = nextmv.Output(
options=options,
solution=None,
statistics=nextmv.Statistics(
result=nextmv.ResultStatistics(
value=1.23,
custom={"message": message},
),
),
assets=assets,
)
nextmv.write(output)
The .assets
can be a list of Asset
objects, or a list of
dictionaries that comply with the custom assets and custom
visualization schemas, whichever the case may be.