evalue

package module
v0.0.0-...-a5100e4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2025 License: BSD-3-Clause Imports: 7 Imported by: 0

README

E-value Go Reference

This essay is an introduction to the motivation and theory behind e-values.

Documentation

Overview

Package evalue provides tools for performing e-value statistical tests.

References:

  • A. Ly, U. Boehm, G., A. Ramdas, D. van Ravenzwaaij. Safe Anytime-Valid Inference: Practical Maximally Flexible Sampling Designs for Experiments Based on e-Values, doi.org/10.31234/osf.io/h5vae
Example
package main

import (
	"fmt"

	"github.com/fumin/evalue"
)

func main() {
	// Prepare data.
	// Data is from the Moral Typecasting study of the Many Labs 2 project (Klein et al., 2018).
	// For simplicity, data is further filtered to contain only those collected from Carleton University, Ottawa, Canada.
	//
	// Klein RA, Vianello M, Hasselman F, et al. Many Labs 2: Investigating Variation in Replicability Across Samples and Settings. Advances in Methods and Practices in Psychological Science. 2018;1(4):443-490. doi:10.1177/2515245918810225
	data := getData()

	// Design the experiment.
	// The null hypothesis is that the difference between the mean of the two groups is zero.
	// The statistical test to be performed is an e-value based two sample t-test.
	// Set the significance level at the usual 5%.
	alpha := 0.05
	// Create an e-process that is tuned to reject the null hypothesis at the fastest rate.
	// delta is the minimal clinically relevant standardized effect size.
	delta := 0.5176537
	eProcess := evalue.NewMom(delta)

	// Perform the e-value based test while the experiment is running.
	// In contrast to p-values, e-values control the Type I error at all times, and thus allow optional stopping.
	stoppingTime := -1
	for n := range data {
		// Prepare group data for the two sample test.
		group1, group2 := splitGroups(data[:n])
		if !(len(group1) > 1 && len(group2) > 1) {
			continue
		}

		// Perform the e-value based test with optional stopping.
		eValue := eProcess.EValue(group1, group2)
		if eValue > 1./alpha {
			stoppingTime = n
			break
		}
	}

	needed := 100 * float64(stoppingTime) / float64(len(data))
	fmt.Printf("Null hypothesis rejected with only %.0f%% (%d/%d) of the data needed.\n", needed, stoppingTime, len(data))
}

type datum struct {
	group int
	value float64
}

func splitGroups(data []datum) ([]float64, []float64) {
	var g1, g2 []float64
	for _, d := range data {
		if d.group == 1 {
			g1 = append(g1, d.value)
		} else {
			g2 = append(g2, d.value)
		}
	}
	return g1, g2
}

func getData() []datum {
	return []datum{
		datum{group: 1, value: 2},
		datum{group: 1, value: 3},
		datum{group: 1, value: 7},
		datum{group: 1, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 4},
		datum{group: 1, value: 7},
		datum{group: 1, value: 3},
		datum{group: 2, value: 1},
		datum{group: 1, value: 6},
		datum{group: 1, value: 7},
		datum{group: 1, value: 2},
		datum{group: 1, value: 7},
		datum{group: 2, value: 5},
		datum{group: 1, value: 7},
		datum{group: 2, value: 5},
		datum{group: 2, value: 1},
		datum{group: 1, value: 6},
		datum{group: 1, value: 6},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 2, value: 2},
		datum{group: 2, value: 3},
		datum{group: 2, value: 4},
		datum{group: 2, value: 1},
		datum{group: 2, value: 5},
		datum{group: 1, value: 4},
		datum{group: 2, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 2},
		datum{group: 1, value: 7},
		datum{group: 2, value: 5},
		datum{group: 1, value: 5},
		datum{group: 1, value: 7},
		datum{group: 2, value: 1},
		datum{group: 2, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 7},
		datum{group: 2, value: 4},
		datum{group: 2, value: 7},
		datum{group: 1, value: 6},
		datum{group: 2, value: 3},
		datum{group: 2, value: 5},
		datum{group: 2, value: 3},
		datum{group: 1, value: 6},
		datum{group: 2, value: 5},
		datum{group: 1, value: 3},
		datum{group: 2, value: 7},
		datum{group: 1, value: 7},
		datum{group: 2, value: 7},
		datum{group: 2, value: 6},
		datum{group: 2, value: 5},
		datum{group: 1, value: 7},
		datum{group: 1, value: 5},
		datum{group: 2, value: 5},
		datum{group: 2, value: 5},
		datum{group: 2, value: 5},
		datum{group: 2, value: 5},
		datum{group: 2, value: 3},
		datum{group: 2, value: 5},
		datum{group: 2, value: 4},
		datum{group: 1, value: 5},
		datum{group: 1, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 2},
		datum{group: 1, value: 7},
		datum{group: 2, value: 2},
		datum{group: 1, value: 6},
		datum{group: 1, value: 5},
		datum{group: 1, value: 5},
		datum{group: 2, value: 1},
		datum{group: 1, value: 3},
		datum{group: 2, value: 2},
		datum{group: 2, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 1},
		datum{group: 1, value: 6},
		datum{group: 1, value: 7},
		datum{group: 1, value: 5},
		datum{group: 1, value: 4},
		datum{group: 1, value: 7},
		datum{group: 2, value: 7},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 2, value: 5},
		datum{group: 1, value: 6},
		datum{group: 2, value: 4},
		datum{group: 1, value: 6},
		datum{group: 2, value: 3},
		datum{group: 1, value: 7},
		datum{group: 1, value: 5},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 1, value: 4},
		datum{group: 2, value: 5},
		datum{group: 1, value: 6},
		datum{group: 1, value: 6},
		datum{group: 2, value: 2},
		datum{group: 2, value: 3},
		datum{group: 1, value: 3},
		datum{group: 1, value: 6},
		datum{group: 2, value: 2},
		datum{group: 2, value: 1},
		datum{group: 1, value: 5},
		datum{group: 1, value: 7},
		datum{group: 1, value: 7},
		datum{group: 2, value: 4},
		datum{group: 2, value: 5},
		datum{group: 2, value: 3},
		datum{group: 2, value: 6},
		datum{group: 1, value: 5},
		datum{group: 1, value: 4},
		datum{group: 1, value: 5},
		datum{group: 2, value: 5},
		datum{group: 1, value: 7},
		datum{group: 2, value: 5},
		datum{group: 1, value: 3},
		datum{group: 1, value: 5},
		datum{group: 2, value: 5},
	}
}
Output:

Null hypothesis rejected with only 25% (30/121) of the data needed.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type GetNPlanOptions

type GetNPlanOptions struct {
	// Ratio is the size ratio between the two groups in our sample.
	Ratio float64

	// NumSimulations is the number of simulations performed.
	NumSimulations int

	// RandSource is the random source used in simulations.
	Rsrc rand.Source
}

GetNPlanOptions are options for GetNPlan.

type Mom

type Mom struct {
	// G is the tuning parameter of the mom e-process.
	G float64
}

A Mom is an e-process based on a non-local moment prior.

func NewMom

func NewMom(deltaMin float64) *Mom

NewMom creates a mom e-process. deltaMin is a lower bound of the true effect size based on domain knowledge. The returned mom e-process is tuned such that it rejects the null hypothesis at the fastest rate, when the true data generating process has effect size deltaMin.

func (*Mom) CI

func (p *Mom) CI(x, y []float64, alpha float64) [2]float64

CI returns the confidence interval of the two sample data.

func (*Mom) EValue

func (p *Mom) EValue(x, y []float64) float64

EValue returns the e-value of the two sample data.

type NPlan

type NPlan struct {
	// N is the planned sample size with early stopping.
	N int
	// Mean is the average sample size for rejecting the null hypothesis with early stopping.
	Mean int
	// Batch is the sample size without early stopping.
	Batch int

	// EValue is the e-values during simulation.
	EValue [][]float64
	// StopT is the stopping times during simulation.
	StopT []int
}

NPlan is the planned sample size of an experiment.

func GetNPlan

func GetNPlan(alpha, beta, deltaMin float64, options ...GetNPlanOptions) NPlan

GetNPlan returns the planned sample size of an experiment. alpha is the significance level, and beta is one minus statistical power. deltaMin is a lower bound of the true effect size based on domain knowledge.

Example
package main

import (
	"fmt"

	"github.com/fumin/evalue"
)

func main() {
	// alpha is our desired significance level.
	alpha := 0.05
	// power is our desired statistical power.
	power := 0.8
	// deltaMin is a lower bound of the true effect size based on domain knowledge.
	deltaMin := 0.5

	nplan := evalue.GetNPlan(alpha, 1-power, deltaMin)
	fmt.Printf("We should plan for a sample size of %d, which would achieve both our desired statistical power and significance level.\n", nplan.N)
	fmt.Printf("The good news is we can early-stop our experiment with only %d samples on average, which is not allowed with conventional t-tests.\n", nplan.Mean)

}
Output:

We should plan for a sample size of 102, which would achieve both our desired statistical power and significance level.
The good news is we can early-stop our experiment with only 61 samples on average, which is not allowed with conventional t-tests.

type TStatistic

type TStatistic struct {
	// Nu is the degree of freedom.
	Nu float64
	// NEff is the effective sample size.
	NEff float64
	// Mean1 is the mean of the first group.
	Mean1 float64
	// Mean2 is the mean of the second group.
	Mean2 float64
	// Sp is defined in equation 2 in Ly.
	Sp float64
	// t is the t statistic
	T float64
}

TStatistic holds information about a t-statistic.

func TStat

func TStat(x1, x2 []float64, phi0 float64) TStatistic

TStat returns the two sample t-statistic. See equation 1 in Ly for more details.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL