numpy replace a value by a negative int

Viewed 73

I am doing a game of tic tac toe as an exercise. I am trying to replace X with 3, O with -3 and - with 0.

import numpy as np

playfield = np.array([
   ['X', 'O', 'X'],
   ['-', 'X', 'O'],
   ['-', '-', 'X']
])
codes = {'X': 3, 'O': -3, '-': 0}

for idx, x in np.ndenumerate(playfield):

    playfield[idx] = codes[x]

However, every time the program tries to replace an O by -3, my array is like this

[['3' '-' 'X']
 ['-' 'X' 'O']
 ['-' '-' 'X']]

It happens even if I try to replace it by -9. Also, when I try to replace '_' by -3, it appears as '-' in the array

If I make playfield as a normal list, replace everything and make it back as a np.array, everything works. Why is it happening? Thank you!

4 Answers

I can answer why it is happening. Your playfield is an array of U1 (whatever it is), and so -3 gets cast to the <U1 type If you define

playfield = np.array([
   ['X', 'O', 'X'],
   ['-', 'X', 'O'],
   ['-', '-', 'X']
], dtype=object) 

and also

codes = {"X": "3", "O": "-3", "-": "0"}

Everything works (you will have to cast the array back to ints at the end of your substitution. ) A little horrible, but not too surprising, since numpy is (as the name suggests) designed for arrays of numbers.

playfield is an array of characters, so any negative number becomes '-'. If you change 'O' to a positive integer, it should work fine.

For example if you use:

codes = {'X': 3, 'O': 2, '-': 0}

Then the result is:

array([['3', '2', '3'],
       ['0', '3', '2'],
       ['0', '0', '3']], dtype='<U1')

However, iteration over an entire array is perhaps the worst way to do substitution in numpy.

Something like this would be more efficient:

for k in codes.keys():
    playfield[playfield == k] = codes[k]

If you want your final array to be an array of ints, I would go about the conversions like so:

import numpy as np

playfield = np.array([
   ['X', 'O', 'X'],
   ['-', 'X', 'O'],
   ['-', '-', 'X']
])
codes = {'X': 6, 'O': 3, '-': 0}

for k in codes:
    playfield[playfield == k] = codes[k]

playfield = playfield.astype(int) - 3

This way you can still start with an original array of characters, and you'll only have to do a type conversion once.

This is what you would need:

playfield = playfield.astype('U2')
for k, v in codes.items():  playfield[playfield==k] = str(v)
playfield = playfield.astype(np.int)

output:

[[ 3 -3  3]
 [ 0  3 -3]
 [ 0  0  3]]

Explanation: Your playfield array is an array of a single character. To include a two character "-3", first convert playfield to a two character array 'U2'(please be aware that if you choose to use larger than single digit numbers, you need to expand your string array to maximum characters in the largest number 'Un') and then replace with string version of numbers and finally convert them to numbers on last line.

I would just create a new array with the correct dtype before:

p2 = empty_like(playfield, dtype=int)
for k, v in codes.items():
    p2[playfield==k] = v
playfield = p2
Related