Skip to content

Social venue distributor

SocialVenueDistributor

Tool to associate social venues to people.

Source code in june/groups/leisure/social_venue_distributor.py
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
class SocialVenueDistributor:
    """Tool to associate social venues to people."""

    def __init__(
        self,
        social_venues: SocialVenues,
        times_per_week: Dict[Dict, float],
        daytypes: Dict[str, str] = default_daytypes,
        hours_per_day: Dict[Dict, float] = None,
        drags_household_probability=0.0,
        invites_friends_probability=0.0,
        neighbours_to_consider=5,
        maximum_distance=5,
        leisure_subgroup_type=0,
        nearest_venues_to_visit=0,
        open={"weekday": "0-24", "weekend": "0-24"},
    ):
        """
        A sex/age profile for the social venue attendees can be specified as
        male_age_probabilities = {"18-65" : 0.3}
        any non-specified ages in the range (0,99) will have 0 probabilty
        Parameters
        ----------
        social_venues
            A SocialVenues object
        times_per_week:
            How many times per day type, age, and sex, a person does this activity.
            Example:
            times_per_week = {"weekday" : {"male" : {"0-50":0.5, "50-100" : 0.2},
                                            "female" : {"0-100" : 0.5}},
                              "weekend" : {"male" : {"0-100" : 1.0},
                                            "female" : {"0-100" : 1.0}}}
        hours_per_day:
            How many leisure hours per day a person has. This is the time window in which
            a person can do leisure.
            Example:
            hours_per_day = {"weekday" : {"male" : {"0-65": 3, "65-100" : 11},
                                          "female" : {"0-65" : 3, "65-100" : 11}},
                              "weekend" : {"male" : {"0-100" : 12},
                                            "female" : {"0-100" : 12}}}
        drags_household_probabilitiy:
            Probability of doing a certain activity together with the housheold.
        maximum_distance:
            Maximum distance to travel until the social venue
        leisure_subgroup_type
            Subgroup of the venue that the person will be appended to
            (for instance, the visitors subgroup of the care home)
        nearest_venues_to_visit:
            restrict people only travelling to nearest venue(s). 0 means no restriction.
            if >0, "neighbours_to_consider" will be ignored.
        """
        self.spec = re.findall("[A-Z][^A-Z]*", self.__class__.__name__)[:-1]
        self.spec = "_".join(self.spec).lower()
        if hours_per_day is None:
            hours_per_day = {
                "weekday": {
                    "male": {"0-65": 3, "65-100": 11},
                    "female": {"0-65": 3, "65-100": 11},
                },
                "weekend": {"male": {"0-100": 12}, "female": {"0-100": 12}},
            }
        self.social_venues = social_venues
        self.open = open
        self.daytypes = daytypes

        self.poisson_parameters = self._parse_poisson_parameters(
            times_per_week=times_per_week, hours_per_day=hours_per_day
        )
        self.neighbours_to_consider = neighbours_to_consider
        self.maximum_distance = maximum_distance
        self.drags_household_probability = drags_household_probability
        self.invites_friends_probability = invites_friends_probability
        self.leisure_subgroup_type = leisure_subgroup_type
        self.spec = re.findall("[A-Z][^A-Z]*", self.__class__.__name__)[:-1]
        self.spec = "_".join(self.spec).lower()
        self.nearest_venues_to_visit = nearest_venues_to_visit
        self.social_network = SocialNetwork()

    @classmethod
    def from_config(
        cls,
        social_venues: SocialVenues,
        daytypes: dict = default_daytypes,
        config_filename: str = None,
        config_override: Dict[str, int] = None,
    ):
        """Load social venue settings from configuration and apply overrides.

        Args:
            social_venues (SocialVenues): 
            daytypes (dict, optional): (Default value = default_daytypes)
            config_filename (str, optional): (Default value = None)
            config_override (Dict[str, int], optional): a dict of parameters overrides their values in "config_filename" (Default value = None)

        """
        # Load config
        if config_filename is None:
            config_filename = cls.default_config_filename
        with open(config_filename) as f:
            config = yaml.load(f, Loader=yaml.FullLoader)

        # Apply overrides if present
        if config_override is not None:
            for key, value in config_override.items():
                if value is not None:
                    config[key] = value

        social_venues_instance = cls(social_venues, daytypes=daytypes, **config)

        return social_venues_instance

    def _compute_poisson_parameter_from_times_per_week(
        self, times_per_week, hours_per_day, day_type
    ):
        """

        Args:
            times_per_week: 
            hours_per_day: 
            day_type: 

        """
        if times_per_week == 0:
            return 0
        ndays = len(self.daytypes[day_type])
        return (times_per_week / ndays) * (24 / hours_per_day)

    def _parse_poisson_parameters(self, times_per_week, hours_per_day):
        """

        Args:
            times_per_week: 
            hours_per_day: 

        """
        ret = {}
        _sex_t = {"male": "m", "female": "f"}

        for day_type in ["weekday", "weekend"]:
            ret[day_type] = {}
            for sex in ["male", "female"]:
                parsed_times_per_week = parse_age_probabilities(
                    times_per_week[day_type][sex]
                )
                parsed_hours_per_day = parse_age_probabilities(
                    hours_per_day[day_type][sex]
                )

                ret[day_type][_sex_t[sex]] = [
                    self._compute_poisson_parameter_from_times_per_week(
                        times_per_week=parsed_times_per_week[i],
                        hours_per_day=parsed_hours_per_day[i],
                        day_type=day_type,
                    )
                    for i in range(len(parsed_times_per_week))
                ]
        return ret

    def get_poisson_parameter(
        self, sex, age, day_type, working_hours, region=None, policy_reduction=None
    ):
        """Poisson parameter (lambda) of a person going to one social venue according to their
        age and sex and the distribution of visitors in the venue.

        Args:
            sex: 
            age: 
            day_type: 
            working_hours: 
            region: (Default value = None)
            policy_reduction: (Default value = None)

        """
        if region is None:
            regional_compliance = 1
        else:
            if self.spec in region.closed_venues:
                return 0
            regional_compliance = region.regional_compliance

        original_poisson_parameter = self.poisson_parameters[day_type][sex][age]
        if policy_reduction is None:
            return original_poisson_parameter
        poisson_parameter = original_poisson_parameter * (
            1 + regional_compliance * (policy_reduction - 1)
        )
        return poisson_parameter

    def probability_to_go_to_social_venue(
        self, person, delta_time, day_type, working_hours
    ):
        """Probabilty of a person going to one social venue according to their
        age and sex and the distribution of visitors in the venue.

        Args:
            person: an instance of Person
            delta_time: 
            day_type: weekday or weekend
            working_hours: 

        """
        poisson_parameter = self.get_poisson_parameter(
            sex=person.sex,
            age=person.age,
            day_type=day_type,
            working_hours=working_hours,
        )
        return 1 - np.exp(-poisson_parameter * delta_time)


    def get_possible_venues_for_area(self, area: Area):
        """Given an area, searches for the social venues inside
        ``self.maximum_distance``. It then returns ``self.neighbours_to_consider``
        of them randomly, or ``nearest_venues_to_visit`` of them sorting by distance ascending.
        If there are no social venues inside the maximum distance, it returns the closest one.

        Args:
            area (Area): 

        """
        area_location = area.coordinates
        potential_venues = self.social_venues.get_venues_in_radius(
            area_location, self.maximum_distance
        )
        if potential_venues is None:
            closest_venue = self.social_venues.get_closest_venues(area_location, k=1)
            if closest_venue is None:
                return
            return (closest_venue[0],)
        if self.nearest_venues_to_visit > 0:
            indices_len = min(len(potential_venues), self.nearest_venues_to_visit)
            return tuple([potential_venues[idx] for idx in range(indices_len)])
        else:
            indices_len = min(len(potential_venues), self.neighbours_to_consider)
            random_idx_choice = sample(range(len(potential_venues)), indices_len)
            return tuple([potential_venues[idx] for idx in random_idx_choice])


    def get_leisure_group(self, person):
        """

        Args:
            person: 

        """
        candidates = person.area.social_venues[self.spec]
        n_candidates = len(candidates)
        if n_candidates == 0:
            return
        elif n_candidates == 1:
            group = candidates[0]
        else:
            group = candidates[randint(0, n_candidates - 1)]
        return group

    def get_leisure_subgroup(self, person, to_send_abroad=None):
        """

        Args:
            person: 
            to_send_abroad: (Default value = None)

        """
        group = self.get_leisure_group(person)
        # this may not necessary be the same subgroup, allow for customization here.
        if group is None:
            return
        subgroup = group.get_leisure_subgroup(
            person=person,
            subgroup_type=self.leisure_subgroup_type,
            to_send_abroad=to_send_abroad,
        )
        return subgroup

    def person_drags_household(self):
        """Check whether person drags household or not."""
        return rnd.random() < self.drags_household_probability

    def person_invites_friends(self):
        """Check whether person invites friends or not."""
        return rnd.random() < self.invites_friends_probability

__init__(social_venues, times_per_week, daytypes=default_daytypes, hours_per_day=None, drags_household_probability=0.0, invites_friends_probability=0.0, neighbours_to_consider=5, maximum_distance=5, leisure_subgroup_type=0, nearest_venues_to_visit=0, open={'weekday': '0-24', 'weekend': '0-24'})

A sex/age profile for the social venue attendees can be specified as male_age_probabilities = {"18-65" : 0.3} any non-specified ages in the range (0,99) will have 0 probabilty Parameters


social_venues A SocialVenues object times_per_week: How many times per day type, age, and sex, a person does this activity. Example: times_per_week = {"weekday" : {"male" : {"0-50":0.5, "50-100" : 0.2}, "female" : {"0-100" : 0.5}}, "weekend" : {"male" : {"0-100" : 1.0}, "female" : {"0-100" : 1.0}}} hours_per_day: How many leisure hours per day a person has. This is the time window in which a person can do leisure. Example: hours_per_day = {"weekday" : {"male" : {"0-65": 3, "65-100" : 11}, "female" : {"0-65" : 3, "65-100" : 11}}, "weekend" : {"male" : {"0-100" : 12}, "female" : {"0-100" : 12}}} drags_household_probabilitiy: Probability of doing a certain activity together with the housheold. maximum_distance: Maximum distance to travel until the social venue leisure_subgroup_type Subgroup of the venue that the person will be appended to (for instance, the visitors subgroup of the care home) nearest_venues_to_visit: restrict people only travelling to nearest venue(s). 0 means no restriction. if >0, "neighbours_to_consider" will be ignored.

Source code in june/groups/leisure/social_venue_distributor.py
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def __init__(
    self,
    social_venues: SocialVenues,
    times_per_week: Dict[Dict, float],
    daytypes: Dict[str, str] = default_daytypes,
    hours_per_day: Dict[Dict, float] = None,
    drags_household_probability=0.0,
    invites_friends_probability=0.0,
    neighbours_to_consider=5,
    maximum_distance=5,
    leisure_subgroup_type=0,
    nearest_venues_to_visit=0,
    open={"weekday": "0-24", "weekend": "0-24"},
):
    """
    A sex/age profile for the social venue attendees can be specified as
    male_age_probabilities = {"18-65" : 0.3}
    any non-specified ages in the range (0,99) will have 0 probabilty
    Parameters
    ----------
    social_venues
        A SocialVenues object
    times_per_week:
        How many times per day type, age, and sex, a person does this activity.
        Example:
        times_per_week = {"weekday" : {"male" : {"0-50":0.5, "50-100" : 0.2},
                                        "female" : {"0-100" : 0.5}},
                          "weekend" : {"male" : {"0-100" : 1.0},
                                        "female" : {"0-100" : 1.0}}}
    hours_per_day:
        How many leisure hours per day a person has. This is the time window in which
        a person can do leisure.
        Example:
        hours_per_day = {"weekday" : {"male" : {"0-65": 3, "65-100" : 11},
                                      "female" : {"0-65" : 3, "65-100" : 11}},
                          "weekend" : {"male" : {"0-100" : 12},
                                        "female" : {"0-100" : 12}}}
    drags_household_probabilitiy:
        Probability of doing a certain activity together with the housheold.
    maximum_distance:
        Maximum distance to travel until the social venue
    leisure_subgroup_type
        Subgroup of the venue that the person will be appended to
        (for instance, the visitors subgroup of the care home)
    nearest_venues_to_visit:
        restrict people only travelling to nearest venue(s). 0 means no restriction.
        if >0, "neighbours_to_consider" will be ignored.
    """
    self.spec = re.findall("[A-Z][^A-Z]*", self.__class__.__name__)[:-1]
    self.spec = "_".join(self.spec).lower()
    if hours_per_day is None:
        hours_per_day = {
            "weekday": {
                "male": {"0-65": 3, "65-100": 11},
                "female": {"0-65": 3, "65-100": 11},
            },
            "weekend": {"male": {"0-100": 12}, "female": {"0-100": 12}},
        }
    self.social_venues = social_venues
    self.open = open
    self.daytypes = daytypes

    self.poisson_parameters = self._parse_poisson_parameters(
        times_per_week=times_per_week, hours_per_day=hours_per_day
    )
    self.neighbours_to_consider = neighbours_to_consider
    self.maximum_distance = maximum_distance
    self.drags_household_probability = drags_household_probability
    self.invites_friends_probability = invites_friends_probability
    self.leisure_subgroup_type = leisure_subgroup_type
    self.spec = re.findall("[A-Z][^A-Z]*", self.__class__.__name__)[:-1]
    self.spec = "_".join(self.spec).lower()
    self.nearest_venues_to_visit = nearest_venues_to_visit
    self.social_network = SocialNetwork()

from_config(social_venues, daytypes=default_daytypes, config_filename=None, config_override=None) classmethod

Load social venue settings from configuration and apply overrides.

Parameters:

Name Type Description Default
social_venues SocialVenues
required
daytypes dict

(Default value = default_daytypes)

default_daytypes
config_filename str

(Default value = None)

None
config_override Dict[str, int]

a dict of parameters overrides their values in "config_filename" (Default value = None)

None
Source code in june/groups/leisure/social_venue_distributor.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@classmethod
def from_config(
    cls,
    social_venues: SocialVenues,
    daytypes: dict = default_daytypes,
    config_filename: str = None,
    config_override: Dict[str, int] = None,
):
    """Load social venue settings from configuration and apply overrides.

    Args:
        social_venues (SocialVenues): 
        daytypes (dict, optional): (Default value = default_daytypes)
        config_filename (str, optional): (Default value = None)
        config_override (Dict[str, int], optional): a dict of parameters overrides their values in "config_filename" (Default value = None)

    """
    # Load config
    if config_filename is None:
        config_filename = cls.default_config_filename
    with open(config_filename) as f:
        config = yaml.load(f, Loader=yaml.FullLoader)

    # Apply overrides if present
    if config_override is not None:
        for key, value in config_override.items():
            if value is not None:
                config[key] = value

    social_venues_instance = cls(social_venues, daytypes=daytypes, **config)

    return social_venues_instance

get_leisure_group(person)

Parameters:

Name Type Description Default
person
required
Source code in june/groups/leisure/social_venue_distributor.py
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def get_leisure_group(self, person):
    """

    Args:
        person: 

    """
    candidates = person.area.social_venues[self.spec]
    n_candidates = len(candidates)
    if n_candidates == 0:
        return
    elif n_candidates == 1:
        group = candidates[0]
    else:
        group = candidates[randint(0, n_candidates - 1)]
    return group

get_leisure_subgroup(person, to_send_abroad=None)

Parameters:

Name Type Description Default
person
required
to_send_abroad

(Default value = None)

None
Source code in june/groups/leisure/social_venue_distributor.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
def get_leisure_subgroup(self, person, to_send_abroad=None):
    """

    Args:
        person: 
        to_send_abroad: (Default value = None)

    """
    group = self.get_leisure_group(person)
    # this may not necessary be the same subgroup, allow for customization here.
    if group is None:
        return
    subgroup = group.get_leisure_subgroup(
        person=person,
        subgroup_type=self.leisure_subgroup_type,
        to_send_abroad=to_send_abroad,
    )
    return subgroup

get_poisson_parameter(sex, age, day_type, working_hours, region=None, policy_reduction=None)

Poisson parameter (lambda) of a person going to one social venue according to their age and sex and the distribution of visitors in the venue.

Parameters:

Name Type Description Default
sex
required
age
required
day_type
required
working_hours
required
region

(Default value = None)

None
policy_reduction

(Default value = None)

None
Source code in june/groups/leisure/social_venue_distributor.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def get_poisson_parameter(
    self, sex, age, day_type, working_hours, region=None, policy_reduction=None
):
    """Poisson parameter (lambda) of a person going to one social venue according to their
    age and sex and the distribution of visitors in the venue.

    Args:
        sex: 
        age: 
        day_type: 
        working_hours: 
        region: (Default value = None)
        policy_reduction: (Default value = None)

    """
    if region is None:
        regional_compliance = 1
    else:
        if self.spec in region.closed_venues:
            return 0
        regional_compliance = region.regional_compliance

    original_poisson_parameter = self.poisson_parameters[day_type][sex][age]
    if policy_reduction is None:
        return original_poisson_parameter
    poisson_parameter = original_poisson_parameter * (
        1 + regional_compliance * (policy_reduction - 1)
    )
    return poisson_parameter

get_possible_venues_for_area(area)

Given an area, searches for the social venues inside self.maximum_distance. It then returns self.neighbours_to_consider of them randomly, or nearest_venues_to_visit of them sorting by distance ascending. If there are no social venues inside the maximum distance, it returns the closest one.

Parameters:

Name Type Description Default
area Area
required
Source code in june/groups/leisure/social_venue_distributor.py
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def get_possible_venues_for_area(self, area: Area):
    """Given an area, searches for the social venues inside
    ``self.maximum_distance``. It then returns ``self.neighbours_to_consider``
    of them randomly, or ``nearest_venues_to_visit`` of them sorting by distance ascending.
    If there are no social venues inside the maximum distance, it returns the closest one.

    Args:
        area (Area): 

    """
    area_location = area.coordinates
    potential_venues = self.social_venues.get_venues_in_radius(
        area_location, self.maximum_distance
    )
    if potential_venues is None:
        closest_venue = self.social_venues.get_closest_venues(area_location, k=1)
        if closest_venue is None:
            return
        return (closest_venue[0],)
    if self.nearest_venues_to_visit > 0:
        indices_len = min(len(potential_venues), self.nearest_venues_to_visit)
        return tuple([potential_venues[idx] for idx in range(indices_len)])
    else:
        indices_len = min(len(potential_venues), self.neighbours_to_consider)
        random_idx_choice = sample(range(len(potential_venues)), indices_len)
        return tuple([potential_venues[idx] for idx in random_idx_choice])

person_drags_household()

Check whether person drags household or not.

Source code in june/groups/leisure/social_venue_distributor.py
309
310
311
def person_drags_household(self):
    """Check whether person drags household or not."""
    return rnd.random() < self.drags_household_probability

person_invites_friends()

Check whether person invites friends or not.

Source code in june/groups/leisure/social_venue_distributor.py
313
314
315
def person_invites_friends(self):
    """Check whether person invites friends or not."""
    return rnd.random() < self.invites_friends_probability

probability_to_go_to_social_venue(person, delta_time, day_type, working_hours)

Probabilty of a person going to one social venue according to their age and sex and the distribution of visitors in the venue.

Parameters:

Name Type Description Default
person

an instance of Person

required
delta_time
required
day_type

weekday or weekend

required
working_hours
required
Source code in june/groups/leisure/social_venue_distributor.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
def probability_to_go_to_social_venue(
    self, person, delta_time, day_type, working_hours
):
    """Probabilty of a person going to one social venue according to their
    age and sex and the distribution of visitors in the venue.

    Args:
        person: an instance of Person
        delta_time: 
        day_type: weekday or weekend
        working_hours: 

    """
    poisson_parameter = self.get_poisson_parameter(
        sex=person.sex,
        age=person.age,
        day_type=day_type,
        working_hours=working_hours,
    )
    return 1 - np.exp(-poisson_parameter * delta_time)

random_choice_numba(arr, prob)

Fast implementation of np.random.choice

Parameters:

Name Type Description Default
arr
required
prob
required
Source code in june/groups/leisure/social_venue_distributor.py
17
18
19
20
21
22
23
24
25
26
@jit(nopython=True)
def random_choice_numba(arr, prob):
    """Fast implementation of np.random.choice

    Args:
        arr: 
        prob: 

    """
    return arr[np.searchsorted(np.cumsum(prob), rnd(), side="right")]