2016-09-01 13 views
11

Czytam duży plik z setkami tysięcy par liczbowych reprezentujących krawędzie wykresu. Podczas tworzenia chcę tworzyć dwie listy: jedną z przednimi i jedną z odwróconą.Twórz 2 listy za jednym razem podczas odczytu z pliku, pythonowo

Obecnie robię wyraźną pętlę for, ponieważ muszę wykonać pewne wstępne przetwarzanie na liniach, które przeczytałem. Zastanawiam się jednak, czy istnieje bardziej pythonic podejście do budowania tych list, takich jak listy zrozumienia, itp.

Ale, jak mam 2 listy, nie widzę sposobu, aby wypełnić je za pomocą zrozumienia bez czytania plik dwa razy.

Moje kodu teraz to:

with open('SCC.txt') as data: 
    for line in data: 
     line = line.rstrip() 
     if line: 
      edge_list.append((int(line.rstrip().split()[0]), int(line.rstrip().split()[1]))) 
      reversed_edge_list.append((int(line.rstrip().split()[1]), int(line.rstrip().split()[0]))) 

Odpowiedz

11

chciałbym zachować swoją logikę jak to jest w Pythonie ONIC podejście po prostu nie split/rstrip tej samej linii wielokrotnie:

with open('SCC.txt') as data: 
    for line in data: 
     spl = line.split() 
     if spl: 
      i, j = map(int, spl) 
      edge_list.append((i, j)) 
      reversed_edge_list.append((j, i)) 

Wywołanie rstrip kiedy już nazywany jest zbędny w sobie jeszcze bardziej, gdy jesteś dzielenie jako że już usunąć białe spacje, więc dzielenie tylko raz oznacza, że ​​oszczędzasz, wykonując wiele niepotrzebnej pracy.

Można również użyć csv.reader do odczytywania danych i filtrowanie pustych wierszy raz masz pojedynczy białymi ograniczającej:

from csv import reader 

with open('SCC.txt') as data: 
    edge_list, reversed_edge_list = [], [] 
    for i, j in filter(None, reader(data, delimiter=" ")): 
     i, j = int(i), int(j) 
     edge_list.append((i, j)) 
     reversed_edge_list.append((j, i)) 

Lub jeśli istnieje wiele spacji ograniczające można użyć map(str.split, data):

for i, j in filter(None, map(str.split, data)): 
     i, j = int(i), int(j) 

Cokolwiek wybierzesz, będzie szybsze niż dwukrotne przeszukiwanie danych lub wielokrotne dzielenie linii samouczków.

+0

Właśnie robię 'rstrip()', aby usunąć końcowe znaki '\ r \ n' w pustych wierszach. Bez niego twój kod jest pomyłki z 'i, j = map (int, spl) ValueError: niewystarczające wartości do rozpakowania (oczekiwane 2, ale otrzymałeś 0)' –

+1

@NickSlavsky, to powinno być 'if spl', spl będzie pusty dla wszystkich linii z tylko białymi odstępami –

+0

whoops, sam tego nie wymyśliłem. Dziękuję Ci! Nawiasem mówiąc, twoje rozwiązanie okazało się prawie 2 razy szybsze niż mój początkowy kod ze zbędnymi podziałami. –

5

Ty nie może utworzyć dwie listy w jednej ze zrozumieniem, więc, zamiast robić te same operacje, dwa razy na dwóch listach, jedna opcja opłacalne byłoby zainicjuj jeden z nich, a następnie utwórz drugi, odwracając każdy wpis w pierwszym. W ten sposób nie wykonujesz iteracji po pliku dwukrotnie.

W tym celu można utworzyć pierwszą listę edge_list ze zrozumieniem (nie wiem, dlaczego nazywa rsplitponownie na nim):

edge_list = [tuple(map(int, line.split())) for line in data] 

A teraz przejść przez każdego wpisu i odwrócić ją [::-1] w celu utworzenia odwróconego rodzeństwa reverse_edge_list.

Wykorzystując dane makiety dla edge_list:

edge_list = [(1, 2), (3, 4), (5, 6)] 

cofania go mogłoby wyglądać tak:

reverse_edge_list = [t[::-1] for t in edge_list] 

Które teraz wygląda:

reverse_edge_list 
[(2, 1), (4, 3), (6, 5)] 
+0

mógłbym zasugerować '[krotka (mapa (line.split())) ...] 'który wygląda trochę czystszy. –

3

Może nie jaśniejsze, ale krócej:

with open('SCC.txt') as data: 
    process_line = lambda line, r: (int(line.rstrip().split()[r]), int(line.rstrip().split()[1-r])) 

    edge_list, reverved_edge_list = map(list, zip(*[(process_line(line, 0), process_line(line, 1)) 
                for line in data 
                if line.rstrip()])) 
+0

hej, nie podsuwał twojej odpowiedzi, działałem w ten sam sposób, +1 – Netwave

3

Nadchodzi rozwiązanie

plik testowy:

In[19]: f = ["{} {}".format(i,j) for i,j in zip(xrange(10), xrange(10, 20))] 
In[20]: f 
Out[20]: 
['0 10', 
'1 11', 
'2 12', 
'3 13', 
'4 14', 
'5 15', 
'6 16', 
'7 17', 
'8 18', 
'9 19'] 

One liner korzystania ze zrozumieniem, ZIP i mapę:

In[27]: l, l2 = map(list,zip(*[(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f])) 
In[28]: l 
Out[28]: 
[(0, 10), 
(1, 11), 
(2, 12), 
(3, 13), 
(4, 14), 
(5, 15), 
(6, 16), 
(7, 17), 
(8, 18), 
(9, 19)] 
In[29]: l2 
Out[29]: 
[(10, 0), 
(11, 1), 
(12, 2), 
(13, 3), 
(14, 4), 
(15, 5), 
(16, 6), 
(17, 7), 
(18, 8), 
(19, 9)] 

Wyjaśnienie ning z [(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f] budujemy listę zawierającą krotki parowania z krotek pary i jej odwróconych formach:

In[24]: [(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f] 
Out[24]: 
[((0, 10), (10, 0)), 
((1, 11), (11, 1)), 
((2, 12), (12, 2)), 
((3, 13), (13, 3)), 
((4, 14), (14, 4)), 
((5, 15), (15, 5)), 
((6, 16), (16, 6)), 
((7, 17), (17, 7)), 
((8, 18), (18, 8)), 
((9, 19), (19, 9))] 

applaying zip do rozpakowania formie Podzieliliśmy krotki wewnątrz głównej krotki, więc mamy 2 krotki zawierające par krotki w pierwszym i odwrócone w innych:

In[25]: zip(*[(tuple(map(int, x.split())), tuple(map(int, x.split()))[::-1]) for x in f]) 
Out[25]: 
[((0, 10), 
    (1, 11), 
    (2, 12), 
    (3, 13), 
    (4, 14), 
    (5, 15), 
    (6, 16), 
    (7, 17), 
    (8, 18), 
    (9, 19)), 
((10, 0), 
    (11, 1), 
    (12, 2), 
    (13, 3), 
    (14, 4), 
    (15, 5), 
    (16, 6), 
    (17, 7), 
    (18, 8), 
    (19, 9))] 

Prawie tam, wystarczy użyć map przekształcić że krotki na listach.

EDIT: jak poprosił @PadraicCunningham, filtrowania pustych linii, wystarczy dodać if x w pojmowaniu [ ... for x in f if x]

+0

Wypróbuj za pomocą pliku z pustymi liniami –

+0

po prostu odfiltruj puste linie z filtrem lub w zrozumieniu @PadraicCunningham – Netwave