为列表中的每个唯一值分配一个数字
我有一个字符串列表。我想为每个字符串分配一个唯一的编号(确切的编号并不重要),并按顺序使用这些编号创建一个长度相同的列表。下面是我最好的尝试,但我不开心的原因有两个:为列表中的每个唯一值分配一个数字
它假定相同的值是彼此相邻
我不得不用
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)
我想让代码更通用,所以它将与未知列表一起工作。有任何想法吗?
不使用外部库(检查编辑为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]
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)
在lambda中包装'indexList.index'有什么意义? –
@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]
如果你做'list(map({}。setdefault,names, COUNT()))'。 –
@StefanPochmann漂亮整洁! –
在第一种解决方案中,您可以使用'len(d)'而不是'next(c)',la:'numbers = [d.setdefault(i,len(d))for i in names] – RootTwo
如果你有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]
这是一个类似的factorizing解决方案与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
。
如何在应用算法之前对列表进行排序 –