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

Flexible ids for merge #3

Merged
merged 15 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions activitysim/abm/models/util/canonical_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pandas as pd
import re

from activitysim.core.util import reindex
from activitysim.core import config
from activitysim.core import pipeline
from activitysim.core import simulate
Expand Down Expand Up @@ -218,6 +219,167 @@ def determine_flavors_from_alts_file(
return flavors


def read_alts_file(file_name, set_index=None):
try:
alts = simulate.read_model_alts(file_name, set_index=set_index)
except RuntimeError:
logger.warning(f"Could not find file {file_name} to determine tour flavors.")
return pd.DataFrame()
return alts


def read_spec_file(file_name, set_index=None):
try:
alts = simulate.read_model_alts(file_name, set_index=set_index)
except RuntimeError:
logger.warning(f"Could not find file {file_name} to determine tour flavors.")
return pd.DataFrame()
return alts


def parse_tour_flavor_from_columns(columns, tour_flavor):
"""
determines the max number from columns if column name contains tour flavor
example: columns={'work1', 'work2'} -> 2

Parameters
----------
columns : list of str
tour_flavor : str
string subset that you want to find in columns

Returns
-------
int
max int found in columns with tour_flavor
"""
# below produces a list of numbers present in each column containing the tour flavor string
tour_numbers = [(re.findall(r"\d+", col)) for col in columns if tour_flavor in col]

# flatten list
tour_numbers = [int(item) for sublist in tour_numbers for item in sublist]

# find max
try:
max_tour_flavor = max(tour_numbers)
return max_tour_flavor
except ValueError:
# could not find a maximum integer for this flavor in the columns
return -1


def determine_mandatory_tour_flavors(mtf_settings, model_spec, default_flavors):
provided_flavors = mtf_settings.get("MANDATORY_TOUR_FLAVORS", None)

mandatory_tour_flavors = {
# hard code work and school tours
"work": parse_tour_flavor_from_columns(model_spec.columns, "work"),
"school": parse_tour_flavor_from_columns(model_spec.columns, "school"),
}

valid_flavors = (mandatory_tour_flavors["work"] >= 1) & (
mandatory_tour_flavors["school"] >= 1
)

if provided_flavors is not None:
if mandatory_tour_flavors != provided_flavors:
logger.warning(
"Specified tour flavors do not match alternative file flavors"
)
logger.warning(
f"{provided_flavors} does not equal {mandatory_tour_flavors}"
)
# use provided flavors if provided
return provided_flavors

if not valid_flavors:
# if flavors could not be parsed correctly and no flavors provided, return the default
logger.warning(
"Could not determine alts from alt file and no flavors were provided."
)
logger.warning(f"Using defaults: {default_flavors}")
return default_flavors

return mandatory_tour_flavors


def determine_non_mandatory_tour_max_extension(
model_settings, extension_probs, default_max_extension=2
):
provided_max_extension = model_settings.get("MAX_EXTENSION", None)

max_extension = parse_tour_flavor_from_columns(extension_probs.columns, "tour")

if provided_max_extension is not None:
if provided_max_extension != max_extension:
logger.warning(
"Specified non mandatory tour extension does not match extension probabilities file"
)
return provided_max_extension

if (max_extension >= 0) & isinstance(max_extension, int):
return max_extension

return default_max_extension


def determine_flavors_from_alts_file(
alts, provided_flavors, default_flavors, max_extension=0
):
"""
determines the max number from alts for each column containing numbers
example: alts={'index': ['alt1', 'alt2'], 'escort': [1, 2], 'othdisc': [3, 4]}
yelds -> {'escort': 2, 'othdisc': 4}

will return provided flavors if available
else, return default flavors if alts can't be groked

Parameters
----------
alts : pd.DataFrame
provided_flavors : dict
tour flavors provided by user in the model yaml
default_flavors : dict
default tour flavors to fall back on
max_extension : int
scale to increase number of tours accross all alternatives

Returns
-------
dict
tour flavors
"""
try:
flavors = {
c: int(alts[c].max() + max_extension)
for c in alts.columns
if all(alts[c].astype(str).str.isnumeric())
}
valid_flavors = all(
[(isinstance(flavor, str) & (num >= 0)) for flavor, num in flavors.items()]
) & (len(flavors) > 0)
except (ValueError, AttributeError):
valid_flavors = False

if provided_flavors is not None:
if flavors != provided_flavors:
logger.warning(
f"Specified tour flavors {provided_flavors} do not match alternative file flavors {flavors}"
)
# use provided flavors if provided
return provided_flavors

if not valid_flavors:
# if flavors could not be parsed correctly and no flavors provided, return the default
logger.warning(
"Could not determine alts from alt file and no flavors were provided."
)
logger.warning(f"Using defaults: {default_flavors}")
return default_flavors

return flavors


def canonical_tours():
"""
create labels for every the possible tour by combining tour_type/tour_num.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#,,,alt file for building tours even though simulation is simple_simulate not interaction_simulate
alt,eat,business,maint
no_subtours,0,0,0
eat,1,0,0
business1,0,1,0
maint,0,0,1
business2,0,2,0
eat_business,1,1,0
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#,,,,,alt file for building joint tours
alt,shopping,othmaint,eatout,social,othdiscr
0_tours,0,0,0,0,0
1_Shop,1,0,0,0,0
1_Main,0,1,0,0,0
1_Eat,0,0,1,0,0
1_Visit,0,0,0,1,0
1_Disc,0,0,0,0,1
2_SS,2,0,0,0,0
2_SM,1,1,0,0,0
2_SE,1,0,1,0,0
2_SV,1,0,0,1,0
2_SD,1,0,0,0,1
2_MM,0,2,0,0,0
2_ME,0,1,1,0,0
2_MV,0,1,0,1,0
2_MD,0,1,0,0,1
2_EE,0,0,2,0,0
2_EV,0,0,1,1,0
2_ED,0,0,1,0,1
2_VV,0,0,0,2,0
2_VD,0,0,0,1,1
2_DD,0,0,0,0,2
Loading