Python 为列表中的每个唯一值指定一个数字

Python 为列表中的每个唯一值指定一个数字,python,list,Python,List,我有一个字符串列表。我想为每个字符串指定一个唯一的数字(确切的数字并不重要),并使用这些数字按顺序创建一个相同长度的列表。下面是我最好的尝试,但我不高兴有两个原因: 它假定相同的值彼此相邻 我必须以0开始列表,否则输出将不正确 我的代码: names = ['ll', 'll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'HL', 'HL'] numbers = [0] num = 0 for item in range(len(names

我有一个字符串列表。我想为每个字符串指定一个唯一的数字(确切的数字并不重要),并使用这些数字按顺序创建一个相同长度的列表。下面是我最好的尝试,但我不高兴有两个原因:

  • 它假定相同的值彼此相邻

  • 我必须以
    0
    开始列表,否则输出将不正确

  • 我的代码:

    names = ['ll', 'll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'HL', 'HL']
    numbers = [0]
    num = 0
    for item in range(len(names)):
        if item == len(names) - 1:
          break
        elif names[item] == names[item+1]:
            numbers.append(num)
        else:
            num = num + 1
            numbers.append(num)
    print(numbers)
    

    我想使代码更通用,这样它就可以处理未知列表。有什么想法吗?

    因为您要将字符串映射到整数,所以建议使用dict。因此您可以执行以下操作:

    d = dict()
    
    counter = 0
    
    for name in names:
        if name in d:
            continue
        d[name] = counter
        counter += 1
    
    numbers = [d[name] for name in names]
    

    您也可以尝试以下方法:-

    names = ['ll', 'll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'HL', 'HL']
    
    indexList = list(set(names))
    
    print map(lambda name:indexList.index(name),names)
    

    无需使用外部库(检查编辑以获得
    Pandas
    解决方案),您可以按如下方式执行:

    d = {ni: indi for indi, ni in enumerate(set(names))}
    numbers = [d[ni] for ni in names]
    
    简要说明:

    在第一行中,为列表中的每个唯一元素分配一个数字(存储在字典
    d
    ;您可以使用字典理解轻松创建它;
    set
    返回
    名称的唯一元素

    然后,在第二行中,进行列表理解,并将实际数字存储在列表
    numbers

    有一个例子可以说明它对未排序的列表也很有效:

    # 'll' appears all over the place
    names = ['ll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'll', 'LL', 'HL', 'HL', 'HL', 'll']
    
    这是编号的输出:

    [1, 1, 3, 3, 3, 2, 2, 1, 2, 0, 0, 0, 1]
    
    如您所见,与
    ll
    关联的编号
    1
    出现在正确的位置

    编辑

    如果您有可用的,您也可以使用(这对于大型列表似乎非常有效,对于元组列表也非常有效,如前所述):

    然后会回来

    (array([(array([0, 0, 1, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0]),
     array(['ll', 'hl', 'LL', 'HL'], dtype=object))
    
    所以,

    numbers = pd.factorize(names)[0]
    

    我设法稍微修改了您的脚本,看起来还可以:

    names = ['ll', 'hl', 'll', 'hl', 'LL', 'll', 'LL', 'HL', 'hl', 'HL', 'LL', 'HL', 'zzz']
    names.sort()
    print(names)
    numbers = []
    num = 0
    for item in range(len(names)):
        if item == len(names) - 1:
          break
        elif names[item] == names[item+1]:
            numbers.append(num)
        else:
            numbers.append(num)
            num = num + 1
    numbers.append(num)
    print(numbers)
    
    你们可以看到它非常相似,唯一的问题是,我为下一个元素添加了数字,而不是为当前元素添加了数字。这就是全部。哦,还有分类。它首先对大写字母进行排序,然后对小写字母进行排序。在本例中,如果希望更改,可以使用
    sort(key=lambda:x…
    )。(可能是这样:
    names.sort(key=lambda x:(x.upper()如果x.lower()==x else x.lower())
    

    为了使其更通用,您可以将其包装在函数中,这样这些硬编码的值不会造成任何伤害,因为它们是本地的

    如果使用高效的查找容器(我将使用普通字典),则可以保留每个字符串的第一个索引,而不会损失太多性能:

    def your_function(list_of_strings):
    
        encountered_strings = {}
        result = []
    
        idx = 0
        for astring in list_of_strings:
            if astring in encountered_strings:  # check if you already seen this string
                result.append(encountered_strings[astring])
            else:
                encountered_strings[astring] = idx
                result.append(idx)
                idx += 1
        return result
    
    这将按顺序分配索引(即使这并不重要):


    这只需要在字符串列表上进行一次迭代,这样就可以处理生成器和类似的程序。

    如果条件是数字是唯一的,而确切的数字并不重要,则可以动态地将列表中的每个项目与唯一的数字相关联,并从count对象分配值:

    from itertools import count
    
    names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll']
    
    d = {}
    c = count()
    numbers = [d.setdefault(i, next(c)) for i in names]
    print(numbers)
    # [0, 0, 2, 2, 4, 4, 4, 7, 0]
    

    您可以通过在列表和count对象上使用,并将map函数设置为
    {}.setdefault
    (请参阅@StefanPochmann的注释)来删除额外的名称:


    另外,如果已经安装了numpy,您还可以使用:

    import numpy as np
    
    _, numbers = np.unique(names, return_inverse=True)
    print(numbers)
    # [3 3 2 2 1 1 1 0 3]
    

    如果有
    k
    不同的值,则会按照第一次出现的顺序将它们映射到整数
    0
    k-1

    >>> names = ['b', 'c', 'd', 'c', 'b', 'a', 'b']
    >>> tmp = {}
    >>> [tmp.setdefault(name, len(tmp)) for name in names]
    [0, 1, 2, 1, 0, 3, 0]
    
    下面是一个类似于
    collections.defaultdict
    itertools.count
    的解决方案:

    import itertools as it
    import collections as ct
    
    
    names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll']
    
    dd = ct.defaultdict(it.count().__next__)
    [dd[i] for i in names]
    # [0, 0, 1, 1, 2, 2, 2, 3, 0]
    
    每次新出现都会调用
    itertools.count
    中的下一个整数,并将新条目添加到
    dd

    中。熊猫可以简单地分解唯一的字符串:

    将熊猫作为pd导入
    代码,uniques=pd.factorize(名称)
    代码
    >>>数组([3,3,3,2,2,2,1,1,0,0,0])
    
    这也可以通过
    LabelEncoder()
    在Scikit学习中完成:

    从sklearn导入预处理
    le=预处理。LabelEncoder()
    代码=le.fit_变换(名称)
    代码
    >>>数组([3,3,3,2,2,2,1,1,0,0,0])
    
    在应用算法之前对列表进行排序怎么样如果你使用
    列表(map({}.setdefault,names,count())
    ,则不需要额外的变量。在第一种解决方案中,你可以使用
    len(d)
    而不是
    next(c)
    ,a la:`numbers=[d.setdefault(i,len(d))来表示名称中的i]@StefanPochmann,是的,你也可以编写这个映射(indexList.index,name),如果不需要编写lambda
    import numpy as np
    
    _, numbers = np.unique(names, return_inverse=True)
    print(numbers)
    # [3 3 2 2 1 1 1 0 3]
    
    >>> names = ['b', 'c', 'd', 'c', 'b', 'a', 'b']
    >>> tmp = {}
    >>> [tmp.setdefault(name, len(tmp)) for name in names]
    [0, 1, 2, 1, 0, 3, 0]
    
    import itertools as it
    import collections as ct
    
    
    names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll']
    
    dd = ct.defaultdict(it.count().__next__)
    [dd[i] for i in names]
    # [0, 0, 1, 1, 2, 2, 2, 3, 0]