Files
ncs/script/Lib_old/tinynumpy.py
boccioli_m 45202c5b09 Startup
2015-09-02 10:43:04 +02:00

1095 lines
36 KiB
Python

# Copyright (c) 2014, Almar Klein and Wade Brainerd
# tinynumpy is distributed under the terms of the MIT License.
#
# Original code by Wade Brainerd (https://github.com/wadetb/tinyndarray)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
A lightweight, pure Python, numpy compliant ndarray class.
The documenation in this module is rather compact. For details on each
function, see the corresponding documentation at:
http://docs.scipy.org/doc/numpy/reference/index.html Be aware that the
behavior of tinynumpy may deviate in some ways from numpy, or that
certain features may not be supported.
"""
# todo: keep track of readonly better
# todo: mathematical operators
# todo: more methods?
# todo: logspace, meshgrid
# todo: Fortran order?
from __future__ import division
import sys
###GOBBO
###This is a fix for ctypes operations like ctypes.c_ulong * 3 that requires __main__
import __builtin__
sys.modules["__main__"]=__builtin__
__main__ = __builtin__
###TODO
# a = array([[1, 2, 3, 4],[5, 6, 7, 8]])
# a[:] generates the following error:
# AttributeError: 'module' object has no attribute 'Array'
import ctypes
from math import sqrt
# Python 2/3 compat
if sys.version_info >= (3, ):
xrange = range
# Define version numer
__version__ = '0.0.1dev'
# Define dtypes: struct name, short name, numpy name, ctypes type
_dtypes = [('B', 'b1', 'bool', ctypes.c_bool),
('b', 'i1', 'int8', ctypes.c_int8),
('B', 'u1', 'uint8', ctypes.c_uint8),
('h', 'i2', 'int16', ctypes.c_int16),
('H', 'u2', 'uint16', ctypes.c_uint16),
('i', 'i4', 'int32', ctypes.c_int32),
('I', 'u4', 'uint32', ctypes.c_uint32),
('q', 'i8', 'int64', ctypes.c_int64),
('Q', 'u8', 'uint64', ctypes.c_uint64),
('f', 'f4', 'float32', ctypes.c_float),
('d', 'f8', 'float64', ctypes.c_double),
]
# Inject common dtype names
_known_dtypes = [d[2] for d in _dtypes]
for d in _known_dtypes:
globals()[d] = d
newaxis = None
def _convert_dtype(dtype, to='numpy'):
""" Convert dtype, if could not find, pass as it was.
"""
if dtype is None:
return dtype
dtype = str(dtype)
index = {'array':0, 'short':1, 'numpy':2, 'ctypes':3}[to]
for dd in _dtypes:
if dtype in dd:
return dd[index]
return dtype # Otherwise return original
def _ceildiv(a, b):
return -(-a // b)
def _get_step(view):
""" Return step to walk over array. If 1, the array is fully
C-contiguous. If 0, the striding is such that one cannot
step through the array.
"""
cont_strides = _strides_for_shape(view.shape, view.itemsize)
step = view.strides[-1] // cont_strides[-1]
corrected_strides = tuple([i * step for i in cont_strides])
almost_cont = view.strides == corrected_strides
if almost_cont:
return step
else:
return 0 # not contiguous
def _strides_for_shape(shape, itemsize):
strides = []
stride_product = 1
for s in reversed(shape):
strides.append(stride_product)
stride_product *= s
return tuple([i * itemsize for i in reversed(strides)])
def _size_for_shape(shape):
stride_product = 1
for s in shape:
stride_product *= s
return stride_product
def squeeze_strides(s):
""" Pop strides for singular dimensions. """
return tuple([s[0]] + [s[i] for i in range(1, len(s)) if s[i] != s[i-1]])
def _shape_from_object(obj):
shape = []
# todo: make more efficient, use len() etc
def _shape_from_object_r(index, element, axis):
try:
for i, e in enumerate(element):
_shape_from_object_r(i, e, axis+1)
while len(shape) <= axis:
shape.append(0)
l = i + 1
s = shape[axis]
if l > s:
shape[axis] = l
except TypeError:
pass
_shape_from_object_r(0, obj, 0)
return tuple(shape)
def _assign_from_object(array, obj):
key = []
# todo: make more efficient, especially the try-except
def _assign_from_object_r(element):
try:
for i, e in enumerate(element):
key.append(i)
_assign_from_object_r(e)
key.pop()
except TypeError:
array[tuple(key)] = element
_assign_from_object_r(obj)
def _increment_mutable_key(key, shape):
for axis in reversed(xrange(len(shape))):
key[axis] += 1
if key[axis] < shape[axis]:
return True
if axis == 0:
return False
key[axis] = 0
def _key_for_index(index, shape):
key = []
cumshape = [1]
for i in reversed(shape):
cumshape.insert(0, cumshape[0] * i)
for s in cumshape[1:-1]:
n = index // s
key.append(n)
index -= n * s
key.append(index)
return tuple(key)
def _zerositer(n):
for i in xrange(n):
yield 0
## Public functions
def array(obj, dtype=None, copy=True, order=None):
""" array(obj, dtype=None, copy=True, order=None)
Create a new array. If obj is an ndarray, and copy=False, a view
of that array is returned. For details see:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html
"""
dtype = _convert_dtype(dtype)
if isinstance(obj, ndarray):
# From existing array
a = obj.view()
if dtype is not None and dtype != a.dtype:
a = a.astype(dtype)
elif copy:
a = a.copy()
return a
if hasattr(obj, '__array_interface__'):
# From something that looks like an array, we can create
# the ctypes array for this and use that as a buffer
D = obj.__array_interface__
# Get dtype
dtype_orig = _convert_dtype(D['typestr'][1:])
# Create array
if D['strides']:
itemsize = int(D['typestr'][-1])
bufsize = D['strides'][0] * D['shape'][0] // itemsize
else:
bufsize = _size_for_shape(D['shape'])
BufType = (_convert_dtype(dtype_orig, 'ctypes') * bufsize)
buffer = BufType.from_address(D['data'][0])
a = ndarray(D['shape'], dtype_orig,
buffer=buffer, strides=D['strides'], order=order)
# Convert or copy?
if dtype is not None and dtype != dtype_orig:
a = a.astype(dtype)
elif copy:
a = a.copy()
return a
else:
# From some kind of iterable
shape = _shape_from_object(obj)
# Try to derive dtype
if dtype is None:
el = obj
while isinstance(el, (tuple, list)) and el:
el = el[0]
if isinstance(el, int):
dtype = 'int64'
# Create array
a = ndarray(shape, dtype, order=None)
_assign_from_object(a, obj)
return a
def zeros_like(a, dtype=None, order=None):
""" Return an array of zeros with the same shape and type as a given array.
"""
dtype = a.dtype if dtype is None else dtype
return zeros(a.shape, dtype, order)
def ones_like(a, dtype=None, order=None):
""" Return an array of ones with the same shape and type as a given array.
"""
dtype = a.dtype if dtype is None else dtype
return ones(a.shape, dtype, order)
def empty_like(a, dtype=None, order=None):
""" Return a new array with the same shape and type as a given array.
"""
dtype = a.dtype if dtype is None else dtype
return empty(a.shape, dtype, order)
def zeros(shape, dtype=None, order=None):
"""Return a new array of given shape and type, filled with zeros
"""
return empty(shape, dtype, order)
def ones(shape, dtype=None, order=None):
"""Return a new array of given shape and type, filled with ones
"""
a = empty(shape, dtype, order)
a.fill(1)
return a
def eye(size):
"""Return a new 2d array with given dimensions, filled with ones on the
diagonal and zeros elsewhere.
"""
a = zeros((size,size))
for i in xrange(size):
a[i,i] = 1
return a
def empty(shape, dtype=None, order=None):
"""Return a new array of given shape and type, without initializing entries
"""
return ndarray(shape, dtype, order=order)
def arange(*args, **kwargs):
""" arange([start,] stop[, step,], dtype=None)
Return evenly spaced values within a given interval.
Values are generated within the half-open interval ``[start, stop)``
(in other words, the interval including `start` but excluding `stop`).
For integer arguments the function is equivalent to the Python built-in
`range <http://docs.python.org/lib/built-in-funcs.html>`_ function,
but returns an ndarray rather than a list.
When using a non-integer step, such as 0.1, the results will often not
be consistent. It is better to use ``linspace`` for these cases.
"""
# Get dtype
dtype = kwargs.pop('dtype', None)
if kwargs:
x = list(kwargs.keys())[0]
raise TypeError('arange() got an unexpected keyword argument %r' % x)
# Parse start, stop, step
if len(args) == 0:
raise TypeError('Required argument "start" not found')
elif len(args) == 1:
start, stop, step = 0, int(args[0]), 1
elif len(args) == 2:
start, stop, step = int(args[0]), int(args[1]), 1
elif len(args) == 3:
start, stop, step = int(args[0]), int(args[1]), int(args[2])
else:
raise TypeError('Too many input arguments')
# Init
iter = xrange(start, stop, step)
a = empty((len(iter),), dtype=dtype)
a[:] = list(iter)
return a
def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None):
""" linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
Return evenly spaced numbers over a specified interval. Returns num
evenly spaced samples, calculated over the interval [start, stop].
The endpoint of the interval can optionally be excluded.
"""
# Prepare
start, stop = float(start), float(stop)
ra = stop - start
if endpoint:
step = ra / (num-1)
else:
step = ra / num
# Create
a = empty((num,), dtype)
a[:] = [start + i * step for i in xrange(num)]
# Return
if retstep:
return a, step
else:
return a
## The class
class ndarray(object):
""" ndarray(shape, dtype='float64', buffer=None, offset=0,
strides=None, order=None)
Array class similar to numpy's ndarray, implemented in pure Python.
This class can be distinguished from a real numpy array in that
the repr always shows the dtype as a string, and for larger arrays
(more than 100 elements) it shows a short one-line repr.
An array object represents a multidimensional, homogeneous array
of fixed-size items. An associated data-type property describes the
format of each element in the array.
Arrays should be constructed using `array`, `zeros` or `empty` (refer
to the See Also section below). The parameters given here refer to
a low-level method (`ndarray(...)`) for instantiating an array.
Parameters
----------
shape : tuple of ints
Shape of created array.
dtype : data-type, optional
Any object that can be interpreted as a numpy data type.
buffer : object contaning data, optional
Used to fill the array with data. If another ndarray is given,
the underlying data is used. Can also be a ctypes.Array or any
object that exposes the buffer interface.
offset : int, optional
Offset of array data in buffer.
strides : tuple of ints, optional
Strides of data in memory.
order : {'C', 'F'}, optional NOT SUPPORTED
Row-major or column-major order.
Attributes
----------
T : ndarray
Transpose of the array. In tinynumpy only supported for ndim <= 3.
data : buffer
The array's elements, in memory. In tinynumpy this is a ctypes array.
dtype : str
Describes the format of the elements in the array. In tinynumpy
this is a string.
flags : dict
Dictionary containing information related to memory use, e.g.,
'C_CONTIGUOUS', 'OWNDATA', 'WRITEABLE', etc.
flat : iterator object
Flattened version of the array as an iterator. In tinynumpy
the iterator cannot be indexed.
size : int
Number of elements in the array.
itemsize : int
The memory use of each array element in bytes.
nbytes : int
The total number of bytes required to store the array data,
i.e., ``itemsize * size``.
ndim : int
The array's number of dimensions.
shape : tuple of ints
Shape of the array.
strides : tuple of ints
The step-size required to move from one element to the next in
memory. For example, a contiguous ``(3, 4)`` array of type
``int16`` in C-order has strides ``(8, 2)``. This implies that
to move from element to element in memory requires jumps of 2 bytes.
To move from row-to-row, one needs to jump 8 bytes at a time
(``2 * 4``).
base : ndarray
If the array is a view into another array, that array is its `base`
(unless that array is also a view). The `base` array is where the
array data is actually stored.
__array_interface__ : dict
Dictionary with low level array information. Used by numpy to
turn into a real numpy array. Can also be used to give C libraries
access to the data via ctypes.
See Also
--------
array : Construct an array.
zeros : Create an array, each element of which is zero.
empty : Create an array, but leave its allocated memory unchanged (i.e.,
it contains "garbage").
Notes
-----
There are two modes of creating an array:
1. If `buffer` is None, then only `shape`, `dtype`, and `order`
are used.
2. If `buffer` is an object exposing the buffer interface, then
all keywords are interpreted.
"""
__slots__ = ['_dtype', '_shape', '_strides', '_itemsize',
'_offset', '_base', '_data']
def __init__(self, shape, dtype='float64', buffer=None, offset=0,
strides=None, order=None):
# Check order
if order is not None:
raise RuntimeError('ndarray order parameter is not supported')
# Check and set shape
assert isinstance(shape, tuple)
assert all([isinstance(x, int) for x in shape])
self._shape = shape
# Check and set dtype
dtype = _convert_dtype(dtype) if (dtype is not None) else 'float64'
if dtype not in _known_dtypes:
raise TypeError('data type %r not understood' % dtype)
self._dtype = dtype
# Itemsize is directly derived from dtype
self._itemsize = int(_convert_dtype(dtype, 'short')[-1])
if buffer is None:
# New array
self._base = None
# Check and set offset and strides
assert offset == 0
self._offset = 0
assert strides is None
self._strides = _strides_for_shape(self._shape, self.itemsize)
else:
# Existing array
if isinstance(buffer, ndarray) and buffer.base is not None:
buffer = buffer.base
# Keep a reference to avoid memory cleanup
self._base = buffer
# for ndarray we use the data property
if isinstance(buffer, ndarray):
buffer = buffer.data
# Check and set offset
assert isinstance(offset, int) and offset >= 0
self._offset = offset
# Check and set strides
if strides is None:
strides = _strides_for_shape(shape, self.itemsize)
assert isinstance(strides, tuple)
assert all([isinstance(x, int) for x in strides])
assert len(strides) == len(shape)
self._strides = strides
# Define our buffer class
buffersize = self._strides[0] * self._shape[0] // self._itemsize
buffersize += self._offset
BufferClass = _convert_dtype(dtype, 'ctypes') * buffersize
# Create buffer
if buffer is None:
self._data = BufferClass()
elif isinstance(buffer, ctypes.Array):
self._data = BufferClass.from_address(ctypes.addressof(buffer))
else:
self._data = BufferClass.from_buffer(buffer)
@property
def __array_interface__(self):
""" Allow converting to real numpy array, or pass pointer to C library
http://docs.scipy.org/doc/numpy/reference/arrays.interface.html
"""
readonly = False
# typestr
typestr = '<' + _convert_dtype(self.dtype, 'short')
# Pointer
if isinstance(self._data, ctypes.Array):
ptr = ctypes.addressof(self._data)
elif hasattr(self._data, '__array_interface__'):
ptr, readonly = self._data.__array_interface__['data']
elif hasattr(self._data, 'buffer_info'): # Python's array.array
ptr = self._data.buffer_info()[0]
elif isinstance(self._data, bytes):
ptr = ctypes.cast(self._data, ctypes.c_void_p).value
readonly = True
else:
raise TypeError('Cannot get address to underlying array data')
ptr += self._offset * self.itemsize
#
return dict(version=3,
shape=self.shape,
typestr=typestr,
descr=[('', typestr)],
data=(ptr, readonly),
strides=self.strides,
#offset=self._offset,
#mask=None,
)
def __len__(self):
return self.shape[0]
def __getitem__(self, key):
offset, shape, strides = self._index_helper(key)
if not shape:
# Return scalar
return self._data[offset]
else:
# Return view
return ndarray(shape, self.dtype,
offset=offset, strides=strides, buffer=self)
def __setitem__(self, key, value):
# Get info for view
offset, shape, strides = self._index_helper(key)
# Is this easy?
if not shape:
self._data[offset] = value
return
# Create view to set data to
view = ndarray(shape, self.dtype,
offset=offset, strides=strides, buffer=self)
# Get data to set as a list (because getting slices from ctype
# arrays yield lists anyway). The list is our "contiguous array"
if isinstance(value, (float, int)):
value_list = [value] * view.size
elif isinstance(value, (tuple, list)):
value_list = value
else:
if not isinstance(value, ndarray):
value = array(value, copy=False)
value_list = value._toflatlist()
# Check if size match
if view.size != len(value_list):
raise ValueError('Number of elements in source does not match ' +
'number of elements in target.')
# Assign data in most efficient way that we can. This code
# looks for the largest semi-contiguous block: the block that
# we can access as a 1D array with a stepsize.
subviews = [view]
value_index = 0
count = 0
while subviews:
subview = subviews.pop(0)
step = _get_step(subview)
if step:
block = value_list[value_index:value_index+subview.size]
s = slice(subview._offset,
subview._offset + subview.size * step,
step)
view._data[s] = block
value_index += subview.size
count += 1
else:
for i in range(subview.shape[0]):
subviews.append(subview[i])
assert value_index == len(value_list)
def __float__(self):
if self.size == 1:
return float(self.data[self._offset])
else:
raise TypeError('Only length-1 arrays can be converted to scalar')
def __int__(self):
if self.size == 1:
return int(self.data[self._offset])
else:
raise TypeError('Only length-1 arrays can be converted to scalar')
def __repr__(self):
# If more than 100 elements, show short repr
if self.size > 100:
shapestr = 'x'.join([str(i) for i in self.shape])
return '<ndarray %s %s at 0x%x>' % (shapestr, self.dtype, id(self))
# Otherwise, try to show in nice way
def _repr_r(s, axis, offset):
axisindent = min(2, max(0, (self.ndim - axis - 1)))
if axis < len(self.shape):
s += '['
for k_index, k in enumerate(xrange(self.shape[axis])):
if k_index > 0:
s += ('\n ' + ' ' * axis) * axisindent
offset_ = offset + k * self._strides[axis] // self.itemsize
s = _repr_r(s, axis+1, offset_)
if k_index < self.shape[axis] - 1:
s += ', '
s += ']'
else:
r = repr(self.data[offset])
if '.' in r:
r = ' ' + r
if r.endswith('.0'):
r = r[:-1]
s += r
return s
s = _repr_r('', 0, self._offset)
if self.dtype != 'float64':
return "array(" + s + ", dtype='%s')" % self.dtype
else:
return "array(" + s + ")"
def __eq__(self, other):
if other.__module__.split('.')[0] == 'numpy':
return other == self
else:
out = empty(self.shape, 'bool')
out[:] = [i1==i2 for (i1, i2) in zip(self.flat, other.flat)]
return out
## Private helper functions
def _index_helper(self, key):
# Indexing spec is located at:
# http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html
# Promote to tuple.
if not isinstance(key, tuple):
key = (key,)
axis = 0
shape = []
strides = []
offset = self._offset
for k in key:
axissize = self._shape[axis]
if isinstance(k, int):
if k >= axissize:
raise IndexError('index %i is out of bounds for axis %i ' +
'with size %s' % (k, axis, axissize))
offset += k * self._strides[axis] // self.itemsize
axis += 1
elif isinstance(k, slice):
start, stop, step = k.indices(self.shape[axis])
shape.append(_ceildiv(stop - start, step))
strides.append(step * self._strides[axis])
offset += start * self._strides[axis] // self.itemsize
axis += 1
elif k is Ellipsis:
raise TypeError("ellipsis are not supported.")
elif k is None:
shape.append(1)
stride = 1
for s in self._strides[axis:]:
stride *= s
strides.append(stride)
else:
raise TypeError("key elements must be instaces of int or slice.")
shape.extend(self.shape[axis:])
strides.extend(self._strides[axis:])
return offset, tuple(shape), tuple(strides)
def _toflatlist(self):
value_list = []
subviews = [self]
count = 0
while subviews:
subview = subviews.pop(0)
step = _get_step(subview)
if step:
s = slice(subview._offset,
subview._offset + subview.size * step,
step)
value_list += self._data[s]
count += 1
else:
for i in range(subview.shape[0]):
subviews.append(subview[i])
return value_list
## Properties
@property
def ndim(self):
return len(self._shape)
@property
def size(self):
return _size_for_shape(self._shape)
@property
def nbytes(self):
return _size_for_shape(self._shape) * self.itemsize
def _get_shape(self):
return self._shape
def _set_shape(self, newshape):
if newshape == self.shape:
return
if self.size != _size_for_shape(newshape):
raise ValueError('Total size of new array must be unchanged')
if _get_step(self) == 1:
# Contiguous, hooray!
self._shape = tuple(newshape)
self._strides = _strides_for_shape(self._shape, self.itemsize)
return
# Else, try harder ... This code supports adding /removing
# singleton dimensions. Although it may sometimes be possible
# to split a dimension in two if the contiguous blocks allow
# this, we don't bother with such complex cases for now.
# Squeeze shape / strides
N = self.ndim
shape = [self.shape[i] for i in range(N) if self.shape[i] > 1]
strides = [self.strides[i] for i in range(N) if self.shape[i] > 1]
# Check if squeezed shapes match
newshape_ = [newshape[i] for i in range(len(newshape))
if newshape[i] > 1]
if newshape_ != shape:
raise AttributeError('incompatible shape for non-contiguous array')
# Modify to make this data work in loop
strides.append(strides[-1])
shape.append(1)
# Form new strides
i = -1
newstrides = []
try:
for s in reversed(newshape):
if s == 1:
newstrides.append(strides[i] * shape[i])
else:
i -= 1
newstrides.append(strides[i])
except IndexError:
# Fail
raise AttributeError('incompatible shape for non-contiguous array')
else:
# Success
newstrides.reverse()
self._shape = tuple(newshape)
self._strides = tuple(newstrides)
shape = property(_get_shape, _set_shape) # Python 2.5 compat (e.g. Jython)
@property
def strides(self):
return self._strides
@property
def dtype(self):
return self._dtype
@property
def itemsize(self):
return self._itemsize
@property
def base(self):
return self._base
@property
def data(self):
return self._data
@property
def flat(self):
subviews = [self]
count = 0
while subviews:
subview = subviews.pop(0)
step = _get_step(subview)
if step:
s = slice(subview._offset,
subview._offset + subview.size * step,
step)
for i in self._data[s]:
yield i
else:
for i in range(subview.shape[0]):
subviews.append(subview[i])
@property
def T(self):
if self.ndim < 2:
return self
else:
return self.transpose()
@property
def flags(self):
c_cont = _get_step(self) == 1
return dict(C_CONTIGUOUS=c_cont,
F_CONTIGUOUS=(c_cont and self.ndim < 2),
OWNDATA=(self._base is None),
WRITEABLE=True, # todo: fix this
ALIGNED=c_cont, # todo: different from contiguous?
UPDATEIFCOPY=False, # We don't support this feature
)
## Methods - managemenet
def fill(self, value):
assert isinstance(value, (int, float))
self[:] = value
def clip(self, a_min, a_max, out=None):
if out is None:
out = empty(self.shape, self.dtype)
L = self._toflatlist()
L = [min(a_max, max(a_min, x)) for x in L]
out[:] = L
return out
def copy(self):
out = empty(self.shape, self.dtype)
out[:] = self
return out
def flatten(self):
out = empty((self.size,), self.dtype)
out[:] = self
return out
def ravel(self):
return self.reshape((self.size, ))
def repeat(self, repeats, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
out = empty((self.size * repeats,), self.dtype)
for i in range(repeats):
out[i*self.size:(i+1)*self.size] = self
return out
def reshape(self, newshape):
out = self.view()
try:
out.shape = newshape
except AttributeError:
out = self.copy()
out.shape = newshape
return out
def transpose(self):
# Numpy returns a view, but we cannot do that since we do not
# support Fortran ordering
ndim = self.ndim
if ndim < 2:
return self.view()
shape = self.shape[::-1]
out = empty(shape, self.dtype)
#
if ndim == 2:
for i in xrange(self.shape[0]):
out[:, i] = self[i, :]
elif ndim == 3:
for i in xrange(self.shape[0]):
for j in xrange(self.shape[1]):
out[:, j, i] = self[i, j, :]
else:
raise ValueError('Tinynumpy supports transpose up to ndim=3')
return out
def astype(self, dtype):
out = empty(self.shape, dtype)
out[:] = self
def view(self, dtype=None, type=None):
if dtype is None:
dtype = self.dtype
if dtype == self.dtype:
return ndarray(self.shape, dtype, buffer=self,
offset=self._offset, strides=self.strides)
elif self.ndim == 1:
itemsize = int(_convert_dtype(dtype, 'short')[-1])
size = self.nbytes // itemsize
offsetinbytes = self._offset * self.itemsize
offset = offsetinbytes // itemsize
return ndarray((size, ), dtype, buffer=self, offset=offset)
else:
raise ValueError('new type not compatible with array.')
## Methods - statistics
# We use the self.flat generator here. self._toflatlist() would be
# faster, but it might take up significantly more memory.
def all(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return all(self.flat)
def any(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return any(self.flat)
def min(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return min(self.flat)
def max(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return max(self.flat)
#return max(self._toflatlist()) # almost twice as fast
def sum(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return sum(self.flat)
def prod(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
p = 1.0
for i in self.flat:
p *= float(i)
return p
def ptp(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
mn = self.data[self._offset]
mx = mn
for i in self.flat:
if i > mx:
mx = i
if i < mn:
mn = i
return mx - mn
def mean(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
return self.sum() / self.size
def argmax(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
r = self.data[self._offset]
r_index = 0
for i_index, i in enumerate(self.flat):
if i > r:
r = i
r_index = i_index
return r_index
def argmin(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
r = self.data[self._offset]
r_index = 0
for i_index, i in enumerate(self.flat):
if i < r:
r = i
r_index = i_index
return r_index
def cumprod(self, axis=None, out=None):
if axis:
raise (TypeError, "axis argument is not supported")
if out is None:
out = empty((self.size,), self.dtype)
p = 1
L = []
for x in self.flat:
p *= x
L.append(p)
out[:] = L
return out
def cumsum(self, axis=None, out=None):
if axis:
raise (TypeError, "axis argument is not supported")
if out is None:
out = empty((self.size,), self.dtype)
p = 0
L = []
for x in self.flat:
p += x
L.append(p)
out[:] = L
return out
def var(self, axis=None):
if axis:
raise (TypeError, "axis argument is not supported")
m = self.mean()
acc = 0
for x in self.flat:
acc += abs(x - m) ** 2
return acc / self.size
def std(self, axis=None):
return sqrt(self.var(axis))
class nditer:
def __init__(self, array):
self.array = array
self.key = [0] * len(self.array.shape)
def __iter__(self):
return self
def __len__(self):
return _size_for_shape(self.array.shape)
def __getitem__(self, index):
key = _key_for_index(index, self.array.shape)
return self.array[key]
def __next__(self):
if self.key is None:
raise StopIteration
value = self.array[tuple(self.key)]
if not _increment_mutable_key(self.key, self.array.shape):
self.key = None
return value
def next(self):
return self.__next__()