Source code for prereise.gather.demanddata.nrel_efs.aggregate_demand
import pandas as pd
from prereise.gather.const import abv2state
[docs]def combine_efs_demand(efs_dem=None, non_efs_dem=None, save=None):
"""Aggregate the sectoral demand data so that a single demand point is observed for
each state and timestamp. This function can either access local copies of the
sectoral demand data or can access the demand data from online.
:param dict efs_dem: A dict of pandas.DataFrame objects that contain sectoral demand
data for each state and time step. This input is intended to be the output of
:py:func:`partition_demand_by_sector`, which is associated with NREL's EFS.
Defaults to None.
:param list non_efs_dem: A list of pandas.DataFrame objects that contain sectoral
demand data for each state and time step. This input is intended to be the
output of :py:func:`access_non_efs_demand`, which is not associated with NREL's
EFS. Defaults to None.
:param str save: Saves a .csv if a string representing a valid file path and file
name is provided. Defaults to None, indicating that a .csv file should not be
saved.
:return: (*pandas.DataFrame*) -- Aggregate demand data for all sectors (both EFS and
non-EFS).
:raises TypeError: if efs_dem is not input as a dict, if non_efs_dem is not input as
a list, if the components of efs_dem and non_efs_dem are not pandas.DataFrames,
or if save is not input as a string.
:raises ValueError: if both efs_dem and non_efs_dem are entered as None or if the
components of efs_dem and non_efs_dem do not have the proper timestamps or the
correct number of states.
"""
# Check that both of the inputs are not input as None
if all(x is None for x in [efs_dem, non_efs_dem]):
raise ValueError(
"No sectoral demand was provided, so there is nothing to aggregate."
)
# Check that the inputs are of an appropriate type
if not isinstance(efs_dem, (dict, type(None))):
raise TypeError("All EFS sectoral demand data must be input as a dict.")
if not isinstance(non_efs_dem, (list, type(None))):
raise TypeError("All non-EFS sectoral demand data must be input as a list.")
# Check that the components of efs_dem and non_efs_dem are DataFrames
if isinstance(efs_dem, dict):
if not all(isinstance(efs_dem[i], pd.DataFrame) for i in efs_dem):
raise TypeError(
"EFS sectoral demand data must be input as a pandas.DataFrame."
)
if isinstance(non_efs_dem, list):
if not all(isinstance(x, pd.DataFrame) for x in non_efs_dem):
raise TypeError(
"Non-EFS sectoral demand data must be input as a pandas.DataFrame."
)
# Initialize agg_dem
agg_dem = pd.DataFrame(
0,
index=pd.date_range("2016-01-01", "2017-01-01", freq="H", inclusive="left"),
columns=sorted(set(abv2state) - {"AK", "HI"}),
)
agg_dem.index.name = "Local Time"
# Aggregate the EFS sectoral demand
if efs_dem is not None:
for i in efs_dem:
# Check the DataFrame dimensions and headers
if not efs_dem[i].index.equals(
pd.date_range("2016-01-01", "2017-01-01", freq="H", inclusive="left")
):
raise ValueError("This data does not have the proper timestamps.")
if set(efs_dem[i].columns) != set(abv2state) - {"AK", "HI"}:
raise ValueError("This data does not include all 48 states.")
# Add the sectoral demand to the aggregate demand
agg_dem += efs_dem[i]
# Aggregate the non-EFS sectoral demand
if non_efs_dem is not None:
for x in non_efs_dem:
# Check the DataFrame dimensions and headers
if not x.index.equals(
pd.date_range("2016-01-01", "2017-01-01", freq="H", inclusive="left")
):
raise ValueError("This data does not have the proper timestamps.")
if set(x.columns) != set(abv2state) - {"AK", "HI"}:
raise ValueError("This data does not include all 48 states.")
# Add the sectoral demand to the aggregate demand
agg_dem += x
# Save the aggregated demand data, if desired
if save is not None:
if not isinstance(save, str):
raise TypeError("The file path and file name must be input as a str.")
else:
agg_dem.to_csv(save)
# Return the aggregate demand for all sectors
return agg_dem
[docs]def access_non_efs_demand(dem_paths):
"""Access any of the sectoral demand that the user intends to use that is external
to NREL's EFS studies. This function also ensures that each data set is formatted
appropriately, so as to allow easy aggregation with the NREL EFS sectoral demand.
:param iterable dem_paths: The paths that point to the .csv files of sectoral demand
data that is not associated with NREL's EFS. Ordering within the iterable does
not need to match that in local_sects.
:return: (*list*) -- A list of pandas.DataFrame objects that contain sectoral demand
data for each state and time step. This sectoral demand is not a part of NREL's
EFS.
:raises TypeError: if dem_paths are not input as an iterable or if the components of
dem_paths are not input as strings.
:raises ValueError: if the data located in each path in dem_path does not have the
proper timestamps or the correct number of states.
"""
# Check that dem_paths is of an appropriate type
if not isinstance(dem_paths, (set, list)):
raise TypeError("File paths of sectoral data must be input as a set or list.")
# Check that the components of dem_paths are str
if not all(isinstance(x, str) for x in dem_paths):
raise TypeError("Individual file paths must be input as a str.")
# Obtain the sectoral demand data
sect_dem = []
for i in dem_paths:
# Try loading the locally-stored sectoral demand
try:
temp_dem = pd.read_csv(i, parse_dates=True, index_col="Local Time")
except ValueError:
raise ValueError("This data does not provide the timestamps correctly.")
# Check the DataFrame dimensions and headers
if not temp_dem.index.equals(
pd.date_range("2016-01-01", "2017-01-01", freq="H", inclusive="left")
):
raise ValueError("This data does not have the proper timestamps.")
if set(temp_dem.columns) != set(abv2state) - {"AK", "HI"}:
raise ValueError("This data does not include all 48 states.")
# Store the setoral demand data
sect_dem.append(temp_dem)
# Return the list of non-EFS sectoral demand
return sect_dem