Source code for simulator.reports.DtnAbstractReport
import abc
import pandas as pd
from pathlib import Path
[docs]class DtnAbstractReport(object, metaclass=abc.ABCMeta):
""" Alias name for this report. When saved, this alias will be
used as the name of the report.
"""
_alias = None
def __init__(self, env):
# Store the simulation environment
self.env = env
# File to store the data
dir = env.config['globals'].outdir
file = env.config['globals'].outfile
self.file = dir/file
# Other variables
self.writer = None # Set it to export to .xlsx
self.store = None # Set it to export to .h5
# Cache with the collected data
self.cache = None
def __get__(self, item):
# If you are not calling ``collect_data``, just do the
# normal thing
if item != 'collect_data': return getattr(self, item)
# If you are calling ``collect_data``, do it and set
# the cached value automatically
self.cache = self.collect_data()
# Return the cached value
return self.cache
@property
def alias(self):
alias = self.__class__._alias
if not alias: raise ValueError(str(self) + 'does not have an alias')
return alias
@property
def data(self):
# If the cached value is available, use it
if self.cache: return self.cache
# Collect the data and return it
return self.collect_data()
[docs] @abc.abstractmethod
def collect_data(self):
""" Collect the data that needs to be exported by this report
:return: pd.DataFrame: Data frame to be exported
"""
pass
def export_to(self, extension):
# Get format
format = extension.replace('.', '')
# Collect data into data frame
df = self.collect_data()
# Trigger the right exporter
exec(f'self.export_to_{format}(df)', locals(), globals())
def __repr__(self):
return self.__str__()
def __str__(self):
return f'<{self.__class__.__name__}>'
[docs]def concat_dfs(data, new_col_names):
""" Consolidate a dictionary of data frames into one data frame
:param dict data: {a: df for a, b: df for b}
:param str/tuple new_col_names: The name for the column [a, b, ...] or columns
:return: Data frame with numeric index
"""
# If data is empty, return empty data frame
if not data: return pd.DataFrame()
# If new_col_names is not iterable, convert it
if not isinstance(new_col_names, (list, tuple)):
new_col_names = [new_col_names]
# Get data frames to concatenate
to_concat = {k: df for k, df in data.items() if not df.empty}
# If no data to concatenate, return empty
if len(to_concat) == 0: return pd.DataFrame()
# Concatenate the data frames
df = pd.concat(to_concat).reset_index(level=0)
# If the data frame is empty, return
if df.empty: return pd.DataFrame()
# Create the mapper to rename the columns
mapper = {f'level_{i}': col_name for i, col_name in enumerate(new_col_names)}
# Rename the new column
df.rename(mapper, axis=1, inplace=True)
return df