Ponowne indeksowanie dużego pliku CSV

11

Przejrzałem odpowiedzi w tym pomocnym wątku , ale mój problem wydaje się na tyle inny, że nie mogę wymyślić dobrej odpowiedzi (przynajmniej z sed).

Mam duży plik CSV (ponad 200 GB) z wierszami, które wyglądają następująco:

<alphanumerical_identifier>,<number>

gdzie <alphanumerical_identifier>jest unikalny w całym pliku. Chciałbym utworzyć osobny plik, który zastąpi pierwszą kolumnę indeksem , tj

<index>,<number>

abyśmy otrzymali:

1, <number>
2, <number>
3, <number>

Czy można awkwygenerować rosnący indeks bez ładowania pełnego pliku do pamięci?

Ponieważ indeks rośnie monotonicznie, może być jeszcze lepiej po prostu go upuścić. Czy rozwiązanie tego byłoby takie inne ?, tj .:

<number>
<number>
<number>
Amelio Vazquez-Reina
źródło
Nie jestem pewien co do wykonalności tego rozwiązania. Ale co powiesz na wygenerowanie tylu liczb, jak w pliku CSV w osobnym pliku, a następnie dołączenie do tego pliku drugiej kolumny pliku CSV?
Ramesh
@Ramesh To jest w porządku, o ile dane wyjściowe są prawidłowe.
Amelio Vazquez-Reina
2
Podejrzewam, że coś źle zrozumiałem; inaczej awk -F, '{print ++n, $2}'działałoby. Lub awk -F, '{print $2}'dla drugiej odmiany.
G-Man mówi „Reinstate Monica”
2
@ G-Man, to chyba FNRtyle, ale i tak będzie dobrze++n
iruvar
1
Potrójnie sprawdzam, czy naprawdę możesz pozbyć się tego Uniq Identifier ... dlaczego nie dodać pierwszej (trzeciej) kolumny z indeksem, ale nadal zachować identyfikator? czy ten identyfikator nie jest używany nigdzie indziej?
Olivier Dulac,

Odpowiedzi:

13

Nie w pobliżu terminala do przetestowania, ale co powiesz na często pomijane nlpolecenie? Coś jak:

cut -f 2 -d , original.csv | nl -w 1 -p -s , > numbered.csv

biskup
źródło
1
PS: plik CSV o pojemności 200 GB ? Wow, i pomyślałem, że praca z North American Ported Number Database jako CSV (kilka DVD) była ogromna!
biskup
1
Działa, chociaż po numerze jest duża spacja. Zastąpiłbym go:cut -d, -f 2- /tmp/aa | nl -w 1 -p -s ,
Ángel
@Angel: Dzięki, zaktualizowałem moją odpowiedź, aby użyć opcji szerokości -w 1zamiast numerowania po lewej stronie.
biskup
Dzięki @bishop - Gdzie idą nazwy plików wejściowych i wyjściowych?
Amelio Vazquez-Reina
1
@ user815423426 Tak, cutpolecenie przed symbolem potoku ( |) da ci tylko drugą kolumnę, skutecznie mającą niejawne numery linii.
biskup
7

Oto kilka podejść, ale żadne nie zbliży się do prędkości powyższego rozwiązania cuti nl:

  1. awk

    awk -F, '{$1=NR;print $1","$2;}' file.csv > newfile.csv
  2. Perl

    perl -pe 's/[^,]+/$./' file.csv > newfile.csv

    lub

    perl -F, -ane '$F[0]=$.; print join ",", @F' file.csv
  3. Shell (ale nie polecam go do pliku 200G, zajmie to wieki)

    i=1; while IFS=, read foo num; do 
            printf "%d,%s\n" $((i++)) $num; 
    done < file.csv > newfile.csv
    

Powyższe rozwiązania są posortowane według prędkości. Przetestowałem na moim laptopie i pliku 40M, a oni wzięli (średnio 10 uruchomień) 2.2282 (awk), 2.4555 (1. perl), 3.1825s (2. perl) i aż 48.6035s dla powłoki. Bardzo mądry cuti nlrozwiązanie masz już było około 4 razy szybciej w 0.6078s.

terdon
źródło
Fajnie, dzięki za statystyki! Wynik powłoki nieco mnie zaskakuje. Jeśli zastąpi printfsię echo, czy czas znacząco poprawić?
biskup
2
Plik 40G przetworzony w 2,2228 sekund? Gdzie mogę dostać tego laptopa?
John B,
2
@JohnB umm, tak, przepraszam, to było 40
mln
Lubię resetowanie $1podejścia za pomocą awk. cutRozwiązanie jest zdecydowanie szybszy, ale to należy się spodziewać, gdyż nie zastąpić <alphanumerical_identifier>niczym. Myślę najszybszy awkwariant może być coś takiego: mawk 'BEGIN{FS=OFS=","}{$1=NR}1' file.csv > newfile.csv.
John B
@JohnB ah, tak, zgaduję, że użycie OFSzamiast jawnego drukowania ,byłoby nieco szybsze, co może przyczynić się do znacznej różnicy w dużych plikach.
terdon