from seeq import spy
import pandas as pd
# Set the compatibility option so that you maximize the chance that SPy will remain compatible with your notebook/script
spy.options.compatibility = 193
# Log into Seeq Server if you're not using Seeq Data Lab:
spy.login(url='http://localhost:34216', credentials_file='../credentials.key', force=False)
spy.swap
Performs “asset swapping” functionality equivalent to what you can achieve in Seeq Workbench by the “Swap in this asset…” button for an Asset found in the Data tab.
When you create a calculated item (Signal, Condition, Scalar, Threshold Metric) that is based on stored signals that are part of an asset tree, Seeq allows you to “swap” the original asset that you used for a different asset that also has all of the constituent signals (named identically).
You can then pull the data for the “swapped” calculation just like you would pull data for any other calculated item.
Let’s walk through an example.
Example
First we will query for some signals to use in our calculation and then push a calculation based on those signals.
area_a_df = spy.search({
'Path': 'Example >> Cooling Tower 1',
'Asset': 'Area A',
'Name': 'Temperature'
})
pushed_items_df = spy.push(metadata=pd.DataFrame([{
'Name': f'Added',
'Formula': '$t + 10',
'Formula Parameters': {'$t': area_a_df.iloc[0]}
}, {
'Name': f'Hot',
'Formula': '$t > 80',
'Formula Parameters': {'$t': area_a_df.iloc[0]}
}, {
'Name': f'Average on One Day in 2016',
'Formula': "$t.average(capsule('2016-12-18'))",
'Formula Parameters': {'$t': area_a_df.iloc[0]}
}]), workbook='SPy Documentation Examples >> spy.swap')
pushed_items_df
Now we’ll query for the assets to swap in for the original Area A asset that we built the calculation for:
areas_def_df = spy.search({
'Path': 'Example >> Cooling Tower 2',
'Name': '/Area [DEF]/',
'Type': 'Asset'
}, old_asset_format=False)
areas_def_df
Now all we have to do is execute the spy.swap()
command to swap in
these assets and get a bunch of new IDs we can use in spy.pull()
:
swapped_df = spy.swap(pushed_items_df, areas_def_df, errors='catalog')
You’ll notice a few things about the returned DataFrame:
The
Original ID
column corresponds to the calculated item for which we’re swapping assets. The values are repeated three times because we’re swapping three assets across three items (a signal, condition and scalar). That’s why there’s nine rows total.Some of the rows don’t have an
ID
field because there were errors, which can be seen in theResult
column.The
Swap Performed
column includes precise details about what was swapped. The format isSwapped Out Asset --> Swapped In Asset
.
In order to get a better look at the Result
and Swap Performed
columns, let’s remove some of the other columns:
pd.set_option('display.max_colwidth', None)
swapped_df[['ID', 'Name', 'Result', 'Swap Performed']]
In Seeq’s Example data, Area F does not include a Temperature
signal, so spy.swap()
could not create a swapped calculation for
that particular asset.
For the swaps that succeeded, you can now pull the data for these swapped items like so:
# First we'll drop the rows that had errors.
successful_swaps_df = swapped_df[swapped_df['Result'] == 'Success']
spy.pull(successful_swaps_df, start='2021-01-01', end='2021-01-02')
Multi-asset Swaps
Calculations can be based on more than one asset. That means swapping is more complex, because you must specify a pairing for each swappable asset. You accomplish this using “Swap Groups”.
In the following example, we’ll push a calculation that is a summation
of Compressor Power
for Area A and Area B. Then we’ll swap out those
assets for Areas D & E and then again for Areas D & F.
areas_df = spy.search({
'Path': 'Example >> Cooling Tower 1',
'Asset': '/Area [AB]/',
'Name': 'Compressor Power'
})
added_together_df = spy.push(metadata=pd.DataFrame([{
'Name': 'Added Together',
'Formula': '$a + $b',
'Formula Parameters': {
'$a': areas_df[areas_df['Asset'] == 'Area A'],
'$b': areas_df[areas_df['Asset'] == 'Area B']
}
}]), workbook='SPy Documentation Examples >> spy.swap')
added_together_df = spy.search({
'Name': 'Added Together'
}, include_swappable_assets=True, workbook='SPy Documentation Examples >> spy.swap')
added_together_df
Notice that we used the spy.search(include_swappable_assets=True)
flag, which means that the added_together_df
DataFrame includes a
Swappable Assets
column where each cell is an embedded DataFrame
detailing the assets upon which the calculation is based:
swappable_assets_df = added_together_df.iloc[0]['Swappable Assets']
swappable_assets_df
That’s what we need in order to specify our “swap groups”. Now we have
to create a DataFrame of assets with a Swap Group
and Swap Out
columns to achieve the pairing we talk about above:
# Grab a DataFrame that contains Areas D, E and F
cooling_tower_2_df = spy.search({
'Path': 'Example >> Cooling Tower 2',
'Type': 'Asset'
}, old_asset_format=False).sort_values(by='Name')
# Create a DataFrame that includes the combination of areas:
swap_groups_df = pd.DataFrame([
cooling_tower_2_df.iloc[0], # Area D
cooling_tower_2_df.iloc[1], # Area E
cooling_tower_2_df.iloc[0], # Area D
cooling_tower_2_df.iloc[2]]) # Area F
# Now we'll specify our "Swap Group" and "Swap Out" columns. The Swap Group column can be any signifier you wish to use
# to group the swaps together. Any rows with the same value for the Swap Group will be grouped together for swapping
# purposes.
swap_groups_df['Swap Group'] = [1, 1, 2, 2]
# The "Swap Out" column can be an asset ID string, the latter part of an asset's path, or a DataFrame row for an asset.
# It must be for an asset that is part of the "Swappable Assets" found above.
swap_groups_df['Swap Out'] = [
swappable_assets_df.iloc[0], # Area A
swappable_assets_df.iloc[1], # Area B
swappable_assets_df.iloc[0], # Area A
swappable_assets_df.iloc[1], # Area B
]
swap_groups_df
Now we swap! Note that we’re going to have two new swapped calculations
– one for each Swap Group. You can see that there are a couple of
actions noted in the Swap Performed
column (separated by a \n
line break).
swapped_df = spy.swap(added_together_df, swap_groups_df, old_asset_format=False)
# Use a specific set of display properties so that the linebreaks are rendered correctly
display(swapped_df[['ID', 'Name', 'Result', 'Swap Performed']].style.set_properties(**{
'text-align': 'left',
'white-space': 'pre-wrap',
}))
We can pull just like we did before:
spy.pull(swapped_df, start='2021-01-01', end='2021-01-02')
Multi-level Swaps
If you have a hierarchy that contains “commonly named” nodes at the bottom of the tree, you’ll need to do a multi-level swap. Here’s an example where “Raw” and “Cleansed” are categorical child “assets” that are common between the uniquely-named assets (Area A & Area B):
Multi-level Swap Example >> Area A >> Raw >> Temperature
Cleansed >> Temperature
Area B >> Raw >> Temperature
Cleansed >> Temperature
You must specify the uniquely-named assets as your “Swap Out” values, and SPy will figure out how to perform the swap at the lower level. Here’s an example where the categories are Raw and Cleansed:
# First we have to create the asset tree we want from Example data.
area_ab_df = spy.search({
'Path': 'Example >> Cooling Tower 1',
'Asset': '/Area [AB]/',
'Name': 'Temperature'
}, old_asset_format=False)
raw_signals = area_ab_df.copy()
raw_signals['Path'] = 'Multi-level Swap Example >> ' + raw_signals['Asset']
raw_signals['Asset'] = 'Raw'
raw_signals['Reference'] = True
cleansed_signals = area_ab_df.copy()
cleansed_signals['Path'] = 'Multi-level Swap Example >> ' + cleansed_signals['Asset']
cleansed_signals['Asset'] = 'Cleansed'
cleansed_signals['Reference'] = True
combined_signal = pd.DataFrame([{
'Name': 'Raw and Cleansed Averaged',
'Type': 'Signal',
'Formula': '($r + $c) / 2',
'Formula Parameters': {
'$r': {'Path': 'Multi-level Swap Example >> Area A', 'Asset': 'Raw', 'Name': 'Temperature'},
'$c': {'Path': 'Multi-level Swap Example >> Area A', 'Asset': 'Cleansed', 'Name': 'Temperature'}
}
}])
pushed_items_df = spy.push(metadata=pd.concat([raw_signals, cleansed_signals, combined_signal], ignore_index=True),
workbook='SPy Documentation Examples >> spy.swap')
pushed_items_df[['ID', 'Type', 'Path', 'Asset', 'Name']]
raw_plus_cleansed = pushed_items_df[pushed_items_df['Name'] == 'Raw and Cleansed Averaged']
area_a_df = pushed_items_df[(pushed_items_df['Type'] == 'Asset') & (pushed_items_df['Name'] == 'Area A')]
area_b_df = pushed_items_df[(pushed_items_df['Type'] == 'Asset') & (pushed_items_df['Name'] == 'Area B')]
# Create a Swap In / Swap Out pairing that uses the unique level of the tree, namely:
# Swap Out: test_multilevel_swap >> Example >> Cooling Tower 1 >> Area A
# Swap In: test_multilevel_swap >> Example >> Cooling Tower 1 >> Area B
assets_df = area_b_df.copy() # Swap In
assets_df['Swap Out'] = [area_a_df.iloc[0]] # Swap Out
swap_results = spy.swap(raw_plus_cleansed, assets_df)
display(swap_results[['ID', 'Name', 'Swap Performed']].style.set_properties(**{
'text-align': 'left',
'white-space': 'pre-wrap',
}))
Detailed Help
All SPy functions have detailed documentation to help you use them. Just
execute help(spy.<func>)
like you see below.
help(spy.swap)