Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quantile estimation v1 #2223

Merged
merged 9 commits into from Mar 27, 2024
Merged

quantile estimation v1 #2223

merged 9 commits into from Mar 27, 2024

Conversation

lukebrawleysmith
Copy link
Contributor

This code has 3 primary classes:

Quantile Statistic - creates effect estimate and CI for quantile nu when data are iid (e.g., event-level data).

Quantile Statistic - creates effect estimate and CI for quantile nu when data are clustered (e.g., user-level data).

GaussianBayesianABTest - creates posterior distribution using prior on treatment effect and frequentist effect estimate.

Copy link

github-actions bot commented Mar 12, 2024

Your preview environment pr-2223-bttf has been deployed.

Preview environment endpoints are available at:

Copy link
Contributor

@lukesonnet lukesonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good! some dataclass config feedback, but otherwise it looks close.

packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/bayesian/tests.py Outdated Show resolved Hide resolved
packages/stats/gbstats/models/statistics.py Outdated Show resolved Hide resolved

@property
def variance_init(self) -> float:
multiplier = scipy.stats.norm.ppf(0.975, loc=0, scale=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't 0.975 need to be computed from some alpha from the configuration? Maybe QuantileStatistic also needs to take alpha as an input and use it here and use it when computing the n for the issue of requesting a quantile < 0 or > 1 (the other comment above?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is there a reason you picked ccr over alpha for the name here? I feel like I'd like to start moving just towards using alpha everywhere across engines, but lmk if you disagree.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to alpha

@lukesonnet
Copy link
Contributor

A few nits, a few slightly bigger questions. I think once there's a few unit tests in place we can land this in anticipation of merging it with the SQL PR.

packages/stats/gbstats/bayesian/tests.py Show resolved Hide resolved
n: int # number of events here
nu: float
ccr: float
q_hat: float # sample estimate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we call these quantile_* instead? I'd rather be a touch more verbouse, but up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you made this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apologies, forgot to save my file, will be in latest push.

packages/stats/gbstats/models/statistics.py Outdated Show resolved Hide resolved

@property
def variance_init(self) -> float:
multiplier = scipy.stats.norm.ppf(0.975, loc=0, scale=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is there a reason you picked ccr over alpha for the name here? I feel like I'd like to start moving just towards using alpha everywhere across engines, but lmk if you disagree.

mu_1 = mu_0 + self.prior_effect.mean
v = 0.5 * self.prior_effect.variance
self.prior_a = GaussianPrior(mean=mu_0, variance=v, pseudo_n=1)
self.prior_b = GaussianPrior(mean=mu_1, variance=v, pseudo_n=1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the pseudo_n from the self.prior_effect GaussianPrior be passed through here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only ask because it seems like there isn't a way to pass through a flat, improper prior to this test class, right? Should we set up a way to do that?

You can kind of hack it into the existing GaussianPrior where if prior_effect.pseudo_n = 0, then we just pass this through like I suggest. Or we can be more explicit, where you can also specify no prior and that modifies this method as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea, I implemented this. When we use non-flat priors in the future, we still want to use pseudo-n = 1.

self.assertEqual(result.chance_to_win, 0.5)
self.assertEqual(result.expected, 0)

def test_inexact_log_approximation(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to replicate this test here? Maybe copy paste?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, i removed this

@@ -211,20 +216,40 @@ def get_configured_test(
),
)
else:
assert isinstance(
stat_a, type(stat_b)
), "stat_a and stat_b must be of same type."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to nit, I should have pointed this out initially, but this will pass even so long as stat_a is the same class or a subclass of stat_b. e.g is stat_a is a SampleMeanStatistic and stat_b is a Statistic, this will pass.

So I think we should instead just do assert type(stat_a) is type(stat_b)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted

…corporating n* approximation in stats engine
Copy link
Contributor

@lukesonnet lukesonnet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! There were two further nits that I just encountered when reading your PR again (sorry for missing these earlier), but you can accommodate them by first merging in my PR to your branch: #2288

@lukebrawleysmith lukebrawleysmith merged commit 91d0d99 into main Mar 27, 2024
3 checks passed
@lukebrawleysmith lukebrawleysmith deleted the smith-quantile-testing branch March 27, 2024 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants