mozanalysis.experiment
- class mozanalysis.experiment.Experiment(experiment_slug: str, start_date, num_dates_enrollment=None, app_id=None, app_name=None, analysis_unit: AnalysisUnit = AnalysisUnit.CLIENT)[source]
Query experiment data; store experiment metadata.
The methods here query data in a way compatible with the following principles, which are important for experiment analysis:
The population of clients in each branch must have the same properties, aside from the intervention itself and its consequences; i.e. there must be no underlying bias in the branch populations.
We must measure the same thing for each client, to minimize the variance associated with our measurement.
So that our analyses follow these abstract principles, we follow these rules:
Start with a list of all clients who enrolled.
We can filter this list of clients only based on information known to us at or before the time that they enrolled, because later information might be causally connected to the intervention.
For any given metric, every client gets a non-null value; we don’t implicitly ignore anyone, even if they churned and stopped sending data.
Typically if an enrolled client no longer qualifies for enrollment, we’ll still want to include their data in the analysis, unless we’re explicitly using stats methods that handle censored data.
We define a “analysis window” with respect to clients’ enrollment dates. Each metric only uses data collected inside this analysis window. We can only analyze data for a client if we have data covering their entire analysis window.
Example usage (in a colab notebook):
from google.colab import auth auth.authenticate_user() print('Authenticated') from mozanalysis.experiment import Experiment from mozanalysis.bq import BigQueryContext from mozanalysis.config import ConfigLoader active_hours = ConfigLoader.get_metric("active_hours", "firefox_desktop") uri_count = ConfigLoader.get_metric("uri_count", "firefox_desktop") bq_context = BigQueryContext( dataset_id='your-dataset-id', # e.g. mine's flawrence project_id='moz-fx-data-bq-data-science' # this is the default anyway ) experiment = Experiment( experiment_slug='pref-fingerprinting-protections-retention-study-release-70', start_date='2019-10-29', num_dates_enrollment=8 ) # Run the query and get the results as a DataFrame res = experiment.get_single_window_data( bq_context, [ active_hours, uri_count ], last_date_full_data='2019-12-01', analysis_start_days=0, analysis_length_days=7 )
- Parameters:
experiment_slug (str) – Name of the study, used to identify the enrollment events specific to this study.
start_date (str) – e.g. ‘2019-01-01’. First date on which enrollment events were received.
num_dates_enrollment (int, optional) – Only include this many dates of enrollments. If
None
then use the maximum number of dates as determined by the metric’s analysis window andlast_date_full_data
. Typically7n+1
, e.g.8
. The factor ‘7’ removes weekly seasonality, and the+1
accounts for the fact that enrollment typically starts a few hours before UTC midnight.app_id (str, optional) – For a Glean app, the name of the BigQuery dataset derived from its app ID, like org_mozilla_firefox.
app_name (str, optional) – The Glean app name, like fenix.
analysis_unit (AnalysisUnit, optional) – the “unit” of analysis, which defines an experimental unit. For example: CLIENT for mobile experiments or GROUP for desktop experiments. Is used as the join key when building queries and sub-unit level data is aggregated up to that level. Defaults to AnalysisUnit.CLIENT unless specified
- experiment_slug
Name of the study, used to identify the enrollment events specific to this study.
- Type:
str
- start_date
e.g. ‘2019-01-01’. First date on which enrollment events were received.
- Type:
str
- num_dates_enrollment
Only include this many days of enrollments. If
None
then use the maximum number of days as determined by the metric’s analysis window andlast_date_full_data
. Typically7n+1
, e.g.8
. The factor ‘7’ removes weekly seasonality, and the+1
accounts for the fact that enrollment typically starts a few hours before UTC midnight.- Type:
int, optional
- get_app_name()[source]
Determine the correct app name.
If no explicit app name has been passed into Experiment, lookup app name from a pre-defined list. (this is deprecated)
- get_single_window_data(bq_context: BigQueryContext, metric_list: list, last_date_full_data: str, analysis_start_days: int, analysis_length_days: int, enrollments_query_type: EnrollmentsQueryType = EnrollmentsQueryType.NORMANDY, custom_enrollments_query: str | None = None, custom_exposure_query: str | None = None, exposure_signal: ExposureSignal | None = None, segment_list=None) DataFrame [source]
Return a DataFrame containing per-client metric values.
Also store them in a permanent table in BigQuery. The name of this table will be printed. Subsequent calls to this function will simply read the results from this table.
- Parameters:
bq_context (BigQueryContext) – BigQuery configuration and client.
metric_list (list of mozanalysis.metric.Metric or str) – The metrics to analyze.
last_date_full_data (str) – The most recent date for which we have complete data, e.g. ‘2019-03-22’. If you want to ignore all data collected after a certain date (e.g. when the experiment recipe was deactivated), then do that here.
analysis_start_days (int) – the start of the analysis window, measured in ‘days since the client enrolled’. We ignore data collected outside this analysis window.
analysis_length_days (int) – the length of the analysis window, measured in days.
enrollments_query_type (EnrollmentsQueryType) – (‘normandy’, ‘glean-event’, ‘cirrus’, or ‘fenix-fallback’) Specifies the query type to use to get the experiment’s enrollments, unless overridden by
custom_enrollments_query
.custom_enrollments_query (str) –
A full SQL query that will generate the enrollments common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_enrolled_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
WARNING: this query’s results must be uniquely keyed by (analysis_id, branch), or else your results will be subtly wrong.
custom_exposure_query (str) –
A full SQL query that will generate the exposures common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_exposure_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
If not provided, the exposure will be determined based on exposure_signal, if provided, or Normandy and Nimbus exposure events. custom_exposure_query takes precedence over exposure_signal.
exposure_signal (ExposureSignal) – Optional signal definition of when a client has been exposed to the experiment. If not provided, the exposure will be determined based on Normandy exposure events for desktop and Nimbus exposure events for Fenix and iOS.
segment_list (list of mozanalysis.segment.Segment or str) – The user segments to study.
- Returns:
A pandas DataFrame of experiment data. One row per
client_id
. Some metadata columns, then one column per metric inmetric_list
, and one column per sanity-check metric. Columns (not necessarily in order):client_id (str): Not necessary for “happy path” analyses.
branch (str): The client’s branch
other columns of
enrollments
.[metric 1]: The client’s value for the first metric in
metric_list
.…
[metric n]: The client’s value for the nth (final) metric in
metric_list
.[sanity check 1]: The client’s value for the first sanity check metric for the first data source that supports sanity checks.
…
[sanity check n]: The client’s value for the last sanity check metric for the last data source that supports sanity checks.
This format - the schema plus there being one row per enrolled client, regardless of whether the client has data in
data_source
- was agreed upon by the DS team, and is the standard format for queried experimental data.
- get_time_series_data(bq_context: BigQueryContext, metric_list: list, last_date_full_data: str, time_series_period: str = 'weekly', enrollments_query_type: EnrollmentsQueryType = EnrollmentsQueryType.NORMANDY, custom_enrollments_query: str | None = None, custom_exposure_query: str | None = None, exposure_signal: ExposureSignal | None = None, segment_list=None) TimeSeriesResult [source]
Return a TimeSeriesResult with per-client metric values.
Roughly equivalent to looping over
get_single_window_data()
with different analysis windows, and reorganising the results.- Parameters:
bq_context (BigQueryContext) – BigQuery configuration and client.
metric_list (list of mozanalysis.metric.Metric) – The metrics to analyze.
last_date_full_data (str) – The most recent date for which we have complete data, e.g. ‘2019-03-22’. If you want to ignore all data collected after a certain date (e.g. when the experiment recipe was deactivated), then do that here.
time_series_period ('daily' or 'weekly') – How long each analysis window should be.
enrollments_query_type (EnrollmentsQueryType) – (‘normandy’, ‘glean-event’, ‘cirrus’, or ‘fenix-fallback’) Specifies the query type to use to get the experiment’s enrollments, unless overridden by
custom_enrollments_query
.custom_enrollments_query (str) –
A full SQL query that will generate the enrollments common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_enrolled_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
WARNING: this query’s results must be uniquely keyed by (analysis_id, branch), or else your results will be subtly wrong.
custom_exposure_query (str) –
A full SQL query that will generate the exposures common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_exposure_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
If not provided, the exposure will be determined based on exposure_signal, if provided, or Normandy and Nimbus exposure events. custom_exposure_query takes precedence over exposure_signal.
exposure_signal (ExposureSignal) – Optional signal definition of when a client has been exposed to the experiment. If not provided, the exposure will be determined based on Normandy exposure events for desktop and Nimbus exposure events for Fenix and iOS.
segment_list (list of mozanalysis.segment.Segment) – The user segments to study.
- Returns:
A
mozanalysis.experiment.TimeSeriesResult
object, which may be used to obtain a pandas DataFrame of per-client metric data, for each analysis window. Each DataFrame is a pandas DataFrame in “the standard format”: one row per client, some metadata columns, plus one column per metric and sanity-check metric. Its columns (not necessarily in order):branch (str): The client’s branch
other columns of
enrollments
.[metric 1]: The client’s value for the first metric in
metric_list
.…
[metric n]: The client’s value for the nth (final) metric in
metric_list
.[sanity check 1]: The client’s value for the first sanity check metric for the first data source that supports sanity checks.
…
[sanity check n]: The client’s value for the last sanity check metric for the last data source that supports sanity checks.
- build_enrollments_query(time_limits: TimeLimits, enrollments_query_type: EnrollmentsQueryType = EnrollmentsQueryType.NORMANDY, custom_enrollments_query: str | None = None, custom_exposure_query: str | None = None, exposure_signal: ExposureSignal | None = None, segment_list=None, sample_size: int = 100, suppress_custom_query_validation: bool = False) str [source]
Return a SQL query for querying enrollment and exposure data.
- Parameters:
time_limits (TimeLimits) – An object describing the interval(s) to query
enrollments_query_type (EnrollmentsQueryType) – (‘normandy’, ‘glean-event’, ‘cirrus’, or ‘fenix-fallback’) Specifies the query type to use to get the experiment’s enrollments, unless overridden by
custom_enrollments_query
.custom_enrollments_query (str) –
A full SQL query that will generate the enrollments common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_enrolled_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
WARNING: this query’s results must be uniquely keyed by (analysis_id, branch), or else your results will be subtly wrong.
custom_exposure_query (str) – A full SQL query that will generate the exposures common table expression used in the main query. The query must produce the columns analysis_id, branch, enrollment_date, and num_exposure_events. analysis_id should be an alias for the client_id or profile_group_id (e.g., SELECT client_id AS analysis_id).
exposure_signal (ExposureSignal) – Optional signal definition of when a client has been exposed to the experiment
segment_list (list of mozanalysis.segment.Segment or str) – The user segments to study.
sample_size (int) – Optional integer percentage of clients, used for downsampling enrollments. Default 100.
- Returns:
A string containing a BigQuery SQL expression.
- build_metrics_query(metric_list: list, time_limits: TimeLimits, enrollments_table: str, analysis_basis=AnalysisBasis.ENROLLMENTS, exposure_signal: ExposureSignal | None = None) str [source]
Return a SQL query for querying metric data.
For interactive use, prefer
get_time_series_data()
orget_single_window_data()
, according to your use case, which will run the query for you and return a materialized dataframe.The optional
exposure_signal
parameter allows to check if clients have received the exposure signal during enrollment or after. When using the exposures analysis basis, metrics will be computed for these clients.- Parameters:
metric_list (list of mozanalysis.metric.Metric or str) – The metrics to analyze.
time_limits (TimeLimits) – An object describing the interval(s) to query
enrollments_table (str) – The name of the enrollments table
basis (AnalysisBasis) – Use exposures as basis for calculating metrics if True, otherwise use enrollments.
exposure_signal (Optional[ExposureSignal]) – Optional exposure signal parameter that will be used for computing metrics for certain analysis bases (such as exposures).
- Returns:
A string containing a BigQuery SQL expression.
Building this query is the main goal of this module.
- class mozanalysis.experiment.TimeLimits(first_enrollment_date: str, last_enrollment_date: str, first_date_data_required: str, last_date_data_required: str, analysis_windows)[source]
Expresses time limits for different kinds of analysis windows.
Instantiated and used by the
Experiment
class; end users should not need to interact with it.Do not directly instantiate: use the constructors provided.
There are several time constraints needed to specify a valid query for experiment data:
When did enrollments start?
When did enrollments stop?
How long after enrollment does the analysis window start?
How long is the analysis window?
Even if these four quantities are specified directly, it is important to check that they are consistent with the available data - i.e. that we have data for the entire analysis window for every enrollment.
Furthermore, there are some extra quantities that are useful for writing efficient queries:
What is the first date for which we need data from our data source?
What is the last date for which we need data from our data source?
Instances of this class store all these quantities and do validation to make sure that they’re consistent. The “store lots of overlapping state and validate” strategy was chosen over “store minimal state and compute on the fly” because different state is supplied in different contexts.
- classmethod for_single_analysis_window(first_enrollment_date: str, last_date_full_data: str, analysis_start_days: int, analysis_length_dates: int, num_dates_enrollment: int | None = None) TimeLimits [source]
Return a
TimeLimits
instance with the following parameters- Parameters:
first_enrollment_date (str) – First date on which enrollment events were received; the start date of the experiment.
last_date_full_data (str) – The most recent date for which we have complete data, e.g. ‘2019-03-22’. If you want to ignore all data collected after a certain date (e.g. when the experiment recipe was deactivated), then do that here.
analysis_start_days (int) – the start of the analysis window, measured in ‘days since the client enrolled’. We ignore data collected outside this analysis window.
analysis_length_days (int) – the length of the analysis window, measured in days.
num_dates_enrollment (int, optional) – Only include this many days of enrollments. If
None
then use the maximum number of days as determined by the metric’s analysis window andlast_date_full_data
. Typically7n+1
, e.g.8
. The factor7
removes weekly seasonality, and the+1
accounts for the fact that enrollment typically starts a few hours before UTC midnight.
- classmethod for_ts(first_enrollment_date: str, last_date_full_data: str, time_series_period: str, num_dates_enrollment: int) TimeLimits [source]
Return a
TimeLimits
instance for a time series.- Parameters:
first_enrollment_date (str) – First date on which enrollment events were received; the start date of the experiment.
last_date_full_data (str) – The most recent date for which we have complete data, e.g. ‘2019-03-22’. If you want to ignore all data collected after a certain date (e.g. when the experiment recipe was deactivated), then do that here.
time_series_period – ‘daily’ or ‘weekly’.
num_dates_enrollment (int) – Take this many days of client enrollments. This is a mandatory argument because it determines the number of points in the time series.
- class mozanalysis.experiment.AnalysisWindow(start: int, end: int)[source]
Represents the range of days in which to measure a metric.
The range is measured in “days relative enrollment”, and is inclusive.
For example,
AnalysisWindow(0, 6)
is the first week after enrollment and AnalysisWindow(-8,-1) is the week before enrollment- Parameters:
start (int) – First day of the analysis window, in days relative to enrollment start. 0 indicates the date of enrollment. Positive numbers are after enrollment, negative are before. Must be the same sign as end (zero counts as positive)
end (int) – Final day of the analysis window, in days relative to enrollment start. 0 indicates the date of enrollment. Positive numbers are after enrollment, negative are before. Must be the same sign as start (zero counts as positive).
- class mozanalysis.experiment.TimeSeriesResult(fully_qualified_table_name: str, analysis_windows: tuple[AnalysisWindow, ...], analysis_unit: AnalysisUnit = AnalysisUnit.CLIENT)[source]
Result from a time series query.
For each analysis window, this object lets us get a dataframe in “the standard format” (one row per client).
Example usage:
result_dict = dict(time_series_result.items(bq_context)) window_0 = result_dict[0]
window_0
would then be a pandas DataFrame of results for the analysis window starting at day 0.result_dict
would be a dictionary of all such DataFrames, keyed by the start days of their analysis windows.Or, to load only one analysis window into RAM:
window_0 = time_series_result.get(bq_context, 0)
- get(bq_context: BigQueryContext, analysis_window) DataFrame [source]
Get the DataFrame for a specific analysis window.
N.B. this makes a BigQuery query each time it is run; caching results is your responsibility.
- Parameters:
bq_context (BigQueryContext)
analysis_window (AnalysisWindow or int) – The analysis window, or its start day as an int.
- get_full_data(bq_context: BigQueryContext) DataFrame [source]
Get the full DataFrame from TimeSeriesResult.
This DataFrame has a row for each client for each period of the time series and may be very large. A warning will print the size of data to be downloaded.
- Parameters:
bq_context (BigQueryContext)
- get_aggregated_data(bq_context: BigQueryContext, metric_list: list, aggregate_function: str = 'AVG') tuple[DataFrame, int] [source]
Results from a time series query, aggregated over analysis windows by a SQL aggregate function.
This DataFrame has a row for each analysis window, with a column for each metric in the supplied metric_list.
- Parameters:
bq_context (BigQueryContext)
metric_list (list of mozanalysis.metrics.Metric)
aggregate_fuction (str)