Skip to content

Commit

Permalink
Add CF uncertainty and many improvements (#356)
Browse files Browse the repository at this point in the history
* Add CF context menu, implement wizard and delegates

* Allow CFTable to handle modifying CFs and storing them correctly in methods

* Add TupleNameDialog for ease of editing a method name

* Implement IA method copy dialog, warn when given name exists

* Add wiring to MethodsTab to filter by copied method

* Simplify MethodsTable selectedItems method and related code

* Modify method to account for possible third item in tuple

* Add uncertainty interface classes to simplify data extraction

* Show the new value that will be set for amount in warning

* Remove lognormal_check property from wizard page

* Add additional code to show mean where distribution does not

* Alter label of 'loc' field to match distribution description

* Show the mean/amount value on the generated distplot

* Ensure cf table always reflects hide/show toggle

* Additional catches for invalid input to avoid error spam
  • Loading branch information
dgdekoning authored Jan 30, 2020
1 parent f1a3c93 commit 2dff150
Show file tree
Hide file tree
Showing 9 changed files with 512 additions and 153 deletions.
4 changes: 4 additions & 0 deletions activity_browser/app/bwutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
from .multilca import MLCA, Contributions
from .pedigree import PedigreeMatrix
from .presamples import PresamplesContributions, PresamplesMLCA
from .uncertainty import (
CFUncertaintyInterface, ExchangeUncertaintyInterface,
ParameterUncertaintyInterface, get_uncertainty_interface
)


def cleanup():
Expand Down
125 changes: 125 additions & 0 deletions activity_browser/app/bwutils/uncertainty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# -*- coding: utf-8 -*
import abc

from bw2data.parameters import ParameterBase
from bw2data.proxies import ExchangeProxyBase
from stats_arrays import (
UncertaintyBase, UndefinedUncertainty, uncertainty_choices as uc
)


class BaseUncertaintyInterface(abc.ABC):
__slots__ = ["_data"]
KEYS = {
"uncertainty type", "loc", "scale", "shape", "minimum", "maximum",
"negative"
}
data_type = ""

def __init__(self, unc_obj):
self._data = unc_obj

@property
def data(self):
return self._data

@property
@abc.abstractmethod
def amount(self) -> float:
pass

@property
@abc.abstractmethod
def uncertainty_type(self) -> UncertaintyBase:
pass

@property
@abc.abstractmethod
def uncertainty(self) -> dict:
pass


class ExchangeUncertaintyInterface(BaseUncertaintyInterface):
"""Many kinds of exchanges use uncertainty to describe how 'correct' the
data is which makes up the amount of the exchange.
"""
_data: ExchangeProxyBase
data_type = "exchange"

@property
def amount(self) -> float:
return self._data.amount

@property
def uncertainty_type(self) -> UncertaintyBase:
return self._data.uncertainty_type

@property
def uncertainty(self) -> dict:
return self._data.uncertainty


class ParameterUncertaintyInterface(BaseUncertaintyInterface):
"""All levels of parameters can describe their amounts with uncertainty."""
_data: ParameterBase
data_type = "parameter"

@property
def amount(self) -> float:
return self._data.amount

@property
def uncertainty_type(self) -> UncertaintyBase:
if "uncertainty type" not in self._data.data:
return UndefinedUncertainty
return uc[self._data.data.get("uncertainty type", 0)]

@property
def uncertainty(self) -> dict:
return {k: self._data.data.get(k) for k in self.KEYS if k in self._data.data}


class CFUncertaintyInterface(BaseUncertaintyInterface):
"""The characterization factors (CFs) of an impact assessment method can also
contain uncertainty.
This is however not certain (ha), as the CF is made up out of a flow key
and either an amount (float) or the uncertainty values + an amount (dict).
See the ``Method`` and ``ProcessedDataStore`` classes in the bw2data library.
"""
_data: tuple
data_type = "cf"

@property
def is_uncertain(self) -> bool:
return isinstance(self._data[1], dict)

@property
def amount(self) -> float:
return self._data[1]["amount"] if self.is_uncertain else self._data[1]

@property
def uncertainty_type(self) -> UncertaintyBase:
if not self.is_uncertain:
return UndefinedUncertainty
return uc[self._data[1].get("uncertainty type", 0)]

@property
def uncertainty(self) -> dict:
if not self.is_uncertain:
return {}
return {k: self._data[1].get(k) for k in self.KEYS if k in self._data[1]}


def get_uncertainty_interface(data: object) -> BaseUncertaintyInterface:
if isinstance(data, ExchangeProxyBase):
return ExchangeUncertaintyInterface(data)
elif isinstance(data, ParameterBase):
return ParameterUncertaintyInterface(data)
elif isinstance(data, tuple):
return CFUncertaintyInterface(data)
else:
raise TypeError(
"No uncertainty interface exists for object type {}".format(type(data))
)
5 changes: 4 additions & 1 deletion activity_browser/app/ui/figures.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,13 @@ def plot(self, df: pd.DataFrame, method: tuple):


class SimpleDistributionPlot(Plot):
def plot(self, data: np.ndarray, label: str = "Mean"):
def plot(self, data: np.ndarray, mean: float, label: str = "Value"):
self.reset_plot()
sns.distplot(data, axlabel=label, ax=self.ax)
self.ax.set_ylabel("Probability density")
# Add vertical line at given mean of x-axis
self.ax.axvline(mean, label="Mean / amount", c="r", ymax=0.98)
self.ax.legend(loc="upper right")
_, height = self.canvas.get_width_height()
self.setMinimumHeight(height / 2)
self.canvas.draw()
2 changes: 1 addition & 1 deletion activity_browser/app/ui/tables/LCA_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def dragMoveEvent(self, event) -> None:

def dropEvent(self, event):
event.accept()
new_methods = [row["method"] for row in event.source().selectedItems()]
new_methods = event.source().selected_methods()
old_methods = set(m for m in self.dataframe["method"])
data = [self.build_row(m) for m in new_methods if m not in old_methods]
if data:
Expand Down
Loading

0 comments on commit 2dff150

Please sign in to comment.