Skip to main content

fero_client

fero is a client-side Python library intended to help users interact with Fero.

Quickstart

from fero import Fero

# Create a Fero client object
fero_client = Fero(username="<your username>", password="<your password>")

# Get a specific analysis by its unique identifier
analysis = fero_client.get_analysis("5dfbbb63-8ad4-4638-9fdb-61e39952d3cf")

# Create a pandas DataFrame with factor values for this analysis
df = pd.DataFrame([{"value": 5, "value2": 2}])

# Make a prediction
prediction = analysis.make_prediction(df)

print(prediction)
'''
   value   value2  target_low90  target_low50 target_mid target_high50  target_high90
0      5        2            70            75         80            88             92
'''

Providing Credentials

The simplest way to provide your Fero login credentials is as arguments to the Fero object on initialization.

fero_client = Fero(username="<your username>", password="<your password>")

While this is fine for interactive shells, it is not ideal for a publicly viewable script. To account for this, Fero also supports setting the FERO_USERNAME and FERO_PASSWORD environment variables or storing your username and password in a .fero file in the home directory. This file needs to be in the the following format.

FERO_USERNAME=fero_user
FERO_PASSWORD=shouldBeAGoodPassword

If you are using the Fero client to access an on-premises installation, both the hostname for the local Fero server can be provided with hostname="https://local.fero-site" and an internal SSL certification via verify="path/to/ca-bundle. (See here for additional details.) Verify is passed directly to the underlying Python requests package; thus, if you desire, verification can be disabled by passing verify=False.

local_client = Fero(hostname="https://fero.self.signed", verify=False)

Finding a Fero Analysis

The Fero client provides two different methods to find an Analysis. The first is Fero.get_analysis which takes a single unique identifier string (UUID) and attempts to look up the analysis matching this ID. The second method is Fero.search_analyses which will return an iterator of available Analysis objects. If no keyword arguments are provided, it will return all analyses you have available on Fero. Optionally, name can be provided to filter to only analyses matching that name.

Examples

from fero import Fero
fero_client = Fero(username="<your username>", password="<your password>")

# Get a specific analysis
analysis = fero_client.get_analysis("5dfbbb63-8ad4-4638-9fdb-61e39952d3cf")

# Get all available analyses
all_analyses = fero_client.search_analyses()

# Only get "plant_A" analyses
plant_A_only =  fero_client.search_analyses(name="plant_A")

Using an Analysis

Along with associated properties such as name and uuid, an Analysis provides a variety of methods for interacting with Fero.

The first thing to call when working with an Analysis is Analysis.has_trained_model, which checks whether the Analysis is ready to use. This will be false if the Analysis is still being configured or if there was an error during configuration.

Making a simple prediction

The Analysis.make_prediction method makes a prediction using the latest revision of the Analysis. This function can take either a pandas DataFrame with columns matching the expected factors or a list of dictionaries with each dictionary containing a key/value pairs for each factor. A prediction will be made for each row in the DataFrame or each dictionary in the list.

The return value will either be a DataFrame or a dictionary, depending on the initial input type. These values will have the suffixes _lowX, _mid, _highX added to each target name to indicate the prediction intervals. Specifically:

  • target_low90 corresponds to the 5% prediction level,
  • target_low50 corresponds to the 25% prediction level,
  • target_mid corresponds to the mean prediction,
  • target_high50 corresponds to the 75% prediction level, and
  • target_high90 corresponds to the 95% prediction level.

The naming convention indicates that:

  • 50% of the time, the corresponding measurement should fall between (target_low50, target_high50), and
  • 90% of the time, the corresponding measurement should fall between (target_low90, target_high90).

Example

raw_data = [{"value": 5, "value2": 2}]

# Using a DataFrame
df = pd.DataFrame([raw_data])
prediction = analysis.make_prediction(df)

print(prediction)
'''
   value   value2  target_low90  target_low50 target_mid target_high50  target_high90
0      5        2            10            20         30            40             50
'''

# Using a list of dicts
prediction = analysis.make_prediction(raw_data)
print(prediction)
'''
[{"value": 5, "value2": 2, "target_low90": 10, "target_low": 20, "target_mid": 30, "target_high50": 40, "target_high90": 50}]
'''

Optimize

A more advanced usage of an Analysis is to create an optimization which will make a prediction that satistifies a specified goal within the context of constraints on other factors or targets. Currently, the Fero optimizer can be used to conduct three different types of optimizations based on an Analysis.

Example 1: Minimize a factor given constraints

Fero can be used to minimize value while keeping target within a set range. The following goal and constraint configurations would need to be provided.

goal = {
  "goal": "minimize",
  "factor": {"name": "value", "min": 50.0, "max": 100.0}
}

constraints = [{"name": "target", "min": 100.0, "max": 200}]

opt = analysis.make_optimization("example_optimization", goal, constraints)

By default, Fero will use the median values of fixed factors while computing the optimization. These can be overridden with custom values by passing a dictionary of factor:value pairs as the fixed_factors argument to the optimization function.

fixed_factors = {
  "value": 10,
  "value2": 20
}

opt = analysis.make_optimization("example_optimization", goal, constraints, fixed_factors)

Example 2: Maximize a target KPI given constraints

Alternatively, a target KPI can be maximized while constraining a value. Note that the same key, factor, is used when defining the target KPI in goal.

goal = {
"goal": "maximize",
"factor": {"name": "target", "min": 100.0, "max": 200.0}
}

constraints = [{"name": "value", "min": 50.0, "max": 100.0}]

opt = analysis.make_optimization("example_optimization", goal, constraints)

By default, Fero will not incorporate confidence intervals while optimizing a target. The lower (5%) and upper (95%) bounds of the confidence intervals can be included during optimization by setting argument include_confidence_intervals to True. This will ensure that the upper or lower prediction level of the optimization result do not exceed the set min/max values for target. (This could have also been set to True in the previous example.)

opt = analysis.make_optimization("example_optimization", goal, constraints, fixed_factors)

Example 3: Optimize a cost function over multiple factors

Fero also supports the idea of a cost optimization, which will weight different factors by specified cost multipliers to find the best combination of inputs. For example, to find the minimum combined cost of value and value2 while meeting the expected values of target, you could do the following:

goal = {
  "goal": "minimize",
  "type": "COST",
  "cost_function": [{"name": "value", "min": 50.0, "max": 100.0, "cost": 5.0}, {"name": "value2", "min": 70.0, "max": 80.0, "cost": 9.0}]
}

constraints = [{"name": "target", "min": 100.0, "max": 200}]

opt = analysis.make_optimization("example_cost_optimization", goal, constraints)

In both cases, a Prediction object is returned, which will provide access to the results of the optimization. By default, the result will be a DataFrame but it can also be configured to be a list of dictionaries by specifying format="record" in get_results.

Example 4: Optimize a target subject to combination constriants

Fero also supports combination constraints, which allow us to find optima subject to more complex relationships between different factors and targets. For example, we can find the maximum tensile strength while ensuring that the tensile/yield strength ratio does not exceed some threshold, and that the mass of carbon plus silicon meets some minimum threshold. Provided methods can assist in structuring these constraints.

from fero.analysis import (
  CombinationConstraintOperandType as operands,
  CombinationConstraintOperator as operators
)
goal = {
  "goal": "maximimze",
  "factor": {"name": "Tensile Strength", "min": 10000.0, "max": 20000.0}
}

constraints = [
  {"name": "Carbon", "min": 0.0, "max": 50.0},
  {"name": "Vanadium", "min": 0.0, "max": 25.0},
  {"name": "Yield Strength", "min": 1000.0, "max" 5000.0},
  {"name": "Copper", "min": 10.0, "max": 15.0}
]

fixed_factors = {
  "Product Type": "Type 1",
  "Product Grade": "Grade B",
  "Silicon": 14.0,
  "STRENGTH_RATIO": 12.5,
  "CARBON_MASS": 12.011,
  "SILICON_MASS": 28.05,
  "Iron": 4501.12,
  "Temperature": 350.1,
  "test_time": "2024-01-01T00:00"
}

combination_constraints = [
  CombinationConstraint(
    ("'Tensile Strength' / 'Yield Strength'", operands.FORMULA),
    operators.LESS_THAN_OR_EQUAL,
    ("STRENGTH_RATIO", operands.COLUMN)
  ),
  CombinationConstraint(
    ("'CARBON_MASS' * 'CARBON' + 'SILICON_MASS' * 'Silicon'", operands.FORMULA),
    operators.GREATER_THAN,
    (134.23, operands.CONSTANT)
  )
]

opt = analysis.make_optimization(
  "example_cost_optimization",
  goal,
  constraints,
  fixed_factors,
  combination_constraints=combination_constraints
)

Retrieving live predictions

Fero continuously runs live predictions and optimizations against an Analysis as new process data arrives. Analysis.get_live_predictions retrieves the most recent of these.

Four kinds of live prediction are available, selected with the type argument using the LivePredictionType enum:

LivePredictionType Returns Description
PREDICTION (default) LivePrediction A prediction of the Analysis targets against a single basis.
FLEXIBLE_PREDICTION FlexibleLivePrediction A prediction evaluated against several scenarios at once.
OPTIMIZATION LiveOptimization An optimization of the Analysis factors against a single basis.
FLEXIBLE_OPTIMIZATION FlexibleLiveOptimization An optimization evaluated against several scenarios at once.

The ordering is controlled with the sort argument using the LivePredictionSort enum:

LivePredictionSort Description
NEWEST_FIRST (default) Most recently created first.
OLDEST_FIRST Oldest created first.
LIVE_ORDER_DESCENDING Ordered by the live data's own ordering value, highest first.
LIVE_ORDER_ASCENDING Ordered by the live data's own ordering value, lowest first.

Prefer the LIVE_ORDER_* options over NEWEST_FIRST/OLDEST_FIRST when your live data can arrive out of order, as they sort by the ordering value carried on the source data rather than by when Fero recorded the prediction.

Finally, limit sets how many predictions to return. It defaults to 10 and is capped at 1000; this method is intended for reading recent live activity rather than for bulk export.

All three arguments are validated before the request is made, so an invalid type, sort or limit raises a FeroError locally. Plain strings are accepted in place of the enum members if you would rather not import them.

Example 1: The latest live predictions

Along with the metadata of the prediction itself, a LivePrediction exposes its results through targets — a dictionary keyed by target name, where each value carries the predicted distribution as plain attributes.

mid is the expected value. The low50/high50 and low90/high90 pairs bound the 50% and 90% confidence intervals around it, so the measurement should fall between low50 and high50 half the time, and between low90 and high90 nine times out of ten.

from fero import Fero, LivePredictionType, LivePredictionSort

fero_client = Fero()
analysis = fero_client.get_analysis("<analysis uuid>")

predictions = analysis.get_live_predictions(limit=5)

latest = predictions[0]
print(latest.created, latest.prediction_tag, latest.complete)
# 2026-08-24 12:00:00.123456+00:00 gc-p-1234 True

# The basis the prediction was made against
print(latest.basis)
# {'CARBON': 0.21, 'SILICON': 0.18}

# Which targets were predicted
print(list(latest.targets))
# ['TENSILE_STRENGTH', 'ELONGATION']

# The expected value for one target, and its 90% interval
strength = latest.targets["TENSILE_STRENGTH"]
print(strength.mid)
# 190.24

print(strength.low90, strength.high90)
# 167.46 213.02

# Or the whole distribution as a plain dictionary
print(strength.to_dict())
# {'low90': 167.46, 'low50': 178.85, 'mid': 190.24, 'high50': 201.63, 'high90': 213.02}

Because targets is an ordinary dictionary, you can loop over it to report every target at once.

for name, target in latest.targets.items():
    print(f"{name}: {target.mid} (90% between {target.low90} and {target.high90})")
# TENSILE_STRENGTH: 190.24 (90% between 167.46 and 213.02)
# ELONGATION: 22.1 (90% between 19.62 and 24.58)

A live prediction that is still running, or that failed, is still included in the results, so check complete and status before using one. An unfinished or failed prediction simply has an empty targets dictionary.

for prediction in analysis.get_live_predictions():
    if not prediction.complete:
        print(f"{prediction.uuid} is still running")
    elif prediction.status == "FAILURE":
        print(f"{prediction.uuid} failed: {prediction.message}")
    else:
        print(prediction.targets["TENSILE_STRENGTH"].mid)

Example 2: Live optimizations

A LiveOptimization reports the optimal factor and target values Fero found through optimal_values, as one dictionary per solution. It is a list because an optimization can return several equally optimal solutions; it is empty if the optimization found none.

optimizations = analysis.get_live_predictions(
    type=LivePredictionType.OPTIMIZATION,
    sort=LivePredictionSort.LIVE_ORDER_DESCENDING,
    limit=3,
)

optimization = optimizations[0]

# How many solutions this optimization found
print(len(optimization.optimal_values))
# 2

# Each solution is a plain dictionary keyed by factor and target name
print(optimization.optimal_values[0])
# {'CARBON': 0.19, 'SILICON': 0.22, 'TENSILE_STRENGTH': 201.63}

print(optimization.optimal_values[0]["CARBON"])
# 0.19

Loop over the list to see every solution.

for index, solution in enumerate(optimization.optimal_values):
    print(f"Solution {index}: CARBON={solution['CARBON']}, TENSILE_STRENGTH={solution['TENSILE_STRENGTH']}")
# Solution 0: CARBON=0.19, TENSILE_STRENGTH=201.63
# Solution 1: CARBON=0.2, TENSILE_STRENGTH=199.84

Every result object also provides a to_dataframe method, which returns the same data as a pandas DataFrame for filtering, sorting and CSV export. See Example 4 below.

Example 3: Flexible predictions and optimizations

A flexible prediction evaluates the same request against several scenarios at once. It is returned as a single object holding a scenarios list, so limit always counts predictions rather than scenarios. Each scenario reports the basis it was evaluated against.

default_scenario is the scenario Fero considers most representative. For a flexible optimization this is the riskiest scenario, which is usually the one worth acting on.

prediction = analysis.get_live_predictions(
    type=LivePredictionType.FLEXIBLE_PREDICTION,
    limit=1,
)[0]

print(len(prediction.scenarios))
# 3

# Each scenario carries the basis it was evaluated against
print(prediction.scenarios[0].basis)
# {'CARBON': 0.21, 'SILICON': 0.18, 'GRADE': 'A'}

print(prediction.default_scenario.targets["TENSILE_STRENGTH"].mid)
# 190.24

# Every scenario in a single frame, indexed by scenario and target
print(prediction.to_dataframe())
#                             low90   low50     mid  high50  high90
# scenario target
# 0        TENSILE_STRENGTH  167.46  178.85  190.24  201.63  213.02
#          ELONGATION         19.62   20.86   22.10   23.34   24.58
# 1        TENSILE_STRENGTH  160.23  171.62  183.01  194.40  205.79
#          ELONGATION         21.87   23.11   24.35   25.59   26.83
# 2        TENSILE_STRENGTH  163.70  175.09  186.48  197.87  209.26
#          ELONGATION         20.54   21.78   23.02   24.26   25.50

Example 4 below walks through working with that frame.

A FlexibleLiveOptimization works the same way, with each scenario holding its own optimal values.

optimization = analysis.get_live_predictions(
    type=LivePredictionType.FLEXIBLE_OPTIMIZATION,
    limit=1,
)[0]

# The riskiest scenario Fero identified -- here scenario 1, not the first one
print(optimization.default_scenario.to_dataframe())
#    CARBON  SILICON  TENSILE_STRENGTH
# 0     0.2     0.21             194.5

# Or every scenario at once, tagged with a scenario column
print(optimization.to_dataframe())
#    scenario  CARBON  SILICON  TENSILE_STRENGTH
# 0         0    0.19     0.22            201.63
# 1         1    0.20     0.21            194.50

Example 4: Working with flexible results as DataFrames

to_dataframe returns a pandas DataFrame. If you have not used pandas before, the short version is that a DataFrame is a table: it has named columns, a labelled index identifying each row, and methods for selecting, filtering and exporting.

The frame from a FlexibleLivePrediction is indexed by two labels rather than one — the scenario number and the target name — because each scenario predicts every target. Rows are the (scenario, target) pairs and columns are the confidence intervals.

frame = prediction.to_dataframe()

print(frame)
#                             low90   low50     mid  high50  high90
# scenario target
# 0        TENSILE_STRENGTH  167.46  178.85  190.24  201.63  213.02
#          ELONGATION         19.62   20.86   22.10   23.34   24.58
# 1        TENSILE_STRENGTH  160.23  171.62  183.01  194.40  205.79
#          ELONGATION         21.87   23.11   24.35   25.59   26.83
# 2        TENSILE_STRENGTH  163.70  175.09  186.48  197.87  209.26
#          ELONGATION         20.54   21.78   23.02   24.26   25.50

Selecting columns. Index the frame with a column name to get a single column, or with a list of names to get a narrower frame.

# One column, as a pandas Series
print(frame["mid"])
# scenario  target
# 0         TENSILE_STRENGTH    190.24
#           ELONGATION           22.10
# 1         TENSILE_STRENGTH    183.01
#           ELONGATION           24.35
# 2         TENSILE_STRENGTH    186.48
#           ELONGATION           23.02
# Name: mid, dtype: float64

# Several columns, as a DataFrame
print(frame[["mid", "high90"]])
#                              mid  high90
# scenario target
# 0        TENSILE_STRENGTH  190.24  213.02
#          ELONGATION         22.10   24.58
# ...

Selecting rows. Use .loc[scenario] for everything predicted by one scenario, and .xs(target, level="target") to pull one target across every scenario. The latter is usually what you want, since it gives a plain one-row-per-scenario table.

# Everything scenario 0 predicted
print(frame.loc[0])
#                    low90   low50     mid  high50  high90
# target
# TENSILE_STRENGTH  167.46  178.85  190.24  201.63  213.02
# ELONGATION         19.62   20.86   22.10   23.34   24.58

# One target across every scenario
strength = frame.xs("TENSILE_STRENGTH", level="target")
print(strength)
#            low90   low50     mid  high50  high90
# scenario
# 0         167.46  178.85  190.24  201.63  213.02
# 1         160.23  171.62  183.01  194.40  205.79
# 2         163.70  175.09  186.48  197.87  209.26

Filtering rows by a condition. Compare a column against a value to get a mask of True/False, then index the frame with it to keep only the matching rows. Do this on a single target's table rather than on the whole frame — different targets are measured in different units, so a threshold that means something for one is meaningless for another.

# Which scenarios could fall below a 165 MPa minimum spec?
print(strength[strength["low90"] < 165])
#            low90   low50     mid  high50  high90
# scenario
# 1         160.23  171.62  183.01  194.40  205.79
# 2         163.70  175.09  186.48  197.87  209.26

Finding the highest or lowest row. idxmax and idxmin give the index label of the largest or smallest value in a column, which you can pass straight to .loc. Wrapping the label in a list keeps the result a DataFrame rather than collapsing it to a Series.

# Which scenario has the highest expected strength?
print(strength["mid"].idxmax())
# 0

print(strength.loc[[strength["mid"].idxmax()]])
#            low90   low50     mid  high50  high90
# scenario
# 0         167.46  178.85  190.24  201.63  213.02

describe summarises a column if you just want the spread across scenarios.

print(strength["mid"].describe())
# count      3.000000
# mean     186.576667
# std        3.615969
# min      183.010000
# 25%      184.745000
# 50%      186.480000
# 75%      188.360000
# max      190.240000
# Name: mid, dtype: float64

Exporting to CSV. to_csv writes the frame to a file, including both index levels as their own columns. Pass no filename to get the CSV back as a string instead.

frame.to_csv("live_prediction.csv")

# scenario,target,low90,low50,mid,high50,high90
# 0,TENSILE_STRENGTH,167.46,178.85,190.24,201.63,213.02
# 0,ELONGATION,19.62,20.86,22.1,23.34,24.58
# 1,TENSILE_STRENGTH,160.23,171.62,183.01,194.4,205.79
# ...

# Leave the row labels out entirely
frame.to_csv("live_prediction.csv", index=False)

If you would rather work with the scenario and target as ordinary columns — which some tools and spreadsheets prefer — reset_index flattens the two index levels into columns and numbers the rows instead.

print(frame.reset_index())
#    scenario            target   low90   low50     mid  high50  high90
# 0         0  TENSILE_STRENGTH  167.46  178.85  190.24  201.63  213.02
# 1         0        ELONGATION   19.62   20.86   22.10   23.34   24.58
# 2         1  TENSILE_STRENGTH  160.23  171.62  183.01  194.40  205.79
# 3         1        ELONGATION   21.87   23.11   24.35   25.59   26.83
# 4         2  TENSILE_STRENGTH  163.70  175.09  186.48  197.87  209.26
# 5         2        ELONGATION   20.54   21.78   23.02   24.26   25.50

Flexible optimizations. A FlexibleLiveOptimization frame is simpler: it has ordinary numbered rows and a scenario column, so every operation above works without the .xs step.

values = optimization.to_dataframe()

print(values[values["TENSILE_STRENGTH"] > 200])
#    scenario  CARBON  SILICON  TENSILE_STRENGTH
# 0         0    0.19     0.22            201.63

# The scenario needing the least carbon
print(values.loc[[values["CARBON"].idxmin()]])
#    scenario  CARBON  SILICON  TENSILE_STRENGTH
# 0         0    0.19     0.22            201.63

values.to_csv("live_optimization.csv", index=False)

Finding a Fero Asset

The Fero client provides two different methods to find an Asset. The first is Fero.get_asset, which takes a single unique identifier string (UUID) and attempts to look up the asset matching this ID. The second method is Fero.search_assets, which will return an iterator of available Asset objects. If no keyword arguments are provided, it will return all assets you have available on the Fero website. Optionally, name can be provided to filter to only assets matching that name.

Examples

from fero import Fero
fero_client = Fero(username="<your username>", password="<your password>")

# Get a specific asset
asset = fero_client.get_asset("fd57ba36-3c5d-40f5-ae0c-d7b76ab39ee5")

# Get all available assets
all_assets = fero_client.search_assets()

# Get only "plant_B" assets
plant_B_only = fero_client.search_assets(name="plant_B")

Using an Asset

Along with associated properties such as name and uuid, an Asset provides a few methods for interacting with Fero.

The first thing to call when working with an asset is Asset.has_trained_model, which checks whether the Asset is ready to use. This will be false if the Asset is still being configured or if there was an error during configuration.

Making a prediction

The Asset.predict method makes a prediction using the latest revision of the Asset. Fero computes predictions for all controllable factors and with those results, predictions for all target variables. Predictions are provided for the 5 time intervals following the end of the training dataset. (Interval size is determined during model configuration and training.) Optionally, you may call Asset.predict with an argument specifying values for one or more of the controllable factors; Fero will predict all targets using your specified values in place of its controllable factor predictions where applicable.

Examples

# With no inputs
prediction = asset.predict()

print(prediction.columns)
['mean:Factor1', 'p5:Factor1', 'p25:Factor1', 'p75:Factor1', 'p95:Factor1',
 'mean:Factor2', 'p5:Factor2', 'p25:Factor2', 'p75:Factor2', 'p95:Factor2',
 'mean:Target1', 'p5:Target1', 'p25:Target1', 'p75:Target1', 'p95:Target1']

print(prediction)
'''
                        mean:Factor1  p5:Factor1  p25:Factor1 ... p75:Target1  p95:Target1
2020-12-25T00:00:00Z    7.937         7.253       7.688       ... 1.921        2.197
2020-12-25T01:00:00Z    8.059         6.962       7.721       ... 1.924        2.202
2020-12-25T02:00:00Z    8.193         6.754       7.692       ... 1.871        2.318
2020-12-25T03:00:00Z    8.349         6.552       7.619       ... 1.830        2.375
2020-12-25T04:00:00Z    8.492         6.199       7.498       ... 1.762        2.425
'''

# Provide specified values as a DataFrame
new_factor_values = pd.DataFrame({
    "Factor1": [8.0, 8.1, 8.2, 8.3, 8.4]
})

prediction = asset.predict(new_factor_values)

print(prediction.columns)
['specified:Factor1',
 'mean:Factor2', 'p5:Factor2', 'p25:Factor2', 'p75:Factor2', 'p95:Factor2',
 'mean:Target1', 'p5:Target1', 'p25:Target1', 'p75:Target1', 'p95:Target1']

print(prediction)
'''
                        specified:Factor1  mean:Factor2  p5:Factor2 ... p75:Target1  p95:Target1
2020-12-25T00:00:00Z    8.0                13.452        11.953     ... 1.921        2.197
2020-12-25T01:00:00Z    8.1                13.119        11.762     ... 1.924        2.202
2020-12-25T02:00:00Z    8.2                13.084        11.454     ... 1.871        2.318
2020-12-25T03:00:00Z    8.3                13.003        11.352     ... 1.830        2.375
2020-12-25T04:00:00Z    8.4                12.976        11.109     ... 1.762        2.425
'''

# Provide specified values as a dictionary
new_factor_values = {
    "Factor1": [8.0, 8.1, 8.2, 8.3, 8.4]
}

prediction = asset.predict(new_factor_values)

print(list(prediction.keys())
'''
[
    'specified:Factor1', 'mean:Factor2', 'p5:Factor2', 'p25:Factor2', 'p75:Factor2',
    'p95:Factor2', 'mean:Target1', 'p5:Target1', 'p25:Target1', 'p75:Target1', 'p95:Target1',
    'index'
]
'''

print(prediction["mean:Factor2"])
'''
[
    13.452, 13.119, 13.084, 13.003, 12.976
]
'''

print(prediction["index"])
'''
[
    2020-12-25T00:00:00Z, 2020-12-25T01:00:00Z, 2020-12-25T02:00:00Z, 2020-12-25T03:00:00Z, 2020-12-25T04:00:00Z
]
'''

Fero Processes

The Fero client provides two different methods to find a Process. The first is Fero.get_process which takes a single unique identifier string (UUID) and attempts to look up the process matching this ID. The second method is Fero.search_processes which will return an iterator of available Process objects. If no keyword arguments are provided, it will return all processes you have available on Fero. Optionally, name can be provided to filter to only processes matching that name.

Processes represent data via two main underlying entities, the Tag and the Stage. A Tag is a column of a specific measurement in the underlying data. A Stage is a logical part of a process consisting of various tags and an order relative to the other stages. For example, a steel process might have first stage for melting the steel and a later stage for casting the steel, each with corresponding measurements in the form of tags.

Example

from fero import Fero
fero_client = Fero(username="<your username>", password="<your password>")

# Get a single process
process = fero_client.get_process("c6f69e96-db4d-43ed-8837-d5827cc81112")

# Search processes by name
processes = [p for p in fero_client.search_processes(name="process X")]

# Get the tags of the process
tags = process.tags

# Get stages of the process
stages = process.stages

# Get tag groups of a stage
tag_groups = stages[0].tag_groups

Downloading Process Data

A Process object can be used to download the pandas DataFrame that the process would produce for analysis. Because not all tags are generally used in a analysis, a list of desired tags is required before data can be downloaded. Additionally, a target or key performance indicator (kpi) tag can be set while requesting data. Functionally, this will limit the data returned to the stage of the kpi tag and any preceding stages. For advanced and batch processes, kpis are optional; however, they are required for continuous processes because the data is computed using the observed times of the kpi.

Example

# Get all data for single process
process = fero_client.get_process("9777bae7-95af-4bea-98b9-c703ab940a05")

df = process.get_data(process.tags)

print(df)
'''
       s1_factor1  s1_factor2  s2_factor1  s3_factor1  s3_factor2   s3_kpi
0               0          14           7        28.5           0     49.5
1               1           8           5        36.0           3     53.0
2               2           2           3        39.5           6     52.5
3               3          10           8        26.5           9     56.5
4               4           4           6        41.5          12     67.5
...           ...         ...         ...         ...         ...      ...
10395       10395           4       10397        38.0       31185  52019.0
10396       10396           0       10396        32.5       31188  52012.5
10397       10397          14       10404        40.5       31191  52046.5
10398       10398           0       10398        25.5       31194  52015.5
10399       10399          14       10406        42.0       31197  52058.0

[10400 rows x 6 columns]
'''

# Limit the process to an earlier kpi

df = process.get_data(["s1_factor1", "s3_kpi"], kpis=["s2_factor1"])

'''
                            dt  s2_factor1  s1_factor1
0    2020-03-01 00:00:00+00:00          10        <NA>
1    2020-03-01 00:01:00+00:00         162        <NA>
2    2020-03-01 00:02:00+00:00          12          16
3    2020-03-01 00:03:00+00:00          12          15
4    2020-03-01 00:04:00+00:00          56         162
...                        ...         ...         ...
1994 2020-03-02 09:14:00+00:00        2006          65
1995 2020-03-02 09:15:00+00:00        2007          20
1996 2020-03-02 09:16:00+00:00         415         174
1997 2020-03-02 09:17:00+00:00        2001         166
1998 2020-03-02 09:18:00+00:00           0           2

[1999 rows x 3 columns]
'''

Downloading DataSource Data

The raw and processed data in a DataSource can be downloaded via the .download() method. It takes a raw boolean keyword argument (defaults to False) that decides whether to download the raw data. It returns the local csv filename where the data was written.

Example

datasource = fero_client.get_datasource("66d2dd0f-2f16-4002-bb3b-445173eedd95")

datasource.download(raw=True)
'''fero-raw-ds-66d2dd0f-2f16-4002-bb3b-445173eedd95.csv'''

datasource.download()
'''fero-ds-66d2dd0f-2f16-4002-bb3b-445173eedd95.csv'''

Copying Workspaces

See: Copy Demo Workspaces

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fero-2.4.0.tar.gz (76.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fero-2.4.0-py3-none-any.whl (43.6 kB view details)

Uploaded Python 3

File details

Details for the file fero-2.4.0.tar.gz.

File metadata

  • Download URL: fero-2.4.0.tar.gz
  • Upload date:
  • Size: 76.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fero-2.4.0.tar.gz
Algorithm Hash digest
SHA256 af3c98074d6e27c18eee23e72041b782d7032d59ddc68375d835cd57ae8b7dec
MD5 f63853aac5cb76158afb16f6dc8541c2
BLAKE2b-256 ac578423bc7ae3413f5d47c126a15e57007d4471d816603a7270affe9cce8b4d

See more details on using hashes here.

File details

Details for the file fero-2.4.0-py3-none-any.whl.

File metadata

  • Download URL: fero-2.4.0-py3-none-any.whl
  • Upload date:
  • Size: 43.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fero-2.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 87e009e3bdb6fb0e16d5222c96b6fe4f8090fc896c38c4e37052d3a51c168564
MD5 f5820c7245d802a30794b0eabf5579f9
BLAKE2b-256 b3ccd90e0266c9555e068c881ae30a7c918cf3d3ebbb1cd752618a5543790c26

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.4.0 This release

2 files

2.3.0

2 files

2.2.13

2 files

2.2.12

2 files

2.2.11

2 files

2.2.10

2 files

2.2.8

2 files

2.2.7

2 files

2.2.5

2 files

2.2.4

2 files

2.2.2

2 files

2.2.1

2 files

2.2.0

2 files

2.1.13

2 files

2.1.12

2 files

2.1.11

2 files

2.1.10

2 files

2.1.9

2 files

2.1.7

2 files

2.1.6

2 files

2.1.5

2 files

2.1.4

2 files

2.1.2

2 files

2.1.1

2 files

2.1.0

2 files

2.0.0

2 files

1.5.7

2 files

1.5.6

2 files

1.5.5

2 files

1.5.4

2 files

1.5.2

2 files

1.5.1

2 files

1.4.3

2 files

1.4.2

2 files

1.4.1

2 files

1.3.0

2 files

1.2.0

2 files

1.1.3

2 files

1.1.2

2 files

1.1.1

2 files

1.1.0

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page