<jupyter_start><jupyter_text>Design of an experiment with TBR using Matched MarketsPlease note that this colab is for TBR Geo experiments only.Using this colab, you can create a geoexperiment design for a client using TBR in combination with Matched Markets. In the following we will use the acronyms TBR for Time Based Regression and MM for Matched Markets. For a general introduction to TBR and MM, please refer to the TBR [paper](https://research.google/pubs/pub45950/), the MM [paper](https://research.google/pubs/pub48983/), and this [introduction](http://www.unofficialgoogledatascience.com/2016/06/estimating-causal-effects-using-geo.html) to geo experiments.<jupyter_code>#@title Load the libraries needed for the design  

BAZEL_VERSION = '3.0.0'
!wget https://github.com/bazelbuild/bazel/releases/download/{BAZEL_VERSION}/bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh
!chmod +x bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh
!./bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh
!sudo apt-get install python3-dev python3-setuptools git
!git clone https://github.com/google/matched_markets
!python3 -m pip install ./matched_markets
!pip install colorama
!pip install gspread-dataframe


"""Loading the necessary python modules."""
import altair as alt
import datetime
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
import pandas as pd
import re
from scipy import stats

from IPython.display import display
from IPython.core.interactiveshell import InteractiveShell

import gspread
import warnings
from colorama import Fore, Style
from gspread_dataframe import set_with_dataframe
from google.colab import auth
from google.colab import data_table
from google.colab import widgets
from oauth2client.client import GoogleCredentials
from google.colab import drive
from matched_markets.methodology.common_classes import GeoAssignment
from matched_markets.methodology import geoeligibility
from matched_markets.methodology import tbrmmdata
from matched_markets.methodology import tbrmmdesignparameters
from matched_markets.methodology import tbrmmdiagnostics
from matched_markets.methodology import tbrmatchedmarkets
from matched_markets.methodology import tbrmmdesign
from matched_markets.methodology import utils

warnings.filterwarnings('ignore')
InteractiveShell.ast_node_interactivity = "all"
#@markdown ---
#@markdown ### Enter the trix url for the sheet file containing the Client Sales Data: 
#@markdown The spreadsheet should contain the mandatory columns:
#@markdown * date: date in the format YYYY-MM-DD
#@markdown * geo: the number which identifies the geo
#@markdown * response: variable on which you want to measure incrementality
#@markdown (e.g. sales, transactions)
#@markdown * cost: variable used as spend proxy (e.g. ad spend)

#@markdown Other columns can be present in the spreadsheet.

#@markdown Spreadsheet URL containing the geo level response and spend data
client_sales_table = "add your url here, which should look like https://docs.google.com/spreadsheets/d/???/edit#gid=???" #@param {type:"string"}

#@markdown Leave the following field empty if you don't want to add constraint to the geo_eligibility
geo_eligibility_table = "add your url here, which should look like https://docs.google.com/spreadsheets/d/???/edit#gid=???" #@param {type:"string"}
auth.authenticate_user()
gc = gspread.authorize(GoogleCredentials.get_application_default())
wks = gc.open_by_url(client_sales_table).sheet1
data = wks.get_all_values()
headers = data.pop(0)
geo_level_time_series = pd.DataFrame(data, columns=headers)

geo_level_time_series["date"] = pd.to_datetime(geo_level_time_series["date"])
for colname in ["response", "geo", "cost"]:
  geo_level_time_series[colname] = pd.to_numeric(geo_level_time_series[colname])

num_geos = geo_level_time_series["geo"].nunique()

if not geo_eligibility_table:
  geo_eligibility = None
else:
  wks = gc.open_by_url(geo_eligibility_table).sheet1
  data = wks.get_all_values()
  headers = data.pop(0)
  geo_eligibility = pd.DataFrame(data, columns=headers)
  for colname in ["geo", "control", "treatment", "exclude"]:
    geo_eligibility[colname] = pd.to_numeric(geo_eligibility[colname])
  # set missing geos in geo_eligibility as eligible for any assignment
  geo_eligibility = utils.default_geo_assignment(geo_level_time_series,
                                                 geo_eligibility)
  geo_eligibility = geoeligibility.GeoEligibility(geo_eligibility)
  geo_eligibility.data.index = pd.to_numeric(geo_eligibility.data.index,
                                             downcast="integer").astype(str)

#@title Select the parameters for the design of the experiment 

## The minimum detectable iROAS is defined as the value of the true iROAS such
## that, given a confidence_level (input) % confidence level for a one-sided
## test, gives a power_level (input) % power if the true iROAS is equal to the
## minimum detectable iROAS.
minimum_detectable_iROAS =  3#@param{type: "number"}
#@markdown Use an average order value of 1 if the design is based on
#@markdown sales/revenue or an actual average order value (e.g. $80) for a
#@markdown design based on transactions/footfall/contracts.
average_order_value =  1#@param{type: "number"}

confidence_level = 0.90 #@param {type:"number"}
power_level = 0.80 #@param {type:"number"}
experiment_duration_in_weeks = 4 #@param {type:"integer"}

#@markdown List the maximum budget for the experiment e.g. 300000
experiment_budget =  300000#@param{type: "number"}
#@markdown List any alternative budget which you would like to test separated
#@markdown by a comma, e.g. 125000, 150000
alternative_budget = "" #@param{type: "string"}
additional_budget = [float(re.sub(r"\W+", "", x)) for x in
                     alternative_budget.split(',') if alternative_budget != ""]

#@markdown List the days and time periods that you want to exclude separated by
#@markdown a comma e.g. 2019/10/10, 2010/10/11, 2018/10/20-2018/11/20.
#@markdown The format for time periods is "YYYY/MM/DD - YYYY/MM/DD",
#@markdown where the two dates specify the start and end date for the period.
#@markdown The format for day is "YYYY/MM/DD". Leave empty to
#@markdown use all days/weeks.
day_week_exclude = "" #@param {type: "string"}
day_week_exclude = [] if day_week_exclude == "" else [
    re.sub(r"\s+", "", x) for x in day_week_exclude.split(",")
]
## Find all the days we should exclude from the analysis from the input
periods_to_exclude = utils.find_days_to_exclude(day_week_exclude)
days_exclude = utils.expand_time_windows(periods_to_exclude)

## Additional constraints which will be flagged in red if not met in
## the design

# upper bound on the minimal detectable relative lift
minimum_detectable_lift_in_response_metric = 0.1 * 100
# lower bound on the baseline revenue covered by the treatment group
minimum_revenue_covered_by_treatment = 0.05 * 100

frequency = utils.infer_frequency(geo_level_time_series, 'date', 'geo')
if frequency == "D":
  n_test = experiment_duration_in_weeks * 7
  ## Use the most recent ~6 months
  n_pretest = 180
elif frequency == "W":
  n_test = experiment_duration_in_weeks
  n_pretest = 26

## Other constraints/parameters which are hidden to the user

## Ratio of avg. control group response / avg. treatment group response must be
## between 1/(1+volume_ratio_tolerance) and 1+volume_ratio_tolerance
volume_ratio_tolerance = np.inf
## Ratio of number of control geos / number treatment geos must be
## between 1/(1+geo_ratio_tolerance) and 1+geo_ratio_tolerance
geo_ratio_tolerance = np.inf
## Constrain on the % of the treatment group response with respect to the
## overall response
treatment_share_range = (0.0001, 0.9999)
## Minimum and maximum number of geos to include in the treatment group
treatment_geos_range = (1, num_geos - 1)
## Minimum and maximum number of geos to include in the control group
control_geos_range = (1, num_geos - 1)
## Maximum number of geos to include in the search
n_geos_max = num_geos
## Maximum number of pretest timepoints to include in the time series for the
## purpose of estimating minimum detectable response 
n_pretest_max = n_pretest
## Number of design to store during the exhaustive search
n_designs = 3
## Maximum assumed treatment-control correlation to use for estimating the MDR
rho_max = 0.995
## Minimum acceptable Pearson correlation between the treatment and control
## time series.
min_corr = 0.8
## Inverse quantile of the f distribution parameter 'phi' used in the TBR
## preanalysis formula.
flevel = 0.9

budget_range = (0.1, experiment_budget)
min_volume_ratio = 1/(1 + volume_ratio_tolerance)
max_volume_ratio = 1 + volume_ratio_tolerance

tbr_parameters = tbrmmdesignparameters.TBRMMDesignParameters(
    n_test=n_test,
    iroas=minimum_detectable_iROAS,
    volume_ratio_tolerance=volume_ratio_tolerance,
    geo_ratio_tolerance=geo_ratio_tolerance,
    treatment_share_range=treatment_share_range,
    budget_range=budget_range,
    treatment_geos_range=treatment_geos_range,
    control_geos_range=control_geos_range,
    n_geos_max=n_geos_max,
    n_pretest_max=n_pretest_max,
    n_designs=n_designs,
    sig_level=confidence_level,
    power_level=power_level,
    min_corr=min_corr,
    rho_max=rho_max,
    flevel=flevel)

# remove dates that the user wants to exclude
data_for_design = geo_level_time_series[~geo_level_time_series['date']
                                        .isin(days_exclude)].copy()
tbrclass = tbrmmdata.TBRMMData(df=data_for_design,
                               response_column='response',
                               geo_eligibility=geo_eligibility)

MMclass = tbrmatchedmarkets.TBRMatchedMarkets(data=tbrclass,
                                              parameters=tbr_parameters)
#@title Summary of the possible designs  

max_feasible_number_of_designs = 5 * 10 ** 6

if MMclass.count_max_designs() < max_feasible_number_of_designs:
  matched_designs = MMclass.exhaustive_search()
else:
  matched_designs = MMclass.greedy_search()

if len(matched_designs) == 0:
  raise ValueError(f'{Fore.RED}It wasn\'t possible to find a design within ' +
                   f'the constraint in input or because all the designs, ' +
                   f'did not pass one among the AA test, structural break ' +
                   f'test, or minimum correlation of 0.8\n{Style.RESET_ALL}')

matched_designs.sort(reverse=True)
chosen_design = matched_designs[0]

if not chosen_design.score.score.aa_test:
  print(f'{Fore.RED}WARNING: the design does not pass the A/A test. ' +
        f'We do not recommend to use the proposed design.\n{Style.RESET_ALL}')

if not chosen_design.score.score.bb_test:
  print(f'{Fore.RED}WARNING: the design does not pass the Brownian Bridge ' +
        f'test. The relationship between treatment/control is ' +
        f'not stable over time. We do not recommend to use ' +
        f'the proposed design.\n{Style.RESET_ALL}')

if not chosen_design.score.score.dw_test:
  print(f'{Fore.RED}WARNING: the design does not pass the Durbin-Watson ' +
        f'test. The residuals are autocorrelated. We do not recommend to use ' +
        f'the proposed design.\n{Style.RESET_ALL}')

minimum_iroas_aov = minimum_detectable_iROAS / average_order_value
minimum_detectable_impact = chosen_design.diag.estimate_required_impact(
    chosen_design.diag.corr)

optimal_budget = minimum_detectable_impact / minimum_iroas_aov
lower_budget = optimal_budget *  0.8
upper_budget = optimal_budget * 1.2
list_of_budgets = [lower_budget, optimal_budget, upper_budget
                  ] + additional_budget

first_day = geo_level_time_series["date"].max() - pd.Timedelta(
    str(experiment_duration_in_weeks) + "W")
most_recent_geo_level_time_series = geo_level_time_series[
    geo_level_time_series['date'] > first_day]

total_response = most_recent_geo_level_time_series["response"].sum()
total_spend = most_recent_geo_level_time_series["cost"].sum()
designs = []
for budget in list_of_budgets:
  baseline = most_recent_geo_level_time_series.loc[
      most_recent_geo_level_time_series["geo"].isin(chosen_design.treatment_geos
                                                   ), "response"].sum()
  cost_in_experiment = most_recent_geo_level_time_series.loc[
      most_recent_geo_level_time_series["geo"].isin(chosen_design.treatment_geos
                                                   ), "cost"].sum()
  min_detectable_iroas = (
      average_order_value * minimum_detectable_impact / budget)
  min_detectable_lift = (minimum_detectable_impact * 100 / baseline)
  num_treatment_geos = len(chosen_design.treatment_geos)
  num_control_geos = len(chosen_design.control_geos)
  num_removed_geos = num_geos - num_treatment_geos - num_control_geos
  treat_control_removed = (f'{num_treatment_geos}  /  {num_control_geos}  / ' +
                           f'{num_removed_geos}')
  revenue_covered = 100 * baseline / total_response
  proportion_cost_in_experiment = cost_in_experiment / total_spend
  national_budget = utils.human_readable_number(
      budget / proportion_cost_in_experiment)
  designs.append({
      "Budget": utils.human_readable_number(budget),
      "Minimum detectable iROAS": f'{min_detectable_iroas:.3}',
      "Minimum detectable lift in response": f'{min_detectable_lift:.2f} %',
      "Treatment/control/excluded geos": treat_control_removed,
      "Revenue covered by treatment group": f'{revenue_covered:.2f} %',
      "Cost/baseline response": f'{(budget / baseline * 100):.2f} %',
      "Cost if test budget is scaled nationally": national_budget
  })


## define function to colorcode rows and cells of the output table
def is_optimal_design(row):
    """Color a row in:
    - orange if the minimum detectable iROAS of the corresponding design
      is larger than the target
    - green if its equal to the target
    - beige if it's lower than the target
    """
    if float(row["Minimum detectable iROAS"]) == minimum_detectable_iROAS:
          return pd.Series('background-color: lightgreen', row.index)
    elif float(row["Minimum detectable iROAS"]) > minimum_detectable_iROAS:
          return pd.Series('background-color: orange', row.index)
    else:
          return pd.Series('background-color: beige', row.index)

def flag_warning_lift(val, value):
    """
    Color a cell in red if its value is larger than the value
    in input
    """
    color = 'red' if float(val.strip(' %')) > value else 'black'
    return 'color: %s' % color

def flag_warning_revenue(val, value):
    """
    Color a cell in red if its value is smaller than the value
    in input
    """
    color = 'red' if float(val.strip(' %')) < value else 'black'
    return 'color: %s' % color


## convert the table to a pd.DataFrame and select a subset of columns
designs = pd.DataFrame(designs)
designs.index.rename("Design", inplace=True)
designs = designs[["Budget", "Minimum detectable iROAS",
                   "Minimum detectable lift in response",
                   "Treatment/control/excluded geos",
                   "Revenue covered by treatment group",
                   "Cost/baseline response",
                   "Cost if test budget is scaled nationally"]]

## apply colorcoding to rows and cells of the table
designs_table = designs.style.applymap(
    flag_warning_lift,
    value=minimum_detectable_lift_in_response_metric,
    subset=["Minimum detectable lift in response"]).applymap(
        flag_warning_revenue,
        value=minimum_revenue_covered_by_treatment,
        subset=["Revenue covered by treatment group"]).apply(
            is_optimal_design, axis=1)

designs_table

#@title Select the design to be used in the experiment  
#@markdown Select the design using the number as displayed in the table in
#@markdown the cell called "Summary of the possible designs".

selected_design =   1#@param {type:"integer"}

if selected_design not in designs.index:
  raise ValueError(f'the selected design must be one of {designs.index.to_list()}, got {selected_design}')

selected_design = int(selected_design)
final_design = designs[designs.index == selected_design]
selected_budget = final_design["Budget"].values[0]

# these are numerical identifier used to denote the two groups
group_treatment = GeoAssignment.TREATMENT
group_control = GeoAssignment.CONTROL
group_excluded = GeoAssignment.EXCLUDED

data_for_design.loc["assignment"] = group_excluded
data_for_design.loc[data_for_design["geo"].isin(
    chosen_design.control_geos), "assignment"] = group_control
data_for_design.loc[data_for_design["geo"].isin(
    chosen_design.treatment_geos), "assignment"] = group_treatment

# set basic plot parameters
chart_width = 600
chart_height = 300
axis_title_font_size = 16
axis_label_font_size = 14
title_font_size = 19

plot_dict = {"Response": {"colname": "response", "format": "s"},
             "Ad Spend": {"colname": "cost", "format": "s"}}
tb = widgets.TabBar(list(plot_dict.keys()))
for k, v in plot_dict.items():
  with tb.output_to(k):
    # Set y-axis format
    format_value = ".1" if v["format"] == "s" else ".2"
    format_string = format_value + v["format"]

    # Set dataframe to use for plotting and define y-variable to plot
    plot_df = data_for_design[data_for_design["assignment"].isin(
        [group_control,
        group_treatment])].groupby(["date", "assignment"],
                                    as_index=False)[["response", "cost"]].sum()
    plot_df["assignment"] = plot_df["assignment"].map({
        group_control: "Control",
        group_treatment: "Treatment"
    })
    y_string = v["colname"] + ":Q"

    # Define how we select based on where the mouse is.
    selection = alt.selection_single(
        fields=["date"],
        nearest=True,
        on="mouseover",
        empty="none",
        clear="mouseout")
    bottom_selection = alt.selection_single(
        fields=["date"],
        nearest=True,
        on="mouseover",
        empty="none",
        clear="mouseout")

    # Brush for selecting a location to zoom into on x-axis.
    brush = alt.selection(type="interval", encodings=["x"])

    # Base chart -- same for top and bottom
    base = alt.Chart(plot_df).mark_line().encode(
        x=alt.X("date:T", axis=alt.Axis(title="", format=("%b %e")))
    )

    # Lines for data
    lines = base.mark_line().encode(
        y=alt.Y(y_string, axis=alt.Axis(title=" ", format=(format_string)))
    )
    bottom_lines = lines.encode(alt.X("date:T", scale=alt.Scale(domain=brush)))

    # Vertical rule
    rule = base.mark_rule().encode(
        opacity=alt.condition(selection, alt.value(0.3), alt.value(0)),
        tooltip=["date:T", y_string]
    ).add_selection(selection)
    bottom_rule = base.mark_rule().encode(
        opacity=alt.condition(bottom_selection, alt.value(0.3), alt.value(0)),
        tooltip=["date:T", y_string]
    ).add_selection(bottom_selection)

    # Points to denote where the mouse is.
    points = lines.mark_point().transform_filter(selection)
    bottom_points = bottom_lines.mark_point().transform_filter(bottom_selection)

    lines = lines.mark_line().encode(
      color=alt.Color("assignment", title=" "))
    bottom_lines = bottom_lines.mark_line().encode(
      color=alt.Color("assignment", title=" "))
    base_rule = base.transform_pivot(
        "assignment", value=v["colname"],
        groupby=["date"]).mark_rule().encode(tooltip=["date:T"] + [
            alt.Tooltip(c, type="quantitative")
            for c in plot_df["assignment"].unique()
        ])
    rule = base_rule.encode(
        opacity=alt.condition(selection, alt.value(0.3), alt.value(0))
    ).add_selection(selection)
    bottom_rule = base_rule.encode(
        opacity=alt.condition(bottom_selection, alt.value(0.3), alt.value(0))
    ).add_selection(bottom_selection)

    scatter_df = plot_df.pivot(
        index="date", values=v["colname"], columns="assignment").reset_index()
    base = alt.Chart(scatter_df).mark_circle(size=60).encode(
        alt.X("Control"),
        alt.Y("Treatment"),
        tooltip=["date", "Control", "Treatment"])
    regression_line = base.transform_regression(
        "Control", "Treatment", method="linear").mark_line(color="red")
    params = alt.Chart(scatter_df).transform_regression(
        'Control', 'Treatment', params=True
    ).mark_text(align='left', fontSize = 20).encode(
        x=alt.value(60),  # pixels from left
        y=alt.value(20),  # pixels from top
        text='rSquared:N'
    )
    string_r2 = alt.Chart({'values':[{'x': 30, 'y': 20}]}).mark_text(
      text='R^2 = ', fontSize = 20).encode( x=alt.value(30), y=alt.value(20))

    # Compile top chart
    top_chart = alt.layer(
        lines,
        points,
        rule
    ).add_selection(
        brush
    ).properties(
        width=chart_width,
        height=chart_height,
        title=k
    )

    # Compile bottom chart
    bottom_chart = alt.layer(
        bottom_lines,
        bottom_rule,
        bottom_points
    ).properties(
        width=chart_width,
        height=chart_height
    )

    # Compile scatterplot
    scatter_plot = alt.layer(base, regression_line, params, string_r2).properties(
        width=chart_width, height=chart_width, title=k).interactive()
    # Combine charts
    final_chart = alt.hconcat(
        alt.vconcat(
            top_chart,
            bottom_chart,
        ), scatter_plot).resolve_scale(color='independent')

    final_chart.configure_axis(
        titleFontSize=axis_title_font_size,
        labelFontSize=axis_label_font_size
    ).configure_title(
        fontSize=title_font_size
    ).display()


#@title Summary and Results  


print("Data in input:\n")
print("-  {} Geos \n".format(
    len(geo_level_time_series["geo"].unique())))

print("Output:\n")
print("The output contains two lists of geos: one for treatment" +
      " and the other for control\n")

human_baseline = utils.human_readable_number(baseline)
cost_baseline = list_of_budgets[selected_design] * 100 / baseline
print('-  {} Geos in treatment and {} geos control\n'.format(
    len(chosen_design.treatment_geos), len(chosen_design.control_geos)))
print("    Baseline store response: ${} for treatment\n".format(human_baseline))
print('    Cost/baseline = ${} / ${} ~ {:.3}%\n'.format(selected_budget,
                                                        human_baseline,
                                                        cost_baseline))

print(f'Minimum detectable iROAS = ' +
      f'{final_design["Minimum detectable iROAS"].values[0]}')
print(f'Minimum detectable lift in % = ' +
      f'{final_design["Minimum detectable lift in response"].values[0]}')

print(f"The design has Power {100 * power_level:.3}+% with Type-I error " +
      f"{100 *(1 - confidence_level):.3}% for testing H0: iROAS=0 vs " +
      f"H1: iROAS >= {final_design['Minimum detectable iROAS'].values[0]}")
#@title Report stores for treatment and control separately and write to trix 

#@markdown ###Insert the name google sheets in which we will save the data.
#@markdown The trix contains 4 worksheets, named:
#@markdown * "pretest data", containing the geo level time series;
#@markdown * "geopairs", containing the pairs of geos and their assignment.
#@markdown * "treatment geos", contains the list of geos in the treatment;
#@markdown * "control geos", contains the geos in the control groups.
Client_Name = "TBRMM_Client_Name" #@param {type:"string"}
filename_design = Client_Name + "_design.csv" #@param {type:"string"}

design_data = geo_level_time_series.copy()

design_data["assignment"] = "Excluded"
design_data.loc[design_data["geo"].isin(
    chosen_design.control_geos), "assignment"] = "Control"
design_data.loc[design_data["geo"].isin(
    chosen_design.treatment_geos), "assignment"] = "Treatment"

design_data["removed"] = False
design_data.loc[design_data["date"].isin(days_exclude), "removed"] = True

tmp_parameters = {
    "minimum_detectable_iROAS": minimum_detectable_iROAS,
    "average_order_value": average_order_value,
    "confidence_level": confidence_level,
    "power_level": power_level,
    "experiment_duration_in_weeks": experiment_duration_in_weeks,
    "experiment_budget": experiment_budget,
    "alternative_budget": alternative_budget,
    "day_week_exclude": ", ".join(day_week_exclude),
    "selected_design": selected_design,
}

parameters = {"parameter": list(tmp_parameters.keys()),
              "value": list(tmp_parameters.values())}


sh = gc.create(filename_design)
wid = sh.add_worksheet("pretest data", rows=1, cols=1)
set_with_dataframe(wid, design_data)
wid = sh.add_worksheet("treatment geos", rows=1, cols=1)
set_with_dataframe(wid, pd.DataFrame({"geo": list(chosen_design.treatment_geos)}))
wid = sh.add_worksheet("control geos", rows=1, cols=1)
set_with_dataframe(wid, pd.DataFrame({"geo": list(chosen_design.control_geos)}))
wid = sh.add_worksheet("parameters used in the design", rows=1, cols=1)
set_with_dataframe(wid, pd.DataFrame(parameters))
out = sh.del_worksheet(sh.sheet1)<jupyter_output><empty_output><jupyter_text>Appendix:<jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10635243450173466, 'fraction_numerical': 0.010487697890497269, 'mean_word_length': 4.069334950475035, 'pattern_counts': {'":': 29, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 6, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 17, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '302268', 'license': 'Apache-2.0', 'n_tokens_mistral': 8204, 'n_tokens_neox': 7669, 'n_words': 1994, 'path': 'matched_markets/notebook/design_colab_for_tbrmm.ipynb', 'repo_name': 'deklareddotcom/matched_markets', 'size': '25077'}