Пример #1
0
def initialize_new_units(buildings, residential_units):
    """
    This data maintenance step initializes units for buildings that have been
    newly created, conforming to the data requirements of the
    'residential_units' table.

    Data expectations
    -----------------
    - 'buildings' table has the following columns:
        - index that serves as its identifier
        - 'residential_units' (int, count of units in building)
    - 'residential_units' table has the following columns:
        - index named 'unit_id' that serves as its identifier
        - 'building_id' corresponding to the index of the 'buildings' table

    Results
    -------
    - extends the 'residential_units' table, following the same schema as the
      'initialize_residential_units' model step
    """

    # Verify initial data characteristics
    '''
    ot.assert_orca_spec(OrcaSpec(
        '',
        TableSpec(
            'buildings',
            ColumnSpec('building_id', primary_key=True),
            ColumnSpec('residential_units', min=0)),
        TableSpec(
            'residential_units',
            ColumnSpec('unit_id', primary_key=True),
            ColumnSpec('building_id', foreign_key='buildings.building_id'))))
    '''

    old_units = residential_units.to_frame(residential_units.local_columns)
    bldgs = buildings.to_frame(['residential_units', 'deed_restricted_units', 'building_id'])

    # Filter for residential buildings not currently represented in
    # the units table
    new_bldgs = bldgs[~bldgs.building_id.isin(old_units.building_id)]
    new_bldgs = new_bldgs[new_bldgs.residential_units > 0]
    
    if len(new_bldgs) == 0:
        return

    # Create new units, merge them, and update the table
    new_units = _create_empty_units(new_bldgs)
    all_units = dev.merge(old_units, new_units)
    all_units.index.name = 'unit_id'

    print "Creating %d residential units for %d new buildings" % \
        (len(new_units), len(new_bldgs))

    orca.add_table('residential_units', all_units)

    # Verify final data characteristics
    '''
def initialize_new_units(buildings, residential_units):
    """
    This data maintenance step initializes units for buildings that have been
    newly created, conforming to the data requirements of the
    'residential_units' table.

    Data expectations
    -----------------
    - 'buildings' table has the following columns:
        - index that serves as its identifier
        - 'residential_units' (int, count of units in building)
    - 'residential_units' table has the following columns:
        - index named 'unit_id' that serves as its identifier
        - 'building_id' corresponding to the index of the 'buildings' table

    Results
    -------
    - extends the 'residential_units' table, following the same schema as the
      'initialize_residential_units' model step
    """

    # Verify initial data characteristics
    '''
    ot.assert_orca_spec(OrcaSpec(
        '',
        TableSpec(
            'buildings',
            ColumnSpec('building_id', primary_key=True),
            ColumnSpec('residential_units', min=0)),
        TableSpec(
            'residential_units',
            ColumnSpec('unit_id', primary_key=True),
            ColumnSpec('building_id', foreign_key='buildings.building_id'))))
    '''

    old_units = residential_units.to_frame(residential_units.local_columns)
    bldgs = buildings.to_frame(['residential_units', 'deed_restricted_units'])

    # Filter for residential buildings not currently represented in
    # the units table
    new_bldgs = bldgs[~bldgs.index.isin(old_units.building_id)]
    new_bldgs = new_bldgs[new_bldgs.residential_units > 0]

    # Create new units, merge them, and update the table
    new_units = _create_empty_units(new_bldgs)
    all_units = dev.merge(old_units, new_units)
    all_units.index.name = 'unit_id'

    print "Creating %d residential units for %d new buildings" % \
        (len(new_units), len(new_bldgs))

    orca.add_table('residential_units', all_units)

    # Verify final data characteristics
    '''
Пример #3
0
def add_buildings(buildings, new_buildings, remove_developed_buildings=True):

    old_buildings = buildings.to_frame(buildings.local_columns)
    new_buildings = new_buildings[buildings.local_columns]

    if remove_developed_buildings:
        unplace_agents = ["households", "jobs"]
        old_buildings = \
            _remove_developed_buildings(old_buildings, new_buildings,
                                        unplace_agents)

    all_buildings = dev.merge(old_buildings, new_buildings)

    orca.add_table("buildings", all_buildings)
Пример #4
0
def add_buildings(buildings, new_buildings,
                  remove_developed_buildings=True):

    old_buildings = buildings.to_frame(buildings.local_columns)
    new_buildings = new_buildings[buildings.local_columns]

    if remove_developed_buildings:
        unplace_agents = ["households", "jobs"]
        old_buildings = \
            _remove_developed_buildings(old_buildings, new_buildings,
                                        unplace_agents)

    all_buildings = dev.merge(old_buildings, new_buildings)

    orca.add_table("buildings", all_buildings)
Пример #5
0
def scheduled_development_events(buildings, scheduled_development_events):
    year = get_year()
    sched_dev = scheduled_development_events.to_frame()
    sched_dev = sched_dev[sched_dev.year_built == year]
    sched_dev[
        'residential_sqft'] = sched_dev.sqft_per_unit * sched_dev.residential_units
    sched_dev['job_spaces'] = sched_dev.non_residential_sqft / 400
    if len(sched_dev) > 0:
        max_bid = buildings.index.values.max()
        idx = np.arange(max_bid + 1, max_bid + len(sched_dev) + 1)
        sched_dev['building_id'] = idx
        sched_dev = sched_dev.set_index('building_id')
        from urbansim.developer.developer import Developer
        merge = Developer(pd.DataFrame({})).merge
        b = buildings.to_frame(buildings.local_columns)
        all_buildings = merge(b, sched_dev[b.columns])
        orca.add_table("buildings", all_buildings)
Пример #6
0
def developer_reprocess(buildings, year, years_per_iter, jobs,
                        parcels, summary, parcel_is_allowed_func):
    # this takes new units that come out of the developer, both subsidized
    # and non-subsidized and reprocesses them as required - please read
    # comments to see what this means in detail

    # 20% of base year buildings which are "residential" have job spaces - I
    # mean, there is a ratio of job spaces to res units in residential
    # buildings of 1 to 5 - this ratio should be kept for future year
    # buildings
    s = buildings.general_type == "Residential"
    res_units = buildings.residential_units[s].sum()
    job_spaces = buildings.job_spaces[s].sum()

    to_add = res_units * .05 - job_spaces
    if to_add > 0:
        print "Adding %d job_spaces" % to_add
        res_units = buildings.residential_units[s]
        # bias selection of places to put job spaces based on res units
        print res_units.describe()
        print res_units[res_units < 0]
        add_indexes = np.random.choice(res_units.index.values, size=to_add,
                                       replace=True,
                                       p=(res_units/res_units.sum()))
        # collect same indexes
        add_indexes = pd.Series(add_indexes).value_counts()
        # this is sqft per job for residential bldgs
        add_sizes = add_indexes * 400
        print "Job spaces in res before adjustment: ", \
            buildings.job_spaces[s].sum()
        buildings.local.loc[add_sizes.index,
                            "non_residential_sqft"] += add_sizes.values
        print "Job spaces in res after adjustment: ",\
            buildings.job_spaces[s].sum()

    # the second step here is to add retail to buildings that are greater than
    # X stories tall - presumably this is a ground floor retail policy
    old_buildings = buildings.to_frame(buildings.local_columns)
    new_buildings = old_buildings.query(
       '%d == year_built and stories >= 4' % year)

    print "Attempting to add ground floor retail to %d devs" % \
        len(new_buildings)
    retail = parcel_is_allowed_func("retail")
    new_buildings = new_buildings[retail.loc[new_buildings.parcel_id].values]
    print "Disallowing dev on these parcels:"
    print "    %d devs left after retail disallowed" % len(new_buildings)

    # this is the key point - make these new buildings' nonres sqft equal
    # to one story of the new buildings
    new_buildings.non_residential_sqft = new_buildings.building_sqft / \
        new_buildings.stories * .8

    new_buildings["residential_units"] = 0
    new_buildings["residential_sqft"] = 0
    new_buildings["building_sqft"] = new_buildings.non_residential_sqft
    new_buildings["stories"] = 1
    new_buildings["building_type"] = "RB"

    # this is a fairly arbitrary rule, but we're only adding ground floor
    # retail in areas that are underserved right now - this is defined as
    # the location where the retail ratio (ratio of income to retail sqft)
    # is greater than the median
    ratio = parcels.retail_ratio.loc[new_buildings.parcel_id]
    new_buildings = new_buildings[ratio.values > ratio.median()]

    print "Adding %d sqft of ground floor retail in %d locations" % \
        (new_buildings.non_residential_sqft.sum(), len(new_buildings))

    all_buildings = dev.merge(old_buildings, new_buildings)
    orca.add_table("buildings", all_buildings)

    new_buildings["form"] = "retail"
    # this is sqft per job for retail use - this is all rather
    # ad-hoc so I'm hard-coding
    new_buildings["job_spaces"] = \
        (new_buildings.non_residential_sqft / 445.0).astype('int')
    new_buildings["net_units"] = new_buildings.job_spaces
    summary.add_parcel_output(new_buildings)

    # got to get the frame again because we just added rows
    buildings = orca.get_table('buildings')
    buildings_df = buildings.to_frame(
        ['year_built', 'building_sqft', 'general_type'])
    sqft_by_gtype = buildings_df.query('year_built >= %d' % year).\
        groupby('general_type').building_sqft.sum()
    print "New square feet by general type in millions:\n",\
        sqft_by_gtype / 1000000.0
Пример #7
0
def office_developer(feasibility, jobs, buildings, parcels, year,
                     settings, summary, form_to_btype_func, scenario,
                     add_extra_columns_func, parcels_geography,
                     limits_settings):

    dev_settings = settings['non_residential_developer']

    # I'm going to try a new way of computing this because the math the other
    # way is simply too hard.  Basically we used to try and apportion sectors
    # into the demand for office, retail, and industrial, but there's just so
    # much dirtyness to the data, for instance 15% of jobs are in residential
    # buildings, and 15% in other buildings, it's just hard to know how much
    # to build, we I think the right thing to do is to compute the number of
    # job spaces that are required overall, and then to apportion that new dev
    # into the three non-res types with a single set of coefficients
    all_units = dev.compute_units_to_build(
        len(jobs),
        buildings.job_spaces.sum(),
        dev_settings['kwargs']['target_vacancy'])

    print "Total units to build = %d" % all_units
    if all_units <= 0:
        return

    for typ in ["Office"]:

        print "\nRunning for type: ", typ

        num_units = all_units * float(dev_settings['type_splits'][typ])

        targets = []
        # now apply limits - limits are assumed to be yearly, apply to an
        # entire jurisdiction and be in terms of residential_units or
        # job_spaces
        if year > 2015 and typ in limits_settings:

            juris_name = parcels_geography.juris_name.\
                reindex(parcels.index).fillna('Other')

            juris_list = limits_settings[typ].keys()
            for juris, limit in limits_settings[typ].items():

                # the actual target is the limit times the number of years run
                # so far in the simulation (plus this year), minus the amount
                # built in previous years - in other words, you get rollover
                # and development is lumpy

                current_total = parcels.total_job_spaces[
                    (juris_name == juris) & (parcels.newest_building > 2015)]\
                    .sum()

                target = (year - 2015 + 1) * limit - current_total

                if target <= 0:
                    print "Already met target for juris = %s" % juris
                    print "    target = %d, current_total = %d" %\
                        (target, current_total)
                    continue

                targets.append((juris_name == juris, target, juris))
                num_units -= target

            # other cities not in the targets get the remaining target
            targets.append((~juris_name.isin(juris_list), num_units, "none"))

        else:
            # otherwise use all parcels with total number of units
            targets.append((parcels.index == parcels.index, num_units, "none"))

        for parcel_mask, target, juris in targets:

            print "Running developer for %s with target of %d" % \
                (str(juris), target)
            print "Parcels in play:\n", pd.Series(parcel_mask).value_counts()

            # this was a fairly heinous bug - have to get the building wrapper
            # again because the buildings df gets modified by the run_developer
            # method below
            buildings = orca.get_table('buildings')

            new_buildings = utils.run_developer(
                typ.lower(),
                jobs,
                buildings,
                "job_spaces",
                parcels.parcel_size[parcel_mask],
                parcels.ave_sqft_per_unit[parcel_mask],
                parcels.total_job_spaces[parcel_mask],
                feasibility,
                year=year,
                form_to_btype_callback=form_to_btype_func,
                add_more_columns_callback=add_extra_columns_func,
                residential=False,
                num_units_to_build=target,
                profit_to_prob_func=subsidies.profit_to_prob_func,
                **dev_settings['kwargs'])

            if new_buildings is not None:
                new_buildings["subsidized"] = False

            summary.add_parcel_output(new_buildings)
Пример #8
0
def retail_developer(jobs, buildings, parcels, nodes, feasibility,
                     settings, summary, add_extra_columns_func, net):

    dev_settings = settings['non_residential_developer']
    all_units = dev.compute_units_to_build(
        len(jobs),
        buildings.job_spaces.sum(),
        dev_settings['kwargs']['target_vacancy'])

    target = all_units * float(dev_settings['type_splits']["Retail"])
    # target here is in sqft
    target *= settings["building_sqft_per_job"]["HS"]

    feasibility = feasibility.to_frame().loc[:, "retail"]
    feasibility = feasibility.dropna(subset=["max_profit"])

    feasibility["non_residential_sqft"] = \
        feasibility.non_residential_sqft.astype("int")

    feasibility["retail_ratio"] = parcels.retail_ratio
    feasibility = feasibility.reset_index()

    # create features
    f1 = feasibility.retail_ratio / feasibility.retail_ratio.max()
    f2 = feasibility.max_profit / feasibility.max_profit.max()

    # combine features in probability function - it's like combining expense
    # of building the building with the market in the neighborhood
    p = f1 * 1.5 + f2
    p = p.clip(lower=1.0/len(p)/10)

    print "Attempting to build {:,} retail sqft".format(target)

    # order by weighted random sample
    feasibility = feasibility.sample(frac=1.0, weights=p)

    bldgs = buildings.to_frame(buildings.local_columns + ["general_type"])

    devs = []

    for dev_id, d in feasibility.iterrows():

        if target <= 0:
            break

        # any special logic to filter these devs?

        # remove new dev sqft from target
        target -= d.non_residential_sqft

        # add redeveloped sqft to target
        filt = "general_type == 'Retail' and parcel_id == %d" % \
            d["parcel_id"]
        target += bldgs.query(filt).non_residential_sqft.sum()

        devs.append(d)

    if len(devs) == 0:
        return

    # record keeping - add extra columns to match building dataframe
    # add the buidings and demolish old buildings, and add to debug output
    devs = pd.DataFrame(devs, columns=feasibility.columns)

    print "Building {:,} retail sqft in {:,} projects".format(
        devs.non_residential_sqft.sum(), len(devs))
    if target > 0:
        print "   WARNING: retail target not met"

    devs["form"] = "retail"
    devs = add_extra_columns_func(devs)

    add_buildings(buildings, devs)

    summary.add_parcel_output(devs)
Пример #9
0
def residential_developer(feasibility, households, buildings, parcels, year,
                          settings, summary, form_to_btype_func,
                          add_extra_columns_func, parcels_geography,
                          limits_settings, final_year):

    kwargs = settings['residential_developer']

    target_vacancy = pd.read_csv("data/regional_controls.csv",
                                 index_col="year").loc[year].st_res_vac

    num_units = dev.compute_units_to_build(
        len(households),
        buildings["residential_units"].sum(),
        target_vacancy)

    targets = []
    typ = "Residential"
    # now apply limits - limits are assumed to be yearly, apply to an
    # entire jurisdiction and be in terms of residential_units or job_spaces
    if typ in limits_settings:

        juris_name = parcels_geography.juris_name.\
            reindex(parcels.index).fillna('Other')

        juris_list = limits_settings[typ].keys()
        for juris, limit in limits_settings[typ].items():

            # the actual target is the limit times the number of years run
            # so far in the simulation (plus this year), minus the amount
            # built in previous years - in other words, you get rollover
            # and development is lumpy

            current_total = parcels.total_residential_units[
                (juris_name == juris) & (parcels.newest_building >= 2010)]\
                .sum()

            target = (year - 2010 + 1) * limit - current_total
            # make sure we don't overshoot the total development of the limit
            # for the horizon year - for instance, in Half Moon Bay we have
            # a very low limit and a single development in a far out year can
            # easily build over the limit for the total simulation
            max_target = (final_year - 2010 + 1) * limit - current_total

            if target <= 0:
                    continue

            targets.append((juris_name == juris, target, max_target, juris))
            num_units -= target

        # other cities not in the targets get the remaining target
        targets.append((~juris_name.isin(juris_list), num_units, None, "none"))

    else:
        # otherwise use all parcels with total number of units
        targets.append((parcels.index == parcels.index,
                        num_units, None, "none"))

    for parcel_mask, target, final_target, juris in targets:

        print "Running developer for %s with target of %d" % \
            (str(juris), target)

        # this was a fairly heinous bug - have to get the building wrapper
        # again because the buildings df gets modified by the run_developer
        # method below
        buildings = orca.get_table('buildings')

        new_buildings = utils.run_developer(
            "residential",
            households,
            buildings,
            "residential_units",
            parcels.parcel_size[parcel_mask],
            parcels.ave_sqft_per_unit[parcel_mask],
            parcels.total_residential_units[parcel_mask],
            feasibility,
            year=year,
            form_to_btype_callback=form_to_btype_func,
            add_more_columns_callback=add_extra_columns_func,
            num_units_to_build=target,
            profit_to_prob_func=subsidies.profit_to_prob_func,
            **kwargs)

        buildings = orca.get_table('buildings')

        if new_buildings is not None:
            new_buildings["subsidized"] = False

        if final_target is not None and new_buildings is not None:
            # make sure we don't overbuild the target for the whole simulation
            overshoot = new_buildings.net_units.sum() - final_target

            if overshoot > 0:
                index = new_buildings.tail(1).index[0]
                index = int(index)
                # make sure we don't get into a negative unit situation
                overshoot = min(overshoot,
                                buildings.local.loc[index,
                                                    "residential_units"])
                buildings.local.loc[index, "residential_units"] -= overshoot

        summary.add_parcel_output(new_buildings)
Пример #10
0
def developer_reprocess(buildings, year, years_per_iter, jobs,
                        parcels, summary, parcel_is_allowed_func):
    # this takes new units that come out of the developer, both subsidized
    # and non-subsidized and reprocesses them as required - please read
    # comments to see what this means in detail

    # 20% of base year buildings which are "residential" have job spaces - I
    # mean, there is a ratio of job spaces to res units in residential
    # buildings of 1 to 5 - this ratio should be kept for future year
    # buildings
    s = buildings.general_type == "Residential"
    res_units = buildings.residential_units[s].sum()
    job_spaces = buildings.job_spaces[s].sum()

    to_add = res_units * .05 - job_spaces
    if to_add > 0:
        print "Adding %d job_spaces" % to_add
        res_units = buildings.residential_units[s]
        # bias selection of places to put job spaces based on res units
        print res_units.describe()
        print res_units[res_units < 0]
        add_indexes = np.random.choice(res_units.index.values, size=to_add,
                                       replace=True,
                                       p=(res_units / res_units.sum()))
        # collect same indexes
        add_indexes = pd.Series(add_indexes).value_counts()
        # this is sqft per job for residential bldgs
        add_sizes = add_indexes * 400
        print "Job spaces in res before adjustment: ", \
            buildings.job_spaces[s].sum()
        buildings.local.loc[add_sizes.index,
                            "non_residential_sqft"] += add_sizes.values
        print "Job spaces in res after adjustment: ",\
            buildings.job_spaces[s].sum()

    # the second step here is to add retail to buildings that are greater than
    # X stories tall - presumably this is a ground floor retail policy
    old_buildings = buildings.to_frame(buildings.local_columns)
    new_buildings = old_buildings.query(
       '%d == year_built and stories >= 4' % year)

    print "Attempting to add ground floor retail to %d devs" % \
        len(new_buildings)
    retail = parcel_is_allowed_func("retail")
    new_buildings = new_buildings[retail.loc[new_buildings.parcel_id].values]
    print "Disallowing dev on these parcels:"
    print "    %d devs left after retail disallowed" % len(new_buildings)

    # this is the key point - make these new buildings' nonres sqft equal
    # to one story of the new buildings
    new_buildings.non_residential_sqft = new_buildings.building_sqft / \
        new_buildings.stories * .8

    new_buildings["residential_units"] = 0
    new_buildings["residential_sqft"] = 0
    new_buildings["deed_restricted_units"] = 0
    new_buildings["building_sqft"] = new_buildings.non_residential_sqft
    new_buildings["stories"] = 1
    new_buildings["building_type"] = "RB"

    # this is a fairly arbitrary rule, but we're only adding ground floor
    # retail in areas that are underserved right now - this is defined as
    # the location where the retail ratio (ratio of income to retail sqft)
    # is greater than the median
    ratio = parcels.retail_ratio.loc[new_buildings.parcel_id]
    new_buildings = new_buildings[ratio.values > ratio.median()]

    print "Adding %d sqft of ground floor retail in %d locations" % \
        (new_buildings.non_residential_sqft.sum(), len(new_buildings))

    all_buildings = dev.merge(old_buildings, new_buildings)
    orca.add_table("buildings", all_buildings)

    new_buildings["form"] = "retail"
    # this is sqft per job for retail use - this is all rather
    # ad-hoc so I'm hard-coding
    new_buildings["job_spaces"] = \
        (new_buildings.non_residential_sqft / 445.0).astype('int')
    new_buildings["net_units"] = new_buildings.job_spaces
    summary.add_parcel_output(new_buildings)

    # got to get the frame again because we just added rows
    buildings = orca.get_table('buildings')
    buildings_df = buildings.to_frame(
        ['year_built', 'building_sqft', 'general_type'])
    sqft_by_gtype = buildings_df.query('year_built >= %d' % year).\
        groupby('general_type').building_sqft.sum()
    print "New square feet by general type in millions:\n",\
        sqft_by_gtype / 1000000.0
Пример #11
0
def office_developer(feasibility, jobs, buildings, parcels, year,
                     settings, summary, form_to_btype_func, scenario,
                     add_extra_columns_func, parcels_geography,
                     limits_settings):

    dev_settings = settings['non_residential_developer']

    # I'm going to try a new way of computing this because the math the other
    # way is simply too hard.  Basically we used to try and apportion sectors
    # into the demand for office, retail, and industrial, but there's just so
    # much dirtyness to the data, for instance 15% of jobs are in residential
    # buildings, and 15% in other buildings, it's just hard to know how much
    # to build, we I think the right thing to do is to compute the number of
    # job spaces that are required overall, and then to apportion that new dev
    # into the three non-res types with a single set of coefficients
    all_units = dev.compute_units_to_build(
        len(jobs),
        buildings.job_spaces.sum(),
        dev_settings['kwargs']['target_vacancy'])

    print "Total units to build = %d" % all_units
    if all_units <= 0:
        return

    for typ in ["Office"]:

        print "\nRunning for type: ", typ

        num_units = all_units * float(dev_settings['type_splits'][typ])

        targets = []
        # now apply limits - limits are assumed to be yearly, apply to an
        # entire jurisdiction and be in terms of residential_units or
        # job_spaces
        if year > 2015 and typ in limits_settings:

            juris_name = parcels_geography.juris_name.\
                reindex(parcels.index).fillna('Other')

            juris_list = limits_settings[typ].keys()
            for juris, limit in limits_settings[typ].items():

                # the actual target is the limit times the number of years run
                # so far in the simulation (plus this year), minus the amount
                # built in previous years - in other words, you get rollover
                # and development is lumpy

                current_total = parcels.total_job_spaces[
                    (juris_name == juris) & (parcels.newest_building > 2015)]\
                    .sum()

                target = (year - 2015 + 1) * limit - current_total

                if target <= 0:
                    print "Already met target for juris = %s" % juris
                    print "    target = %d, current_total = %d" %\
                        (target, current_total)
                    continue

                targets.append((juris_name == juris, target, juris))
                num_units -= target

            # other cities not in the targets get the remaining target
            targets.append((~juris_name.isin(juris_list), num_units, "none"))

        else:
            # otherwise use all parcels with total number of units
            targets.append((parcels.index == parcels.index, num_units, "none"))

        for parcel_mask, target, juris in targets:

            print "Running developer for %s with target of %d" % \
                (str(juris), target)
            print "Parcels in play:\n", pd.Series(parcel_mask).value_counts()

            # this was a fairly heinous bug - have to get the building wrapper
            # again because the buildings df gets modified by the run_developer
            # method below
            buildings = orca.get_table('buildings')

            new_buildings = utils.run_developer(
                typ.lower(),
                jobs,
                buildings,
                "job_spaces",
                parcels.parcel_size[parcel_mask],
                parcels.ave_sqft_per_unit[parcel_mask],
                parcels.total_job_spaces[parcel_mask],
                feasibility,
                year=year,
                form_to_btype_callback=form_to_btype_func,
                add_more_columns_callback=add_extra_columns_func,
                residential=False,
                num_units_to_build=target,
                profit_to_prob_func=subsidies.profit_to_prob_func,
                **dev_settings['kwargs'])

            if new_buildings is not None:
                new_buildings["subsidized"] = False

            summary.add_parcel_output(new_buildings)
Пример #12
0
def retail_developer(jobs, buildings, parcels, nodes, feasibility,
                     settings, summary, add_extra_columns_func, net):

    dev_settings = settings['non_residential_developer']
    all_units = dev.compute_units_to_build(
        len(jobs),
        buildings.job_spaces.sum(),
        dev_settings['kwargs']['target_vacancy'])

    target = all_units * float(dev_settings['type_splits']["Retail"])
    # target here is in sqft
    target *= settings["building_sqft_per_job"]["HS"]

    feasibility = feasibility.to_frame().loc[:, "retail"]
    feasibility = feasibility.dropna(subset=["max_profit"])

    feasibility["non_residential_sqft"] = \
        feasibility.non_residential_sqft.astype("int")

    feasibility["retail_ratio"] = parcels.retail_ratio
    feasibility = feasibility.reset_index()

    # create features
    f1 = feasibility.retail_ratio / feasibility.retail_ratio.max()
    f2 = feasibility.max_profit / feasibility.max_profit.max()

    # combine features in probability function - it's like combining expense
    # of building the building with the market in the neighborhood
    p = f1 * 1.5 + f2
    p = p.clip(lower=1.0 / len(p) / 10)

    print "Attempting to build {:,} retail sqft".format(target)

    # order by weighted random sample
    feasibility = feasibility.sample(frac=1.0, weights=p)

    foreign_columns = ["general_type"]
    bldgs = buildings.to_frame(buildings.local_columns + foreign_columns)

    devs = []

    for dev_id, d in feasibility.iterrows():

        if target <= 0:
            break

        # any special logic to filter these devs?

        # remove new dev sqft from target
        target -= d.non_residential_sqft

        # add redeveloped sqft to target
        filt = "general_type == 'Retail' and parcel_id == %d" % \
            d["parcel_id"]
        target += bldgs.query(filt).non_residential_sqft.sum()

        devs.append(d)

    if len(devs) == 0:
        return

    # record keeping - add extra columns to match building dataframe
    # add the buidings and demolish old buildings, and add to debug output
    devs = pd.DataFrame(devs, columns=buildings.local_columns)

    print "Building {:,} retail sqft in {:,} projects".format(
        devs.non_residential_sqft.sum(), len(devs))
    if target > 0:
        print "   WARNING: retail target not met"

    devs["form"] = "retail"
    devs = add_extra_columns_func(devs)

    add_buildings(buildings, devs)

    summary.add_parcel_output(devs)
Пример #13
0
def residential_developer(feasibility, households, buildings, parcels, year,
                          settings, summary, form_to_btype_func,
                          add_extra_columns_func, parcels_geography,
                          limits_settings, final_year,
                          regional_controls):

    kwargs = settings['residential_developer']
    rc = regional_controls.to_frame()
    target_vacancy = rc.loc[year].st_res_vac

    num_units = dev.compute_units_to_build(
        len(households),
        buildings["residential_units"].sum(),
        target_vacancy)

    targets = []
    typ = "Residential"
    # now apply limits - limits are assumed to be yearly, apply to an
    # entire jurisdiction and be in terms of residential_units or job_spaces
    if typ in limits_settings:

        juris_name = parcels_geography.juris_name.\
            reindex(parcels.index).fillna('Other')

        juris_list = limits_settings[typ].keys()
        for juris, limit in limits_settings[typ].items():

            # the actual target is the limit times the number of years run
            # so far in the simulation (plus this year), minus the amount
            # built in previous years - in other words, you get rollover
            # and development is lumpy

            current_total = parcels.total_residential_units[
                (juris_name == juris) & (parcels.newest_building >= 2010)]\
                .sum()

            target = (year - 2010 + 1) * limit - current_total
            # make sure we don't overshoot the total development of the limit
            # for the horizon year - for instance, in Half Moon Bay we have
            # a very low limit and a single development in a far out year can
            # easily build over the limit for the total simulation
            max_target = (final_year - 2010 + 1) * limit - current_total

            if target <= 0:
                    continue

            targets.append((juris_name == juris, target, max_target, juris))
            num_units -= target

        # other cities not in the targets get the remaining target
        targets.append((~juris_name.isin(juris_list), num_units, None, "none"))

    else:
        # otherwise use all parcels with total number of units
        targets.append((parcels.index == parcels.index,
                        num_units, None, "none"))

    for parcel_mask, target, final_target, juris in targets:

        print "Running developer for %s with target of %d" % \
            (str(juris), target)

        # this was a fairly heinous bug - have to get the building wrapper
        # again because the buildings df gets modified by the run_developer
        # method below
        buildings = orca.get_table('buildings')

        new_buildings = utils.run_developer(
            "residential",
            households,
            buildings,
            "residential_units",
            parcels.parcel_size[parcel_mask],
            parcels.ave_sqft_per_unit[parcel_mask],
            parcels.total_residential_units[parcel_mask],
            feasibility,
            year=year,
            form_to_btype_callback=form_to_btype_func,
            add_more_columns_callback=add_extra_columns_func,
            num_units_to_build=target,
            profit_to_prob_func=subsidies.profit_to_prob_func,
            **kwargs)

        buildings = orca.get_table('buildings')

        if new_buildings is not None:
            new_buildings["subsidized"] = False

        if final_target is not None and new_buildings is not None:
            # make sure we don't overbuild the target for the whole simulation
            overshoot = new_buildings.net_units.sum() - final_target

            if overshoot > 0:
                index = new_buildings.tail(1).index[0]
                index = int(index)
                # make sure we don't get into a negative unit situation
                current_units = buildings.local.loc[index, "residential_units"]
                # only can reduce by as many units as we have
                overshoot = min(overshoot, current_units)
                # used below - this is the pct we need to reduce the building
                overshoot_pct = \
                    (current_units - overshoot) / float(current_units)

                buildings.local.loc[index, "residential_units"] -= overshoot

                # we also need to fix the other columns so they make sense
                for col in ["residential_sqft", "building_sqft",
                            "deed_restricted_units"]:
                    val = buildings.local.loc[index, col]
                    # reduce by pct but round to int
                    buildings.local.loc[index, col] = int(val * overshoot_pct)

        summary.add_parcel_output(new_buildings)
Пример #14
0
def residential_developer(feasibility, households, buildings, parcels, year,
                          settings, summary, form_to_btype_func,
                          add_extra_columns_func, parcels_geography,
                          limits_settings, final_year,
                          regional_controls):

    kwargs = settings['residential_developer']
    rc = regional_controls.to_frame()
    target_vacancy = rc.loc[year].st_res_vac

    num_units = dev.compute_units_to_build(
        len(households),
        buildings["residential_units"].sum(),
        target_vacancy)

    targets = []
    typ = "Residential"
    # now apply limits - limits are assumed to be yearly, apply to an
    # entire jurisdiction and be in terms of residential_units or job_spaces
    if typ in limits_settings:

        juris_name = parcels_geography.juris_name.\
            reindex(parcels.index).fillna('Other')

        juris_list = limits_settings[typ].keys()
        for juris, limit in limits_settings[typ].items():

            # the actual target is the limit times the number of years run
            # so far in the simulation (plus this year), minus the amount
            # built in previous years - in other words, you get rollover
            # and development is lumpy

            current_total = parcels.total_residential_units[
                (juris_name == juris) & (parcels.newest_building >= 2010)]\
                .sum()

            target = (year - 2010 + 1) * limit - current_total
            # make sure we don't overshoot the total development of the limit
            # for the horizon year - for instance, in Half Moon Bay we have
            # a very low limit and a single development in a far out year can
            # easily build over the limit for the total simulation
            max_target = (final_year - 2010 + 1) * limit - current_total

            if target <= 0:
                continue

            targets.append((juris_name == juris, target, max_target, juris))
            num_units -= target

        # other cities not in the targets get the remaining target
        targets.append((~juris_name.isin(juris_list), num_units, None, "none"))

    else:
        # otherwise use all parcels with total number of units
        targets.append((parcels.index == parcels.index,
                        num_units, None, "none"))

    for parcel_mask, target, final_target, juris in targets:

        print("Running developer for %s with target of %d" %
              (str(juris), target))

        # this was a fairly heinous bug - have to get the building wrapper
        # again because the buildings df gets modified by the run_developer
        # method below
        buildings = orca.get_table('buildings')
        print('Stats of buildings before run_developer(): \n{}'.format(
             buildings.to_frame()[['deed_restricted_units','preserved_units','inclusionary_units']].sum()))
        new_buildings = utils.run_developer(
            "residential",
            households,
            buildings,
            "residential_units",
            parcels.parcel_size[parcel_mask],
            parcels.ave_sqft_per_unit[parcel_mask],
            parcels.total_residential_units[parcel_mask],
            feasibility,
            year=year,
            form_to_btype_callback=form_to_btype_func,
            add_more_columns_callback=add_extra_columns_func,
            num_units_to_build=int(target),
            profit_to_prob_func=subsidies.profit_to_prob_func,
            **kwargs)
        print('Stats of buildings before run_developer(): \n{}'.format(
             buildings.to_frame()[['deed_restricted_units','preserved_units','inclusionary_units']].sum()))

        buildings = orca.get_table('buildings')

        if new_buildings is not None:
            new_buildings["subsidized"] = False

        if final_target is not None and new_buildings is not None:
            # make sure we don't overbuild the target for the whole simulation
            overshoot = new_buildings.net_units.sum() - final_target

            if overshoot > 0:
                index = new_buildings.tail(1).index[0]
                index = int(index)
                # make sure we don't get into a negative unit situation
                current_units = buildings.local.loc[index, "residential_units"]
                # only can reduce by as many units as we have
                overshoot = min(overshoot, current_units)
                # used below - this is the pct we need to reduce the building
                overshoot_pct = \
                    (current_units - overshoot) / float(current_units)

                buildings.local.loc[index, "residential_units"] -= overshoot

                # we also need to fix the other columns so they make sense
                for col in ["residential_sqft", "building_sqft",
                            "deed_restricted_units", "inclusionary_units",
                            "subsidized_units"]:
                    val = buildings.local.loc[index, col]
                    # reduce by pct but round to int
                    buildings.local.loc[index, col] = int(val * overshoot_pct)
                # also fix the corresponding columns in new_buildings
                for col in ["residential_sqft","building_sqft",
                            "residential_units", "deed_restricted_units",
                            "inclusionary_units", "subsidized_units"]:
                    val = new_buildings.loc[index, col]
                    new_buildings.loc[index, col] = int(val * overshoot_pct)
                for col in ["policy_based_revenue_reduction",
                            "max_profit"]:
                    val = new_buildings.loc[index, col]
                    new_buildings.loc[index, col] = val * overshoot_pct

        summary.add_parcel_output(new_buildings)
Пример #15
0
def initialize_new_units(buildings, residential_units):
    """
    This data maintenance step initializes units for buildings that have been
    newly created, conforming to the data requirements of the
    'residential_units' table.

    Data expectations
    -----------------
    - 'buildings' table has the following columns:
        - index that serves as its identifier
        - 'residential_units' (int, count of units in building)
    - 'residential_units' table has the following columns:
        - index named 'unit_id' that serves as its identifier
        - 'building_id' corresponding to the index of the 'buildings' table

    Results
    -------
    - extends the 'residential_units' table, following the same schema as the
      'initialize_residential_units' model step
    """

    # Verify initial data characteristics
    '''
    ot.assert_orca_spec(OrcaSpec(
        '',
        TableSpec(
            'buildings',
            ColumnSpec('building_id', primary_key=True),
            ColumnSpec('residential_units', min=0)),
        TableSpec(
            'residential_units',
            ColumnSpec('unit_id', primary_key=True),
            ColumnSpec('building_id', foreign_key='buildings.building_id'))))
    '''

    old_units = residential_units.to_frame(residential_units.local_columns)
    bldgs = buildings.to_frame(['residential_units', 'deed_restricted_units'])

    # Filter for residential buildings not currently represented in
    # the units table, and create new units
    new_bldgs = bldgs[~bldgs.index.isin(old_units.building_id)]
    new_bldgs = new_bldgs[new_bldgs.residential_units > 0]
    new_units = _create_empty_units(new_bldgs)

    # Filter for residential buildings where ADUs were added and
    # create new units
    old_units_by_bldg = old_units.groupby(['building_id']).agg({
        'unit_num':
        max,
        'num_units':
        'count'
    }).reset_index()
    old_units_by_bldg.rename(columns={
        'num_units': 'num_units_old',
        'unit_num': 'max_num_old'
    },
                             inplace=True)
    old_bldgs = bldgs[bldgs.index.isin(old_units.building_id)]
    old_bldgs = old_bldgs[old_bldgs.residential_units > 0]

    adu_bldgs = pd.merge(old_bldgs,
                         old_units_by_bldg,
                         left_index=True,
                         right_on='building_id')
    adu_bldgs['adu_count'] = \
        adu_bldgs.residential_units - adu_bldgs.num_units_old
    adu_bldgs = adu_bldgs[adu_bldgs.adu_count > 0]

    if len(adu_bldgs) > 0:
        new_adus = pd.DataFrame({
            'unit_residential_price':
            0.0,
            'unit_residential_rent':
            0.0,
            'num_units':
            1,
            'building_id':
            np.repeat(adu_bldgs.building_id,
                      adu_bldgs.adu_count.values.astype(int)),
            # counter of the AUDs in a building
            'unit_num_adu':
            np.concatenate([
                np.arange(num_adus)
                for num_adus in adu_bldgs.adu_count.values.astype(int)
            ]),
            # ADUs are not deed restricted
            'deed_restricted':
            0.0,
            'adu_count_start':
            np.repeat(adu_bldgs.max_num_old + 1,
                      adu_bldgs.adu_count.values.astype(int))
        }).sort_values(by=['building_id', 'unit_num_adu']).reset_index(
            drop=True)

        # update unit_num of ADUs to continue with the previous
        # unit_num of non-ADUs in the same buildings
        new_adus.unit_num = \
            new_adus.unit_num_adu + new_adus.adu_count_start
        new_adus.drop(columns=['adu_count_start', 'unit_num_adu'],
                      inplace=True)
        new_adus.index.name = 'unit_id'

        new_units = dev.merge(new_units, new_adus)
    else:
        print('No ADUs were built.')

    # Merge new units with old units and update the table

    all_units = dev.merge(old_units, new_units)
    all_units.index.name = 'unit_id'

    print("Creating %d residential units for %d new buildings" %
          (len(new_units), len(new_bldgs)))

    orca.add_table('residential_units', all_units)

    # Verify final data characteristics
    '''