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

时间:2017-02-20 16:47:56

标签: python list

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

  1. 它假设相同的值彼此相邻

  2. 我必须以0开始列表,否则输出会不正确

  3. 我的代码:

    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)
    

    我想让代码更通用,因此它可以使用未知列表。有什么想法吗?

9 个答案:

答案 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个不同的值,则会按照首次出现的顺序将它们映射到整数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]

答案 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.defaultdictitertools.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)