| 1 |
"""Analysis tools for dejavu Units.""" |
|---|
| 2 |
|
|---|
| 3 |
def sort(attrs, descending=False): |
|---|
| 4 |
"""sort(attrs, descending=False) -> create a function for list.sort() for Units.""" |
|---|
| 5 |
if isinstance(attrs, (str, unicode)): |
|---|
| 6 |
attrs = (attrs, ) |
|---|
| 7 |
def sort_func(x, y): |
|---|
| 8 |
for attr in attrs: |
|---|
| 9 |
xv = getattr(x, attr) |
|---|
| 10 |
if callable(xv): |
|---|
| 11 |
xv = xv() |
|---|
| 12 |
if xv is None: |
|---|
| 13 |
diff = -1 |
|---|
| 14 |
else: |
|---|
| 15 |
yv = getattr(y, attr) |
|---|
| 16 |
if callable(yv): |
|---|
| 17 |
yv = yv() |
|---|
| 18 |
if yv is None: |
|---|
| 19 |
diff = 1 |
|---|
| 20 |
else: |
|---|
| 21 |
diff = cmp(xv, yv) |
|---|
| 22 |
if descending: |
|---|
| 23 |
diff = -diff |
|---|
| 24 |
if diff != 0: |
|---|
| 25 |
return diff |
|---|
| 26 |
return 0 |
|---|
| 27 |
return sort_func |
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
def _force_function(attr): |
|---|
| 31 |
if callable(attr): |
|---|
| 32 |
return attr |
|---|
| 33 |
|
|---|
| 34 |
def g(obj): |
|---|
| 35 |
return getattr(obj, attr) |
|---|
| 36 |
|
|---|
| 37 |
return g |
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 |
def SUM(attribute): |
|---|
| 41 |
"""sum(attribute) -> create an aggregate function for use with crosstab(). |
|---|
| 42 |
|
|---|
| 43 |
'attribute' can be either the name of an attribute defined for |
|---|
| 44 |
all objects in self.source, or a further callable to which each obj |
|---|
| 45 |
is passed and evaluated. |
|---|
| 46 |
""" |
|---|
| 47 |
if callable(attribute): |
|---|
| 48 |
def aggfunc(obj, current_agg_value): |
|---|
| 49 |
a, b = current_agg_value, attribute(obj) |
|---|
| 50 |
if a is None: |
|---|
| 51 |
return b |
|---|
| 52 |
if b is None: |
|---|
| 53 |
return a |
|---|
| 54 |
return a + b |
|---|
| 55 |
else: |
|---|
| 56 |
def aggfunc(obj, current_agg_value): |
|---|
| 57 |
a, b = current_agg_value, getattr(obj, attribute) |
|---|
| 58 |
if a is None: |
|---|
| 59 |
return b |
|---|
| 60 |
if b is None: |
|---|
| 61 |
return a |
|---|
| 62 |
return a + b |
|---|
| 63 |
return aggfunc |
|---|
| 64 |
|
|---|
| 65 |
def COUNT(obj, current_agg_value): |
|---|
| 66 |
"""count -> an aggregate function for use with crosstab().""" |
|---|
| 67 |
return (current_agg_value or 0) + 1 |
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
class CrossTab(list): |
|---|
| 71 |
"""Tool to form crosstabs of Unit property values. |
|---|
| 72 |
|
|---|
| 73 |
Example: |
|---|
| 74 |
data = CrossTab(source, (lambda x: x.FirstDate.month,), |
|---|
| 75 |
'Field', CrossTab.sum('Size')).results() |
|---|
| 76 |
""" |
|---|
| 77 |
|
|---|
| 78 |
def __init__(self, source=[], groups=[], pivot=None, aggfunc=COUNT): |
|---|
| 79 |
"""CrossTab(source, groups, pivot, aggfunc=count) |
|---|
| 80 |
|
|---|
| 81 |
groups: a sequence of attribute names or callables, |
|---|
| 82 |
which will form the rows of the result. |
|---|
| 83 |
|
|---|
| 84 |
pivot: either an attribute name or a callable, which will |
|---|
| 85 |
form the columns of the result. |
|---|
| 86 |
""" |
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
self.source = [x for x in source] |
|---|
| 91 |
|
|---|
| 92 |
if not isinstance(groups, (tuple, list)): |
|---|
| 93 |
groups = [groups,] |
|---|
| 94 |
self.groups = groups |
|---|
| 95 |
|
|---|
| 96 |
self.pivot = pivot |
|---|
| 97 |
self.aggfunc = aggfunc |
|---|
| 98 |
|
|---|
| 99 |
def results(self): |
|---|
| 100 |
|
|---|
| 101 |
|
|---|
| 102 |
|
|---|
| 103 |
groups = [_force_function(group) for group in self.groups] |
|---|
| 104 |
pivot = _force_function(self.pivot) |
|---|
| 105 |
aggfunc = self.aggfunc |
|---|
| 106 |
|
|---|
| 107 |
data = {} |
|---|
| 108 |
columns = {} |
|---|
| 109 |
for obj in self.source: |
|---|
| 110 |
key = tuple([group(obj) for group in groups]) |
|---|
| 111 |
val = pivot(obj) |
|---|
| 112 |
columns[val] = None |
|---|
| 113 |
|
|---|
| 114 |
row = data.setdefault(key, {}) |
|---|
| 115 |
row[val] = aggfunc(obj, row.get(val)) |
|---|
| 116 |
|
|---|
| 117 |
columns = columns.keys() |
|---|
| 118 |
columns.sort() |
|---|
| 119 |
return data, columns |
|---|
| 120 |
|
|---|