The shortest way to linearize a list in Python

I want to create a list with ramp values ​​from a list with non-ramp values ​​in Python. for instance

input =[10,10,10,6,6,4,1,1,1,10,10]

should be converted to:

output=[0,0,0,1,1,2,3,3,3,0,0]

My code uses a python dictionary

def linearize(input):
    """
    Remap a input list containing values in non linear-indices list
    i.e.
    input = [10,10,10,6,6,3,1,1]
    output= [0,0,0,1,1,2,3,3] 
    """
    remap={}
    i=0
    output=[0]*len(input)
    for x in input:
        if x not in remap.keys():
            remap[x]=i
            i=i+1
    for i in range(0,len(input)):
        output[i]=remap[input[i]]
    return output

but I know this code may be more efficient. Some ideas to make this task a better and more pythonic way is an option? This function must be called very often on large lists.

+3
source share
4 answers

According to your comment in the question , are you looking for something like this

data = [8,8,6,6,3,8]
from itertools import count
from collections import defaultdict
counter = defaultdict(lambda x=count(): next(x))
print([counter[item] for item in data])
# [0, 0, 1, 1, 2, 0]

Thanks poke ,

list(map(lambda i, c=defaultdict(lambda c=count(): next(c)): c[i], data))

Now this is just one liner :)

+5
source

Use collections.OrderedDict:

In [802]: from collections import OrderedDict
     ...: odk=OrderedDict.fromkeys(l).keys()
     ...: odk={k:i for i, k in enumerate(odk)}
     ...: [odk[i] for i in l]
Out[802]: [0, 0, 0, 1, 1, 2, 3, 3, 3]
+2

:

input =[10,10,10,6,6,4,1,1,1,10,10]
d = {}
result = [d.setdefault(x, len(d)) for x in input]
+1

, numpy, , , 2 000 000 .

import numpy as np
def linearize(input):
    unique, inverse = np.unique(input, return_inverse=True)
    output = (len(unique)-1) - inverse
    return output

In addition, this function only works if your entry is in descending order , as your example. Let me know if this helps.

0
source

All Articles