Использование Numpy Hook __array_ufunc__
Numpy позволяет обойти эту проблему, правильно определив метод __array_ufunc__
для вашего объекта.
Следующий метод добавлен в Zora
класс выше, выполняет свою работу. Его легко обобщить для всех ufunc
бинарных операций, включая и некоммутативные операции. Но я показываю только это, чтобы не усложнять ситуацию.
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
lhs, rhs = inputs
return rhs * lhs
Следовательно, следующие результаты покажут ожидаемые результаты ...
import numpy as np
class Zora(object):
def __init__(self, array):
self._array = array
self._values_field_name = array.dtype.names[-1]
@property
def a(self):
return self._array
def __repr__(self):
return repr(self._array)
def __mul__(self, other):
result = self.copy()
result *= other
return result
def __imul__(self, other):
self._array[self._values_field_name] *= other
return self
def __rmul__(self, other):
return self * other
def copy(self):
return self.__class__(self.a.copy())
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
lhs, rhs = inputs
return rhs * lhs
@classmethod
def create(cls, fields, codes, data):
array = np.array([(*c, d) for c, d in zip(codes, data)], dtype=fields)
return cls(array)
if __name__ == '__main__':
# Let's create a Zora dataset with scenarios
scenarios = 7
fields = np.dtype([('LegalEntity', np.unicode_, 32), ('Division', np.unicode_, 32),
('Scenarios', np.float64, (scenarios, ))])
legal_entities = ['A', 'A', 'B', 'B', 'C']
divisions = ['a', 'b', 'a', 'b', 'b']
codes = list(zip(legal_entities, divisions))
data = np.random.uniform(0., 1., (len(codes), scenarios))
zora = Zora.create(fields, codes, data)
# The dataset looks like the following
print(zora)
# We can multiply it from the left with scalars ...
z = zora * 2
print(zora * 2)
# ... and with column vectors, for example ...
# ... for this we generate a columns vector with some weights ...
numrows = zora.a.shape[0]
weights = np.expand_dims(np.array(list(range(numrows))), 1)
# ... the weights ...
print(weights)
# ... left side multiplication works fine too with this
print(zora * weights)
# Let's show inplace multiplication ...
# Which we apply on a copy, so that we can still compare ...
z = zora.copy()
z *= 2
# Is pretty fine too, ...
print(zora)
print(z)
# Now it becomes a bit special ...
# ... when multiplying from the left.
# It works fine with a scalar..
z = 2 * zora
print(z)
# But becomes special with np.ndarrays ...
print('-------------------------------------')
print('-------------------------------------')
print('The following result ...')
z = weights * zora
print(z)
# which is now the same ...
print('-------------------------------------')
print('... should be the same as this one ...')
z = zora * weights
print(z)