@@ -153,7 +153,7 @@ def to_yaml(self, str_or_buffer=None):
153153 logger .debug ('serializing Developer model to YAML' )
154154 return utils .convert_to_yaml (self .to_dict , str_or_buffer )
155155
156- def pick (self , profit_to_prob_func = None ):
156+ def pick (self , profit_to_prob_func = None , custom_selection_func = None ):
157157 """
158158 Choose the buildings from the list that are feasible to build in
159159 order to match the specified demand.
@@ -166,6 +166,11 @@ def pick(self, profit_to_prob_func=None):
166166 a function which takes the feasibility dataframe and returns
167167 a series of probabilities. If no function is passed, the behavior
168168 of this method will not change
169+ custom_selection_func: func
170+ User passed function that decides how to select buildings for
171+ development after probabilities are calculated. Must have
172+ parameters (self, df, p) and return a numpy array of buildings to
173+ build (i.e. df.index.values)
169174
170175 Returns
171176 -------
@@ -174,26 +179,28 @@ def pick(self, profit_to_prob_func=None):
174179 DataFrame of buildings to add. These buildings are rows from the
175180 DataFrame that is returned from feasibility.
176181 """
182+ df = self .feasibility
183+ empty_warn = "WARNING THERE ARE NO FEASIBLE BUILDINGS TO CHOOSE FROM"
177184
178- if len (self . feasibility ) == 0 :
179- # no feasible buildings, might as well bail
185+ if len (df ) == 0 or df . empty :
186+ print ( empty_warn )
180187 return
181188
182189 # Get DataFrame of potential buildings from SqFtProForma steps
183190 df = self ._get_dataframe_of_buildings ()
184191 df = self ._remove_infeasible_buildings (df )
185192 df = self ._calculate_net_units (df )
186193
187- if len (df ) == 0 :
188- print ("WARNING THERE ARE NO FEASIBLE BUILDING TO CHOOSE FROM" )
194+ if len (df ) == 0 or df . empty :
195+ print (empty_warn )
189196 return
190197
191198 print ("Sum of net units that are profitable: {:,}" .format (
192199 int (df .net_units .sum ())))
193200
194201 # Generate development probabilities and pick buildings to build
195202 p , df = self ._calculate_probabilities (df , profit_to_prob_func )
196- build_idx = self ._buildings_to_build (df , p )
203+ build_idx = self ._select_buildings (df , p , custom_selection_func )
197204
198205 # Drop built buildings from self.feasibility attribute if desired
199206 self ._drop_built_buildings (build_idx )
@@ -295,6 +302,8 @@ def _remove_infeasible_buildings(self, df):
295302 -------
296303 df : DataFrame
297304 """
305+ if len (df ) == 0 or df .empty :
306+ return df
298307
299308 df = df [df .max_profit_far > 0 ]
300309 self .ave_unit_size [
@@ -326,6 +335,8 @@ def _calculate_net_units(self, df):
326335 -------
327336 df : DataFrame
328337 """
338+ if len (df ) == 0 or df .empty :
339+ return df
329340
330341 if self .residential :
331342 df ['net_units' ] = df .residential_units - df .current_units
@@ -362,7 +373,7 @@ def _calculate_probabilities(df, profit_to_prob_func):
362373 p = df .max_profit_per_size .values / df .max_profit_per_size .sum ()
363374 return p , df
364375
365- def _buildings_to_build (self , df , p ):
376+ def _select_buildings (self , df , p , custom_selection_func ):
366377 """
367378 Helper method to pick(). Selects buildings to build based on
368379 development probabilities.
@@ -373,6 +384,11 @@ def _buildings_to_build(self, df, p):
373384 DataFrame of buildings from _calculate_probabilities method
374385 p : Series
375386 Probabilities from _calculate_probabilities method
387+ custom_selection_func: func
388+ User passed function that decides how to select buildings for
389+ development after probabilities are calculated. Must have
390+ parameters (self, df, p) and return a numpy array of buildings to
391+ build (i.e. df.index.values)
376392
377393 Returns
378394 -------
@@ -381,7 +397,9 @@ def _buildings_to_build(self, df, p):
381397
382398 """
383399
384- if df .net_units .sum () < self .target_units :
400+ if custom_selection_func is not None :
401+ build_idx = custom_selection_func (self , df , p )
402+ elif df .net_units .sum () < self .target_units :
385403 print ("WARNING THERE WERE NOT ENOUGH PROFITABLE UNITS TO" ,
386404 "MATCH DEMAND" )
387405 build_idx = df .index .values
@@ -442,7 +460,11 @@ def _prepare_new_buildings(self, df, build_idx):
442460 """
443461
444462 new_df = df .loc [build_idx ]
445- new_df .index .name = "parcel_id"
463+
464+ drop = True
465+ if 'parcel_id' not in df .columns :
466+ new_df .index .name = "parcel_id"
467+ drop = False
446468
447469 if self .year is not None :
448470 new_df ["year_built" ] = self .year
@@ -453,4 +475,4 @@ def _prepare_new_buildings(self, df, build_idx):
453475
454476 new_df ["stories" ] = new_df .stories .apply (np .ceil )
455477
456- return new_df .reset_index ()
478+ return new_df .reset_index (drop = drop )
0 commit comments