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  }