Skip to content

Commit

Permalink
progress toward test of generic aggregation function for new stats #75
Browse files Browse the repository at this point in the history
  • Loading branch information
roznet committed Feb 18, 2021
1 parent 4fbf0af commit e21a360
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 20 deletions.
6 changes: 3 additions & 3 deletions ConnectStats/src/GCCellGrid+Templates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,16 @@ extension GCCellGrid {
extension GCHistoryFieldDataHolder {
func relevantNumbers(histStats which: gcHistoryStats) -> (main: GCNumberWithUnit?, extra : GCNumberWithUnit?, extraLabel: String?) {
var rv : (main: GCNumberWithUnit?, extra : GCNumberWithUnit?, extraLabel: String?)
if field.canSum() {
if field.canSum {
rv.main = self.sum(withUnit: which)
rv.extra = self.average(withUnit: which)
rv.extraLabel = NSLocalizedString("Avg", comment: "Summary Field Stats")
}else if field.isWeightedAverage() {
}else if field.isWeightedAverage {
rv.main = self.weightedAverage(withUnit: which)
}else {
rv.main = self.average(withUnit: which)

if field.isMax() {
if field.isMax {
rv.extra = self.max(withUnit: which)
rv.extraLabel = NSLocalizedString("Max", comment: "Summary Field Stats")
}else{
Expand Down
17 changes: 9 additions & 8 deletions ConnectStats/src/GCField.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@
/// @param other field to compare
-(BOOL)matchesField:(GCField*)other;

-(BOOL)isNoisy;
-(BOOL)canSum;
-(BOOL)validForGraph;
-(BOOL)isWeightedAverage;
-(BOOL)isMax;
-(BOOL)isMin;
-(BOOL)isSpeedOrPace;
-(BOOL)isZeroValid;
@property (nonatomic,readonly) BOOL isNoisy;
@property (nonatomic,readonly) BOOL canSum;
@property (nonatomic,readonly) BOOL validForGraph;
@property (nonatomic,readonly) BOOL isWeightedAverage;
@property (nonatomic,readonly) BOOL isMax;
@property (nonatomic,readonly) BOOL isMin;
@property (nonatomic,readonly) BOOL isSpeedOrPace;
@property (nonatomic,readonly) BOOL isZeroValid;

-(BOOL)isValidForActivityType:(NSString*)activityType;

/**
Expand Down
94 changes: 87 additions & 7 deletions ConnectStats/src/GCHistoryStatistics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,28 @@
import Foundation


extension Array : Comparable where Element : Comparable {
public static func < (lhs : [Element], rhs : [Element]) -> Bool {
for (lhe, rhe) in zip(lhs, rhs) {
if lhe < rhe {
return true
}else if lhe > rhe {
return false
}
}
return lhs.count < rhs.count
}
}

class SummaryStatistics {
enum stat {
enum Stat {
case sum
case avg
case cnt
case max
case min
case std
//case std
case wavg
}

var unit : GCUnit
Expand Down Expand Up @@ -123,11 +137,35 @@ class SummaryStatistics {
self.timeweightsum += timeweight * val
self.distweightsum += distweight * val
}

static private let mps = GCUnit.mps()
static private let dimensionless = GCUnit.dimensionless()

func numberWithUnit(stats : Stat) -> GCNumberWithUnit {
switch stats {
case .avg:
return GCNumberWithUnit(unit: self.unit, andValue: self.avg)
case .max:
return GCNumberWithUnit(unit: self.unit, andValue: self.max)
case .min:
return GCNumberWithUnit(unit: self.unit, andValue: self.min)
case .cnt:
return GCNumberWithUnit(unit: SummaryStatistics.dimensionless, andValue: Double(self.cnt))
case .sum:
return GCNumberWithUnit(unit: self.unit, andValue: self.sum)
case .wavg:
if self.unit.canConvert(to: SummaryStatistics.mps) {
return GCNumberWithUnit(unit: SummaryStatistics.mps, andValue: self.distweightsum/self.timeweightsum).convert(to: self.unit)
}else{
return GCNumberWithUnit(unit: self.unit, andValue:self.timeweightsum/self.timeweight)
}
}
}
}

extension SummaryStatistics : CustomStringConvertible {
var description: String {
return "Statistics(cnt: \(self.cnt), sum: \(self.sum), avg: \(self.avg), max: \(self.max))"
return "Statistics(\(self.unit.abbr) cnt: \(self.cnt), sum: \(self.sum), avg: \(self.avg), max: \(self.max))"
}
}

Expand Down Expand Up @@ -200,6 +238,8 @@ class IndexData {

for field in fields {
if let nu = activity.numberWithUnit(for: field) {
guard nu.value != 0.0 || field.isZeroValid else { continue }

if let stats = self.data[field] {
stats.add(numberWithUnit: nu, timeweight: duration, distweight: distance)
}else{
Expand All @@ -213,16 +253,38 @@ class IndexData {

extension IndexData : CustomStringConvertible{
var description : String {
return "IndexData(\(self.data))"
return "\(self.data)"
}

}

enum IndexValue : Equatable,Hashable {
enum IndexValue : Equatable,Hashable,Comparable,CustomStringConvertible {
case dateBucket(DateBucket)
case label(String)
case string(String)

var description: String {
switch self {
case .dateBucket(let bucket):
return "\(bucket)"
case .string(let string):
return string
}
}

static func < (lhs: IndexValue, rhs: IndexValue) -> Bool {
switch (lhs,rhs) {
case (.dateBucket(let lhe), .dateBucket(let rhe)):
return lhe.interval.start < rhe.interval.start
case (.string(let lhe), .string(let rhe)):
return lhe < rhe
default:
return false
}
}
}



class Index {
let indexValues : [IndexValue]

Expand All @@ -245,11 +307,15 @@ extension Index : CustomStringConvertible {
}
}

extension Index : Equatable,Hashable {
extension Index : Equatable,Hashable,Comparable {
static func ==(lhs:Index, rhs:Index) -> Bool {
return lhs.indexValues == rhs.indexValues
}

static func <(lhs:Index, rhs:Index) -> Bool {
return lhs.indexValues < rhs.indexValues
}

func hash(into hasher: inout Hasher) {
hasher.combine(indexValues)
}
Expand Down Expand Up @@ -311,5 +377,19 @@ extension Index : Equatable,Hashable {
}
}
}

func index(sortedBy: (Index,Index) -> Bool) -> [Index] {
return [Index](self.groupedBy.keys).sorted(by: sortedBy)
}

func data(sortedBy: (Index,Index) -> Bool) -> [(Index,IndexData)] {
var rv : [(Index,IndexData)] = []
for index in self.index(sortedBy: sortedBy){
if let data = self.groupedBy[index] {
rv.append((index,data))
}
}
return rv
}
}

29 changes: 29 additions & 0 deletions ConnectStatsTests/GCTestAggregatedStats.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,23 @@ class GCTestAggregatedStats: XCTestCase {
aggstats.aggregate()
print( "\(aggstats) \(performance)")

for (index,data) in aggstats.data(sortedBy: >) {
guard let value = index.indexValues.first else { XCTAssertTrue(false); continue }
if case let .dateBucket(bucket) = value {
let old = stats.data(for: bucket.interval.start)
print( "\(old)")

for (k,v) in data.data {
XCTAssertTrue(old.hasField(k))
XCTAssertEqual(old.number(withUnit: k, statType: .cnt),v.numberWithUnit(stats: .cnt), "\(k) cnt")
print( " \(k): \(v)")
}
}else{
XCTAssertTrue(false)
}

}

performance.reset()
let fieldsdata = GCHistoryFieldSummaryStats.fieldStats(withActivities: activities, matching: nil, referenceDate: nil, ignoreMode: gcIgnoreMode.activityFocus)
print( "\(fieldsdata) \(performance)")
Expand All @@ -147,5 +164,17 @@ class GCTestAggregatedStats: XCTestCase {
aggfieldstats.aggregate()
print( "\(aggfieldstats) \(performance)")
}

let aggtypestats = HistoryAggregator(activities: activities, fields: GCHistoryAggregatedActivityStats.defaultFields(forActivityType: GC_TYPE_ALL)) {
act in
let bucket = DateBucket(date: act.date, unit: .month, calendar: calendar)
let rv = [IndexValue.string(act.activityType), IndexValue.dateBucket(bucket!)]
return Index(indexValues: rv )
}
performance.reset()
aggtypestats.aggregate()

print( "\(aggtypestats) \(performance)")

}
}
4 changes: 2 additions & 2 deletions FitFileExplorer/src/FITFitValueStatistics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class FITFitValueStatistics: NSObject {
return [StatsType.avg,StatsType.count,StatsType.max,StatsType.min]
}else{
if let field = FITFitEnumMap.activityField(fromFitField: fieldKey, forActivityType: nil){
if field.isWeightedAverage() || field.isMax() || field.isMin() || field.validForGraph(){
if field.isWeightedAverage || field.isMax || field.isMin || field.validForGraph{
return [StatsType.avg,StatsType.count,StatsType.max,StatsType.min]
}else if field.canSum() {
}else if field.canSum {
return [StatsType.total,StatsType.count]
}
}
Expand Down

0 comments on commit e21a360

Please sign in to comment.