open-wether / Sources /App /Helper /Deaveraging.swift
soiz1's picture
Migrated from GitHub
6ee917b verified
import Foundation
extension Array3DFastTime {
/// Deaverages a running mean over time.
///
/// This function operates on an array of floating-point numbers that represents a time series, where each element in the array corresponds to a measurement at a specific time.
/// The function modifies the input array in place, deaveraging a running mean over time for each location in the array.
mutating func deavergeOverTime() {
data.deavergeOverTime(nTime: nTime)
}
/// Deaccumulates a time series by subtracting the previous value from the current value.
///
/// This function operates on an array of floating-point numbers that represents a time series, where each element in the array corresponds to a measurement at a specific time.
/// The function modifies the input array in place, subtracting the previous value from the current value for each element in the sliding window within each location in the array.
/// If the previous value is `NaN`, the function replaces it with the value of the element two time steps before or with 0 if it's negative.
/// The deaccumulation process ensures that the time series is transformed from a cumulative sum to a series of differences.
mutating func deaccumulateOverTime() {
data.deaccumulateOverTime(nTime: nTime)
}
}
extension Array2DFastTime {
/// Deaverages a running mean over time.
///
/// This function operates on an array of floating-point numbers that represents a time series, where each element in the array corresponds to a measurement at a specific time.
/// The function modifies the input array in place, deaveraging a running mean over time for each location in the array.
mutating func deavergeOverTime() {
data.deavergeOverTime(nTime: nTime)
}
/// Deaccumulates a time series by subtracting the previous value from the current value.
///
/// This function operates on an array of floating-point numbers that represents a time series, where each element in the array corresponds to a measurement at a specific time.
/// The function modifies the input array in place, subtracting the previous value from the current value for each element in the sliding window within each location in the array.
/// If the previous value is `NaN`, the function replaces it with the value of the element two time steps before or with 0 if it's negative.
/// The deaccumulation process ensures that the time series is transformed from a cumulative sum to a series of differences.
mutating func deaccumulateOverTime() {
data.deaccumulateOverTime(nTime: nTime)
}
}
extension Array where Element == Float {
/// Deaverages a running mean over time.
///
/// - Parameters:
/// - nTime: The number of time intervals in the time series.
///
/// - Precondition: `nTime` must be less than or equal to the length of the input array.
/// - Precondition: The length of the input array must be divisible by `nTime`.
///
/// This function operates on an array of floating-point numbers that represents a time series, where each element in the array corresponds to a measurement at a specific time.
/// The function modifies the input array in place, deaveraging a running mean over time for each location in the array.
mutating func deavergeOverTime(nTime: Int) {
precondition(nTime <= self.count)
precondition(self.count % nTime == 0)
let nLocations = self.count / nTime
for l in 0..<nLocations {
var startStep = 0
var prev = Float.nan
var prevH = 0
for hour in 0 ..< nTime {
let d = self[l * nTime + hour]
if prev.isNaN {
// seek to first valid value
prev = d
startStep = hour
prevH = hour
continue
}
if d.isNaN {
// ignore missing values
continue
}
let deltaHours = Float(hour - startStep + 1)
let deltaHoursPrevious = Float(prevH - startStep + 1)
self[l * nTime + hour] = (d * deltaHours - prev * deltaHoursPrevious) / (deltaHours - deltaHoursPrevious)
prev = d
prevH = hour
}
}
}
/// Deaccumulates a time series by subtracting the previous value from the current value.
/// Assumes data is oriented spatially
mutating func deaccumulateOverTimeSpatial(nTime: Int) {
precondition(nTime <= self.count)
precondition(self.count % nTime == 0)
let nLocations = self.count / nTime
for l in 0..<nLocations {
var prev = Float.nan
var skipped = 0
for hour in 0 ..< nTime {
let d = self[hour * nLocations + l]
if prev.isNaN {
prev = d
continue
}
if d.isNaN {
// ignore missing values
skipped += 1
continue
}
self[hour * nLocations + l] = (d - prev) / Float(skipped + 1)
prev = d
skipped = 0
}
}
}
mutating func deaccumulateOverTime(nTime: Int) {
precondition(nTime <= self.count)
precondition(self.count % nTime == 0)
let nLocations = self.count / nTime
for l in 0..<nLocations {
var prev = Float.nan
var skipped = 0
for hour in 0 ..< nTime {
let d = self[l * nTime + hour]
if prev.isNaN {
prev = d
continue
}
if d.isNaN {
// ignore missing values
skipped += 1
continue
}
self[l * nTime + hour] = (d - prev) / Float(skipped + 1)
prev = d
skipped = 0
}
}
}
}