(self, form, target_units, parcel_size, ave_unit_size,
             current_units, max_parcel_size=200000, min_unit_size=400,
             drop_after_build=True, residential=True, bldg_sqft_per_job=400.0,
             profit_to_prob_func=None)
return new_df.reset_index()
Choose the buildings from the list that are feasible to build in
        order to match the specified demand.

        Parameters
        ----------
        form : string or list
            One or more of the building forms from the pro forma specification -
            e.g. "residential" or "mixedresidential" - these are configuration
            parameters passed previously to the pro forma.  If more than one form
            is passed the forms compete with each other (based on profitability)
            for which one gets built in order to meet demand.
        target_units : int
            The number of units to build.  For non-residential buildings this
            should be passed as the number of job spaces that need to be created.
        parcel_size : series
            The size of the parcels.  This was passed to feasibility as well,
            but should be passed here as well.  Index should be parcel_ids.
        ave_unit_size : series
            The average residential unit size around each parcel - this is
            indexed by parcel, but is usually a disaggregated version of a
            zonal or accessibility aggregation.
        bldg_sqft_per_job : float (default 400.0)
            The average square feet per job for this building form.
        min_unit_size : float
            Values less than this number in ave_unit_size will be set to this
            number.  Deals with cases where units are currently not built.
        current_units : series
            The current number of units on the parcel.  Is used to compute the
            net number of units produced by the developer model.  Many times
            the developer model is redeveloping units (demolishing them) and
            is trying to meet a total number of net units produced.
        max_parcel_size : float
            Parcels larger than this size will not be considered for
            development - usually large parcels should be specified manually
            in a development projects table.
        drop_after_build : bool
            Whether or not to drop parcels from consideration after they
            have been chosen for development.  Usually this is true so as
            to not develop the same parcel twice.
        residential: bool
            If creating non-residential buildings set this to false and
            developer will fill in job_spaces rather than residential_units
        profit_to_prob_func: function
            As there are so many ways to turn the development feasibility
            into a probability to select it for building, the user may pass
            a function which takes the feasibility dataframe and returns
            a series of probabilities.  If no function is passed, the behavior
            of this method will not change

        Returns
        -------
        None if thar are no feasible buildings
        new_buildings : dataframe
            DataFrame of buildings to add.  These buildings are rows from the
            DataFrame that is returned from feasibility.
Choose the buildings from the list that are feasible to build in
        order to match the specified demand.
Choose the buildings from the list that are feasible to build in order to match the specified demand .
def pick(self, form, target_units, parcel_size, ave_unit_size,
             current_units, max_parcel_size=200000, min_unit_size=400,
             drop_after_build=True, residential=True, bldg_sqft_per_job=400.0,
             profit_to_prob_func=None):
        """
        Choose the buildings from the list that are feasible to build in
        order to match the specified demand.

        Parameters
        ----------
        form : string or list
            One or more of the building forms from the pro forma specification -
            e.g. "residential" or "mixedresidential" - these are configuration
            parameters passed previously to the pro forma.  If more than one form
            is passed the forms compete with each other (based on profitability)
            for which one gets built in order to meet demand.
        target_units : int
            The number of units to build.  For non-residential buildings this
            should be passed as the number of job spaces that need to be created.
        parcel_size : series
            The size of the parcels.  This was passed to feasibility as well,
            but should be passed here as well.  Index should be parcel_ids.
        ave_unit_size : series
            The average residential unit size around each parcel - this is
            indexed by parcel, but is usually a disaggregated version of a
            zonal or accessibility aggregation.
        bldg_sqft_per_job : float (default 400.0)
            The average square feet per job for this building form.
        min_unit_size : float
            Values less than this number in ave_unit_size will be set to this
            number.  Deals with cases where units are currently not built.
        current_units : series
            The current number of units on the parcel.  Is used to compute the
            net number of units produced by the developer model.  Many times
            the developer model is redeveloping units (demolishing them) and
            is trying to meet a total number of net units produced.
        max_parcel_size : float
            Parcels larger than this size will not be considered for
            development - usually large parcels should be specified manually
            in a development projects table.
        drop_after_build : bool
            Whether or not to drop parcels from consideration after they
            have been chosen for development.  Usually this is true so as
            to not develop the same parcel twice.
        residential: bool
            If creating non-residential buildings set this to false and
            developer will fill in job_spaces rather than residential_units
        profit_to_prob_func: function
            As there are so many ways to turn the development feasibility
            into a probability to select it for building, the user may pass
            a function which takes the feasibility dataframe and returns
            a series of probabilities.  If no function is passed, the behavior
            of this method will not change

        Returns
        -------
        None if thar are no feasible buildings
        new_buildings : dataframe
            DataFrame of buildings to add.  These buildings are rows from the
            DataFrame that is returned from feasibility.
        """

        if len(self.feasibility) == 0:
            # no feasible buildings, might as well bail
            return

        if form is None:
            df = self.feasibility
        elif isinstance(form, list):
            df = self.keep_form_with_max_profit(form)
        else:
            df = self.feasibility[form]

        # feasible buildings only for this building type
        df = df[df.max_profit_far > 0]
        ave_unit_size[ave_unit_size < min_unit_size] = min_unit_size
        df["ave_unit_size"] = ave_unit_size
        df["parcel_size"] = parcel_size
        df['current_units'] = current_units
        df = df[df.parcel_size < max_parcel_size]

        df['residential_units'] = (df.residential_sqft / df.ave_unit_size).round()
        df['job_spaces'] = (df.non_residential_sqft / bldg_sqft_per_job).round()

        if residential:
            df['net_units'] = df.residential_units - df.current_units
        else:
            df['net_units'] = df.job_spaces - df.current_units
        df = df[df.net_units > 0]

        if len(df) == 0:
            print("WARNING THERE ARE NO FEASIBLE BUILDING TO CHOOSE FROM")
            return

        # print "Describe of net units\n", df.net_units.describe()
        print("Sum of net units that are profitable: {:,}"
              .format(int(df.net_units.sum())))

        if profit_to_prob_func:
            p = profit_to_prob_func(df)
        else:
            df['max_profit_per_size'] = df.max_profit / df.parcel_size
            p = df.max_profit_per_size.values / df.max_profit_per_size.sum()

        if df.net_units.sum() < target_units:
            print("WARNING THERE WERE NOT ENOUGH PROFITABLE UNITS TO",
                  "MATCH DEMAND")
            build_idx = df.index.values
        elif target_units <= 0:
            build_idx = []
        else:
            # we don't know how many developments we will need, as they differ in net_units.
            # If all developments have net_units of 1 than we need target_units of them.
            # So we choose the smaller of available developments and target_units.
            choices = np.random.choice(df.index.values, size=min(len(df.index), target_units),
                                       replace=False, p=p)
            tot_units = df.net_units.loc[choices].values.cumsum()
            ind = int(np.searchsorted(tot_units, target_units, side="left")) + 1
            build_idx = choices[:ind]

        if drop_after_build:
            self.feasibility = self.feasibility.drop(build_idx)

        new_df = df.loc[build_idx]
        new_df.index.name = "parcel_id"
        return new_df.reset_index()
def pick ( self , form , target_units , parcel_size , ave_unit_size , current_units , max_parcel_size = 200000 , min_unit_size = 400 , drop_after_build = True , residential = True , bldg_sqft_per_job = 400.0 , profit_to_prob_func = None ) : if len ( self . feasibility ) == 0 : # no feasible buildings, might as well bail return if form is None : df = self . feasibility elif isinstance ( form , list ) : df = self . keep_form_with_max_profit ( form ) else : df = self . feasibility [ form ] # feasible buildings only for this building type df = df [ df . max_profit_far > 0 ] ave_unit_size [ ave_unit_size < min_unit_size ] = min_unit_size df [ "ave_unit_size" ] = ave_unit_size df [ "parcel_size" ] = parcel_size df [ 'current_units' ] = current_units df = df [ df . parcel_size < max_parcel_size ] df [ 'residential_units' ] = ( df . residential_sqft / df . ave_unit_size ) . round ( ) df [ 'job_spaces' ] = ( df . non_residential_sqft / bldg_sqft_per_job ) . round ( ) if residential : df [ 'net_units' ] = df . residential_units - df . current_units else : df [ 'net_units' ] = df . job_spaces - df . current_units df = df [ df . net_units > 0 ] if len ( df ) == 0 : print ( "WARNING THERE ARE NO FEASIBLE BUILDING TO CHOOSE FROM" ) return # print "Describe of net units\n", df.net_units.describe() print ( "Sum of net units that are profitable: {:,}" . format ( int ( df . net_units . sum ( ) ) ) ) if profit_to_prob_func : p = profit_to_prob_func ( df ) else : df [ 'max_profit_per_size' ] = df . max_profit / df . parcel_size p = df . max_profit_per_size . values / df . max_profit_per_size . sum ( ) if df . net_units . sum ( ) < target_units : print ( "WARNING THERE WERE NOT ENOUGH PROFITABLE UNITS TO" , "MATCH DEMAND" ) build_idx = df . index . values elif target_units <= 0 : build_idx = [ ] else : # we don't know how many developments we will need, as they differ in net_units. # If all developments have net_units of 1 than we need target_units of them. # So we choose the smaller of available developments and target_units. choices = np . random . choice ( df . index . values , size = min ( len ( df . index ) , target_units ) , replace = False , p = p ) tot_units = df . net_units . loc [ choices ] . values . cumsum ( ) ind = int ( np . searchsorted ( tot_units , target_units , side = "left" ) ) + 1 build_idx = choices [ : ind ] if drop_after_build : self . feasibility = self . feasibility . drop ( build_idx ) new_df = df . loc [ build_idx ] new_df . index . name = "parcel_id" return new_df . reset_index ( )