np.unique has some optional returns
return_inverse gives the integer encoding, which I use very often
>>> b, c = np.unique(a, return_inverse=True)
>>> b
array(['a', 'b', 'c'],
dtype="|S1")
>>> c
array([0, 1, 2, 0, 1, 2])
>>> c+1
array([1, 2, 3, 1, 2, 3])
it can be used to recreate the original array from uniques
>>> b[c]
array(['a', 'b', 'c', 'a', 'b', 'c'],
dtype="|S1")
>>> (b[c] == a).all()
True