2017-02-20 208 views
11

我有一個字符串列表。我想爲每個字符串分配一個唯一的編號(確切的編號並不重要),並按順序使用這些編號創建一個長度相同的列表。下面是我最好的嘗試,但我不開心的原因有兩個:爲列表中的每個唯一值分配一個數字

  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

如何在應用算法之前對列表進行排序 –

回答

11

不使用外部庫(檢查編輯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] 
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,小心解釋一下? –

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

在lambda中包裝'indexList.index'有什麼意義? –

+0

@StefanPochmann,是的,你可以寫這個也map(indexList.index,names),如果你不需要寫lambda –

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) 

你可以看到它是非常simmilar,唯一的一點是,對於下一個元素,而不是增加數量我添加的編號爲當前元素。就這樣。哦,並整理。它首先排序資本,然後在這個例子中小寫,如果你想改變它,你可以使用sort(key= lambda:x ...)。 (也許是這樣的:names.sort(key = lambda x: (x.upper() if x.lower() == x else x.lower()))

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] 

這隻需要對字符串列表進行一次迭代,這使得甚至可以處理生成器和類似的操作。

6

如果條件是數字是唯一的,並且確切的數字並不重要,那麼您可以建立一個映射,將列表中的每個項目與動態中的唯一編號相關聯,從計數對象分配值:

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

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

相關問題