github.com/mithrandie/csvq@v1.18.1/lib/query/aggregate_function.go (about) 1 package query 2 3 import ( 4 "math" 5 "sort" 6 "strings" 7 8 "github.com/mithrandie/csvq/lib/option" 9 10 "github.com/mithrandie/csvq/lib/json" 11 "github.com/mithrandie/csvq/lib/value" 12 txjson "github.com/mithrandie/go-text/json" 13 14 "github.com/mithrandie/ternary" 15 ) 16 17 type AggregateFunction func([]value.Primary, *option.Flags) value.Primary 18 19 var AggregateFunctions = map[string]AggregateFunction{ 20 "COUNT": Count, 21 "MAX": Max, 22 "MIN": Min, 23 "SUM": Sum, 24 "AVG": Avg, 25 "STDEV": StdEV, 26 "STDEVP": StdEVP, 27 "VAR": Var, 28 "VARP": VarP, 29 "MEDIAN": Median, 30 } 31 32 func Count(list []value.Primary, _ *option.Flags) value.Primary { 33 var count int64 34 for _, v := range list { 35 if !value.IsNull(v) { 36 count++ 37 } 38 } 39 40 return value.NewInteger(count) 41 } 42 43 func Max(list []value.Primary, flags *option.Flags) value.Primary { 44 var result value.Primary 45 result = value.NewNull() 46 47 for _, v := range list { 48 if value.IsNull(v) { 49 continue 50 } 51 52 if value.IsNull(result) { 53 result = v 54 continue 55 } 56 57 if value.Greater(v, result, flags.DatetimeFormat, flags.GetTimeLocation()) == ternary.TRUE { 58 result = v 59 } 60 } 61 62 return result 63 } 64 65 func Min(list []value.Primary, flags *option.Flags) value.Primary { 66 var result value.Primary 67 result = value.NewNull() 68 69 for _, v := range list { 70 if value.IsNull(v) { 71 continue 72 } 73 74 if value.IsNull(result) { 75 result = v 76 continue 77 } 78 79 if value.Less(v, result, flags.DatetimeFormat, flags.GetTimeLocation()) == ternary.TRUE { 80 result = v 81 } 82 } 83 84 return result 85 } 86 87 func Sum(list []value.Primary, _ *option.Flags) value.Primary { 88 values := floatList(list) 89 if len(values) < 1 { 90 return value.NewNull() 91 } 92 return value.NewFloat(sum(values)) 93 } 94 95 func Avg(list []value.Primary, _ *option.Flags) value.Primary { 96 values := floatList(list) 97 if len(values) < 1 { 98 return value.NewNull() 99 } 100 return value.NewFloat(average(values)) 101 } 102 103 func StdEV(list []value.Primary, _ *option.Flags) value.Primary { 104 values := floatList(list) 105 if len(values) < 2 { 106 return value.NewNull() 107 } 108 return value.NewFloat(standardDeviation(values, false)) 109 } 110 111 func StdEVP(list []value.Primary, _ *option.Flags) value.Primary { 112 values := floatList(list) 113 if len(values) < 1 { 114 return value.NewNull() 115 } 116 return value.NewFloat(standardDeviation(values, true)) 117 } 118 119 func Var(list []value.Primary, _ *option.Flags) value.Primary { 120 values := floatList(list) 121 if len(values) < 2 { 122 return value.NewNull() 123 } 124 return value.NewFloat(variance(values, false)) 125 } 126 127 func VarP(list []value.Primary, _ *option.Flags) value.Primary { 128 values := floatList(list) 129 if len(values) < 1 { 130 return value.NewNull() 131 } 132 return value.NewFloat(variance(values, true)) 133 } 134 135 func floatList(list []value.Primary) []float64 { 136 values := make([]float64, 0, len(list)) 137 for _, v := range list { 138 if f := value.ToFloat(v); !value.IsNull(f) { 139 values = append(values, f.(*value.Float).Raw()) 140 } 141 } 142 return values 143 } 144 145 func sum(list []float64) float64 { 146 var sum float64 147 for _, v := range list { 148 sum += v 149 } 150 return sum 151 } 152 153 func average(list []float64) float64 { 154 denom := float64(len(list)) 155 sum := sum(list) 156 157 if denom == 0 || sum == 0 { 158 return 0 159 } 160 161 return sum / denom 162 } 163 164 func variance(list []float64, isP bool) float64 { 165 avg := average(list) 166 denom := float64(len(list)) 167 if !isP { 168 denom = denom - 1 169 } 170 171 var sum float64 172 for _, v := range list { 173 sum += math.Pow(v-avg, 2) 174 } 175 176 if denom == 0 || sum == 0 { 177 return 0 178 } 179 180 return sum / denom 181 } 182 183 func standardDeviation(list []float64, isP bool) float64 { 184 return math.Sqrt(variance(list, isP)) 185 } 186 187 func Median(list []value.Primary, flags *option.Flags) value.Primary { 188 var values []float64 189 190 for _, v := range list { 191 if f := value.ToFloat(v); !value.IsNull(f) { 192 values = append(values, f.(*value.Float).Raw()) 193 continue 194 } 195 if d := value.ToDatetime(v, flags.DatetimeFormat, flags.GetTimeLocation()); !value.IsNull(d) { 196 values = append(values, float64(d.(*value.Datetime).Raw().UnixNano())/1e9) 197 continue 198 } 199 } 200 201 if values == nil || len(values) < 1 { 202 return value.NewNull() 203 } 204 205 sort.Float64s(values) 206 207 var median float64 208 if len(values)%2 == 1 { 209 idx := ((len(values) + 1) / 2) - 1 210 median = values[idx] 211 } else { 212 idx := (len(values) / 2) - 1 213 median = (values[idx] + values[idx+1]) / float64(2) 214 } 215 return value.NewFloat(median) 216 } 217 218 func ListAgg(list []value.Primary, separator string) value.Primary { 219 strlist := make([]string, 0) 220 for _, v := range list { 221 s := value.ToString(v) 222 if value.IsNull(s) { 223 continue 224 } 225 strlist = append(strlist, s.(*value.String).Raw()) 226 } 227 228 if len(strlist) < 1 { 229 return value.NewNull() 230 } 231 232 return value.NewString(strings.Join(strlist, separator)) 233 } 234 235 func JsonAgg(list []value.Primary) value.Primary { 236 if len(list) < 1 { 237 return value.NewNull() 238 } 239 240 array := make(txjson.Array, 0, len(list)) 241 242 for _, v := range list { 243 array = append(array, json.ParseValueToStructure(v)) 244 } 245 246 return value.NewString(array.Encode()) 247 }