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

Buyout Model Implementation #553

Merged
merged 4 commits into from
Jun 5, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Gas Facility Damage Analysis [#568](https://github.com/IN-CORE/pyincore/issues/568)
- Copyrights to transportation recovery analysis [#579](https://github.com/IN-CORE/pyincore/issues/579)
- Buyout Model Analyses [#539](https://github.com/IN-CORE/pyincore/issues/539)

### Fixed
- Permission error in clearing cache process [#563](https://github.com/IN-CORE/pyincore/issues/563)
Expand Down
5 changes: 5 additions & 0 deletions docs/source/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ analyses/buildingportfolio
.. autoclass:: pyincore.analyses.buildingportfolio.recovery.BuildingData
:members:

analyses/buyoutdecision
=======================
.. autoclass:: buyoutdecision.buyoutdecision.BuyoutDecision
:members:

analyses/capitalshocks
======================

Expand Down
7 changes: 7 additions & 0 deletions pyincore/analyses/buyoutdecision/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2024 University of Illinois and others. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/

from pyincore.analyses.buyoutdecision.buyoutdecision import BuyoutDecision
177 changes: 177 additions & 0 deletions pyincore/analyses/buyoutdecision/buyoutdecision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Copyright (c) 2024 University of Illinois and others. All rights reserved.

# This program and the accompanying materials are made available under the
Vismayak marked this conversation as resolved.
Show resolved Hide resolved
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/

import pandas as pd
from pyincore import BaseAnalysis
from pyincore.utils.dataprocessutil import DataProcessUtil


class BuyoutDecision(BaseAnalysis):
"""A framework to select households for buyout based on past and future flood damaged.

Args:
incore_client(IncoreClient): Service authentication.

"""

def __init__(self, incore_client):
super(BuyoutDecision, self).__init__(incore_client)

def run(self):
# Get input parameters
fema_buyout_cap = self.get_parameter('fema_buyout_cap')
residential_archetypes = self.get_parameter('residential_archetypes')

# Get input datasets
past_building_damage = self.get_input_dataset('past_building_damage').get_dataframe_from_csv(low_memory=False)
future_building_damage = self.get_input_dataset('future_building_damage').get_dataframe_from_csv(
low_memory=False)

building_inventory = self.get_input_dataset('buildings').get_dataframe_from_shapefile()

hua = (self.get_input_dataset('housing_unit_allocation').get_dataframe_from_csv(low_memory=False))
pop_dislocation = self.get_input_dataset('population_dislocation').get_dataframe_from_csv(low_memory=False)

buyout_decision_df = self.buyout_decision(past_building_damage, future_building_damage, building_inventory, hua,
pop_dislocation, fema_buyout_cap, residential_archetypes)
# Create the result dataset
self.set_result_csv_data("result", buyout_decision_df, self.get_parameter("result_name") + "_loss",
"dataframe")

def buyout_decision(self, past_building_damage, future_building_damage, building_inventory, hua, pop_dislocation,
fema_buyout_cap, residential_archetpyes):
"""Select households for buyout based on past and future flood damaged.

Args:
past_building_damage (DataFrame): Past building damage.
future_building_damage (DataFrame): Future event building damage.
building_inventory (DataFrame): Building inventory.
hua (DataFrame): Housing unit allocation.
pop_dislocation (DataFrame): Population dislocation from past hazard event.
fema_buyout_cap (float): FEMA buyout cap.
residential_archetpyes (list): Residential archetypes.

Returns:
buyout_decision_df (DataFrame): A dataframe with buyout decision for each household.
"""

past_building_max_damage = DataProcessUtil.get_max_damage_state(past_building_damage)
future_building_max_damage = DataProcessUtil.get_max_damage_state(future_building_damage)

# Criterion 1: Filter only residential buildings with damage state DS3 from past building damage
buyout_inventory = pd.merge(building_inventory, past_building_max_damage, on='guid', how='outer')
buyout_inventory = buyout_inventory[buyout_inventory['arch_wind'].isin(residential_archetpyes)
longshuicy marked this conversation as resolved.
Show resolved Hide resolved
& (buyout_inventory['max_state'] == 'DS_3')]
buyout_inventory.rename(columns={'max_state': 'max_state_past_damage'}, inplace=True)

# Criterion 2: Filter only residential buildings with damage state DS3 from predicted future building damage
buyout_inventory = pd.merge(buyout_inventory, future_building_max_damage, on='guid', how='inner')
buyout_inventory = buyout_inventory[buyout_inventory['max_state'] == 'DS_3']
buyout_inventory.rename(columns={'max_state': 'max_state_future_damage'}, inplace=True)

# Criterion 3: Fall within the FEMA buyout cap
buyout_inventory = buyout_inventory[buyout_inventory['appr_bldg'] <= fema_buyout_cap]
buyout_inventory = buyout_inventory[
["guid", "appr_bldg", "max_state_future_damage", "max_state_past_damage", "geometry"]]

# Criterion 4: Use HUA to filter out buildings with 0 occupants
buyout_inventory = pd.merge(buyout_inventory, hua, on='guid', how='left')
buyout_inventory = buyout_inventory[(buyout_inventory['numprec'] != 0) & (~buyout_inventory['numprec'].isna())]

# Removing any rows with NAN values in column "Race"
buyout_inventory = buyout_inventory.dropna(subset=['race'])

# Merging with population dislocation
buyout_inventory = pd.merge(buyout_inventory, pop_dislocation[['huid', 'dislocated']], on='huid', how='left')

# Create a new column showing the appraisal value of each building ('appr_bldg' divided by the number of times
# a guid is repeated)
# For the instances that a structure has more than one housing units.
buyout_inventory['count'] = buyout_inventory.groupby('guid')['guid'].transform('count')
buyout_inventory['housing_unit_appraisal_value'] = buyout_inventory['appr_bldg'] / buyout_inventory['count']

# Cleaning the dataframe
buyout_inventory.drop(['blockid', 'bgid', 'tractid', 'FIPScounty',
'gqtype', 'BLOCKID10_str', 'placeNAME10', 'geometry_y'], axis=1, inplace=True)
buyout_inventory.rename(columns={'appr_bldg': 'building_appraisal_value', 'ownershp': 'ownership',
'dislocated_combined_dmg': 'dislocated', 'count': 'number_of_housing_units',
'geometry_x': 'geometry'},
inplace=True)
buyout_inventory = buyout_inventory[
['guid', 'huid', 'building_appraisal_value', 'housing_unit_appraisal_value', 'geometry',
'number_of_housing_units', 'numprec', 'ownership', 'race', 'hispan', 'family', 'vacancy', 'incomegroup',
'hhinc', 'randincome', 'poverty', 'huestimate', 'dislocated', 'max_state_future_damage',
'max_state_past_damage', 'x', 'y', ]]

return buyout_inventory

def get_spec(self):
return {
"name": "buyout-decision",
"description": "Buyout decision framework",
"input_parameters": [
{
'id': 'fema_buyout_cap',
'required': True,
'description': 'FEMA buyout cap',
'type': float,
},
{
'id': 'residential_archetypes',
'required': True,
'description': 'Residential archetypes',
'type': list,
},
{
'id': 'result_name',
'required': True,
'description': 'Result name',
'type': str,
}
],
"input_datasets": [
{
'id': 'past_building_damage',
'required': True,
'description': 'Building Damage Results',
'type': ['ergo:buildingDamageVer6'],
},
{
'id': 'future_building_damage',
'required': True,
'description': 'Building Damage Results',
'type': ['ergo:buildingDamageVer6'],
},
{
'id': 'buildings',
'required': True,
'description': 'Building Inventory',
'type': ['ergo:buildingInventoryVer4', 'ergo:buildingInventoryVer5',
'ergo:buildingInventoryVer6', 'ergo:buildingInventoryVer7'],
},
{
'id': 'housing_unit_allocation',
'required': True,
'description': 'A csv file with the merged dataset of the inputs, aka Probabilistic'
'House Unit Allocation',
'type': ['incore:housingUnitAllocation']
},
{
'id': 'population_dislocation',
'required': True,
'description': 'Population Dislocation from past hazard event',
'type': ['incore:popDislocation']
}
],
"output_datasets": [
{
'id': 'result',
'label': 'Buyout Decision Results',
'description': 'Buyout Decision Results',
'type': ['incore:buyoutDecision']
}
]
}
46 changes: 46 additions & 0 deletions tests/pyincore/analyses/buyoutdecision/test_buyoutdecision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This program and the accompanying materials are made available under the
# terms of the Mozilla Public License v2.0 which accompanies this distribution,
# and is available at https://www.mozilla.org/en-US/MPL/2.0/


from pyincore.analyses.buyoutdecision import BuyoutDecision
from pyincore import IncoreClient, Dataset
import pyincore.globals as pyglobals
import pandas as pd


def BuyoutDecisionTest():
client = IncoreClient(pyglobals.INCORE_API_DEV_URL)

past_building_damage_id = "6632d2605da5fd22b268511f"
future_building_damage_id = "6632d45b5da5fd22b2685136"
past_pop_dislocation_id = "6632d5205da5fd22b26878bb"

hua_id = "64227016b18d026e7c80d2bc"

buildings_id = "63ff69a96d3b2a308baaca12"

fema_buyout_cap = 321291.600
residential_archetypes = [1, 2, 3, 4, 5, 17]

buyout_decision = BuyoutDecision(client)
buyout_decision.set_parameter("fema_buyout_cap", fema_buyout_cap)
buyout_decision.set_parameter("residential_archetypes", residential_archetypes)
buyout_decision.set_parameter("result_name", "galveston_buyout")

buyout_decision.load_remote_input_dataset("buildings", buildings_id)
buyout_decision.load_remote_input_dataset("housing_unit_allocation", hua_id)
buyout_decision.load_remote_input_dataset("past_building_damage", past_building_damage_id)
buyout_decision.load_remote_input_dataset("future_building_damage", future_building_damage_id)
buyout_decision.load_remote_input_dataset("population_dislocation", past_pop_dislocation_id)

buyout_decision.run_analysis()

result = buyout_decision.get_output_dataset("result")
result_df = result.get_dataframe_from_csv()
print(result_df.head())
print(len(result_df))


if __name__ == "__main__":
BuyoutDecisionTest()
Loading