Writing Your Own Metrics Tutorial¶
This tutorial will describe how to write your own metrics for your own custom analyses. This takes place within Python, so you may want to check out the Cymetric Python tutorial first.
Writing Metrics¶
Naturally, you do not want to be limited to the metrics that come predefined by cymetric! You have your own data and your own analysis that you want to perform. It is easy to write metrics and fully hook into the cymetric tools.
A metric is a function that accepts a pandas Series, returns a
pandas DataFrame, and is decorated by the @metric()
decorator found in
cymetric. The general format is as follows:
import cyclus.typesystem as ts
import cymetic as cym
dependencies = [
('Table1', ('Col1', 'Col2'), 'Value1'),
('Table2', ('Col3',), 'Value2')
]
schema = [('Id', ts.INT), ('MetricValue', ts.DOUBLE)]
@cym.metric(name='MyMetric', depends=dependencies, schema=schema)
def my_metric(series):
one = series[0]
two = series[1]
#calculations and pandas manipulations go here
return dataframe
This metric may be evaulated with:
cym.eval('MyMetric', db)
In the above, the @metric()
decorator takes three arguments. The first is
the name
of the metric (e.g., 'MyMetric'
). Note that this can be
distinct from the function name.
The second is depends
, which represents the metric dependencies. This is a
list of 3-tuples that represents which series
to pull out of the database
and pass into the metric function (e.g., my_metric()
). The entries in the
dependency list have three components. The first element is the table name as a
string (e.g., 'Table1'
). The second element is a tuple of column names that
become the index of the series (e.g., ('Col1', 'Col2')
). Finally, the last
element is the column of the table that becomes the values of the series (e.g.,
'Value1'
). A metric may have as many dependencies as required. Circular
dependencies are not allowed!
Lastly, the @metric()
decorator takes a schema
argument. The schema is
defined by a list of 2-tuples. The first entry is the column name and the
second is the Cyclus database type. This represents the structure of the
metric table on disk and in Cyclus. Thus, it is highly tied to the Cyclus
type system.
The DataFrame that is returned should have column names that match
the schema provided. It is generally a good idea to include a SimId
column.
For a more concrete example, if you wanted to square the mass of materials as a
metric, you could write a MaterialsSquared
metric.
import pandas as pd
import cyclus.typesystem as ts
import cymetic as cym
deps = [('Materials', ('SimId', 'ResourceId', 'NucId'), 'Mass')]
schema = [('SimId', ts.UUID), ('ResourceId', ts.INT),
('NucId', ts.INT), ('MassSquared', ts.DOUBLE)]
@cym.metric(name='MaterialsSquared', depends=deps, schema=schema)
def mats_sqrd(series):
mats = series[0].reset_index()
mats = pd.DataFrame(data={'SimId': mats.SimId,
'ResourceId': mats.ResourceId,
'NucId': mats.NucId,
'MassSquared': mats.Mass.apply(lambda x: x**2)},
columns=['SimId', 'ResourceId', 'NucId', 'MassSquared'])
mats.name = 'MaterialsSquared'
return mats
This metric may be evaulated with:
cym.eval('MaterialsSquared', db)
Note that to write this metric, no knowledge of the database or any filters is assumed. Cymetric handles all of these details for you!
If the pandas functionality seems mysterious to you, it may be beneficial to review a quick tutorial, 10 Minutes to pandas.
Making Metrics from Custom Database Tables¶
The above shows how easy it is to incorporate metrics that are computed via
cymetric. Moreover, Cyclus databases can be comprised of both default tables and custom tables. Cymetric also helps you
bring in data that might come as a custom table in a Cyclus database. All you
need to do is use the root_metric()
function somewhere. This simply accepts
the name of the table. For example,
my_root_table = cym.root_metric(name='MyRootTable')
This metric may be evaulated with:
cym.eval('MyRootTable', db)
And that is all!