Search code examples
pythonarraysnumpycountcharacter

Count num of occurences of every 26 characters for every word in numpy


I have this numpy array that stored on wordlist_arr:

[b'aabi\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz'
 b'aabinomin\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz'
 b'aaji\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz'
 ...
 b'zus\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz'
 b'zuzumo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz'
 b'zuzuni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00abcdefghijklmnopqrstuvwxyz']
(71729,)
|S64

I expect I could count 26 characters for every wordlist, so the output shape will be (71729, 26):

[[3,2,1,1,1,1,1,1,2,1,...,1],
...
]

'''
In [3,2,1,1,1,1,1,1,2,1,...,1]
index 0 represent num "a" counted, index 1 represent num "b" counted, and so on...
'''

I change POV and reshape so that it will be viewed as per characters

wordlist.view('S1').reshape((wordlist.size, -1))

'''
[[b'a' b'a' b'b' ... b'x' b'y' b'z']
 [b'a' b'a' b'b' ... b'x' b'y' b'z']
 [b'a' b'a' b'j' ... b'x' b'y' b'z']
 ...
 [b'z' b'u' b's' ... b'x' b'y' b'z']
 [b'z' b'u' b'z' ... b'x' b'y' b'z']
 [b'z' b'u' b'z' ... b'x' b'y' b'z']]
(71729, 64)
|S1
'''

As you see I put abcdefghijklmnopqrstuvwxyz at the end of index so that I could use method np.unique so that at least there is 1 counted of every alphabetic. But it seems it doesn't work, instead it returned every possible characters used.

[b'' b'a' b'b' b'c' b'd' b'e' b'f' b'g' b'h' b'i' b'j' b'k' b'l' b'm' b'n'
 b'o' b'p' b'q' b'r' b's' b't' b'u' b'v' b'w' b'x' b'y' b'z']
(27,)
|S1

I tried another approach using numpy char count but it seems doesn't work too:

np.char.count(wordlist.view('S1').reshape(wordlist.size, -1), alphabet, axis=1)
# ValueError: shape mismatch: objects cannot be broadcast to a single shape.  Mismatch is between arg 0 with shape (71729, 64) and arg 1 with shape (26,).

Actually I can use native way, but for now I'm looking for numpythonic way so that I can utilize numpy performance that written in C lang


Solution

  • IIUC, you can do:

    Consider this array:

    s = np.array([b"aabbdd", b"aabbcc", b"eeffgg", b"xxyyzz"], dtype="S6")
    print(s.view("uint8").reshape(s.shape[0], -1))
    

    This prints:

    [[ 97  97  98  98 100 100]
     [ 97  97  98  98  99  99]
     [101 101 102 102 103 103]
     [120 120 121 121 122 122]]
    

    Then:

    x = np.apply_along_axis(
        lambda x: np.resize(np.bincount(x), 128)[97:123],  # 97 - a, 123 - z according ASCII
        axis=1,
        arr=s.view("uint8").reshape(s.shape[0], -1),
    )
    print(x)
    

    Prints:

    [[2 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
     [2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
     [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2]]