ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/missionmatch / internal/modules/recruiting/analytics_math.go

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/missionmatch / internal/modules/recruiting/analytics_math.go
DateNot specified in source
RepositoryJanady13/missionmatch
Revision720d47fae476b0d38477ae4401697d372b1ea2bc
Branchfeature/opportunity-to-outcome
Path or archive memberinternal/modules/recruiting/analytics_math.go
Original SHA-256c49e24965eec7e15b091b462f7e686daa12dc158968ddd1a007cb0ea8185fa5f
Git blob SHA4f044fee7ca684bdda5a008abe463aeb70d67ba5
Reading-copy SHA-256c49e24965eec7e15b091b462f7e686daa12dc158968ddd1a007cb0ea8185fa5f

Original source ↗

Every recorded occurrence

These 2 records contain identical original source bytes. Their paths and revisions remain part of the archive.

Original recordBranch or archiveRevision / identifierOriginal location
Janady13/missionmatch / internal/modules/recruiting/analytics_math.gofeature/opportunity-to-outcome720d47fae476b0d38477ae4401697d372b1ea2bcOpen original
Janady13/missionmatch / internal/modules/recruiting/analytics_math.gofeature/aioe-upgrade2178e7298d0e5d9af83afa12402fd587f0ba6ed8Open original

Recovered source transcript

package recruiting import ( "sort" "github.com/shopspring/decimal" ) // The mathematics of recruiting analytics, pure and free of a database. // // The defect this file exists to prevent is survivorship bias in time to fill. Averaging the days it // took to fill only the requisitions that FILLED systematically understates the truth, because the slow // ones are still open and therefore excluded. The bias grows with the number of open orders, so the // worse a desk is doing the better the naive number looks, which is the exact opposite of what a metric // should do. // // The correct treatment is to keep the still open orders in the calculation as right censored // observations: each one is known to have taken AT LEAST as long as it has been open so far. That is // what the Kaplan Meier estimator does, and it is what this file implements. // Observation is one requisition in a cohort. Days is how long it took to fill, or, when Filled is // false, how long it has been open so far. A censored observation is not missing data, it is a lower // bound, and treating it as either a filled order or as nothing at all is what produces the bias. type Observation struct { Days decimal.Decimal Filled bool } // SurvivalPoint is one step of the curve. Surviving is the share of the cohort still unfilled at Days. type SurvivalPoint struct { Days string `json:"days"` AtRisk int `json:"at_risk"` Filled int `json:"filled"` Surviving string `json:"surviving"` } // TimeToFill reports the distribution honestly, including what it cannot know. type TimeToFill struct { // Cohort is every requisition opened in the window, filled or not. Cohort int `json:"cohort"` FilledN int `json:"filled"` OpenN int `json:"still_open"` // NaiveMedian is the median over filled orders only. Reported because it is the number every other // tool shows, and shown NEXT TO the corrected one so the gap between them is visible rather than // hidden. It is biased low whenever anything is still open. NaiveMedian string `json:"naive_median_days,omitempty"` // Median is the Kaplan Meier median, which keeps the still open orders in the calculation as lower // bounds. Empty when the curve never reaches half, which is itself the answer: fewer than half the // cohort has filled, so a median does not exist yet and inventing one would be a fabrication. Median string `json:"median_days,omitempty"` // MedianReached says whether a median exists, so a consumer never mistakes an absent value for zero. MedianReached bool `json:"median_reached"` // Curve is the full survival function, so the shape is inspectable rather than taken on trust. Curve []SurvivalPoint `json:"curve"` } // kaplanMeier computes the survival function for a set of observations. // // S(t) = product over event times ti <= t of (1 - di / ni), where ni is the number still at risk // immediately before ti and di is the number that filled at exactly ti. // // Censored observations contribute to ni up to their censoring time and then leave the risk set without // causing a drop. That is precisely the information a still open requisition carries: it had not filled // by the time we looked, and we do not know when it will. // // Ties are handled by grouping every observation at the same duration, and censored observations at the // same time as an event are treated as leaving AFTER it, which is the standard convention and the // conservative one. func kaplanMeier(obs []Observation) []SurvivalPoint { if len(obs) == 0 { return []SurvivalPoint{} } sorted := make([]Observation, len(obs)) copy(sorted, obs) // Ascending by time; at equal times, events before censorings so the censored one is still counted // in the risk set for that event. sort.SliceStable(sorted, func(i, j int) bool { c := sorted[i].Days.Cmp(sorted[j].Days) if c != 0 { return c < 0 } return sorted[i].Filled && !sorted[j].Filled }) out := []SurvivalPoint{} surviving := decimal.NewFromInt(1) atRisk := len(sorted) for i := 0; i < len(sorted); { t := sorted[i].Days events, leaving := 0, 0 j := i for ; j < len(sorted) && sorted[j].Days.Equal(t); j++ { leaving++ if sorted[j].Filled { events++ } } if events > 0 { // The drop. atRisk is the count immediately before this instant, which includes everything // censored at exactly this time, by the convention above. factor := decimal.NewFromInt(1).Sub( decimal.NewFromInt(int64(events)).Div(decimal.NewFromInt(int64(atRisk)))) surviving = surviving.Mul(factor) out = append(out, SurvivalPoint{ Days: t.StringFixed(2), AtRisk: atRisk, Filled: events, Surviving: surviving.Round(6).String(), }) } atRisk -= leaving i = j } return out } // kmMedian is the first time the survival function is at or below one half. // // Returns false when the curve never gets there. That happens when fewer than half the cohort has // filled, and the honest answer is that the median is not yet reached. Returning the largest observed // duration instead, which some tools do, would report a number that is guaranteed to be too small and // would look like a real measurement. func kmMedian(curve []SurvivalPoint) (decimal.Decimal, bool) { half := decimal.RequireFromString("0.5") for _, p := range curve { s := decimal.RequireFromString(p.Surviving) if s.LessThanOrEqual(half) { return decimal.RequireFromString(p.Days), true } } return decimal.Zero, false } // naiveMedian is the median over filled observations only, ignoring everything still open. It is // computed solely so it can be displayed beside the corrected figure; nothing downstream should use it. func naiveMedian(obs []Observation) (decimal.Decimal, bool) { var filled []decimal.Decimal for _, o := range obs { if o.Filled { filled = append(filled, o.Days) } } if len(filled) == 0 { return decimal.Zero, false } sort.Slice(filled, func(i, j int) bool { return filled[i].LessThan(filled[j]) }) n := len(filled) if n%2 == 1 { return filled[n/2], true } // Even count: the mean of the two central values, exact. return filled[n/2-1].Add(filled[n/2]).Div(decimal.NewFromInt(2)), true } // timeToFillFrom assembles the reported figure from a cohort. func timeToFillFrom(obs []Observation) TimeToFill { t := TimeToFill{Cohort: len(obs), Curve: []SurvivalPoint{}} for _, o := range obs { if o.Filled { t.FilledN++ } else { t.OpenN++ } } if len(obs) == 0 { return t } t.Curve = kaplanMeier(obs) if m, ok := kmMedian(t.Curve); ok { t.Median, t.MedianReached = m.StringFixed(2), true } if m, ok := naiveMedian(obs); ok { t.NaiveMedian = m.StringFixed(2) } return t } // minCohortForRate is the floor below which a rate is not reported. // // A single filled requisition is a 100 percent fill rate, and a single lost one is zero. Both are // arithmetically correct and neither means anything. Suppressing them is not hiding data; the counts are // always shown, and only the ratio is withheld. const minCohortForRate = 5 // FillRate reports outcomes with BOTH denominators, because the definition genuinely forks and picking // one silently is how two reports of the same desk disagree. // // A cancelled requisition is a role the client pulled. Counting it against the recruiter understates // them; excluding it flatters them. Rather than choose, both rates are reported and named. type FillRate struct { Filled int `json:"filled"` ClosedUnfilled int `json:"closed_unfilled"` Cancelled int `json:"cancelled"` StillOpen int `json:"still_open"` // OfResolved counts cancellations against the outcome. OfDecided excludes them. Empty when the // cohort is below the reporting floor. OfResolved string `json:"rate_of_resolved,omitempty"` OfDecided string `json:"rate_of_decided,omitempty"` Suppressed bool `json:"suppressed"` } func fillRateFrom(filled, closed, cancelled, open int) FillRate { f := FillRate{Filled: filled, ClosedUnfilled: closed, Cancelled: cancelled, StillOpen: open} resolved := filled + closed + cancelled decided := filled + closed if resolved < minCohortForRate { f.Suppressed = true return f } f.OfResolved = rateOf(filled, resolved) if decided > 0 { f.OfDecided = rateOf(filled, decided) } return f } // AgingBucket groups still open work by how long it has been open. The distribution is reported rather // than an average, because an average age hides the tail, and the tail is the problem. type AgingBucket struct { Label string `json:"label"` Min int `json:"min_days"` Max int `json:"max_days"` // -1 means unbounded Count int `json:"count"` } var agingBands = []AgingBucket{ {Label: "Under a week", Min: 0, Max: 6}, {Label: "1 to 2 weeks", Min: 7, Max: 13}, {Label: "2 to 4 weeks", Min: 14, Max: 29}, {Label: "1 to 2 months", Min: 30, Max: 59}, {Label: "Over 2 months", Min: 60, Max: -1}, } // agingFrom buckets ages in whole days. The bands are contiguous and exhaustive over the non negative // integers, which agingBandsCover asserts, so no open requisition can fall between two buckets and // disappear from the report. func agingFrom(ages []int) []AgingBucket { out := make([]AgingBucket, len(agingBands)) copy(out, agingBands) for _, a := range ages { if a < 0 { a = 0 // an order opened later today is zero days old, never negative } for i := range out { if a >= out[i].Min && (out[i].Max == -1 || a <= out[i].Max) { out[i].Count++ break } } } return out }

Mathematical records from this source

Search equations, builds, manuscript text, and recovered sources.