Skip to content

Commit a2860a3

Browse files
authored
Merge pull request #1433 from marc-vdm/cum_percent_ca
Add 'cumulative percent' contribution analysis mode
2 parents 21baefc + b8a6f82 commit a2860a3

6 files changed

Lines changed: 237 additions & 155 deletions

File tree

activity_browser/docs/wiki/LCA-Results.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,19 @@ in the next sections.
119119
![contributions cutoff](./assets/contribution_manipulation.png)
120120

121121
#### Cut-off
122-
You can manually change the `Cut-off type` of the results in two ways, `Relative` or `Top #`.
123-
- The `Relative` mode shows contributions _from_ entities of _x_% or higher.
122+
You can manually change the `Cut-off type` of the results in three ways:
123+
- The `Minimum %` mode shows contributions _from_ entities of at least _x_% or higher.
124+
- For example: If the cut-off is set to 5% for process contribtions, then all contributions of at least 5% are shown.
125+
- The `Cumulative %` mode shows contributions that cumulatively contribute at least _x_%.
126+
- For example: If the cut-off is set to 80% for process contributions, then the first _n_ processes (sorted highest
127+
to lowest) that count up to 80% are shown.
124128
- The `Top #` mode shows contributions from the _x_ entities that contribute the most (as absolute).
129+
- For example: If the cut-off is set to 5, then the first 5 processes (sorted highest
130+
to lowest) will be shown.
131+
132+
The cut-off is applied per item (e.g. per reference flow or impact category, see [compare](#compare)) below).
133+
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
134+
also be present for another item.
125135

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

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

152162
#### Relative and Absolute
153163
You can choose between `Relative` and `Absolute` results.
154-
The `Relative` results will sum to 100% (the total `Range` or `Score`),
164+
The `Relative` results will sum to 100% (the total `Score` or `Range`),
155165
the `Absolute` results will sum to the impact score.
156-
For `Relative`, you can choose what you use as the 100% reference, the `Range` or the `Score`.
166+
For `Relative`, you can choose what you use as the 100% reference, the `Score` or the `Range`.
157167

158-
#### Range and Score
159-
The `Range`/`Score` determines what you use as the _total_ to which the contributions are counted.
160-
- For `Range`, this is the full _range_ of results
161-
- For example, if all your negative results together have a score of -2 and all your positive results together have a
162-
score of 10, the _range_ is 12 (-2 * -1 + 10).
163-
- An entity with a contribution of 4 would have a relative contribution of 4/12 = 33.3...%.
168+
#### Score and Range
169+
The `Score`/`Range` determines what you use as the _total_ to which the contributions are counted.
164170
- For `Score`, this is the total score (sum) of the results
165171
- For example, if all your negative results together have a score of -2 and all your positive results together have a
166172
score of 10, the _score_ is 8 (-2 + 10).
167173
- An entity with a contribution of 4 would have a relative contribution of 4/8 = 50%.
174+
- For `Range`, this is the full _range_ of results
175+
- For example, if all your negative results together have a score of -2 and all your positive results together have a
176+
score of 10, the _range_ is 12 (-2 * -1 + 10).
177+
- An entity with a contribution of 4 would have a relative contribution of 4/12 = 33.3...%.
168178

169-
The `Range` or `Score` setting are only relevant when your results contain both positive and negative contributions.
179+
The `Score` or `Range` setting are only relevant when your results contain both positive and negative contributions.
170180

171181
### Positive and negative numbers in contribution results
172182
It can happen in LCA that you get both positive and negative numbers in your contribution results.
29.1 KB
Loading
18 KB
Loading

activity_browser/mod/bw2analyzer/contribution.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@ def sort_array(self, data: np.array, limit: float = 25, limit_type: str = "numbe
1212
See PR above on why we overwrite this function.
1313
"""
1414
if not total:
15-
abs_total_flag = True
1615
total = np.abs(data).sum()
17-
else:
18-
abs_total_flag = False
1916

2017
if total == 0 and limit_type == "cum_percent":
21-
raise ValueError("Cumulative percentage cannot be calculated to a total of 0, use a different limit type or total")
18+
raise ValueError(
19+
"Cumulative percentage cannot be calculated to a total of 0, use a different limit type or total")
2220

2321
if limit_type not in ("number", "percent", "cum_percent"):
2422
raise ValueError(f"limit_type must be either 'number', 'percent' or 'cum_percent' not '{limit_type}'.")
25-
if limit_type in ("percent", "cum_percent"):
23+
if limit_type in ("percent", "cum_percent"):
2624
if not 0 < limit <= 1:
2725
raise ValueError("Percentage limits > 0 and <= 1.")
26+
if limit_type == "number":
27+
if not int(limit) == limit:
28+
raise ValueError("Number limit must a whole number.")
29+
if not 0 < limit:
30+
raise ValueError("Number limit must be < 0.")
2831

2932
results = np.hstack(
3033
(data.reshape((-1, 1)), np.arange(data.shape[0]).reshape((-1, 1)))
@@ -38,30 +41,10 @@ def sort_array(self, data: np.array, limit: float = 25, limit_type: str = "numbe
3841
limit = (np.abs(data) >= (abs(total) * limit))
3942
results = results[limit, :]
4043
return results[np.argsort(np.abs(results[:, 0]))[::-1]]
41-
elif limit_type == "cum_percent" and abs_total_flag:
44+
elif limit_type == "cum_percent":
4245
# if we would apply this on the 'correct' order, this would stop just before the limit,
4346
# we want to be on or the first step over the limit.
4447
results = results[np.argsort(np.abs(data))] # sort low to high impact
4548
cumsum = np.cumsum(np.abs(results[:, 0])) / abs(total)
4649
limit = (cumsum >= (1 - limit)) # find items under limit
4750
return results[limit, :][::-1] # drop items under limit and set correct order
48-
elif limit_type == "cum_percent" and not abs_total_flag:
49-
# iterate over positive and negative values until limit is achieved or surpassed.
50-
results = results[np.argsort(np.abs(data))][::-1]
51-
pos_neg = [ # split into positive and negative sections
52-
results[results[:, 0] > 0],
53-
results[results[:, 0] < 0],
54-
]
55-
# iterate over positive and negative sections
56-
for i, arr in enumerate(pos_neg):
57-
c = 0
58-
# iterate over array until we have equalled or surpassed limit
59-
for j, row in enumerate(arr):
60-
c += abs(row[0] / total)
61-
if c >= limit:
62-
break
63-
arr = arr[:min(j + 1, len(arr)), :]
64-
pos_neg[i] = arr
65-
66-
results = np.concatenate(pos_neg) # rebuild into 1 array
67-
return results[np.argsort(np.abs(results[:, 0]))][::-1] # sort values

0 commit comments

Comments
 (0)