Jump to content

Ben Johnson

Seeq Team
  • Posts

    29
  • Joined

  • Last visited

  • Days Won

    12

Posts posted by Ben Johnson

  1. The formula above needs some tweaks for new version of Seeq to be compatible with upgrades to Seeq Formula

    For 0.48:

    $condition.transformToSamples($cap -> 
    Sample($cap.getStart(),$cap.getStart().toString()), 1d)
    .replace('/(?<year>....)-(?<month>..)-(?<day>..)T(?<hour>..):(?<minute>..):(?<sec>..)(?<dec>.*)Z/' , '${month}/${day}/${year} ${hour}:${minute}')

    For 0.49

    $condition.toSamples($cap -> Sample($cap.startKey(), $cap.startKey().toString()), 1d)
       .replace('/(?<year>....)-(?<month>..)-(?<day>..)T(?<hour>..):(?<minute>..):(?<sec>..)(?<dec>.*)Z/' , 
                '${month}/${day}/${year} ${hour}:${minute}')

    All that said, even back in 0.47, the more succinct way to express this is

    $condition.toSignal('Start').toLinear(1d).toString()
      .replace('/(?<year>....)-(?<month>..)-(?<day>..)T(?<hour>..):(?<minute>..):(?<sec>..)(?<dec>.*)Z/' , 
               '${month}/${day}/${year} ${hour}:${minute}')

     

    • Like 2
  2. When creating a value based condition, the usual default is the Identify -> Value Search tool, but it is also possible using Formula with common math operators like <, <=, >, >=, == (is equal to), != (is not equal to), and && (logical and) for use as signal against scalar, or signal against signal comparisons! 

    Whenever the mathematical condition is met, a condition will be created just like a typical Value Search. For more information, search for "Comparisons" in your formula documentation.

    Here are a few examples of these operators in use:

    Signal and scalar comparison:

    hightemp.png

    Signal and scalar comparison with logical and:

    highttempstage1.png

    Signal against Signal:

    image.png

    In addition to the common math symbols, there are also some new concise ways to search for other signal states:

    • $signal.isValid() - make a condition that enumerates when the signal has data
    • $signal.isNotValid() - make a condition that enumerates the gaps in the signal
    • $signal.isBetween(10, 20) - make a condition whenever the signal is between 10 and 20
    • $signal.isNotBetween(10, 20) - make a condition whenever the signal is outside the boundary of 10-20

    Also for string signals, you can search for regular expressions or glob-like syntax:

    • $signal ~= "*STAG*" - find whenever the state signal contains "STAG".
    • $signal !~ "*STAG*" - find whenever the state signal does not contain "STAG".

    All of these things can be combined and given precedence with parenthesis. For example, to find "when is temp < 90 and either of humidity > 60 or compressor is off "

    $temp < 90 && ( $humidity > 60 || $compressor == 'OFF')

    Content Verified DEC2023

    • Like 1
  3. Solution 3 Pure Seeq Formula

    The desired condition can be expressed in one step with Seeq Formula:

    periods(5 months, 1 year, '2015-05-01')

    That function allows you to mix the durations of the capsules with a different period. The 3rd parameter is an example origin for the capsule. In this case you could pick any year, as long as it starts on May 1 and the engine will create capsules using various increments of the period.  

    (There's also a 4th parameter that can be used to fine tune the timezone of the midnight origin)

    • Like 2
  4. Predicting into the future is a great use of now(), and it's uncertainty is a key part of keeping the predicted data from polluting the cache.

    I'm sure you're working toward your own use case, but here's a different approach that's a bit more concise (and uses some newer functions). I'm sure we agree that yesterday's temperature has no prediction value for future, but that's just the demo data. It seems you're just looking for a golden batch to repeat with some adjustment into the future

    // extract a single good day to repeat, then repeat it every day
    $repeatShape = referenceTable(5min)
      .addRow($temp, capsule("2019-05-15"))
      .repeatOver(days(), ReferenceTableStat.Average)
    // this is 5F/day rise since now
    $slopeLine = timeSince(now(), 1d).setUnits('F') * 5
    // stitch known with future
    $temp.forecastSplice($repeatShape + $slopeLine )

    image.png

  5. Yes. It may seem odd to call now() "uncertain" - don't we have accurate clocks? It really means that the value will change, and any computation using that value is likely to get get different results in the future so it shouldn't be cached. 

    The reference series feature is really designed to create static profiles - the performance impact of having to recompute the profile every time that it is used was considered too great. So I don't think there's a workaround.

    Perhaps you could describe your use case where a dynamic profile has significant value over the static profile? Are the profiles expected to change significantly over time such that recent behavior is more relevant than a training window(s) that was identified as the "golden batch"? If there are interesting use cases, perhaps it's worth filing a feature request as a result.

  6. This is a creative solution, but I wouldn't depend on it. It's exposed a bug in the delay() function regarding uncertainty. The offset that is being passed to delay() is "uncertain". That means it's possible the value could change. But delay() is not reflecting the uncertainty of the offset in its output properly (the whole signal should be uncertain), which makes it possible to use the delayed signal in the reference profile.

    You'll see the negative effects of this after a few days - the portion of the signal that was cached using an older training window won't get updated to reflect the more recent training window.

  7. ConvertUnits requires a compatible units of its input and will do the scaling. Some examples

    • 5m.convertUnits('cm')  = 500cm.
    • 5m.setUnits('cm') = 5cm
    • 5m.setUnits('g') = 5g
    • 5m.convertUnits('g') is an error

    I'm a bit surprised by the failure in your 2nd case. setUnits should never fail (almost - there are edge cases in converting string and numeric). I imagine the issue is that the underlying signals have incompatible units for the math prior to the setUnits.

    If you have the formulas you tried, we can see if there are precedence issues vs actual bugs.

  8. The alternative to setUnits is convertUnits. The former just adds overrides the current value with the new unit, the latter applies the math needed to the existing values.

    For your case you should probably use

    ($a/($b+$c)).convertUnits('%')

     

    • Like 1
  9. You can create more specialized periodic conditions using the "periods()" function in the formula tool. For example "periods(1min)" will give you one capsule every minute. That condition can then be used in the signal from condition tool.

    The periods function can also create overlapping or gapped capsules. For example, if you wanted to compare your actual signal against 5 minutes of data but do it every minute, you'd use "periods(5min, 1min)". (Then you'd probably want to align your signal from condition on the middle of the capsules)

    • Like 2
  10. The hover cursors are always subject to the resolution of the screen. You can see the precise start date of that capsule in the capsule detail pane in the lower right.

    It looks like your reference signal might be off an hour or two, probably due to timezone adjustments. You can adjust the capsule or the days() formula with a timezone parameter in order to get it to repeat over the right day boundaries.

  11. It's possible to do it with a reference profile. Reference profiles are usually for aggregating multiple training periods, but if you take the average of just one period, it's equivalent to capturing the slice of the signal you want. 

    Here's a formula that gets the gist of it:

    referenceTable(5min)
      .addRow($inputSignal, capsule('Feb 13 2019'))
      .repeatOver(days(), ReferenceTableStat.Average)

     

  12. You've found a bug! It's a two-fold issue

    1. The histogram is localizing the "day of week" labels using your browser's settings (The rest of the app is in English, but I infer your browser is set to prefer German)
    2. That localization code is treating the "start of the week" differently. Much of the world starts their week on Monday, others start it on Sunday, so there's an off-by-one error in looking up the proper label.

    I've filed CRAB-14766 so that we can track this.

    You've identified a creative workaround that sidesteps the bug. If your analysis can be done in English (US), changing your browser settings would also resolve it. 

    Thanks for the question!

  13. To @dkuecker's question, I believe my example will work for all time without requiring any specific dates to be chosen. The shortcoming is everything after the signal ends is going to be considered "uncertain" (the zero values will show as a dotted line). The system is simply waiting for more data to arrive, and not making any assumptions that if data hasn't arrived since 2016, there's probably not any more coming.

    • Like 2
  14. Here's another approach to replacing empty regions of the $choppySignal with 0 . The quick answer:

    $invalidCondition = days().minus($choppySignal.toCapsules())
    $choppySignal.splice(0.toSignal(), $invalidCondition)

    How does that work? The goal is get to the last line using splice, replacing the gaps with 0. The trick is defining $invalidCondition. The straightforward way to create it is with the inverse of the valid regions of the signal:

    $invalidCondition = inverse( $a.validityCapsules(1wk), 2wk)

    But that suffers from performance problems because you need to choose the maxDuration for both when $choppySignal is good and when it's not (1wk and 2wk in this case).

    We don't really care about creating 1 capsule per gap because splice works just fine during adjacent capsules. So we can get rid of the inverse() by using minus(). We start with days() to make capsules that are always adjacent. Then we use minus() operator to remove the portion of those days where data is present. 

    $invalidCondition = days().minus($choppySignal.validityCapsules(1wk))

    But we still have the maxDuration of the validity capsules. Again, we don't care about getting a single capsule of validity; minus can handle adjacent capsules. The toCapsules() will create a condition with lots of short adjacent capsules. So now we have a performant condition of when the data doesn't exist.

    $invalidCondition = days().minus($a.toCapsules())

    You can see where the adjacent days() comes into play when you change the display style of the resulting signal to show the samples. You can clearly see the daily sample during the zero portions

    image.png.cf1ac1940f3eaa35c15df883d5479b9e.png

    • Thanks 1
×
×
  • Create New...