我有一个字符串列表。我想为每个字符串分配一个唯一的编号(确切的数字并不重要),并按顺序使用这些编号创建一个相同长度的列表。以下是我最好的尝试,但我不满意有两个原因:
它假设相同的值彼此相邻
我必须以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 :(得分:15)
不使用外部库(查看编辑以获取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]
如您所见,与1
相关联的数字ll
出现在正确的位置。
修改强>
如果你有Pandas可用,你也可以使用pandas.factorize
(这对于大型列表来说似乎非常有效,并且对于元组列表也可以正常解释here):< / p>
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]
答案 1 :(得分:6)
如果条件是数字是唯一的并且确切的数字不重要,那么您可以构建一个映射,将列表中的每个项目动态地分配给一个唯一的数字,从中分配值计数对象:
CREATE TABLE Bundles (
BundleID INT,
ItemID INT, -- Foreign key to Items table
Amount INT,
PRIMARY KEY (BundleID, ItemID)
);
您可以使用列表中的map
和计数对象,并将地图函数设置为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]
来消除额外的名称(请参阅@ StefanPochmann的评论):
{}.setdefault
作为额外的,你也可以使用np.unique
,以防你已经安装了numpy:
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]
答案 2 :(得分:3)
为了使它更通用,你可以把它包装在一个函数中,所以这些硬编码的值不会造成任何伤害,因为它们是本地的。
如果您使用高效的查找容器(我将使用普通字典),您可以保留每个字符串的第一个索引而不会失去很多性能:
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]
这只需要对您的字符串列表进行一次迭代,这样就可以处理生成器等等。
答案 3 :(得分: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]
答案 4 :(得分:2)
我设法稍微修改了你的脚本,看起来没问题:
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)
你可以看到它非常相似,唯一的事情就是为NEXT元素添加数字我为CURRENT元素添加数字。就这样。哦,排序。它首先对资本进行排序,然后在此示例中为小写,如果您希望更改,则可以使用sort(key= lambda:x ...)
。 (也许是这样的:names.sort(key = lambda x: (x.upper() if x.lower() == x else x.lower()))
)
答案 5 :(得分:0)
由于您将字符串映射到整数,因此建议使用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]
答案 6 :(得分:0)
以下是与collections.defaultdict
和itertools.count
类似的factorizing解决方案:
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
添加新条目。
答案 7 :(得分:0)
Pandas' factorize
可以简单地分解唯一字符串:
import pandas as pd
codes, uniques = pd.factorize(names)
codes
>>> array([3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0])
这也可以在 Scikit-learn 中使用 LabelEncoder()
完成:
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
codes = le.fit_transform(names)
codes
>>> array([3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0])
答案 8 :(得分:-1)
你也可以试试这个: -
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)