如何将一个字符串列表映射为整数列表

20

我有一个包含n个元素的列表:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']

我需要给每个字符串分配一个数字,从零开始,如果元素不同则递增一,如果元素重复则给相同的数字。例如:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
[ 0,    1,      1,      2,        0,     3,     4,     4,     5,       3    ]

我该怎么做?


请更新你的问题并提供你已经尝试过的代码。 - quamrana
那段代码的缩进不正确。不清楚 countcount2 应该是什么以及为什么它们有不同的类型。你能用普通语言告诉我们吗?没有条件可以区分重复和非重复元素,因此预计这段代码不起作用。另外,请提供一个 [mcve],其中包含所有的代码和示例数据。最后,作为一个新用户,在这里参加 [tour] 并阅读 [ask]。 - Ulrich Eckhardt
你从未检查该元素是否重复。 - bereal
1
我的建议是与您的老师、导师或同学坐下来,让他们指导您朝正确的方向前进。我们直接给您答案可以解决您当前的问题,但这并不能教会您如何思考和分解问题,而这是编程的基本部分。您可能在下一个作业问题中也会遇到类似的问题。课程还应该建立在早期的概念之上,随着课程的进展,后面的问题会比前面的问题更加困难,如果您没有自己解决前面的问题,那么后面的问题就会更加困难。 - Bernhard Barker
7个回答

15

利用辅助字典:

>>> [*map({k: v for v, k in enumerate(dict.fromkeys(final))}.get, final)]
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]
另一种方法:
>>> d = {}
>>> [d.setdefault(x, len(d)) for x in final]
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

@superb rain,感谢你提供的第二个选项。这太棒了,它可以直接将值输出到列表中,并同时分配给字典。 - Joe Ferndz
18
如果有人对编程还不够熟悉,不知道如何完成所问的内容,我非常怀疑他们能否理解这些复杂的一行代码。 - Bernhard Barker

11

使用字典可以实现这一点。

def counts(a):
    dis = {}
    count=0
    for i in range(len(a)):
        if a[i] not in dis.keys():
            dis[a[i]] = count
            count+=1
        
    return([dis[x] for x in a])

嗨!一个给出所请求的结果的答案! - CryptoFool
7
我认为 for i, _ in enumerate(a)for i in range(len(a)) 更符合 Python 风格。但你只在 a[i] 中使用了 i,因此直接使用 for x in a 并使用 x 而不是 a[i] 更有意义。 - Bernhard Barker
@BernhardBarker 同意 - algorythms

6
使用defaultdict,并使用计数器作为默认值函数。
每当键存在时,它返回存储的“第一个遇到的位置”,否则调用Incr.__call__,该函数增加其计数以提供新的第一个遇到的位置。
根据超级大脑的建议,使用现有的计数器类:
from collections import defaultdict 
from itertools import count

li = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
seen = defaultdict(count().__next__)
print( [seen[val] for val in li] )

像之前一样,自己编写Incr函数也有好处,这样你可以返回任何东西(例如GUID):

from collections import defaultdict 

class Incr:
    def __init__(self):
        self.count = -1

    def __call__(self):
        self.count +=1 
        return self.count

li = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']

seen = defaultdict(Incr())

print( [seen[val] for val in li] )

两者提供相同的输出:

[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

2
还可以使用 itertools.count().__next__seen.__len__lambda: len(seen) 作为默认工厂。 - superb rain
@superbrain itertools.count().next 可能是一个不错的选择。说实话,我觉得你的 len(dict) 技巧很厉害。但这有点太聪明了,过了6个月后可能就不够明显了。不过这绝对是很好的思路。 - JL Peyret

3

试试这个:

a = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
dct = {}
counter = 0
for i in range(len(a)):
    if a[i] not in dct.keys():
        dct[a[i]] = counter 
        counter += 1
print([(i, dct[i]) for i in a])

为什么要加上+1?这样做并不会得到OP所要求的结果。 - CryptoFool

2
你只需要证明你已经拥有它。
def counts(final):
    count3 = [] # contains all objects that were already found
    count2=[]
    count=0
    for x in final:
        if x not in count3: # test if it's not already in count3
            count+=1
            count2.append(count)
            count3.append(x)
        else:
            count2.append(count)
    
return count2

您的解决方案对于示例数据返回[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],而不是预期的输出。您从未向count3添加任何内容以测试是否已经查看了该元素。 - Michael Szczesny
现在它返回 [1, 2, 3, 4, 5, 6] - Michael Szczesny
是啊。为什么要在这个上面加1呢?人们只是看着任何被提供作为答案的代码并说“没问题,很好!”而不去阅读或尝试它吗? - CryptoFool
我知道,它没有给出我想要的结果,但我不知道其他的方法。 - Somethink
4
那你为什么要把它发布为答案呢?这样有帮助吗? - CryptoFool
显示剩余2条评论

1
最干净的方法可能是使用pandas:
import pandas as pd
lst =  ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
pd.factorize(lst)

输出结果为:

(array([0, 1, 1, 2, 0, 3, 4, 4, 5, 3], dtype=int64),
 array(['pea', 'rpai', 'schiai', 'rpe', 'zoi', 'briai'], dtype=object))

0
我被证明是错的,我必须使用字典(感谢@Steve)。这是更新后包含字典的版本:
a = ['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
b = [None]*len(a)
d = {}
for i,x in enumerate(a):
    if x not in d: d[x] = len (d) #or use d.setdefault(x, len(d)) instead of the if statement (using the algo from @superb rain's)
    b[i] = d[x]    

print (a)
print (b)

这个的输出将会是:

['pea', 'rpai', 'rpai', 'schiai', 'pea', 'rpe', 'zoi', 'zoi', 'briai', 'rpe']
[0, 1, 1, 2, 0, 3, 4, 4, 5, 3]

首先,答案是错误的。其次,使用字典的原因是为了避免反复搜索列表,而这正是您的代码正在做的事情。因此,您的代码效率低下...但它确实避免了使用字典。 - CryptoFool
1
非常好。但是你应该做一个大修复。当你使用if x not in d.keys()if x not in d时,你会抹掉使用字典的全部原因。你从字典中提取了整个键列表,这需要时间。然后你通过这个列表进行线性搜索。所有这些操作都可以直接在字典中查找值来完成,而这正是字典擅长的。 - CryptoFool
@Steve,你认为我们仍在使用Python 2吗?尽管它已经正式死亡了? - superb rain
1
@Steve 对于成员测试,“x in d”是正确的方式,“x in d.keys()”是毫无意义且更慢(只是没有你想象中那么糟糕)。如果您需要其类似集合的行为,则它提供的视图可能有益处。 - superb rain
我刚刚阅读了Python 3.0的新特性,不想再犯类似的错误。在“陷阱”中,d.keys()问题位列第二,紧随print的更改之后。我很惊讶自己还没有注意到这个问题。再次感谢您,超级大脑! - CryptoFool
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接