///|
/// An ordered, validated numeric sequence used by offline evaluators.
pub struct NumericSeries {
values : Array[Double]
}
///|
pub fn NumericSeries::new(
values : Array[Double],
) -> Result[NumericSeries, DetectError] {
if values.length() == 0 {
return Err(InvalidRunLengthLimit)
}
for value in values {
if !finite(value) {
return Err(NonFiniteObservation)
}
}
Ok({ values: values.copy(), })
}
///|
pub fn NumericSeries::values(self : NumericSeries) -> Array[Double] {
self.values.copy()
}
///|
pub fn NumericSeries::length(self : NumericSeries) -> Int {
self.values.length()
}
///|
pub fn NumericSeries::at(self : NumericSeries, index : Int) -> Double? {
self.values.get(index)
}
///|
pub fn NumericSeries::first(self : NumericSeries) -> Double {
self.values[0]
}
///|
pub fn NumericSeries::last(self : NumericSeries) -> Double {
self.values[self.values.length() - 1]
}
///|
pub fn NumericSeries::difference(self : NumericSeries) -> Array[Double] {
let result = []
for index in 1.. Array[Double] {
let result = []
let mut total = 0.0
for value in self.values {
total = total + value
result.push(total)
}
result
}
///|
pub fn NumericSeries::centered(
self : NumericSeries,
) -> Result[Array[Double], DetectError] {
let average = match mean(self.values) {
Ok(value) => value
Err(error) => return Err(error)
}
let result = []
for value in self.values {
result.push(value - average)
}
Ok(result)
}
///|
pub fn NumericSeries::standardized(
self : NumericSeries,
) -> Result[Array[Double], DetectError] {
let average = match mean(self.values) {
Ok(value) => value
Err(error) => return Err(error)
}
let variance = match sample_variance(self.values) {
Ok(value) => value
Err(error) => return Err(error)
}
if variance == 0.0 {
return Ok(self.centered().unwrap())
}
let scale = @math.exp(@math.ln(variance) * 0.5)
let result = []
for value in self.values {
result.push((value - average) / scale)
}
Ok(result)
}
///|
pub fn NumericSeries::moving_means(
self : NumericSeries,
width : Int,
) -> Result[Array[Double], DetectError] {
if width <= 0 || width > self.values.length() {
return Err(InvalidRunLengthLimit)
}
let result = []
let mut sum = 0.0
for index in 0..= width {
sum = sum - self.values[index - width]
}
if index + 1 >= width {
result.push(sum / width.to_double())
}
}
Ok(result)
}
///|
pub fn NumericSeries::moving_ranges(
self : NumericSeries,
width : Int,
) -> Result[Array[Double], DetectError] {
if width <= 0 || width > self.values.length() {
return Err(InvalidRunLengthLimit)
}
let result = []
for start in 0..<(self.values.length() - width + 1) {
let mut smallest = self.values[start]
let mut largest = self.values[start]
for offset in 1.. largest {
largest = value
}
}
result.push(largest - smallest)
}
Ok(result)
}
///|
pub fn NumericSeries::subseries(
self : NumericSeries,
start : Int,
finish : Int,
) -> Result[NumericSeries, DetectError] {
if start < 0 || finish <= start || finish > self.values.length() {
return Err(InvalidRunLengthLimit)
}
let result = []
for index in start.. Result[Array[NumericSeries], DetectError] {
if width <= 0 {
return Err(InvalidRunLengthLimit)
}
let result = []
let mut start = 0
while start < self.values.length() {
let mut finish = start + width
if finish > self.values.length() {
finish = self.values.length()
}
let part = match self.subseries(start, finish) {
Ok(value) => value
Err(error) => return Err(error)
}
result.push(part)
start = finish
}
Ok(result)
}
///|
pub fn NumericSeries::rank_indices(self : NumericSeries) -> Array[Int] {
let indices = []
for index in 0.. self.values[right] {
1
} else {
left - right
}
})
indices
}
///|
pub fn concatenate_series(
series : Array[NumericSeries],
) -> Result[NumericSeries, DetectError] {
let result = []
for item in series {
for value in item.values {
result.push(value)
}
}
NumericSeries::new(result)
}
///|
pub struct TimedValue {
time : Int
value : Double
}
///|
pub fn TimedValue::new(
time : Int,
value : Double,
) -> Result[TimedValue, DetectError] {
if time < 0 {
return Err(InvalidRunLengthLimit)
}
if !finite(value) {
return Err(NonFiniteObservation)
}
Ok({ time, value, })
}
///|
pub struct TimedSeries {
points : Array[TimedValue]
}
///|
pub fn TimedSeries::new(
points : Array[TimedValue],
) -> Result[TimedSeries, DetectError] {
if points.length() == 0 {
return Err(InvalidRunLengthLimit)
}
let mut previous = -1
for point in points {
if point.time <= previous {
return Err(InvalidRunLengthLimit)
}
previous = point.time
}
Ok({ points: points.copy(), })
}
///|
pub fn TimedSeries::points(self : TimedSeries) -> Array[TimedValue] {
self.points.copy()
}
///|
pub fn TimedSeries::values(self : TimedSeries) -> Array[Double] {
let values = []
for point in self.points {
values.push(point.value)
}
values
}
///|
pub fn TimedSeries::intervals(self : TimedSeries) -> Array[Int] {
let result = []
for index in 1.. Bool {
if self.points.length() < 3 {
return true
}
let expected = self.points[1].time - self.points[0].time
for index in 2.. NumericSeries {
NumericSeries::new(self.values()).unwrap()
}