2017-02-20 3 views
11

Ich habe eine Liste von Zeichenfolgen. Ich möchte jeder Zeichenfolge eine eindeutige Nummer zuweisen (die genaue Nummer ist nicht wichtig) und eine Liste der gleichen Länge mit diesen Nummern in der Reihenfolge erstellen. Unten ist es mein Bestes versuchen, aber ich bin aus zwei Gründen nicht glücklich:Weisen Sie jedem eindeutigen Wert in einer Liste eine Zahl zu

  1. Er geht davon aus, dass die gleichen Werte zueinander

    nächsten sind
  2. ich die Liste mit einem 0 starten musste, sonst wäre der Ausgang

Mein Code falsch:

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) 

Ich möchte den Code allgemeiner machen, damit er mit einer unbekannten Liste funktioniert. Irgendwelche Ideen?

+0

Wie wäre es mit der Sortierung der Liste vor der Anwendung des Algorithmus –

Antwort

11

Ohne eine externe Bibliothek (überprüfen Sie die EDIT für eine Pandas Lösung) Sie können es tun, wie folgt :

d = {ni: indi for indi, ni in enumerate(set(names))} 
numbers = [d[ni] for ni in names] 

Kurzerklärung:

In der ersten Zeile weisen Sie jedem eindeutigen Element in Ihrer Liste eine Nummer zu (gespeichert im Wörterbuch d; Sie können es leicht mit einem Wörterbuch Verständnis erstellen; set gibt die einzigartigen Elemente von names zurück).

Dann machen Sie in der zweiten Zeile ein Listenverständnis und speichern die aktuellen Nummern in der Liste numbers.

Ein Beispiel zu verdeutlichen, dass es funktioniert auch für unsortierte Listen:

# 'll' appears all over the place 
names = ['ll', 'll', 'hl', 'hl', 'hl', 'LL', 'LL', 'll', 'LL', 'HL', 'HL', 'HL', 'll'] 

dass der Ausgang für numbers ist:

[1, 1, 3, 3, 3, 2, 2, 1, 2, 0, 0, 0, 1] 

Wie Sie sehen können, die Zahl 1 im Zusammenhang mit ll erscheint an den richtigen Stellen.

EDIT

Wenn Sie Pandas zur Verfügung haben, können Sie auch pandas.factorize verwenden:

import pandas as pd 

pd.factorize(names) 

dann

(array([(array([0, 0, 1, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0]), 
array(['ll', 'hl', 'LL', 'HL'], dtype=object)) 

Daher zurückkehren,

numbers = pd.factorize(names)[0] 
0

Da Sie Zeichenfolgen ganzen Zahlen zuordnen, schlägt das die Verwendung eines Diktats vor. So können Sie die folgenden Aktionen durchführen:

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, vorsichtig zu erklären? –

-1

können Sie diesen Versuchen Sie auch: -

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

Was ist der Sinn des Umschließens von 'indexList.index' in einem Lambda? –

+0

@StefanPochmann, ja du kannst das auch map (indexList.index, name) schreiben, wenn du kein lambda schreiben musst –

2

ich es geschafft, das Skript sehr leicht zu ändern und es sieht ok:

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) 

Sie können sehen, es ist sehr simmilar ist, dass statt nur Sache ist für das nächste Element hinzugefügt wird Nummer i-Nummer für CURRENT Element hinzufügen. Das ist alles. Oh, und sortieren. Es sortiert zuerst das Kapital, dann in diesem Beispiel in Kleinbuchstaben, Sie können mit sort(key= lambda:x ...) spielen, wenn Sie das ändern möchten. (Vielleicht so: names.sort(key = lambda x: (x.upper() if x.lower() == x else x.lower())) )

3

Um es generischer zu machen, können Sie es in einer Funktion umhüllen, so dass diese fest codierten Werte keinen Schaden verursachen, weil sie lokal sind.

Wenn Sie effiziente Lookup-Container verwenden (Ich werde ein einfaches Wörterbuch verwenden) Sie ohne Verlust zu viel Leistung den ersten Index jeder Saite halten:

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 

Und dies wird die Indizes um zuweisen (auch wenn das nicht wichtig):

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

Dies ist nur eine Iteration über die Liste der Strings benötigt, die es ermöglicht, auch Prozess-Generatoren und ähnliche.

6

Wenn die Bedingung ist, dass die Zahlen eindeutig sind und die genaue Nummer nicht wichtig ist, dann können Sie eine Zuordnung jedes Element in der Liste zu einer eindeutigen Zahl im laufenden Betrieb erstellen, Werte aus einem Zählerobjekt zuweisen:

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] 

Sie könnten mit den zusätzlichen Namen abschaffen durch map auf der Liste mit und einer Zählung Objekt und die Einstellung der Kartenfunktion als {}.setdefault (siehe @ StefanPochmann Kommentar):

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] 

Als Extra können Sie auch np.unique, verwenden, falls Sie bereits numpy installiert:

import numpy as np 

_, numbers = np.unique(names, return_inverse=True) 
print(numbers) 
# [3 3 2 2 1 1 1 0 3] 
+4

Du brauchst keine extra Variablen wenn du 'list (map ({}. Setdefault, name, count())) '. –

+0

@StefanPochmann Ziemlich ordentlich! –

+0

In der ersten Lösung können Sie 'len (d)' anstelle von 'next (c)' verwenden, a la: 'numbers = [d.setdefault (i, len (d)) für i in Namen] – RootTwo

3

Wenn Sie k unterschiedliche Werte haben, diese ordnet sie ganze Zahlen 0-k-1 in der Reihenfolge der erster Auftritt:

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

Hier ist eine ähnliche factorizing Lösung mit collections.defaultdict und 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] 

Jedes neues Vorkommen rufen die nächste ganze Zahl in itertools.count und fügen neuen Eintrag dd.

Verwandte Themen