Merge branch '263_use_median_for_statsseries' into Refactor-job-footprint

This commit is contained in:
Christoph Kluge
2024-07-09 09:28:21 +02:00
9 changed files with 160 additions and 30 deletions

View File

@@ -249,9 +249,10 @@ type ComplexityRoot struct {
}
StatsSeries struct {
Max func(childComplexity int) int
Mean func(childComplexity int) int
Min func(childComplexity int) int
Max func(childComplexity int) int
Mean func(childComplexity int) int
Median func(childComplexity int) int
Min func(childComplexity int) int
}
SubCluster struct {
@@ -1326,6 +1327,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.StatsSeries.Mean(childComplexity), true
case "StatsSeries.median":
if e.complexity.StatsSeries.Median == nil {
break
}
return e.complexity.StatsSeries.Median(childComplexity), true
case "StatsSeries.min":
if e.complexity.StatsSeries.Min == nil {
break
@@ -1860,9 +1868,10 @@ type MetricStatistics {
}
type StatsSeries {
mean: [NullableFloat!]!
min: [NullableFloat!]!
max: [NullableFloat!]!
mean: [NullableFloat!]!
median: [NullableFloat!]!
min: [NullableFloat!]!
max: [NullableFloat!]!
}
type MetricFootprints {
@@ -4662,6 +4671,8 @@ func (ec *executionContext) fieldContext_JobMetric_statisticsSeries(ctx context.
switch field.Name {
case "mean":
return ec.fieldContext_StatsSeries_mean(ctx, field)
case "median":
return ec.fieldContext_StatsSeries_median(ctx, field)
case "min":
return ec.fieldContext_StatsSeries_min(ctx, field)
case "max":
@@ -8649,6 +8660,50 @@ func (ec *executionContext) fieldContext_StatsSeries_mean(ctx context.Context, f
return fc, nil
}
func (ec *executionContext) _StatsSeries_median(ctx context.Context, field graphql.CollectedField, obj *schema.StatsSeries) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_StatsSeries_median(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Median, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]schema.Float)
fc.Result = res
return ec.marshalNNullableFloat2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐFloatᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_StatsSeries_median(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "StatsSeries",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type NullableFloat does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _StatsSeries_min(ctx context.Context, field graphql.CollectedField, obj *schema.StatsSeries) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_StatsSeries_min(ctx, field)
if err != nil {
@@ -14431,6 +14486,11 @@ func (ec *executionContext) _StatsSeries(ctx context.Context, sel ast.SelectionS
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "median":
out.Values[i] = ec._StatsSeries_median(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "min":
out.Values[i] = ec._StatsSeries_min(ctx, field, obj)
if out.Values[i] == graphql.Null {

View File

@@ -263,7 +263,7 @@ func cacheKey(
// For /monitoring/job/<job> and some other places, flops_any and mem_bw need
// to be available at the scope 'node'. If a job has a lot of nodes,
// statisticsSeries should be available so that a min/mean/max Graph can be
// statisticsSeries should be available so that a min/median/max Graph can be
// used instead of a lot of single lines.
func prepareJobData(
job *schema.Job,

View File

@@ -7,6 +7,10 @@ package util
import (
"github.com/ClusterCockpit/cc-backend/pkg/schema"
"golang.org/x/exp/constraints"
"fmt"
"math"
"sort"
)
func Min[T constraints.Ordered](a, b T) T {
@@ -34,3 +38,36 @@ func LoadJobStat(job *schema.JobMeta, metric string) float64 {
return 0.0
}
func sortedCopy(input []float64) []float64 {
sorted := make([]float64, len(input))
copy(sorted, input)
sort.Float64s(sorted)
return sorted
}
func Mean(input []float64) (float64, error) {
if len(input) == 0 {
return math.NaN(), fmt.Errorf("input array is empty: %#v", input)
}
sum := 0.0
for _, n := range input {
sum += n
}
return sum / float64(len(input)), nil
}
func Median(input []float64) (median float64, err error) {
c := sortedCopy(input)
// Even numbers: add the two middle numbers, divide by two (use mean function)
// Odd numbers: Use the middle number
l := len(c)
if l == 0 {
return math.NaN(), fmt.Errorf("input array is empty: %#v", input)
} else if l%2 == 0 {
median, _ = Mean(c[l/2-1 : l/2+1])
} else {
median = c[l/2]
}
return median, nil
}