Code Monkey home page Code Monkey logo

bayesab's Introduction

bayesAB

Travis-CI Build Status codecov CRAN_Status_Badge

Fast Bayesian Methods for AB Testing

bayesAB provides a suite of functions that allow the user to analyze A/B test data in a Bayesian framework. bayesAB is intended to be a drop-in replacement for common frequentist hypothesis test such as the t-test and chi-sq test.

Bayesian methods provide several benefits over frequentist methods in the context of A/B tests - namely in interpretability. Instead of p-values you get direct probabilities on whether A is better than B (and by how much). Instead of point estimates your posterior distributions are parametrized random variables which can be summarized any number of ways.

While Bayesian AB tests are still not immune to peeking in the broadest sense, you can use the 'Posterior Expected Loss' provided in the package to draw conclusions at any point with respect to your threshold for error.

The general bayesAB workflow is as follows:

  • Decide how you want to parametrize your data (Poisson for counts of email submissions, Bernoulli for CTR on an ad, etc.)
  • Use our helper functions to decide on priors for your data (?bayesTest, ?plotDistributions)
  • Fit a bayesTest object
    • Optional: Use combine to munge together several bayesTest objects together for an arbitrary / non-analytical target distribution
  • print, plot, and summary to interpret your results
    • Determine whether to stop your test early given the Posterior Expected Loss in summary output

Optionally, use banditize and/or deployBandit to turn a pre-calculated (or empty) bayesTest into a multi-armed bandit that can serve recipe recommendations and adapt as new data comes in.

Note, while bayesAB was designed to exploit data related to A/B/etc tests, you can use the package to conduct Bayesian analysis on virtually any vector of data, as long as it can be parametrized by the available functions.

Installation

Get the latest stable release from CRAN:

install.packages("bayesAB")

Or the dev version straight from Github:

install.packages("devtools")
devtools::install_github("frankportman/bayesAB", build_vignettes = TRUE)

Usage

Some useful links from my blog with bayesAB examples (and pictures!!):

For a more in-depth look please check the package vignettes with browseVignettes(package = "bayesAB") or the pre-knit HTML version on CRAN here. Brief example below. Run the following code for a quick overview of bayesAB:

library(bayesAB)

# Choose bernoulli test priors
plotBeta(2, 3)

image

# Choose normal test priors
plotInvGamma(12, 4)

image

A_binom <- rbinom(100, 1, .5)
B_binom <- rbinom(100, 1, .55)

# Fit bernoulli test
AB1 <- bayesTest(A_binom,
                 B_binom,
                 priors = c('alpha' = 1, 'beta' = 1),
                 distribution = 'bernoulli')

plot(AB1)

image image image

print(AB1)
--------------------------------------------
Distribution used: bernoulli 
--------------------------------------------
Using data with the following properties: 
           A    B
Min.    0.00 0.00
1st Qu. 0.00 0.00
Median  1.00 0.00
Mean    0.55 0.44
3rd Qu. 1.00 1.00
Max.    1.00 1.00
--------------------------------------------
Priors used for the calculation: 
$alpha
[1] 1

$beta
[1] 1

--------------------------------------------
Calculated posteriors for the following parameters: 
Probability 
--------------------------------------------
Monte Carlo samples generated per posterior: 
[1] 1e+05

summary(AB1)
Quantiles of posteriors for A and B:

$Probability
$Probability$A
       0%       25%       50%       75%      100% 
0.3330638 0.5159872 0.5496165 0.5824940 0.7507997 

$Probability$B
       0%       25%       50%       75%      100% 
0.2138149 0.4079403 0.4407221 0.4742673 0.6369742 


--------------------------------------------

P(A > B) by (0)%: 

$Probability
[1] 0.93912

--------------------------------------------

Credible Interval on (A - B) / B for interval length(s) (0.9) : 

$Probability
         5%         95% 
-0.01379425  0.58463290 

--------------------------------------------

Posterior Expected Loss for choosing A over B:

$Probability
[1] 0.03105786

bayesab's People

Contributors

evanhaldane avatar frankportman avatar jessekolb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bayesab's Issues

Support for more than 2 groups

Hi!
Since a lot of AB tests involve more than 2 groups, have you thought about adding support for more 2 groups? Or do you advise using the current structure and just compare all groups to A?

Thank you for creating the package.

Best,
Miha

Update rule in drawMusAndSigmas of dist-normal.R seems incorrect

Your code does not correspond to the update rule in https://en.wikipedia.org/wiki/Conjugate_prior, which is the same as https://www.cs.ubc.ca/~murphyk/Papers/bayesGauss.pdf under a different parameterization.

Another problem: when sampling from the gamma distribution, the default arguments have rate as the third argument instead of shape. You would need to name your shape argument.

Great package overall. Thanks for the great work.

Issues with next ggplot2 release

Hi

We are working on the next ggplot2 release and bayesAB fails its unit tests due to a new deprecation warning in ggplot2 which breaks the silent expectations in

test_that("Success", {
expect_silent(plot(x))
expect_silent(plot(x, rep(.5, 4)))
expect_equal(length(plot(x, posteriors = FALSE, samples = FALSE)$priors), 1)
expect_equal(names(plot(x, posteriors = FALSE, samples = FALSE)$priors), c('NormalInvGamma'))
expect_silent(print(plot(x)))
expect_silent(print(plot(x, rep(.5, 4))))
expect_output(print(x), "Distribution used")
expect_silent(summary(x))
expect_silent(summary(x, rep(.5, 4)))
expect_silent(summary(x, rep(.5, 4), rep(.5, 4)))
expect_output(print(summary(x)), 'P(A > B)', fixed = TRUE)
expect_output(print(summary(AB3)), 'P(A > B)', fixed = TRUE)
expect_is(c(x, x), "bayesTest")
expect_identical(rep(x$posteriors$Mu$A, 2), c(x, x)$posteriors$Mu$A)
expect_identical(rep(x$posteriors$Mu$B, 2), c(x, x)$posteriors$Mu$B)
expect_identical(rep(x$posteriors$Mu$A, 3), c(x, x, x)$posteriors$Mu$A)
})

We plan to submit on May 31st and hope you are able to fix these small issues beforehand

Loss calculation in getPostError

I think to calculate the expected loss for choosing A over B, in this case, is to calculate the percentage change of A over B, when B is better than A. Should the formula be: loss = -f(A_samples, B_samples) instead of loss = f(B_samples, A_samples)?

loss <- f(B_samples, A_samples)

Issues with next version of ggplot2

Hi

We preparing the next release of ggplot2 and our reverse dependency checks show that your package is failing with the new version. Looking into it we see that this is due to changes in the warnings and errors thrown by ggplot2 that you test for in your package

You can install the release candidate of ggplot2 using devtools::install_github('tidyverse/[email protected]') to test this out.

We plan to submit ggplot2 by the end of October and hope you can have a fix ready before then

Kind regards
Thomas

Get Posterior Error Function

Can you explain how does this function work? When I try running it on two dummy vectors, I get an Inf.
Do A_samples and B_samples encode the entire data vectors? Is this the squared loss? Can you explain the logic behind this implementation? What does loss do if A_samples and B_samples have 0s?
I highlighted the unclear code snipets.
`set.seed(1)
a <- rbinom(100, 1, .5)
b <- rbinom(100, 1, .6)
coalesce <- function(n) ifelse(is.na(n) | is.nan(n), 0, n)

Expected loss from switching from B to A

getPostError <- function(A_samples, B_samples, f = function(a, b) (a-b)/b) {
BoverA <- B_samples > A_samples
loss <- f(B_samples, A_samples)
coalesce(mean(BoverA) * mean(loss[BoverA]))

}
getPostError(b,a)`

How does summary(bayesTest(a,b, priors = c('alpha' = 1, 'beta' = 1), n_samples = 1e5, distribution = 'bernoulli')) reach a posterior loss of 0.260855 ?

Thank you very much for making this clear.

Forthcoming ggplot2 release and bayesAB

We're in the process of preparing a ggplot2 release. As part of the release process, we run the R CMD check on packages that use ggplot2 to make sure we don't accidentally break code for downstream packages.

In running the R CMD check on bayesAB, we identified the following issue:

  • checking examples ... ERROR
    ...
    $Probability
    [1] 0.04541
    
    --------------------------------------------
    
    Credible Interval on (A - B) / B for interval length(s) (0.9) : 
    
    $Probability
              5%          95% 
    -0.355943366 -0.006198834 
    
    --------------------------------------------
    
    Posterior Expected Loss for choosing B over A:
    
    $Probability
    [1] 0.2601664
    
    > plot(AB1)
    Error: Either ymin or ymax must be given as an aesthetic.
    Execution halted
    

These failures are because bayesAB is using geom_ribbon() without layer data. The behaviour of layers when "setting" an aesthetic (outside aesI()) with length > 1 is not defined and may change in the future (in the case of geom_ribbon(), we now require that at least one of ymin or ymax is mapped to prevent bugs from occurring).

To fix this error, we suggest using a geom_*() function with its own data. Note that using .data$col_name within aes() is the preferred way to avoid CMD check issues about undefined variables when mapping columns (make sure you include #' importFrom rlang .data in at least one roxygen documentation block to avoid an error about the name .data being undefined).

ggplot2::ggplot() +
  ggplot2::geom_ribbon(
    ggplot2::aes(x = .data$x, ymax = .data$ymax),
    data = data.frame(x = 1:5, ymax = c(0, 1, 3, 1, 0)),
    ymin = 0
  )

Created on 2019-05-09 by the reprex package (v0.2.1)

We hope to release the new version of ggplot2 in the next two weeks, at which point you will get a note from CRAN that your package checks are failing. Let me know if I can help!

Include Credible Intervals for A and B metrics in summary

I see two options for this addition:

  1. include an argument in the summary function such as quantile_probs to be able to modify the quantile function's probabilities when applied to A and B's posteriors.
  2. Apply the custom function getCredInt to A and B's posteriors. these values o be printed in an additional section in the summary.

Add support for arbitrary # of 'tests'?

This is including one variable, where all you want is to sample from the posterior and get a parameter estimate. Is this outside the scope of the package / does it break the current purpose?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.