How to map a list of strings to a list of integers

Viewed 774

I have a list with n elements:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']

I have to assign a number to each string, zero at the start, and then increment by one if the element is different, instead give the same number if the element repeats. Example:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
[ 0,    1,      1,      2,        0,     3,     4,     4,     5,       3    ]

How can I do it?

7 Answers

With a helper dict:

>>> [*map({k: v for v, k in enumerate(dict.fromkeys(final))}.get, final)]
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

Another way:

>>> d = {}
>>> [d.setdefault(x, len(d)) for x in final]
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

using a dictionary would achieve this.

def counts(a):
    dis = {}
    count=0
    for i in range(len(a)):
        if a[i] not in dis.keys():
            dis[a[i]] = count
            count+=1
        
    return([dis[x] for x in a])

Use a defaultdict and use a counter as a default value function.

Whenever the key exists, it returns the stored "first encountered position", otherwise it calls Incr.__call__ which increments its count to provide a new first encountered position.

With super brain's suggestion, use an existing counter class:

from collections import defaultdict 
from itertools import count

li = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
seen = defaultdict(count().__next__)
print( [seen[val] for val in li] )

Rolling my own Incr, as before, which does give you the advantage that you could return anything (such as a GUID):

from collections import defaultdict 

class Incr:
    def __init__(self):
        self.count = -1

    def __call__(self):
        self.count +=1 
        return self.count

li = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']

seen = defaultdict(Incr())

print( [seen[val] for val in li] )

both provide same output:

[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

Try this:

a = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
dct = {}
counter = 0
for i in range(len(a)):
    if a[i] not in dct.keys():
        dct[a[i]] = counter 
        counter += 1
print([(i, dct[i]) for i in a])

You just need to proof if you had it already

def counts(final):
    count3 = [] # contains all objects that were already found
    count2=[]
    count=0
    for x in final:
        if x not in count3: # test if it's not already in count3
            count+=1
            count2.append(count)
            count3.append(x)
        else:
            count2.append(count)
    
return count2

Cleanest way might be to use pandas:

import pandas as pd
lst =  ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
pd.factorize(lst)

Which outputs:

(array([0, 1, 1, 2, 0, 3, 4, 4, 5, 3], dtype=int64),
 array(['pea', 'rpai', 'schiai', 'rpe', 'zoi', 'briai'], dtype=object))

I was proven wrong and I have to use a dictionary (thanks @Steve). Here's the updated version with dictionary included:

a = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
b = [None]*len(a)
d = {}
for i,x in enumerate(a):
    if x not in d: d[x] = len (d) #or use d.setdefault(x, len(d)) instead of the if statement (using the algo from @superb rain's)
    b[i] = d[x]    

print (a)
print (b)

The output of this will be:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]
Related