为列表中的每个唯一值分配一个数字

问题描述:

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

  1. 它假定相同的值是彼此相邻

  2. 我不得不用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) 

我想让代码更通用,所以它将与未知列表一起工作。有任何想法吗?

+0

如何在应用算法之前对列表进行排序 –

不使用外部库(检查编辑Pandas解决方案),你可以按如下做:

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

简要说明:

在第一行中,您为列表中的每个唯一元素指定一个编号(存储在字典d中;你可以使用字典理解来轻松创建它; set返回names的独特元素)。

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

一个例子来说明,它也能正常工作的无序列表:

# 'll' appears all over the place 
names = ['ll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'll', 'LL', 'HL', 'HL', 'HL', 'll'] 

这是输出numbers

[1, 1, 3, 3, 3, 2, 2, 1, 2, 0, 0, 0, 1] 

正如你所看到的,与ll出现相关的数字1在正确的地方。

编辑

如果你有Pandas可用,还可以使用pandas.factorize

import pandas as pd 

pd.factorize(names) 

将回

(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] 

由于您将字符串映射为整数,因此建议使用字典。所以,你可以做到以下几点:

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] 
+1

Downvoter,小心解释一下? –

你可以试试这个另外: -

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) 
+2

在lambda中包装'indexList.index'有什么意义? –

+0

@StefanPochmann,是的,你可以写这个也map(indexList.index,names),如果你不需要写lambda –

我设法非常轻微的修改脚本,它看起来不错:

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) 

你可以看到它是非常simmilar,唯一的一点是,对于下一个元素,而不是增加数量我添加的编号为当前元素。就这样。哦,并整理。它首先排序资本,然后在这个例子中小写,如果你想改变它,你可以使用sort(key= lambda:x ...)。 (也许是这样的:names.sort(key = lambda x: (x.upper() if 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 

,这将在顺序分配指标(即使这并不重要):

>>> your_function(['ll', 'll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'HL', 'HL']) 
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3] 

这只需要对字符串列表进行一次迭代,这使得甚至可以处理生成器和类似的操作。

如果条件是数字是唯一的,并且确切的数字并不重要,那么您可以建立一个映射,将列表中的每个项目与动态中的唯一编号相关联,从计数对象分配值:

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] 

您可以通过列表和计数对象上使用map,并设置地图功能{}.setdefault废除多余的名称(见@ StefanPochmann的评论):

from itertools import count 

names = ['ll', 'll', 'hl', 'hl', 'LL', 'LL', 'LL', 'HL', 'll'] 
numbers = map({}.setdefault, names, count()) # call list() on map for Py3 
print(numbers) 
# [0, 0, 2, 2, 4, 4, 4, 7, 0] 

作为一个额外的,你也可以使用np.unique,如果你已经numpy的安装:

import numpy as np 

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

如果你做'list(map({}。setdefault,names, COUNT()))'。 –

+0

@StefanPochmann漂亮整洁! –

+0

在第一种解决方案中,您可以使用'len(d)'而不是'next(c)',la:'numbers = [d.setdefault(i,len(d))for i in names] – RootTwo

如果你有k不同的价值观,这将它们映射到整数0k-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] 

这是一个类似的factorizing解决方案与collections.defaultdictitertools.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