-
Notifications
You must be signed in to change notification settings - Fork 11
/
array.py
227 lines (163 loc) · 6.1 KB
/
array.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
from future.utils import with_metaclass
from builtins import str
import abc
import numpy
from ...core.data import Array as core_Array
class Array(with_metaclass(abc.ABCMeta, core_Array)):
'''Abstract base class for a container of an underlying array.
The form of the array is defined by the initialization parameters of a
subclass.
See `cfdm.NumpyArray` for an example implementation.
.. versionadded:: 1.7.0
'''
def __array__(self, *dtype):
'''The numpy array interface.
.. versionadded:: 1.7.0
:Returns:
`numpy.ndarray`
An independent numpy array of the data.
**Examples:**
>>> isinstance(a, Array)
True
>>> n = numpy.asanyarray(a)
>>> isinstance(n, numpy.ndarray)
True
'''
array = self.array
if not dtype:
return array
else:
return array.astype(dtype[0], copy=False)
#--- End: def
@abc.abstractmethod
def __getitem__(self, indices):
'''Return a subspace as an independent numpy array.
x.__getitem__(indices) <==> x[indices]
Indexing follows rules that are very similar to the numpy indexing
rules, the only differences being:
* An integer index i takes the i-th element but does not reduce the
rank by one.
..
* When two or more dimensions' indices are sequences of integers then
these indices work independently along each dimension (similar to
the way vector subscripts work in Fortran). This is the same
behaviour as indexing on a Variable object of the netCDF4 package.
.. versionadded:: 1.7.0
'''
raise NotImplementedError() # pragma: no cover
#--- End: def
def __repr__(self):
'''Called by the `repr` built-in function.
x.__repr__() <==> repr(x)
.. versionadded:: 1.7.0
'''
return "<{0}{1}: >".format(self.__class__.__name__, self.shape)
#--- End: def
def __str__(self):
'''Called by the `str` built-in function.
x.__str__() <==> str(x)
.. versionadded:: 1.7.0
'''
return "shape={0}, dtype={1}".format(self.shape, self.dtype)
#--- End: def
def get_compression_type(self):
'''The type of compression that has been applied to the underlying
array.
.. versionadded:: 1.7.0
:Returns:
`str`
The compression type. An empty string means that no
compression has been applied.
**Examples:**
>>> a.compression_type
''
>>> a.compression_type
'gathered'
>>> a.compression_type
'ragged contiguous'
'''
return self._get_component('compression_type', '')
#--- End: def
@classmethod
def get_subspace(cls, array, indices, copy=True):
'''Return a subspace, defined by indices, of a numpy array.
Only certain type of indices are allowed. See the *indices* parameter
for details.
Indexing is similar to numpy indexing. Given the restrictions on the
type of indices allowed - see the *indicies* parameter - the only
difference to numpy indexing is
* When two or more dimension's indices are sequences of integers
then these indices work independently along each dimension
(similar to the way vector subscripts work in Fortran).
.. versionadded:: 1.7.0
:Parameters:
array: `numpy.ndarray`
The array to be subspaced.
indices:
The indices that define the subspace.
Must be either `Ellipsis` or a sequence that contains an index
for each dimension. In the latter case, each dimension's index
must either be a `slice` object or a sequence of two or more
integers.
*Parameter example:*
indices=Ellipsis
*Parameter example:*
indices=[[5, 7, 8]]
*Parameter example:*
indices=[slice(4, 7)]
*Parameter example:*
indices=[slice(None), [5, 7, 8]]
*Parameter example:*
indices=[[2, 5, 6], slice(15, 4, -2), [8, 7, 5]]
copy: `bool`
If `False` then the returned subspace may (or may not) be
independent of the input *array*. By default the returned
subspace is independent of the input *array*.
:Returns:
`numpy.ndarray`
'''
if indices is not Ellipsis:
# if indices is Ellipsis:
# pass
# elif not isinstance(indices, tuple):
# array = array[indices]
# else:
axes_with_list_indices = [i for i, x in enumerate(indices)
if not isinstance(x, slice)]
n_axes_with_list_indices = len(axes_with_list_indices)
if n_axes_with_list_indices < 2:
# ----------------------------------------------------
# At most one axis has a list-of-integers index so we
# can do a normal numpy subspace
# ----------------------------------------------------
array = array[tuple(indices)]
else:
# ----------------------------------------------------
# At least two axes have list-of-integers indices so
# we can't do a normal numpy subspace
# ----------------------------------------------------
if numpy.ma.isMA(array):
take = numpy.ma.take
else:
take = numpy.take
indices = list(indices)
for axis in axes_with_list_indices:
array = take(array, indices[axis], axis=axis)
indices[axis] = slice(None)
if n_axes_with_list_indices < len(indices):
# Apply subspace defined by slices
array = array[tuple(indices)]
#--- End: if
if copy:
if numpy.ma.isMA(array) and not array.ndim:
# This is because numpy.ma.copy doesn't work for
# scalar arrays (at the moment, at least)
ma_array = numpy.ma.empty((), dtype=array.dtype)
ma_array[...] = array
array = ma_array
else:
array = array.copy()
#--- End: if
return array
#--- End: def
#--- End: class