Skip to content

Add 'cumulative percent' contribution analysis mode #1433

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
34 changes: 22 additions & 12 deletions activity_browser/docs/wiki/LCA-Results.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,19 @@ in the next sections.
![contributions cutoff](./assets/contribution_manipulation.png)

#### Cut-off
You can manually change the `Cut-off type` of the results in two ways, `Relative` or `Top #`.
- The `Relative` mode shows contributions _from_ entities of _x_% or higher.
- The `Top #` mode shows contributions from the _x_ entities that contribute the most (as absolute).
You can manually change the `Cut-off type` of the results in three ways:
- The `Percent` mode shows contributions _from_ entities of at least _x_% or higher.
- For example: If the cut-off is set to 5% for process contribtions, then all contributions of at least 5% are shown.
- The `Cumulative percent` mode shows contributions that cumulatively contribute at least _x_%.
- For example: If the cut-off is set to 80% for process contributions, then the first _n_ processes (sorted highest
to lowest) that count up to 80% are shown.
- The `Number` mode shows contributions from the _x_ entities that contribute the most (as absolute).
- For example: If the cut-off is set to 5, then the first 5 processes (sorted highest
to lowest) will be shown.

The cut-off is applied per item (e.g. per reference flow or impact category, see [compare](#compare)) below).
This means that if you want to see the top 5 contributors, you will only see the top 5 per item, even if a contributor would
also be present for another item.

You can adjust the `Cut-off level` to change how many results you see.

Expand Down Expand Up @@ -151,22 +161,22 @@ You can disable one of them if you want to focus on the other.

#### Relative and Absolute
You can choose between `Relative` and `Absolute` results.
The `Relative` results will sum to 100% (the total `Range` or `Score`),
The `Relative` results will sum to 100% (the total `Score` or `Range`),
the `Absolute` results will sum to the impact score.
For `Relative`, you can choose what you use as the 100% reference, the `Range` or the `Score`.
For `Relative`, you can choose what you use as the 100% reference, the `Score` or the `Range`.

#### Range and Score
The `Range`/`Score` determines what you use as the _total_ to which the contributions are counted.
- For `Range`, this is the full _range_ of results
- For example, if all your negative results together have a score of -2 and all your positive results together have a
score of 10, the _range_ is 12 (-2 * -1 + 10).
- An entity with a contribution of 4 would have a relative contribution of 4/12 = 33.3...%.
#### Score and Range
The `Score`/`Range` determines what you use as the _total_ to which the contributions are counted.
- For `Score`, this is the total score (sum) of the results
- For example, if all your negative results together have a score of -2 and all your positive results together have a
score of 10, the _score_ is 8 (-2 + 10).
- An entity with a contribution of 4 would have a relative contribution of 4/8 = 50%.
- For `Range`, this is the full _range_ of results
- For example, if all your negative results together have a score of -2 and all your positive results together have a
score of 10, the _range_ is 12 (-2 * -1 + 10).
- An entity with a contribution of 4 would have a relative contribution of 4/12 = 33.3...%.

The `Range` or `Score` setting are only relevant when your results contain both positive and negative contributions.
The `Score` or `Range` setting are only relevant when your results contain both positive and negative contributions.

### Positive and negative numbers in contribution results
It can happen in LCA that you get both positive and negative numbers in your contribution results.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified activity_browser/docs/wiki/assets/contribution_manipulation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 9 additions & 26 deletions activity_browser/mod/bw2analyzer/contribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ def sort_array(self, data: np.array, limit: float = 25, limit_type: str = "numbe
See PR above on why we overwrite this function.
"""
if not total:
abs_total_flag = True
total = np.abs(data).sum()
else:
abs_total_flag = False

if total == 0 and limit_type == "cum_percent":
raise ValueError("Cumulative percentage cannot be calculated to a total of 0, use a different limit type or total")
raise ValueError(
"Cumulative percentage cannot be calculated to a total of 0, use a different limit type or total")

if limit_type not in ("number", "percent", "cum_percent"):
raise ValueError(f"limit_type must be either 'number', 'percent' or 'cum_percent' not '{limit_type}'.")
if limit_type in ("percent", "cum_percent"):
if limit_type in ("percent", "cum_percent"):
if not 0 < limit <= 1:
raise ValueError("Percentage limits > 0 and <= 1.")
if limit_type == "number":
if not int(limit) == limit:
raise ValueError("Number limit must a whole number.")
if not 0 < limit:
raise ValueError("Number limit must be < 0.")

results = np.hstack(
(data.reshape((-1, 1)), np.arange(data.shape[0]).reshape((-1, 1)))
Expand All @@ -38,30 +41,10 @@ def sort_array(self, data: np.array, limit: float = 25, limit_type: str = "numbe
limit = (np.abs(data) >= (abs(total) * limit))
results = results[limit, :]
return results[np.argsort(np.abs(results[:, 0]))[::-1]]
elif limit_type == "cum_percent" and abs_total_flag:
elif limit_type == "cum_percent":
# if we would apply this on the 'correct' order, this would stop just before the limit,
# we want to be on or the first step over the limit.
results = results[np.argsort(np.abs(data))] # sort low to high impact
cumsum = np.cumsum(np.abs(results[:, 0])) / abs(total)
limit = (cumsum >= (1 - limit)) # find items under limit
return results[limit, :][::-1] # drop items under limit and set correct order
elif limit_type == "cum_percent" and not abs_total_flag:
# iterate over positive and negative values until limit is achieved or surpassed.
results = results[np.argsort(np.abs(data))][::-1]
pos_neg = [ # split into positive and negative sections
results[results[:, 0] > 0],
results[results[:, 0] < 0],
]
# iterate over positive and negative sections
for i, arr in enumerate(pos_neg):
c = 0
# iterate over array until we have equalled or surpassed limit
for j, row in enumerate(arr):
c += abs(row[0] / total)
if c >= limit:
break
arr = arr[:min(j + 1, len(arr)), :]
pos_neg[i] = arr

results = np.concatenate(pos_neg) # rebuild into 1 array
return results[np.argsort(np.abs(results[:, 0]))][::-1] # sort values
Loading