Map string to integers with position in ASCII table

Viewed 240

I have a string like this:

word = 'python'

Based on string.ascii_lowercase, I'd like to create a new array which looks like this:

[15, 24, 19, 7, 14, 13]

My solution for this problem was to do the following:

alphabet = {char: i for i, char in enumerate(string.ascii_lowercase)}
indices = [alphabet[char] for char in word]
print(indices)

Output: [15, 24, 19, 7, 14, 13]

But I'm looking for a more efficient way, without using loops. How can I do that in a vectorized way?

2 Answers

One way is to build an array from the string using np.fromiter and specifying a 'U1' dtype, cast to integer, and subtract the starting position of the alphabet in the unicode table, 97 or we can just use ord('a'): as suggested by Antoine Dubuis:

import numpy as np
word = 'python'
np.fromiter(word, dtype='U1').view(np.uint32) - ord('a')
array([15, 24, 19,  7, 14, 13])

We can use np.frombuffer for a pretty efficient one -

import numpy as np

np.frombuffer(word.encode(), dtype=np.uint8)-97

Timings on a 1M long string :

In [23]: import string

In [24]: p = string.ascii_lowercase

In [25]: word = ''.join([p[i] for i in np.random.randint(0,len(p), 1000000)])

In [26]: %timeit np.frombuffer(word.encode(), dtype=np.uint8)-97
136 µs ± 1.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

# @yatu's soln with np.fromiter
In [27]: %timeit np.fromiter(word, dtype='U1').view(np.uint32) - ord('a')
24.8 ms ± 423 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Related