2016-09-12 26 views
7

Mam plik składający się ze słów, jednego słowa w każdym wierszu. Plik wygląda następująco:TypeError: obiekt str nie jest iteratorem

aaa 
bob 
fff 
err 
ddd 
fff 
err 

Chcę policzyć częstotliwość pary słów, które występują jedna po drugiej.

Przykładowo

aaa,bob: 1 
bob,fff:1 
fff,err:2 

i tak dalej. Próbowałem to

f=open(file,'r') 
content=f.readlines() 
f.close() 
dic={} 
it=iter(content) 
for line in content: 
    print line, next(line); 
    dic.update({[line,next(line)]: 1}) 

mam błąd:

TypeError: str object is not an iterator 

Potem spróbował wykorzystać iterator:

it=iter(content) 
for x in it: 
    print x, next(x); 

Mam ten sam błąd ponownie. Proszę o pomoc!

+3

Jak sądzisz, co "następna rzecz"? Nie oznacza to "rzeczy, która przychodzi po rzeczy". – user2357112

+0

Na stronie: Jednym z rozwiązań jest: 'pprint.pprint (collections.Counter (zip (zawartość [0:], treść [1:])) most_common())' –

+0

@ user2357112: To, co myślałem, że następny był! Co dalej? Python doc mówi: "Pobierz następny element z iteratora, wywołując jego metodę next(). Jeśli podana jest wartość domyślna, jest zwracana, jeśli iterator jest wyczerpany, w przeciwnym razie podniesiona zostanie wartość StopIteration. – rowana

Odpowiedz

6

Musisz tylko śledzić poprzedni wiersz, obiekt pliku zwraca iterator, więc nie potrzebujesz iter lub readlines w ogóle nazwać następny raz na samym początku do tworzenia zmiennej prev potem po prostu zachować aktualizowania prev w pętli:

from collections import defaultdict 

d = defaultdict(int) 

with open("in.txt") as f: 
    prev = next(f).strip() 
    for line in map(str.strip,f): # python2 use itertools.imap 
     d[prev, line] += 1 
     prev = line 

co dałoby ci:

defaultdict(<type 'int'>, {('aaa', 'bob'): 1, ('fff', 'err'): 2, ('err', 'ddd'): 1, ('bob', 'fff'): 1, ('ddd', 'fff'): 1}) 
+1

To doskonały przykład użycia iteratora. Pomyśl o 'następnym' rodzaju '[] .pop()': zwraca następną wartość, ale także usuwa ją z iteratora. Wielkim tego pożytkiem jest pseudo-redukcja w następujący sposób: uzyskaj wartość początkową, a następnie przeprowadź pętlę nad resztą. –

-1

Twoja wartość x zawiera ciąg "ddd/ccc/etc". to nie następna. next() należy do iteratora i służy do uzyskania następnego elementu z iteratora. Prawidłowy sposób nazwać to it.next()

it=iter(content) 
for x in it: 
    print x, it.next(); 

Ale dostaniesz wyjątek po zakończeniu spożywać wszystkie elementy w iteracyjnej. Musisz więc złapać wyjątek StopIteration.

for x in it: 
    try: 
     line, next_line = x, it.next() 
     # do your count logic overhere 
    except StopIteration: 
     break 

dic.update({[line,next_line]: 1}) nie działa. Pominiesz możliwe kombinacje.

+1

Czy nie powinno to być 'next (it)'? –

+0

@PavelGurkov it.next() działa również. – levi

+0

@levi, dziękuję. Działało idealnie. Niestety nie mam wystarczającej liczby kredytów, aby móc głosować. Kiedy to zrobię, zrobię to! – rowana

3

line, jak wszystkie strs, jest iter stanie, co oznacza, że ​​jest to sposób __iter__. Ale next współpracuje z iterem atorami, które mają metodę __next__ (w Pythonie 2 jest to metoda next). Kiedy interpreter wykonuje next(line), próbuje wywołać line.__next__. Ponieważ line nie ma metody __next__, podnosi ona TypeError: str object is not an iterator.

Od line jest iter stanie i ma metodę __iter__, możemy ustawić it = iter(line). it to iter ator z metodą __next__, a next(it) zwraca następną literę z line. Ale szukasz następnego wiersza w pliku, więc spróbuj coś takiego:

from collections import defaultdict 

dic = defaultdict(int) 
with open('file.txt') as f: 
    content = f.readlines() 
    for i in range(len(content) - 1): 
     key = content[i].rstrip() + ',' + content[i+1].rstrip() 
     dic[key] += 1 

for k,v in dic.items(): 
    print(k,':',v) 

Wyjście (plik.txt jak w OP)

err,ddd : 1 
ddd,fff : 1 
aaa,bob : 1 
fff,err : 2 
bob,fff : 1 
0

Jak inni wspomniano, można użyj linii next w linii, która jest ciągiem znaków.Można użyć itertools.tee stworzyć dwa niezależne iteratora z obiektu pliku, a następnie użyć collections.Counter i zip utworzyć obiekt licznik z parami linii

from itertools import tee 
from collections import Counter 
with open('test.txt') as f: 
    # f = (line.rstrip() for line in f) # if you don't want the trailing new lines 
    f, ne = tee(f) 
    next(ne) 
    print(Counter(zip(f, ne))) 

uwagę, że ponieważ przedmiotem Plik zawiera linie z nowej linii na ich ciągnięcie, jeśli nie chcesz, możesz usunąć linie.

3
from collections import Counter 
with open(file, 'r') as f: 
    content = f.readlines() 
result = Counter((a, b) for a, b in zip(content[0:-1], content[1:])) 

Będzie to słownik, którego kluczami są pary wierszy (w kolejności) i których wartości są liczbą razy, kiedy para wystąpiła.

1

W innym powiedział linia jest ciągiem i w ten sposób nie może być stosowany z kolejnego() metody. Ponadto nie można użyć listy jako klucza do słownika, ponieważ są one nieosiągalne. Zamiast tego możesz użyć krotki. Proste rozwiązanie:

f=open(file,'r') 
content=f.readlines() 
f.close() 

dic={} 

for i in range(len(content)-1): 
    print(content[i], content[i+1]) 
    try: 
     dic[(content[i], content[i+1])] += 1 
    except KeyError: 
     dic[(content[i], content[i+1])] = 1 

zauważyć również, że za pomocą readlines() również utrzymać '\ n' z każdej linii. Może chcesz rozebrać go najpierw:

content = [] 
    with open(file,'r') as f: 
     for line in f: 
      content.append(line.strip('\n')) 
1

Można użyć 2 Linia deque i Counter:

from collections import Counter, deque 

lc=Counter() 
d=deque(maxlen=2) 
with open(fn) as f: 
    d.append(next(f)) 
    for line in f: 
     d.append(line) 
     lc+=Counter(["{},{}".format(*[e.rstrip() for e in d])]) 

>>> lc 
Counter({'fff,err': 2, 'ddd,fff': 1, 'bob,fff': 1, 'aaa,bob': 1, 'err,ddd': 1}) 

Można również użyć regex z przechwytywania patrzeć w przyszłość:

with open(fn) as f: 
    lc=Counter((m.group(1)+','+m.group(2),) for m in re.finditer(r"(\w+)\n(?=(\w+))", f.read())) 
+0

Czy mogę zapytać, dlaczego głosowanie w dół? – dawg