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 ¶
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.
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.