1. ## Thorsten Vogt

Super Seeqer

92

• ### Posts

137

2. ## Allison Buenemann

Seeq Team

75

• ### Posts

71

3. ## Joe Reckamp

Seeq Team

69

• ### Posts

108

4. 44

81

## Popular Content

Showing content with the highest reputation since 08/08/2018 in all areas

1. A few weeks ago in Office Hours, a Seeq user asked how to perform iterative calculation in which the next value of the calculation relies on its previous values. This functionality is currently logged as a feature request. In the meantime, users can utilize Seeq Data Lab and push the calculated result from Seeq Data Lab to Seeq Workbench. Let's check out the example below: There are a total of 4 signals, Signal G, Signal H, Signal J, and Signal Y added to Seeq workbench. The aim is to calculate the value of Signal Y, under the selected period. Step 1: Set the start date and end date of the calculation. #Set the start date and end date of calculation startdate = '2023-01-01' enddate = '2023-01-09' Step 2: Set the workbook URL and workbook ID. #Set the workbook URL and workbook ID workbook_url = 'workbook_url' workbook_id = 'workbook_id' Step 3: Retrieve all raw data points for the time interval specified in Step 1 using spy.pull(). #Retrieve all raw data points for the time internal specified in Step 1: data = spy.pull(workbook_url, start = startdate, end = enddate, grid = None) data Step 4: Calculate the value of Signal Y, (Yi = Gi * Y(i-1) + Hi * Ji) #Calculate the value of Signal Y (Yi = Gi * Y(i-1) + Hi * Ji) for n in range(len(data)-1): data['Signal Y'][n+1] = data['Signal G'][n+1] * data['Signal Y'][n] + data['Signal H'][n+1] * data['Signal J'][n+1] data Step 5: Push the calculated value of Signal Y to the source workbook using spy.push(). #Push the calculated result of Signal Y to the source workbook spy.push(data = data[['Signal Y']], workbook = workbook_id)
7 points
2. While Seeq does not officially have dark mode, google chrome has a plug in that may be a feasible workaround. Here is what organizer topic looks like And here is workbench analysis screenshot Interested in getting one for your Seeq? Check out the dark reader plugin. DISCLAIMER: PLEASE CHECK WITH YOUR LOCAL IT BEFORE INSTALLING ANYTHING FROM THE INTERNET Here goes the link: https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh Cheers, Sanman
7 points
3. The SPy Documentation for spy.assets includes an example of specifying metrics as attributes in asset classes. It is also possible to push scorecard metrics using the spy.push functionality by defining the appropriate metadata. An example of this process is given in the code snippets below: #import relevant libraries import pandas as pd from seeq import spy Log in to the SPY module if running locally using spy.login, or skip this step if running Seeq Data Lab. #Search for data that will be used to create the scorecard. This example will search the Example asset tree to find tags in Area A. search_result = spy.search({'Path': 'Example >> Cooling Tower 1 >> Area A'}) The next code segment creates and pushes a signal that will be used as a threshold limit in the scorecard metric. This can be skipped if threshold signals will not be used in the final metric. #Define data frame for low limit threshold signal. my_lo_signal = { 'Type': 'Signal', 'Name': 'Lo Signal', 'Formula': '\$signal * 50', 'Formula Parameters': {'\$signal': search_result[search_result['Name'] == 'Optimizer']['ID'].iloc} } #Push data frame for low limit threshold signal. lo_push_result = spy.push(metadata=pd.DataFrame([my_lo_signal]), workbook='Example Scorecard') Finally, create the and push the scorecard metric. This example metric measures the average temperature and apply a static high limit threshold of 90 and a moving low limit threshold using the signal defined above. #Define data frame for scorecard metric. my_metric_input = { 'Type': 'Metric', 'Name': 'My Metric', 'Measured Item': {'ID': search_result[search_result['Name'] == 'Temperature']['ID'].iloc}, 'Statistic': 'Average', 'Thresholds': { 'Lo': {'ID': lo_push_result['ID'].iloc}, 'Hi': 90 } } #Push data frame for scorecard metric. spy.push(metadata = pd.DataFrame([my_metric_input]), workbook='Example Scorecard') The final result can be seen in the created workbook.
5 points
4. FAQ: How do I put a pump curve in Seeq? As of R21.0.44.00 this can be done the with the scatter-plot tool. This method does require version R21.0.44.00 or newer to work. See the steps below for details. Determine the X&Y components of the curve. This can be done with a tool such as https://apps.automeris.io/wpd/. Enter or paste the components in columns A and B in the CurveFitter excel sheet. See screenshot below for details. The CurveFitter file can be found here. CurveFitter.zip Once the new Flow and Head data has been pasted into excel copy the contents in from D2 to E9 and paste them into the Seeq formula tool. See screenshots below for copy paste details Copy: Paste: Paste the following syntax in the same formula under the coefficients. Be sure that the flow signal has the variable name “\$flow”. \$f=\$flow.remove(\$flow.isNotBetween(\$lower,\$upper)).setunits('') \$coeff4*\$f^4+\$coeff3*\$f^3+\$coeff2*\$f^2+\$coeff1*\$f+\$const Final formula view: Add the line to the Scatterplot by selecting the f(x) in the Scatterplot tool bar and pick the correct item from the select item dropdown. If adding more than one curve, then click on the item properties “i” of the first curve and click on duplicate. Once in the formula tool copy the new coefficients from excel replacing the old one and hit execute. Follow step 5 to add the curve to the plot. Final View:
5 points
5. A small team of us (with help from Seeq team members) built a short script to extract signal names from our legacy excel workbooks so that we could push them to Seeq workbench. Perhaps, like us, you are involved in migrating workbooks for monitoring/ reporting over to Seeq and could do with a boost to get started so you can get to real work of setting up the Seeq workbench. The attached script will extract the signal names (assuming you can craft your own regex search filter) from each excel worksheet and then push them to workbench with labeling for organization. Hopefully its a help to some 🙂 signal_transfer_excel2seeq_rev1.ipynb
5 points
6. Hi Mattheus, You can do this by using the move (v49+) or delay (<=v48) function. For example, your equation would be: (\$signal.move(5s)+\$signal.move(10s)+\$signal.move(60s))/3
5 points
7. Contextual data is often brought into Seeq to add more information to time series data. This data tends to be brought in as a condition, with the capsule properties of this condition containing different pieces of information. In some cases, a particular capsule property may not contain just one piece of information; it may contain different pieces that are separated based on some logic or code. Rather than having users visually parse the code to extract the segments of interest, Seeq can be used to extract the substring continuously. The code below extracts a substring based on its location in the property. This code is based on incrementing from left to right, starting at the beginning of the string. Changing the inputs will extract a substring from different positions in the property selected. //Inputs Section (Start and end assume reading left to right) \$condition = \$hex_maint //Recommend to filter condition to only include correct property values \$property_to_capture = 'Reason Code' \$start_position = 1 //Incrementing starts from 1 \$number_of_characters = 2 //Including the start //Code Section \$property_signal = \$condition.toSignal(\$property_to_capture).toStep(2wk) //Change duration for interpolation \$start_position_regex = (\$start_position - 1).toString() //Regular exression indexes from 0 \$number_of_characters_regex = (\$number_of_characters - 1).toString() \$property_signal.replace('/.{'+\$start_position_regex+'}(?<Hold>.{'+\$number_of_characters_regex+'}.).*/','\${Hold}') This alternative version is based on incrementing right to left, starting at the end of the string. //Inputs Section (Start and end assume reading left to right) \$condition = \$hex_maint //Recommend to filter condition to only include correct property values \$property_to_capture = 'Reason Code' \$end_position = 1 //Relative to end, incremented from 1 \$number_of_characters = 4 //Including the end character //Code Section \$property_signal = \$condition.toSignal(\$property_to_capture).toStep(2wk) //Change duration for interpolation \$end_position_regex = (\$end_position).toString() \$number_of_characters_regex = (\$number_of_characters - 1).toString() \$property_signal.replace('/.*(?<Hold>.{'+\$number_of_characters_regex+'}.{'+\$end_position_regex+'})\$/','\${Hold}') Note the output of these formulas is a string. In the case that a numeric value is wanted, append .toNumber() after '\${Hold}') Below is an example of the results. With this substring parsed, all of Seeq's analytical tools can be further leveraged. Some examples are developing histograms based on the values of the substring and making conditions to highlight whenever a particular value in the substring is occurring.
5 points
4 points
4 points
4 points
11. When creating signal forecasts, especially for cyclic signals that degrade, we often use forecastLinear() in formula to easily forecast a signal out into the future to determine when a threshold is met. The methodology is often the same regardless of if we are looking at a filter, a heat exchanger, or any other equipment that fouls overtime or any equipment that needs to go through some periodic maintenance when a KPI threshold is met. A question that comes up occasionally from users is how to create a signal forecast that only uses data from the current operation cycle for signal forecasting. The forecastlinear() operator only takes into account a historical training period and does not determine if that data is coming from the current cycle or not (which results in unexpected results). Before entering the formula, you will need to define: a condition that identifies the current cycle, here i have called it "\$runningCycle" a Signal to do a linear forecast on, i have called it "\$signal" To forecast out into the future based on only the most recent cycle, the following code snippet can be used in formula: \$training = \$runningCycle.setmaximumduration(10d).toGroup(capsule(now()-2h, now())) \$forecast=\$Signal.predict(\$training, timesince(toTime('2000-01-01T00:00Z'))) \$signal.forecastSplice(\$forecast, 1d) In this code snippet, there are a few parameters that you might want to change: .setMaximumDuration(10d): results in a longest cycle duration of 10 days, this should be changed to be longer than the longest cycle you would expect to see capsule(now-2h, now()): this creates a period during which seeq will look for the most recent cycle. In this case it is any time in the last 2 hours. If you have very frequent data (data comes in every few seconds to minutes) then 2 hours or less will work. If you have infrequent data (data that comes in once a day or less) then extend this so that it covers the last 3-4 data points. \$signal.forecastSplice(\$forecast, 1d): When using forecastLinear(), there is an option to force the prediction through the last sample point. This date parameter (1 day in this case) does something similar- it blends the last historical data point with the forecast over the given time range. In other words, if the last data point was a value of 5, but my first forecasted datapoint had a value of 10, this parameter is the time frame over which to smooth from the historical data point to the forecast. Here is a screenshot of my formula : and the formula in action:
4 points
4 points
13. It is common in manufacturing processing plants such as oil and gas refineries, to monitor the temperature trend in furnaces. Example in the refineries, the furnaces tube metal temperature (TMT) monitoring severity increases for dirty services such as crude distillation and coker units. Operation team uses this information to decide either to go for rate cut or feed rate skewing before the TMT reaching mechanical limits to prolong the run length. Upon reaching the limit, the furnace will be taken out-of-service by means of spalling or pigging, and consequently impacts the production rate. Use case: The objective is to highlight the highest and the second highest temperature out of several temperatures in a matrix. Seeq enables users to build a matrix table (or Scorecard prior to R51) to highlight the temperature priority sequence by using a combination of functions and tools including max(), splice(), composite condition and scorecard metrics. Step 1: Start by loading all of the signals we want to include in the matrix into the display. Step 2: Use max() to look for the highest value signal at any time in the formula tool. Type in formula below into formula editor. \$t.max(\$t2).max(\$t3).max(\$t4).max(\$t5).max(\$t6).max(\$t7).max(\$t8) Step 3: Create the second highest signal using splice() and composite condition. To capture the second highest signal, we need first to exclude a signal with the highest temperature at any time and then identify the highest value out of the remaining seven signals. To achieve that, use the highest temperature signal we created in step 2, we then create a condition when a signal reads the highest value, for each eight signals. //Which is the max \$if_t1_is_the_max = \$t1 == \$max \$if_t2_is_the_max = \$t2 == \$max \$if_t3_is_the_max = \$t3 == \$max \$if_t4_is_the_max = \$t4 == \$max \$if_t5_is_the_max = \$t5 == \$max \$if_t6_is_the_max = \$t6 == \$max \$if_t7_is_the_max = \$t7 == \$max \$if_t8_is_the_max = \$t8 == \$max Prior to looking for the max a second time, we must remove or replace the values from each of the signals when they are equal to the max. In this method, we will replace the highest signal values with zero using the splice function during the condition when that signal was the max. With these highest values replaced by zero (or removed), applying the same technique with the max function will yield the value of the second highest signal. //replace the max with 0 \$removing_the_max_value = (\$t1.splice(0,\$if_t1_is_the_max)) .max(\$t2.splice(0,\$if_t2_is_the_max)) .max(\$t3.splice(0,\$if_t3_is_the_max)) .max(\$t4.splice(0,\$if_t4_is_the_max)) .max(\$t5.splice(0,\$if_t5_is_the_max)) .max(\$t6.splice(0,\$if_t6_is_the_max)) .max(\$t7.splice(0,\$if_t7_is_the_max)) .max(\$t8.splice(0,\$if_t8_is_the_max)) .toStep() return \$removing_the_max_value Step 4: Create Metric Threshold Limits Subtract the highest signal by a fairly small value using Formula tool in order to use signal as a threshold limit. Repeat the step for second highest limit. \$max-0.001 Step 5: Create scorecard metric for each signal. Create scorecards for all 8 signals, as an example we choose value at the end for statistic for daily condition and apply the threshold accordingly. In the table view: Do check this post by Nick. He used different approach to yield the maximum of three signals, and displayed signal string in a matrix table.
4 points
14. For those Formula-savvy users, a one Formula approach to this would be as follows where you would define your percentage of the capsule in the first line: \$percent = 10%.tosignal().resample(1s) \$condition.transform(\$capsule ->{ \$movetime = \$capsule.duration()*\$percent.toScalars(\$capsule).first() capsule(\$capsule.startKey(),\$capsule.startKey()+\$movetime)})
4 points
4 points
16. Various parts of the world display date and time stamps differently. Often times, we get requests for changing the order of month and day in the timestamp string or to display the date as a Scorecard metric in a specific format. This can be done using the replace() operator in Formula. For example, let's say we wanted to pull the start time for each capsule in a condition and display it as mm/dd/yyyy hh:mm format: \$condition.transformToSamples(\$cap -> Sample(\$cap.getStart(),\$cap.getProperty('Start')), 1d) .replace('/(?<year>....)-(?<month>..)-(?<day>..)T(?<hour>..):(?<minute>..):(?<sec>..)(?<dec>.*)Z/' , '\${month}/\${day}/\${year} \${hour}:\${minute}') This takes the original timestamp (for example: '2019-11-13T17:04:13.7220714157Z') and parses it into the year, month, day, hour, minute, second, and decimal to be able to set up any format desired. The various parts of the string can then be called in the second half of the replace to get the desired format as shown above with \${month}/\${day}/\${year} \${hour}:\${minute}. From there, you can either view this data in the trend or use Scorecard Metric to display the Value at Start in a condition based metric. If the end time is desired instead of the start, the only changes needed would be to (1) switch the .getStart operator to .getEnd, and (2) switch the .getProperty('Start') to .getProperty('End'). Note: The '1d' at the end of the 2nd line of the formula represents the maximum interpolation for the data, which is important if you want to view this as a string signal. This value may need to be increased depending on the prevalence of the capsules in the condition.
4 points
4 points
18. Sometimes users want to find more documentation on the SPy functions than what is provided in the SPy Documentation Notebooks. A quick way to access SPy object documentation from your notebook is by using the Shift + Tab shortcut to access the docstring documentation of the function. Example view of the docstrings after using the Shift + Tab shortcut: You can expand the docstrings to view more details by clicking the + button circled in red in the above image. Expanded docstrings: From here, you can scroll through the documentation in the pop-up window. Another useful shortcut is the Tab shortcut. This will show a list of available functions or methods for either the SPy module or any other python object you have in memory. Example view of the Tab shortcut:
4 points
19. Hi Theresa, no, you cannot adjust the size of the lanes itself.But you can use the "Dimming" feature of Seeq to show only lanes with items selected in the Details Pane: Regards, Thorsten
4 points
20. Hi Esther, you can do this the following way: 1. Create a Periodic Condition (found in Tools - Pane): 2. Use Signal from Condition to calculate the total duration. Result: Regards, Thorsten
4 points
21. To better understand their process, users often want to compare time-series signals in a dimension other than time. For example, seeing how the temperature within a reactor changes as a function of distance. Seeq is built to compare data against time but this method highlights how we can use time to mimic an alternate dimension. Step 1: Sample Alignment In order to accurately mimic the alternate dimension, the samples to be included in each profile must occur at the same time. This can be achieved through a couple methods in Seeq if the samples don't already align. Option 1: Re-sampling Re-sampling selects points along a signal at select intervals. You can also re-sample based on another signal's keys. Since its possible for there not to be a sample at that select interval, the interpolated value is chosen. An example Formula demonstrating how to use the function is shown below. //Function to resample a signal \$signal.resample(5sec) Option 2: Average Aggregation Aggregating allows users to determine the average of a signal over a given period of time and then place this average at a specific point within that period. Signal From condition can be used to find the average over a period and place this average at a specific timestamp within the period. In the example below, the sample is placed at the start but alignment will occur if the samples are placed at the middle or end as well. Step 2: Delay Samples In Formula, apply a delay to the samples of the signal that represents their value in the alternative dimension. For example, if a signal occurs at 6 feet from the start of a reactor, delay it by 6. If there is not a signal with a 0 value in the alternate dimension, the final graph will be offset by the smallest value in the alternate dimension. To fix this, in Formula create a placeholder signal such as 0 and ensure its samples align with the other samples using the code listed below. This placeholder would serve as a signal delayed by 0, meaning it would have a value of 0 in the alternate dimension. //Substitute Period_of_Time_for_Alignment with the period used above for aligning your samples 0.toSignal(Period_of_Time_for_Alignment) Note: Choosing the unit of the delay depends upon the new sampling frequency of your aligned signals as well as the largest value you will have in the alternative dimension. For example, if your samples occur every 5 minutes, you should choose a unit where your maximum delay is not greater than 5 minutes. Please refer to the table below for selecting units Largest Value in Alternate Dimension Highest Possible Delay Unit 23 Hour, Hour (24 Hour Clock) 59 Minute 99 Centisecond 999 Millisecond Step 3: Develop Sample Profiles Use the Formula listed below to create a new signal that joins the samples from your separate signals into a new signal. Replace "Max_Interpolation" with a number large enough to connect the samples within a profile, but small enough to not connect the separate profiles. For example, if the signals were re-sampled every 5 minutes but the largest delay applied was 60 seconds, any value below 4 minutes would work for the Max_Interpolation. This is meant to ensure the last sample within a profile does not interpolate to the first sample of the next profile. //Make signals into discrete to only get raw samples, and then use combineWith and toLinear to combine the signals while maintaining their uniqueness combineWith(\$signal1.toDiscrete() , \$signal2.toDiscrete() , \$signal3.toDiscrete()).toLinear(Max_Interpolation) Step 4: Condition Highlighting Profiles Create a condition in Formula for each instance of this new signal using the formula below. The isValid() function was introduced in Seeq version 44. For versions 41 to 43, you can use .valueSearch(isValid()). Versions prior to 41 can use .validityCapsules() //Develop capsule highlighting the profile to leverage other views based on capsules to compare profiles \$sample_profiles.isValid() Step 5: Comparing Profiles Now with a condition highlighting each profile, Seeq views built around conditions can be used. Chain View can be used to compare the profiles side by side while Capsule View can overlay these profiles. Since we delayed our samples before, we are able to look at their relative times and use that to represent the alternate dimension. Further Applications With these profiles now available in Seeq, all of the tools available in Seeq can be used to gain more insight from these examples. Below are a few examples. Comparing profiles against a golden profile Determine at what value in the alternate dimension does each profile reach a threshold Developing a soft sensor based on another sensor and a calibration curve profile
4 points
22. The replace operator can be applied a signal without a transform. Using the replace function without the transform will be less computationally expensive, resulting in better performance. Both ways will get to the same answer. Syntax for replace function without transform: \$signal.replace('/(\\d+)_\\d+_\\w+/', '\$1')
4 points
23. Hi Pablo, you can use transform() and replace() to do this. I made an example with a signal that contains the following data: To create a signal that contains only the numeric values I used the following formula: \$originalSignal.transform((\$p, \$c) -> sample(\$c.getKey(), \$c.getValue().replace('/\\w+\\s(\\d+)/', '\$1'))) Transform is used to access every sample in the signal by specifying a lambda expression. The current sample is temporarily stored in the variable \$c. \$p contains the previous sample which is not used here. For each sample of the original signal a new sample is created by using the timestamp (key) of the original sample. The value for the new sample is determined by the value of the sample of the original signal on which replace() is executed. As the name suggests replace() is used to replace portions of a string. In this case I make use of a regular expression to determine the numeric value inside the original string and replace the original string by the determined value. The result looks like this: For your example (12345_20200326_PJR) you have to modify the replace part to: .replace('/(\\d+)_\\d+_\\w+/', '\$1') Hope this helps. Regards, Thorsten
4 points
24. Hi Banderson, you can create a duration signal from each capsule in a condition, using "signal from condition" tool. As you may know these point and click tools create a Seeq formula underneath. So after using point and click signal from condition tool, you can find the syntax of formula in item properties of that calculation. You can copy this syntax and paste it in Formula and use it to further develop your calculations.
4 points
25. For those like me who keep getting this error: Error getting data: condition must have a maximum duration. Consider using removeLongerThan() to apply a maximum duration. Click the wrench icon to add a Maximum Capsule duration. The resolution seemed to make sense to apply removeLongerThan() or setMaximumDuration() to the signal, but the correct answer is to set it to the capsule. For example, this is the incorrect formula I attempted. \$series.aggregate(maxValue(), \$capsules, endKey(), 0s) Here is the resolution: \$series.aggregate(maxValue(), \$capsules.setMaximumDuration(40h), endKey(), 0s) or \$series.aggregate(maxValue(), \$capsules.removeLongerThan(40h), endKey(), 0s) Hope this helps others who didn't have luck searching this specific alarm previously.
4 points
26. Check out the data lab script and video that walks through it to automate data pull->apply ml->push results to workbench in an efficient manner. Of course you can skin the cat many different ways however this gives a good way to do it in bulk. Use case details: Apply ML on Temperature signals across the whole Example Asset Tree on a weekly basis. For your case, you can build you own asset tree and filter the relevant attributes instead of Temperature and set spy.jobs.schedule frequency to whatever works for you. Let me know if there are any unanswered questions in my post or demo. Happy to update as needed. apply_ml_at_scale.mp4 Apply ML on Asset Tree Rev0.ipynb
3 points
3 points
28. Users of OSIsoft Asset Framework often want to filter elements and attributes based on the AF Templates they were built on. At this time though, the spy.search command in Seeq Data Lab only filters based on the properties Type, Name, Description, Path, Asset, Datasource Class, Datasource ID, Datasource Name, Data ID, Cache Enabled, and Scoped To. This post discusses a way in which we can still filter elements and/or attributes based on AF Template. Step 1: Retrieve all elements in the AF Database The code below will return all assets in an AF Database that are based on a AF Template whose name contains Location. asset_search = spy.search({"Path":"Example-AF", "Type":"Asset"}, all_properties=True) #Make sure to include all properties since this will also return the AF Template asset_search.dropna(subset=['Template'], inplace=True) # Remove assets not based on a template since we can't filter with NaN values asset_search_location = asset_search[asset_search['Template'].str.contains('Location')] # Apply filter to only consider Location AF Template assets Step 2: Find all relevant attributes This code will retrieve the desired attributes. Note wildcards and regular expression can be used to find multiple attributes. signal_search = spy.search({"Path":"Example-AF", "Type":"Signal", "Name":"Compressor Power"}) #Find desired attributes Step 3: Filter attributes based on if they come from an element from the desired AF template Last step cross references the signals returned with the desired elements. This is done by looking at their paths. # Define a function to recreate paths, items directly beneath the database asset don't have a Path def path_merger(row): row = row.dropna() return ' >> '.join(row) asset_search_location['Full Path'] = asset_search_location[['Path', 'Asset', 'Name']].apply(lambda row: path_merger(row),axis=1) # Create path for the asset that includes its name signal_search['Parent Path'] = signal_search[['Path', 'Asset']].apply(lambda row: path_merger(row),axis=1) # Create path for the parents of the signals signal_search_location = signal_search[signal_search['Parent Path'].isin((asset_search_location['Full Path']))] # Cross reference parent path in signals with full paths in assets to see if these signals are children of the desired elements
3 points
29. The following steps will create a prediction model for every capsule in a condition. Step 1. pick a condition with capsules that isolate the desired area of regression. Any condition with non-overlapping capsules will work as long as there are enough sample points within its duration. For this example, an increasing temperature condition will be used. However, periodic conditions and value search conditions will work as well. Step 2. Create a time counter for each capsule in the condition. This can be done with the new timesince() function in the formula tool. The timesince() function will have samples spaced depending on the selected period so it is important to select a period that has enough points to build a model with. See below for details on the timesince() formula setup. Step 3. In this step a condition with capsule properties that hold the regression constants will be made. This will be done in the formula tool with one formula. The concept behind the formula below is to split the condition from step one into individual capsules and use each of the capsules as the training window for a regression model. Once the regression model is done for one capsule the coefficients of the model are assigned as properties to the capsule used for the training window. The formula syntax for a linear model-based condition can be seen below. An example of a polynomial regression model can be found in the post below. \$Condtition.removeLongerThan(24h).transform(\$cap-> { \$model=\$SignalToModel.validValues().regressionModelOLS( group(\$cap),false,\$Time) \$cap.setProperty('Slope',\$model.get('coefficient1')) .setProperty('Intercept',\$model.get('intercept'))}) Below is a screenshot of how the formula looks in Seeq. Note: The regression constants can be added to the capsule pane by clicking on the black stats button and selecting add column. Below is a screen shot of the results. Step 4. Once a condition with the regression coefficients has been created the information will need to be extracted to a signal form. The following formula syntax will extract the information. This will need to be repeated for every constant from your regression model. e.g.(So for a linear model this must be done for both the slope and for the intercept.) The formula syntax for extracting the regression coefficients can be seen below. \$signal=\$Condition.transformToSamples( \$cap -> sample(\$cap.getmiddle(), \$cap.getProperty('Intercept').toNumber()), 1min) \$signal.aggregate(average(),\$Condition,durationKey()) Below is a screenshot of the formula in Seeq. Below is a screenshot of the display window of how the signals should look. Step 5. Use the formula tool to plot the equation. See screenshot below for details. Final Result
3 points
3 points
31. Seeq Data Lab allows users to programatically interact with data connected to Seeq through Python. With this, users can create numerous advanced visualizations. Some examples of these are Sankey diagrams, Waterfall plots, radar plots and 3D contour plots. These plots can then be pushed back into Seeq Organizer for other users to consume the visualizations. A common workflow that can stem from this process is the need to update the Python visualizations in an existing Organizer Topic with new ones as newer data become available. Here we'll look over the steps of how you can update an existing Organizer Topic with a new graphic. Step 1: Retrieve the Workbook HTML Behind every organizer topic is the HTML that controls what the reports display. We'll need to modify this HTML to add a new image will also retaining whatever pieces of Seeq content were already on the report. pulled_workbooks = spy.workbooks.pull(spy.workbooks.search({'Name':'Organizer Topic Name'})) org_topic = pulled_workbooks # Note you may need to confirm that the first item in pulled_workbooks is the topic of interest ws_to_update = org_topic.worksheets # Choose the index based on the worksheet intended to be updated ws_to_update.html # View the HTML behind the worksheet Step 2: Create the HTML for The Image The "add_image" function can be used to generate the HTML that will be inserted into the Organizer Topic HTML replace_html = ws_to_update.document.add_image(filename = "Image_To_Insert.png") replace_html Step 3: Replace HTML and Push Back to Seeq To find where in the Organizer Topic HTML to replace, we can use the re module. This will allow us to parse the HTML string to find our previously inserted image, which should begin with "<img src=". Note additional changes are required if multiple images are included in the report. import re before_html = re.findall("(.*)<img src=", ws_to_update.html) # Capture everything before the image after_html = re.findall(".*<img src=.*?>(.*)", ws_to_update.html) # Captures everything after the image full_html = before_html + replace_html + after_html # Combine the before and after with the html generated for the new picture ws_to_update.html = full_html # Reassign the html to the worksheet and push it back to Seeq spy.workbooks.push(pulled_workbooks)
3 points
32. I believe the unbounded error is because the original code creates conditions for >=0 and <0 with no maximum duration. You can change \$gte and \$lte to have max durations and this may fix the issue. \$gte = (\$delta >= 0).setMaximumDuration(7d) \$lt = (\$delta < 0).setMaximumDuration(7d) In your example, the reversal count will not count 3, it will count one. Let's transform your example as runningDelta() would and assume the previous value was also 5. 5 -> 5 -> 10 -> 15 -> 10 becomes 0 -> 5 -> 5 -> -5 Now let's identify the gte and lt conditions. We would see an \$gte {0 -> 5 -> 5->}, and an \$lt at {-> -5}. Use our formula and the result would be 1 reversal. However this does bring up a potential issue. If the example had another 10 at the end, the delta samples would become 0 -> 5 -> 5 -> -5 -> 0 and that would create an additional \$gte capsule at the very end, resulting in 2 reversals. You could fix this by change the \$gte to only greater than instead of greater than or equal zero. Thanks, Andrew
3 points
33. Hi Julian, The first formula (\$signal.toCondition()) will create a capsule the length of each time frame between step changes. From there, you can use Signal from Condition to calculate the "Total Duration" statistic for each of the capsules created (use the result of the formula for both the condition of interest and bounding condition).
3 points
34. Hi Vegini, I guess there are two ways to accomplish this. First one is using Seeq CLI using the command seeq datasource items on the Seeq server. As you can see below you are able to filter for specific datasources you are interested in and also export the results to a .csv file. To get the name, class or id of a datasource you can execute seeq datasource list first to get the desired information to be used for filtering: More information on the CLI can be found here: https://support.seeq.com/space/KB/215384254/Seeq%20Server%20Command%20Line%20Interface%20(CLI) The second way would be utilizing the API, for example in a python script. As you see you have to specify the ID of the datasource to get the associated items. The ID of a datasource can be determined by calling the datasources endpoint: Hope this helps. Regards, Thorsten
3 points
35. Sometimes it is desired to have custom units of measure display in Seeq Scorecards. This could be used when the signal or condition has no units or when you want to add a custom display or a unit that might not be a recognized Seeq unit. You can use Seeq's Number Format customization in the item properties panel to add custom text units to your scorecard. Here are some examples showing different ways to add text display units. The key here is including the text in quotation marks. More information on how to customize these number displays, including the syntax for adding custom text, can be found by clicking the "?" icon next to "Number Format".
3 points
36. FAQ: I have a signal with a gap in the data from a system outage. I want to replace the gap with a constant value, ideally the average of the time period immediately before the data. Solution: 1. Once you've identified your data gaps, extend the capsules backwards by the amount over which time you want to take the average. In this example, we want to fill in the gap with the average of the 10 minutes before the signal dropped, so we will extend the start of the data gap capsule 10 minutes in the past. This is done using the move function in Formula: \$conditionForDataGaps.move(-10min,0min) 2. Use Signal from Condition to calculate the average of the gappy signal during the condition created in step 1. Make sure to select "Duration" for the timestamp of the statistic. 3. Stitch the two signals together using the splice function. The validvalues() function at the end ensures a continuous output signal. \$gappysignal.splice(\$replacementsignal,\$gaps).validvalues()
3 points
37. Hi Dominic, You can use the splice function in Formula to complete this request. I'm assuming your cost changes each year so you may want to splice in a different cost for each year. For example, you can use the following Formula to say take the current cost of \$20 and splice in \$10 cost for all of 2019 so that on January 1st, it steps up to the new cost: 20\$.tosignal().splice(10\$.tosignal(), condition(1y, capsule('2019'))) Let me also break this Formula down a bit so that you understand it better. In the first part, we are saying take a value of \$20 (as a signal) as the default result, which means that anytime before or after 2019 (in this case), the value would equal \$20. However, when the capsule is present in the condition in the splice condition is met (in this case it's a capsule for all of 2019), the \$10 signal will be spliced instead of the \$20 signal. I also want to note that the '1y' argument in the condition is the maximum duration. If you wanted to expand the capsule to be longer than 1 year, you would also need to edit that value. If you want to add additional years at the \$10 value, you can simply add them as more capsules in the condition argument. For example, adding 2018: 20\$.tosignal().splice(10\$.tosignal(), condition(1y, capsule('2019'), capsule('2018'))) If you want to add different values instead (let's say \$15 for 2018), you could use the following modification: 20\$.tosignal().splice(10\$.tosignal(), condition(1y, capsule('2019'))).splice(15\$.tosignal(), condition(1y, capsule('2018')))
3 points
38. We have similar issues with some tags. In our case, the tags are already Step tags but still have gaps that are smaller then the max interpolation setting. In this case, we use \$signal.validValues formula to achieve exactly what you've described.
3 points
39. Hi Tommy, The easiest way to do this in Seeq is to use a condition to define the if condition, and then splice in a new signal when your condition is true. Follow the steps below to achieve this. 1) Use the Value Search tool to find when your signal .OP <= 0 2) In Formula, enter the following: \$flowsignal.splice(0.toSignal(), \$conditionclosed) where \$flowsignal is your Flow Rate signal, and \$conditionclosed is the condition we created in Step 1. What we are doing here is splicing in a new signal we create ( 0.toSignal() ) which will equal 0 when the .OP <= condition is true. You could also write all of this into 1 Formula (combining steps 1 & 2 together) by writing the following: \$conditionclosed = \$OP.validValues().valueSearch(isLessThanOrEqualTo(0)) \$flowsignal.splice(0.toSignal(),\$conditionclosed) Please let me know if this solved your question. -Kjell
3 points
40. Seeq is often used to contextualize data with respect to production runs. These product runs may be a text or string signal that is the product code, or a very large numerical signal. Users commonly use Value Search to find a specific product run to further analyze. If they want to work with a couple of similar product runs, for example ones that start with or end with the same few letters or numbers, a few Value Searches followed by Composite Condition may be acceptable. This approach may not be realistic if there are hundreds of different product codes to analyze. Recently a user asked for a trim function because they wanted to categorize all product codes by the first few letters the product code. For example, ABC-123-XYZ and ABC-456-DEF would both fall under the "ABC" product category. In Excel, users might use something like the functions LEFT and RIGHT to return the first few characters (LEFT(3) in this ABC example). One way to do this text or string manipulation in Seeq is to use the replace() function with a regular expression. Regular expressions can be intimidating to those who have not used them before, but they can also be very powerful. A little exploration on sites like https://regex101.com can help evaluate what kind of regular expression is appropriate for a specific use case. Given the above example product codes, the below Seeq Formula incorporates a regular expression within the replace() function to parse the string signal by the "-" and then return only the first part of that parsed string based on the "\$1". \$productcode.replace('/(.*)-(.*)-(.*)/', '\$1') I could similarly categorize by the last three characters with a function like \$productcode.replace('/(.*)-(.*)-(.*)/', '\$3') Once this simplified text signal is available, any other tools can be used in the analysis. If the product code was a very large number instead of a string, apply toString() to benefit from the replace() function. There are often many ways to solve a problem. An alternate approach to categorize product codes like this might be to pair toCapsules() and filter() off the Value property in Formula. Perhaps the best solution is incorporating regular expressions into Value Search like in the example below to create conditions any time the product code starts with ABC (/^ABC.*/) or any time it ends with XYZ (/.*XYZ\$/). The slashes here indicate regular expressions should be used, similar to searching with regex in the Data Pane. But this approach is likely not obvious or easy without a little experience with regular expressions. So while regular expressions may feel foreign at first, do not be intimidated! They really can pay off in the long run.
3 points
41. This has been fixed in version R21.0.44.00
3 points
42. Hi Greg Since you already have a condition identifying when your signal changes, to identify the magnitude of the change all you need to do is use Signal From Condition. Here is an example of how it might look: In this case i am using "Range" because it will always give me a positive value of the change in my power signal. If i wanted to know if it was positive or negative I would use "Delta" instead. Here i am using the Duration as my timestamp so i can more easily accomplish the next step- filtering the original change condition. Since you want to count the number of instances the value changes by more than some amount, we can then filter our original condition (the one that identified the change) so it only retains the capsules where the change was over your threshold. To do this i will use Formula: In this case, i am filtering my Load Swing to keep capsules where the swing is greater than 25kW. You can see the filtered condition is shown in blue where my original Condition is shown in green. From here, you can use the Scorecard Metric to count the number of the filtered capsules. Hope this helps!
3 points
3 points
44. Comparing 2 signals in earlier versions requires a bit more slight of hand, comparing the difference between the two signals to 0. \$s1 > \$s2 is the same as (\$s1-\$s2).valueSearch(isGreaterThan(0)) This is
3 points
45. In this example we want to plot both temperature signal and a model signal vs relative humidity in one scatter plot using Seeq. Here are the Steps: RAW Data: Temperature (Area A) Temperature (Area B) X axis - Rel humidity (Area A) Step 1. Delay one of the signals to make sure the data points for both signals are not aligned Model (Area B) \$TempAreaB.delay(1min) Step2. Combine the model and the temperature into one signal and set the max interpolation to 1 sec so it doesn't draw a line in between samples Model & Temp Combined (Area A) \$Temp.combinewith(\$Model).setmaxinterpolation(1sec) Step3. Create a condition per each sample in the Temperature and Model Signal Temp Condition (Area A) \$Temp.setmaxinterpolation(1sec).isvalid() Model Condition (Area B) \$Model.setmaxinterpolation(1sec).isvalid() Step 4. Go to Scatter plot and select "Relative Humidity" as the X-Axis and "Model &Temp Combined" as the Y Axis Step 5. In Scatter Plot, select Model condition and Temperature condition for condition based coloring the sample points
3 points
46. Hi Chris- I think this can be achieved using a combination of the Value Search and Composite Condition tools. In the following screenshot, I have a signal (temperature) and a condition (Start Capsule). Let's say I'd like to create a new condition that starts at the start of each capsule in Start Capsule and ends when the temperature is 90. First, use the Value Search tool to identify when the temperature is 90 F. This results in several small capsules each time the temperature is 90 F. Next, combine these 2 conditions using the join operator in the Composite Condition tool: This results in a new condition (Start to Temp=90) where each capsule starts at the start the Start Capsule capsule and ends at the start of the Temp=90 capsule. Please let me know if you have any additional questions. Thanks, Lindsey
3 points
47. Hi Jitesh, I tried to develop a nested if with the following logic: if (temp > 66) { if (rh > 50) { temp = 1/temp ; } else if (wetbulb < 75) { temp *= 2; } } If no condition is met, the temperature should not be modified. However it would be great if you could provide an example of what you are trying to achieve. Regards, Thorsten
3 points
3 points
49. Summary There are many use cases where the user wants to do an aggregation over a periodic time frame, but only include certain values. Examples abound: for cement calculate the average daily clinker production only when the kiln is running, for biotech pharma the standard deviation of dissolved oxygen only when the batch is running, etc. Here our user is looking into equipment reliability for compressors. She wants to calculate the average daily compressor power to examine its performance over time. Steps 1. Add the signal into Seeq. 2. Use the 'Periodic Condition' or 'Formula' Tool to add a condition for days. This doesn't have to be days, it can be any arbitrary time, but it is usually periodic in nature. To use a custom periodic Condition, consider using the periods() function. For example to do this for days, the formula is: periods(1day) As an example, to change this to 5-minute time periods the formula is: periods(5min) Here are how the days and statistics in the Capsule Pane look for her: 3. Find only the desired values to be used in the aggregate (e.g. Average) statistic. From the values in the Capsule Pane she sees that the average compressor powers results are too low. This is because all the time when the compressor was OFF, near 0, are included in this calculation. She wants to only include times when the compressor is running because that will provide a true picture of how much energy is going into the equipment - and can indicate potential problems. To do this, she creates a 'Value Search' for times to include in this calculation, in this case when the Compressor is ON or > 0.5kW. 4. Create a new signal to only have values *within* the 'Compressor ON' condition. Use the aptly named 'within' function in the 'Formula' Tool. Notice how the resulting Signal in the bottom lane only has values within the 'Compressor ON' condition. Now she can use this because all those 0's that were causing the time-weighted Average to be low... are now gone. 5. Examine the resulting statistical values. She now sees that the calculations are correct and can use this resulting 'Compressor Power only when ON' Signal in other calculations using 'Signal from Condition', 'Scorecard Metric', and other Seeq Tools!
3 points
50. ## How can I identify a profile on one signal and find when it occurs on a different signal?

Seeq Version: R21.0.43.03 but the solution is applicable to previous versions as well. The Profile Search Tool is great for specifying a profile in Signal A and then looking for occurrences of that profile throughout time. In the screenshot below I've used Profile Search to identify when the Compressor Power Area A resembles the shape of a chair. For basics about how to use the tool check out the Seeq KB article Profile Search. However, what if I want to look for that same profile on another signal? In the screenshot below, I've added a second signal, Compressor Power Area G. I'd like to identify when the "chair" profile I previously specified for Compressor Power Area A is present in Compressor Power Area G. I can do this by using the profileSearch() function in Seeq Formula. Here is how... 1. Start with the Chair in Area A condition I previously made and use Duplicate to Formula. The duplicated formula looks like (note that \$cpA refers to Compressor Power Area A): profileSearch(\$cpA, toTime("2019-09-02T15:43:30.135Z"), toTime("2019-09-03T06:28:09.629Z"), 98, 0.5, 0.3, 0.3) 2. Modify the formula to add \$cpG (Compressor Power Area G) to the start of the function. This is an optional argument in Profile Search which allows us to use the profile identified on signal \$cpA and look for when it occurs on signal \$cpG. For more information on the Profile Search function, check out the documentation available in the Formula Tool. \$cpG.profileSearch(\$cpA, toTime("2019-09-02T15:43:30.135Z"), toTime("2019-09-03T06:28:09.629Z"), 98, 0.5, 0.3, 0.3) Here is a screenshot of what it looks like in the Formula Tool: 3. View the final result. In this example two "chairs" where identified in Area G.
3 points
This leaderboard is set to Los Angeles/GMT-07:00
×