def test_salts(self): """Test salting behavior""" i = 20 a = Assignment('assign_salt_a') # assigning variables with different names and the same unit should yield # different randomizations, when salts are not explicitly specified a.x = RandomInteger(min=0, max=100000, unit=i) a.y = RandomInteger(min=0, max=100000, unit=i) self.assertTrue(a.x != a.y) # when salts are specified, they act the same way auto-salting does a.z = RandomInteger(min=0, max=100000, unit=i, salt='x') self.assertTrue(a.x == a.z) # when the Assignment-level salt is different, variables with the same # name (or salt) should generally be assigned to different values b = Assignment('assign_salt_b') b.x = RandomInteger(min=0, max=100000, unit=i) self.assertTrue(a.x != b.x) # when a full salt is specified, only the full salt is used to do # hashing a.f = RandomInteger(min=0, max=100000, unit=i, full_salt='fs') b.f = RandomInteger(min=0, max=100000, unit=i, full_salt='fs') self.assertTrue(a.f == b.f) a.f = RandomInteger(min=0, max=100000, unit=i, full_salt='fs2') b.f = RandomInteger(min=0, max=100000, unit=i, full_salt='fs2') self.assertTrue(a.f == b.f)
def test_set_get_uniform(self): a = Assignment(self.tester_salt) a.foo = UniformChoice(choices=['a', 'b'], unit=self.tester_unit) a.bar = UniformChoice(choices=['a', 'b'], unit=self.tester_unit) a.baz = UniformChoice(choices=['a', 'b'], unit=self.tester_unit) self.assertEqual(a.foo, 'b') self.assertEqual(a.bar, 'a') self.assertEqual(a.baz, 'a')
def test_sample(self): """Test random sampling without replacement""" # returns experiment function with x = sample(c, draws) # experiment salt is a string version of c def sample(choices, draws, fast_sample=False): @experiment_decorator(','.join(map(str, choices))) def exp_func(e, i): if fast_sample: e.x = FastSample(choices=choices, draws=draws, unit=i) else: e.x = Sample(choices=choices, draws=draws, unit=i) self.assertTrue(len(e.x) == draws) return e return exp_func def listDistributionTester(func, value_mass, N=1000): value_density = TestRandomOperators.valueMassToDensity(value_mass) # compute N trials xs_list = [func(i=i).get('x') for i in six.moves.range(N)] # each xs is a row of the transpose of xs_list. # this is expected to have the same distribution as value_density for xs in zip(*xs_list): self.assertProbs(xs, value_density, float(N)) listDistributionTester(sample([1, 2, 3], draws=3), ((1, 1), (2, 1), (3, 1))) listDistributionTester(sample([1, 2, 3], draws=2), ((1, 1), (2, 1), (3, 1))) listDistributionTester(sample([1, 2, 3], draws=2, fast_sample=True), ((1, 1), (2, 1), (3, 1))) listDistributionTester(sample(['a', 'a', 'b'], draws=3), (('a', 2), ('b', 1))) a = Assignment('assign_salt_a') a.old_sample = Sample(choices=[1, 2, 3, 4], draws=1, unit=1) new_sample = a.old_sample a.old_sample = FastSample(choices=[1, 2, 3, 4], draws=1, unit=1) self.assertTrue(len(a.old_sample), 1) self.assertTrue(len(new_sample), 1) self.assertTrue(a.old_sample != new_sample)
def test_sample(self): """Test random sampling without replacement""" # returns experiment function with x = sample(c, draws) # experiment salt is a string version of c def sample(choices, draws, fast_sample=False): @experiment_decorator(','.join(map(str, choices))) def exp_func(e, i): if fast_sample: e.x = FastSample(choices=choices, draws=draws, unit=i) else: e.x = Sample(choices=choices, draws=draws, unit=i) self.assertTrue(len(e.x) == draws) return e return exp_func def listDistributionTester(func, value_mass, N=1000): value_density = TestRandomOperators.valueMassToDensity(value_mass) # compute N trials xs_list = [func(i=i).get('x') for i in six.moves.range(N)] # each xs is a row of the transpose of xs_list. # this is expected to have the same distribution as value_density for xs in zip(*xs_list): self.assertProbs(xs, value_density, float(N)) listDistributionTester( sample([1, 2, 3], draws=3), ((1, 1), (2, 1), (3, 1))) listDistributionTester( sample([1, 2, 3], draws=2), ((1, 1), (2, 1), (3, 1))) listDistributionTester( sample([1, 2, 3], draws=2, fast_sample=True), ((1, 1), (2, 1), (3, 1))) listDistributionTester( sample(['a', 'a', 'b'], draws=3), (('a', 2), ('b', 1))) a = Assignment('assign_salt_a') a.old_sample = Sample(choices=[1, 2, 3, 4], draws=1, unit=1) new_sample = a.old_sample a.old_sample = FastSample(choices=[1, 2, 3, 4], draws=1, unit=1) self.assertTrue(len(a.old_sample), 1) self.assertTrue(len(new_sample), 1) self.assertTrue(a.old_sample != new_sample)
def test_overrides(self): a = Assignment(self.tester_salt) a.set_overrides({'x': 42, 'y': 43}) a.x = 5 a.y = 6 self.assertEqual(a.x, 42) self.assertEqual(a.y, 43)
def __init__(self, db_experiment, salt=None, **inputs): self.db_experiment = db_experiment self.inputs = inputs # input data # True when assignments have been exposure logged self._exposure_logged = False self._salt = self.db_experiment.salt # Experiment-level salt # Determines whether or not exposure should be logged self._in_experiment = True # use the name of the class as the default name self._name = self.db_experiment.name # auto-exposure logging is enabled by default self._auto_exposure_log = True self.setup() # sets name, salt, etc. self._assignment = Assignment(self.salt) self._assigned = False
class SingleTrial(SimpleInterpretedExperiment): def __init__(self, db_experiment, salt=None, **inputs): self.db_experiment = db_experiment self.inputs = inputs # input data # True when assignments have been exposure logged self._exposure_logged = False self._salt = self.db_experiment.salt # Experiment-level salt # Determines whether or not exposure should be logged self._in_experiment = True # use the name of the class as the default name self._name = self.db_experiment.name # auto-exposure logging is enabled by default self._auto_exposure_log = True self.setup() # sets name, salt, etc. self._assignment = Assignment(self.salt) self._assigned = False def __str__(self): return f"Trial {self._salt} of {self._name}" def loadScript(self): self.script = self.db_experiment.get_planout_dict() def setup(self): self.name = self.db_experiment.name def configure_logger(self): pass def log_exposure(self, extras=None): """Logs exposure to treatment""" if not self._in_experiment: return for key, value in self._assignment.items(): variation, created = Variation.objects.get_or_create( experiment=self.db_experiment, key=key, value=value) user_identifier_type = self.inputs.get('user_identifier_type') if user_identifier_type is not None: exposure = Exposure(experiment=self.db_experiment, variation=variation) if user_identifier_type == DJANGO_USER_DB_ID: user = get_user_model().objects.get( id=self.inputs['user_id']) exposure.event_user = user exposure.event_user_identifier_type = DJANGO_USER_DB_ID else: exposure.event_user_identifier = self.inputs['user_id'] exposure.event_user_identifier_type = user_identifier_type exposure.save() self._exposure_logged = True def log(self, data): ExperimentLog.objects.create(experiment=self.db_experiment, data=data)
def test_set_get_constant(self): a = Assignment(self.tester_salt) a.foo = 12 self.assertEqual(a.foo, 12)
def wrapped_f(**kwargs): params = Assignment(name) return f(params, **kwargs)
def get_segment(name, num_segments, **units): # randomly assign primary unit to a segment ass = Assignment(name) ass.segment = RandomInteger(min=0, max=num_segments - 1, **units) return ass.segment
def test_custom_salt(self): a = Assignment(self.tester_salt) custom_salt = lambda x,y: '%s-%s' % (x,y) a.foo = UniformChoice(choices=range(8), unit=self.tester_unit) self.assertEqual(a.foo, 7)
def test_custom_salt(self): a = Assignment(self.tester_salt) custom_salt = lambda x,y: '%s-%s' % (x,y) a.foo = UniformChoice(choices=list(range(8)), unit=self.tester_unit) self.assertEqual(a.foo, 7)