def lcm_simulate(cfg, choosers, buildings, join_tbls, out_fname, supply_fname, vacant_fname, enable_supply_correction=None): """ Simulate the location choices for the specified choosers Parameters ---------- cfg : string The name of the yaml config file from which to read the location choice model choosers : DataFrameWrapper A dataframe of agents doing the choosing buildings : DataFrameWrapper A dataframe of buildings which the choosers are locating in and which have a supply join_tbls : list of strings A list of land use dataframes to give neighborhood info around the buildings - will be joined to the buildings using existing broadcasts. out_fname : string The column name to write the simulated location to supply_fname : string The string in the buildings table that indicates the amount of available units there are for choosers, vacant or not vacant_fname : string The string in the buildings table that indicates the amount of vacant units there will be for choosers enable_supply_correction : Python dict Should contain keys "price_col" and "submarket_col" which are set to the column names in buildings which contain the column for prices and an identifier which segments buildings into submarkets """ cfg = misc.config(cfg) choosers_df = utils.to_frame(choosers, [], cfg, additional_columns=[out_fname]) additional_columns = [supply_fname, vacant_fname] if enable_supply_correction is not None and \ "submarket_col" in enable_supply_correction: additional_columns += [enable_supply_correction["submarket_col"]] if enable_supply_correction is not None and \ "price_col" in enable_supply_correction: additional_columns += [enable_supply_correction["price_col"]] locations_df = utils.to_frame(buildings, join_tbls, cfg, additional_columns=additional_columns) available_units = buildings[supply_fname] vacant_units = buildings[vacant_fname] print("There are %d total available units" % available_units.sum()) print(" and %d total choosers" % len(choosers)) print(" but there are %d overfull buildings" % len(vacant_units[vacant_units < 0])) vacant_units = vacant_units[vacant_units > 0] # sometimes there are vacant units for buildings that are not in the # locations_df, which happens for reasons explained in the warning below indexes = np.repeat(vacant_units.index.values, vacant_units.values.astype('int')) isin = pd.Series(indexes).isin(locations_df.index) missing = len(isin[isin == False]) indexes = indexes[isin.values] units = locations_df.loc[indexes].reset_index() utils.check_nas(units) print(" for a total of %d temporarily empty units" % vacant_units.sum()) print(" in %d buildings total in the region" % len(vacant_units)) if missing > 0: print("WARNING: %d indexes aren't found in the locations df -" % missing) print(" this is usually because of a few records that don't join ") print(" correctly between the locations df and the aggregations tables") movers = choosers_df[choosers_df[out_fname] == -1] print("There are %d total movers for this LCM" % len(movers)) if enable_supply_correction is not None: assert isinstance(enable_supply_correction, dict) assert "price_col" in enable_supply_correction price_col = enable_supply_correction["price_col"] assert "submarket_col" in enable_supply_correction submarket_col = enable_supply_correction["submarket_col"] lcm = utils.yaml_to_class(cfg).from_yaml(str_or_buffer=cfg) if enable_supply_correction.get("warm_start", False) is True: raise NotImplementedError() multiplier_func = enable_supply_correction.get("multiplier_func", None) if multiplier_func is not None: multiplier_func = sim.get_injectable(multiplier_func) kwargs = enable_supply_correction.get('kwargs', {}) new_prices, submarkets_ratios = supply_and_demand( lcm, movers, units, submarket_col, price_col, base_multiplier=None, multiplier_func=multiplier_func, **kwargs) # we will only get back new prices for those alternatives # that pass the filter - might need to specify the table in # order to get the complete index of possible submarkets submarket_table = enable_supply_correction.get("submarket_table", None) if submarket_table is not None: submarkets_ratios = submarkets_ratios.reindex( sim.get_table(submarket_table).index).fillna(1) # write final shifters to the submarket_table for use in debugging sim.get_table(submarket_table)["price_shifters"] = submarkets_ratios print("Running supply and demand") print("Simulated Prices") print(buildings[price_col].describe()) print("Submarket Price Shifters") print(submarkets_ratios.describe()) # we want new prices on the buildings, not on the units, so apply # shifters directly to buildings and ignore unit prices sim.add_column(buildings.name, price_col+"_hedonic", buildings[price_col]) new_prices = buildings[price_col] * \ submarkets_ratios.loc[buildings[submarket_col]].values buildings.update_col_from_series(price_col, new_prices) print("Adjusted Prices") print(buildings[price_col].describe()) #if len(movers) > vacant_units.sum(): # print "WARNING: Not enough locations for movers" # print " reducing locations to size of movers for performance gain" # movers = movers.head(vacant_units.sum()) new_units, _ = utils.yaml_to_class(cfg).predict_from_cfg(movers, units, cfg) # new_units returns nans when there aren't enough units, # get rid of them and they'll stay as -1s new_units = new_units.dropna() # go from units back to buildings new_buildings = pd.Series(units.loc[new_units.values][out_fname].values, index=new_units.index) choosers.update_col_from_series(out_fname, new_buildings) utils._print_number_unplaced(choosers, out_fname) if enable_supply_correction is not None: new_prices = buildings[price_col] if "clip_final_price_low" in enable_supply_correction: new_prices = new_prices.clip(lower=enable_supply_correction[ "clip_final_price_low"]) if "clip_final_price_high" in enable_supply_correction: new_prices = new_prices.clip(upper=enable_supply_correction[ "clip_final_price_high"]) buildings.update_col_from_series(price_col, new_prices) vacant_units = buildings[vacant_fname] print(" and there are now %d empty units" % vacant_units.sum()) print(" and %d overfull buildings" % len(vacant_units[vacant_units < 0]))
def run_developer(forms, agents, buildings, buildings_all, supply_fname, parcel_size, ave_unit_size, total_units, feasibility, year=None, target_vacancy=.1, form_to_btype_callback=None, add_more_columns_callback=None, max_parcel_size=34647265, residential=True, bldg_sqft_per_job=400.0, min_unit_size=400, remove_developed_buildings=True, unplace_agents=['households', 'jobs']): """ Run the developer model to pick and build buildings Parameters ---------- forms : string or list of strings Passed directly dev.pick agents : DataFrame Wrapper Used to compute the current demand for units/floorspace in the area buildings : DataFrame Wrapper Used to compute the current supply of units/floorspace in the area buildings_all: Buildings for the entire region, used to write back to buildings table supply_fname : string Identifies the column in buildings which indicates the supply of units/floorspace parcel_size : Series Passed directly to dev.pick ave_unit_size : Series Passed directly to dev.pick - average residential unit size total_units : Series Passed directly to dev.pick - total current residential_units / job_spaces feasibility : DataFrame Wrapper The output from feasibility above (the table called 'feasibility') year : int The year of the simulation - will be assigned to 'year_built' on the new buildings target_vacancy : float The target vacancy rate - used to determine how much to build form_to_btype_callback : function Will be used to convert the 'forms' in the pro forma to 'building_type_id' in the larger model add_more_columns_callback : function Takes a dataframe and returns a dataframe - is used to make custom modifications to the new buildings that get added max_parcel_size : float Passed directly to dev.pick - max parcel size to consider min_unit_size : float Passed directly to dev.pick - min unit size that is valid residential : boolean Passed directly to dev.pick - switches between adding/computing residential_units and job_spaces bldg_sqft_per_job : float Passed directly to dev.pick - specified the multiplier between floor spaces and job spaces for this form (does not vary by parcel as ave_unit_size does) remove_redeveloped_buildings : optional, boolean (default True) Remove all buildings on the parcels which are being developed on unplace_agents : optional : list of strings (default ['households', 'jobs']) For all tables in the list, will look for field building_id and set it to -1 for buildings which are removed - only executed if remove_developed_buildings is true Returns ------- Writes the result back to the buildings table and returns the new buildings with available debugging information on each new building """ dev = developer.Developer(feasibility.to_frame()) #dev = WFRCDeveloper.WFRCDeveloper(feasibility.to_frame()) target_units = dev.\ compute_units_to_build(len(agents), buildings[supply_fname].sum(), target_vacancy) print("{:,} feasible buildings before running developer".format( len(dev.feasibility))) new_buildings = dev.pick(forms, target_units, parcel_size, ave_unit_size, total_units, max_parcel_size=max_parcel_size, min_unit_size=min_unit_size, drop_after_build=True, residential=residential, bldg_sqft_per_job=bldg_sqft_per_job) sim.add_table("feasibility", dev.feasibility) year = sim.get_injectable('year') if new_buildings is None: return if len(new_buildings) == 0: return new_buildings if not isinstance(forms, list): # form gets set only if forms is a list new_buildings["form"] = forms if form_to_btype_callback is not None: new_buildings["building_type_id"] = new_buildings.\ apply(form_to_btype_callback, axis=1) new_buildings["stories"] = new_buildings.stories.apply(np.ceil) new_buildings["note"] = "simulated" ret_buildings = new_buildings if add_more_columns_callback is not None: new_buildings = add_more_columns_callback(new_buildings) if year is not None: new_buildings["year_built"] = year print("Adding {:,} buildings with {:,} {}". format(len(new_buildings), int(new_buildings[supply_fname].sum()), supply_fname)) print("{:,} feasible buildings after running developer".format( len(dev.feasibility))) old_buildings = buildings.to_frame(buildings.local_columns) old_buildings_all = buildings_all.to_frame(buildings.local_columns) new_buildings = new_buildings[buildings.local_columns] if remove_developed_buildings: redev_buildings = old_buildings.parcel_id.isin(new_buildings.parcel_id) redev_buildings_all = old_buildings_all.parcel_id.isin(new_buildings.parcel_id) l = len(old_buildings) drop_buildings = old_buildings[redev_buildings] drop_buildings_all = old_buildings_all[redev_buildings_all] old_buildings = old_buildings[np.logical_not(redev_buildings)] old_buildings_all = old_buildings_all[np.logical_not(redev_buildings_all)] l2 = len(old_buildings) print("before dropped l:" + str(l)) print("after dropped l2: " + str(l2)) #print redev_buildings #print drop_buildings if l2-l > 0: print("Dropped {} buildings because they were redeveloped". format(l2 - l)) for tbl in unplace_agents: agents = sim.get_table(tbl) agents = agents.to_frame(agents.local_columns) #displaced_agents = agents.building_id.isin(drop_buildings.index) displaced_agents = agents.building_id.isin(drop_buildings_all.index) print("Unplaced {} before: {}".format(tbl, len(agents.query( "building_id == -1")))) agents.building_id[displaced_agents] = -1 print("Unplaced {} after: {}".format(tbl, len(agents.query( "building_id == -1")))) sim.add_table(tbl, agents) all_buildings = dev.merge(old_buildings_all, new_buildings) sim.add_table("buildings", all_buildings) return ret_buildings
def get_run_no(): if 'run_no' not in sim.list_injectables(): sim.add_injectable("run_no", misc.get_run_number()) return sim.get_injectable("run_no")
def get_run_filename(): if 'run_no' not in sim.list_injectables(): get_run_no() return os.path.join(misc.runs_dir(), "run%d.h5" % sim.get_injectable("run_no"))
import datasources, models, variables, utils import orca_wfrc.orca as sim import pandas as pd import numpy as np import os remm_years = sim.get_injectable('settings')['remm'] #sim.run(["travel_time_reset"]) sim.run( [ "clear_cache", "utility_restriction", # run utility restriction for UT County "travel_time_import", "neighborhood_vars", # neighborhood variables "households_transition", # households transition "jobs_transition", # jobs transition "feasibility", # compute feasibility "residential_developer_slc", # build buildings Salt Lake County "residential_developer_davis", # build buildings Davis County "residential_developer_weber", # build buildings Weber County "residential_developer_utah", # build buildings Utah County "office_developer_slc", "office_developer_utah", "office_developer_davis", "office_developer_weber", "retail_developer_slc", "retail_developer_utah", "retail_developer_davis", "retail_developer_weber", "industrial_developer_slc",