I have a numpy array:
>>> b
array([[ 2, 2],
[ 6, 4],
[10, 6]])
I want to multiply first column by float number, and as result I need int number, because when I doing:
>>> b[:,0] *= 2.1
It says:
TypeError: Cannot cast ufunc multiply output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
I need the array that looks like:
array([[ 4, 2],
[12, 4],
[21, 6]])
@Umang Gupta gave a solution to your problem. I was curious myself as to why this worked, so I'm posting what I found as additional context. FWIW this question has already been asked and answered here, but that answer also doesn't really walk through what's happening as much as I would have liked, so here's my attempt:
Using the *=
operator calls the __imul__()
special method for in-place multiplication of Numpy ndarrays, which in turn calls the universal function (ufunc) multiply()
.
There are two arguments in multiply()
which are relevant here: out
and casting
.
The out
argument specifies the output (along with its type). In the in-place multiplication operator, out
is set to self
, i.e. the ndarray
object which called the multiplication operation. In particular, the exact call for *=
looks like this:
ufunc(self, other, out=(self,))
^ where ufunc = multiply
, self = b
(ndarray
, type int64
, and other = 2.1
(scalar, type float
)
The casting
argument, however, determines the rules for what kind of data type casting is permitted as a result of an operation. As of Numpy 1.10, the default value for casting
is same_kind
, which means:
only safe casts or casts within a kind, like float64 to float32, are allowed
Since our ufunc
call didn't specify a value for the casting
argument, the default (same_kind
) is used - but this causes problems because we have specified out
as having an int64
dtype, which is not the same kind as the output of the int-by-float multiplication. With same_kind
casting, the float
result of the operation can't be converted to int
. That's why we see this error.
We can replicate this error using multiply()
explicitly:
np.multiply(b, 2.1, out=b)
TypeError: Cannot cast ufunc multiply output from dtype('float64') to dtype('int64') with casting rule 'same_kind'
It is possible to relax the casting
requirement of multiply()
, by setting the argument value to "unsafe"
. Then, when out
is also set, the output is coerced to the type of out
, regardless of whether it's the same kind or not (if possible):
np.multiply(b, 2.1, out=b, casting="unsafe")
# specifying int output and allowing casting to be "unsafe" allows re-conversion to int
array([[ 4, 4],
[12, 8],
[21, 12]])
Using the normal assignment operator to update b[:,0]
, on the other hand, is ok. That's what @Umang Gupta's solution does.
With:
b[:,0] = b[:,0]* 2.1
*
calls the multiply
ufunc, just like with *=
. But since it isn't calling the inplace version of the operation, there's no out
argument specified, and so no set type for the output. Then, standard typecasting allows ints to upcast to floats:
np.multiply(b, 2.1)
# float output
array([[ 4.2, 4.2],
[ 12.6, 8.4],
[ 21. , 12.6]])
Then the normal assignment operator =
takes the output of the multiplication and stores it in b[:,0]
. Per the Numpy docs on assigning values to indexed arrays:
Note that assignments may result in changes if assigning higher types to lower types (like floats to ints)
So the problem lies in *=
operator's automatic setting of the out
argument without changing the casting
argument from same_kind
to unsafe
. (Not that this is a bug, just that this is why you are getting an error.) And the accepted solution gets around that by leveraging automatic "downcasting" properties of assignment in Numpy. Hope that helps! (Also, Numpy pros, please feel free to correct any misunderstandings on my part.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With