|
1 | 1 | import numpy as np
|
| 2 | +from itertools import product |
2 | 3 |
|
3 | 4 |
|
4 |
| -# Holds all CFA-specific Active routines. |
| 5 | +class ActiveOptionsContainer: |
| 6 | + """ |
| 7 | + Container for ActiveOptions properties. |
| 8 | + """ |
| 9 | + @property |
| 10 | + def active_options(self): |
| 11 | + """ |
| 12 | + Property of the datastore that relates private option variables to the standard |
| 13 | + ``active_options`` parameter. |
| 14 | + """ |
| 15 | + return { |
| 16 | + 'chunks': self._active_chunks, |
| 17 | + 'chunk_limits': self._chunk_limits, |
| 18 | + } |
| 19 | + |
| 20 | + @active_options.setter |
| 21 | + def active_options(self, value): |
| 22 | + self._set_active_options(**value) |
| 23 | + |
| 24 | + def _set_active_options(self, chunks={}, chunk_limits=True): |
| 25 | + |
| 26 | + if chunks == {}: |
| 27 | + raise NotImplementedError( |
| 28 | + 'Default chunking is not implemented, please provide a chunk scheme ' |
| 29 | + ' - active_options = {"chunks": {}}' |
| 30 | + ) |
| 31 | + |
| 32 | + self._active_chunks = chunks |
| 33 | + self._chunk_limits = chunk_limits |
| 34 | + |
| 35 | +# Holds all Active routines. |
5 | 36 | class ActiveChunk:
|
6 | 37 |
|
7 | 38 | description = "Container class for Active routines performed on each chunk. All active-per-chunk content can be found here."
|
8 |
| - |
9 |
| - def __init__(self, *args, **kwargs): |
10 |
| - raise NotImplementedError |
11 | 39 |
|
12 | 40 | def _post_process_data(self, data):
|
13 | 41 | # Perform any post-processing steps on the data here
|
14 | 42 | return data
|
15 | 43 |
|
16 |
| - def _standard_mean(self, axis=None, skipna=None, **kwargs): |
| 44 | + def _standard_sum(self, axes=None, skipna=None, **kwargs): |
17 | 45 | """
|
18 | 46 | Standard Mean routine matches the normal routine for dask, required at this
|
19 | 47 | stage if Active mean not available.
|
20 | 48 | """
|
21 |
| - size = 1 |
22 |
| - for i in axis: |
23 |
| - size *= self.shape[i] |
24 | 49 |
|
25 | 50 | arr = np.array(self)
|
26 | 51 | if skipna:
|
27 |
| - total = np.nanmean(arr, axis=axis, **kwargs) *size |
| 52 | + total = np.nansum(arr, axis=axes, **kwargs) |
28 | 53 | else:
|
29 |
| - total = np.mean(arr, axis=axis, **kwargs) *size |
30 |
| - return {'n': self._numel(arr, axis=axis), 'total': total} |
| 54 | + total = np.sum(arr, axis=axes, **kwargs) |
| 55 | + return total |
| 56 | + |
| 57 | + def _standard_max(self, axes=None, skipna=None, **kwargs): |
| 58 | + return np.max(self, axis=axes) |
| 59 | + |
| 60 | + def _standard_min(self, axes=None, skipna=None, **kwargs): |
| 61 | + return np.min(self, axis=axes) |
31 | 62 |
|
32 |
| - def _numel(self, axis=None): |
33 |
| - if not axis: |
| 63 | + def _numel(self, method, axes=None): |
| 64 | + if not axes: |
34 | 65 | return self.size
|
35 | 66 |
|
36 | 67 | size = 1
|
37 |
| - for i in axis: |
| 68 | + for i in axes: |
38 | 69 | size *= self.shape[i]
|
39 | 70 | newshape = list(self.shape)
|
40 |
| - newshape[axis] = 1 |
| 71 | + for ax in axes: |
| 72 | + newshape[ax] = 1 |
41 | 73 |
|
42 | 74 | return np.full(newshape, size)
|
43 | 75 |
|
44 |
| - def active_mean(self, axis=None, skipna=None, **kwargs): |
| 76 | + def active_method(self, method, axis=None, skipna=None, **kwargs): |
45 | 77 | """
|
46 | 78 | Use PyActiveStorage package functionality to perform mean of this Fragment.
|
47 | 79 |
|
48 |
| - :param axis: (int) The axis over which to perform the active_mean operation. |
| 80 | + :param axis: (int) The axes over which to perform the active_mean operation. |
49 | 81 |
|
50 | 82 | :param skipna: (bool) Skip NaN values when calculating the mean.
|
51 | 83 |
|
52 | 84 | :returns: A ``duck array`` (numpy-like) with the reduced array or scalar value,
|
53 |
| - as specified by the axis parameter. |
| 85 | + as specified by the axes parameter. |
54 | 86 | """
|
| 87 | + |
| 88 | + standard_methods = { |
| 89 | + 'mean': self._standard_sum, |
| 90 | + 'sum' : self._standard_sum, |
| 91 | + 'max' : self._standard_max, |
| 92 | + 'min' : self._standard_min |
| 93 | + } |
| 94 | + ret = None |
| 95 | + n = self._numel(method, axes=axis) |
| 96 | + |
55 | 97 | try:
|
56 | 98 | from activestorage.active import Active
|
57 | 99 | except ImportError:
|
58 | 100 | # Unable to import Active package. Default to using normal mean.
|
59 | 101 | print("ActiveWarning: Unable to import active module - defaulting to standard method.")
|
60 |
| - return self._standard_mean(axis=axis, skipna=skipna, **kwargs) |
61 |
| - |
62 |
| - active = Active(self.filename, self.address) |
63 |
| - active.method = "mean" |
64 |
| - extent = self.get_extent() |
65 |
| - |
66 |
| - if not axis is None: |
67 |
| - return { |
68 |
| - 'n': self._numel(axis=axis), |
69 |
| - 'total': self._post_process_data(active[extent]) |
| 102 | + ret = { |
| 103 | + 'n': n, |
| 104 | + 'total': standard_methods[method](axes=axis, skipna=skipna, **kwargs) |
70 | 105 | }
|
71 | 106 |
|
72 |
| - # Experimental Recursive requesting to get each 1D column along the axis being requested. |
73 |
| - range_recursives = [] |
74 |
| - for dim in range(self.ndim): |
75 |
| - if dim != axis: |
76 |
| - range_recursives.append(range(extent[dim].start, extent[dim].stop+1)) |
77 |
| - else: |
78 |
| - range_recursives.append(extent[dim]) |
79 |
| - results = np.array(self._get_elements(active, range_recursives, hyperslab=[])) |
| 107 | + if not ret: |
| 108 | + |
| 109 | + active = Active(self.filename, self.address) |
| 110 | + active.method = method |
| 111 | + extent = tuple(self.get_extent()) |
| 112 | + |
| 113 | + if axis == None: |
| 114 | + axis = tuple([i for i in range(self.ndim)]) |
| 115 | + |
| 116 | + n = self._numel(method, axes=axis) |
| 117 | + |
| 118 | + if len(axis) == self.ndim: |
| 119 | + data = active[extent] |
| 120 | + t = self._post_process_data(data) * n |
| 121 | + |
| 122 | + ret = { |
| 123 | + 'n': n, |
| 124 | + 'total': t |
| 125 | + } |
| 126 | + |
| 127 | + if not ret: |
| 128 | + # Experimental Recursive requesting to get each 1D column along the axes being requested. |
| 129 | + range_recursives = [] |
| 130 | + for dim in range(self.ndim): |
| 131 | + if dim not in axis: |
| 132 | + range_recursives.append(range(extent[dim].start, extent[dim].stop)) |
| 133 | + else: |
| 134 | + range_recursives.append(extent[dim]) |
| 135 | + results = np.array(self._get_elements(active, range_recursives, hyperslab=[])) |
| 136 | + |
| 137 | + t = self._post_process_data(results) * n |
| 138 | + ret = { |
| 139 | + 'n': n, |
| 140 | + 'total': t |
| 141 | + } |
80 | 142 |
|
81 |
| - return { |
82 |
| - 'n': self._numel(axis=axis), |
83 |
| - 'total': self._post_process_data(results) |
84 |
| - } |
| 143 | + if method == 'mean': |
| 144 | + return ret |
| 145 | + else: |
| 146 | + return ret['total']/ret['n'] |
85 | 147 |
|
86 | 148 | def _get_elements(self, active, recursives, hyperslab=[]):
|
87 | 149 | dimarray = []
|
88 |
| - current = recursives[0] |
89 |
| - if not len(recursives) > 1: |
| 150 | + if not len(recursives) > 0: |
90 | 151 |
|
91 | 152 | # Perform active slicing and meaning here.
|
92 |
| - return active[hyperslab] |
| 153 | + return active[tuple(hyperslab)].flatten()[0] |
| 154 | + |
| 155 | + current = recursives[0] |
93 | 156 |
|
94 | 157 | if type(current) == slice:
|
95 | 158 | newslab = hyperslab + [current]
|
|
0 commit comments