Repository navigation
make (partial) disaggregation easier #199
Copy link
Copy link
Open
Milestone
Description
We need to find some way to make this easier (and fix all the bugs I just came across) -- I will open separate issues for each bug, but the main feature request will remain:
>>> arr = ndtest(10)
>>> a = arr.a
>>> agg = arr.sum(a.by(2)).rename('a', 'group')
>>> agg
group | a0:a1 | a2:a3 | a4:a5 | a6:a7 | a8:a9
| 1 | 5 | 9 | 13 | 17
>>> group = agg.group
>>> expand = zeros(group, a)
>>> expand
group | a0:a1 | a2:a3 | a4:a5 | a6:a7 | a8:a9
| 0.0 | 0.0 | 0.0 | 0.0 | 0.0
>>> expand = zeros((group, a))
>>> expand
group\a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
a0:a1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a2:a3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a4:a5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a6:a7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a8:a9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
>>> expand['a0:a1', 'a0':'a1'] = 0.5
ValueError Traceback (most recent call last)
...
ValueError: a0:a1 is ambiguous (valid in group, a)
>>> expand[group['a0:a1'], 'a0':'a1'] = 0.5
ValueError Traceback (most recent call last)
...
ValueError: group['a0':'a1'] is not a valid label for any axis
>>> expand[x.group['a0:a1'], 'a0':'a1'] = 0.5
>>> expand
group\a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
a0:a1 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a2:a3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a4:a5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a6:a7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a8:a9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
>>> for g in group:
... print(g.split(':'))
AttributeError Traceback (most recent call last)
...
AttributeError: 'PGroup' object has no attribute 'split'
>>> for g in group:
... print(g.eval().split(':'))
['a0', 'a1']
['a2', 'a3']
['a4', 'a5']
['a6', 'a7']
['a8', 'a9']
>>> for g in group:
... expand[g, slice(*g.eval().split(':'))] = 1 / len(g)
>>> expand
group\a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
a0:a1 | 0.2 | 0.2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a2:a3 | 0.0 | 0.0 | 0.2 | 0.2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a4:a5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.2 | 0.2 | 0.0 | 0.0 | 0.0 | 0.0
a6:a7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.2 | 0.2 | 0.0 | 0.0
a8:a9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.2 | 0.2
>>> len(group.i[0])
5
>>> for g in group:
... expand[g, slice(*g.eval().split(':'))] = 0.5
>>> expand
group\a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
a0:a1 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a2:a3 | 0.0 | 0.0 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a4:a5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0
a6:a7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.5 | 0.0 | 0.0
a8:a9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.5
>>> agg
group | a0:a1 | a2:a3 | a4:a5 | a6:a7 | a8:a9
| 1 | 5 | 9 | 13 | 17
>>> agg * expand
group\a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
a0:a1 | 0.5 | 0.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a2:a3 | 0.0 | 0.0 | 2.5 | 2.5 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0
a4:a5 | 0.0 | 0.0 | 0.0 | 0.0 | 4.5 | 4.5 | 0.0 | 0.0 | 0.0 | 0.0
a6:a7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 6.5 | 6.5 | 0.0 | 0.0
a8:a9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 8.5 | 8.5
>>> (agg * expand).sum(group)
a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
| 0.5 | 0.5 | 2.5 | 2.5 | 4.5 | 4.5 | 6.5 | 6.5 | 8.5 | 8.5
>>> arr
a | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9Reactions are currently unavailable
Activity
[-]make reverse aggregation easier[/-][+]make disaggregation easier[/+]gdementen commentedon Apr 4, 2017
see #200, #202 and #203 for linked issues.
gdementen commentedon Apr 5, 2017
The loop can be rewritten more generically as:
gdementen commentedon Apr 5, 2017
and
(agg * expand).sum(group)is equivalent to simplyagg @ expandin this casegdementen commentedon Apr 6, 2017
A first proof of concept:
then
gdementen commentedon Apr 6, 2017
see also:
http://stackoverflow.com/questions/12223689/how-can-i-efficiently-disaggregate-data-in-a-dataframe-given-a-set-of-weights
gdementen commentedon Apr 25, 2017
Here is a new version:
then:
points to improve:
gdementen commentedon May 8, 2017
Note that the @ optimization currently only works when the disag array is 2D, mostly due to the fact that @ uses the axes positions. It should be possible to transpose to make it work in all cases. Not sure it is worth it though.
[-]make disaggregation easier[/-][+]make (partial) disaggregation easier[/+]gdementen commentedon Mar 20, 2023
Katia needs this too but to disaggregate only a few labels. I don't know if the above code already works for that case or not. We should make sure it works though.
gdementen commentedon Mar 20, 2023
The implementation so far assumes the aggregated cells correspond to sums. We should support not dividing by the length of the group (if the aggregation was a mean).
Are there other ways/methods to automatically infer the values (possibly user-defined function???).
If users want an uneven split, I guess we should redirect them (probably worth mentioning in the disag function documentation) to use an explicit disag array.
We could also (probably in addition to mentioning the explicit disag array) provide some special syntax to create it (for disaggregating only a few labels, providing a disag array would seem overkill).
gdementen commentedon Oct 2, 2023
Partial disaggregation is done quite often in user models:
It would be nice if we had something nicer for this. The special syntax above would help: