Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I multiply column of the int numpy array to the float digit and stays in int?

Tags:

python

numpy

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]])
like image 715
karambaq Avatar asked Dec 23 '22 05:12

karambaq


1 Answers

@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.)

like image 196
andrew_reece Avatar answered Dec 25 '22 19:12

andrew_reece