Entfernen doppelter Zeilen basierend auf dem Spaltenwert

Entfernen doppelter Zeilen basierend auf dem Spaltenwert

Ich habe eine Textdatei mit einer Größe von ca. 25 GB. Ich möchte die doppelten Zeilen basierend auf dem Wert in der zweiten Spalte löschen. Wenn in einer Datei Duplikate gefunden werden, möchte ich alle Zeilen mit diesem Wert in der Spalte löschen und nur eine Zeile mit dem höchsten Wert in der vierten Spalte behalten. Die Datei liegt im CSV-Format vor und ist bereits sortiert.

storm_id,Cell_id,Windspeed,Storm_Surge,-1
2,10482422,45,0.06,-1
2,10482422,45,0.18,-1
2,10482422,45,0.4,-1
2,10482423,45,0.15,-1
2,10482423,45,0.43,-1
2,10482424,45,0.18,-1
2,10482424,45,0.49,-1
2,10482425,45,0.21,-1
2,10482425,45,0.52,-1
2,10482426,45,0.27,-1
2,10482426,45,0.64,-1
2,10482427,45,0.09,-1
2,10482427,45,0.34,-1
2,10482427,45,0.73,-1

Im obigen Beispiel möchte ich nur einen maximalen Anstiegswert für jeden, Cell_Idindem ich andere doppelte Zeilen lösche

Die erwartete Ausgabe ist:

2,10482422,45,0.4,-1
2,10482423,45,0.43,-1
2,10482424,45,0.49,-1
2,10482425,45,0.52,-1
2,10482426,45,0.64,-1
2,10482427,45,0.73,-1

Antwort1

Da die Eingabe bereits nach der zweiten Spalte gruppiert/sortiert zu sein scheint, sollte dies recht einfach sein undnichterfordern, den gesamten Datensatz im Speicher zu behalten und zu sortieren, nur zwei Datensätze gleichzeitig. 1

Ich dachte zuerst an eine Awk-Lösung, fand sie aber zu umständlich für den Umgang mit Arrays und nicht leeren Feldtrennzeichen. Dann entschied ich mich für ein eher kurzes Python-Programm:

#!/usr/bin/python3
import sys
DELIMITER = ','

def remove_duplicates(records):
    prev = None
    for r in records:
        r = (int(r[0]), int(r[1]), int(r[2]), float(r[3]), int(r[4]))
        if prev is None:
            prev = r
        elif r[1] != prev[1]:
            yield prev
            prev = r
        elif r[3] > prev[3]:
            prev = r
    if prev is not None:
        yield prev

def main():
    for r in remove_duplicates(
        l.rstrip('\n').rsplit(DELIMITER) for l in sys.stdin
    ):
        print(*r, sep=',')

if __name__ == '__main__':
    main()

Auf meinem System hat es einen Durchsatz von ~250.000 Datensätzen oder 5 MB pro CPU-Sekunde.

Verwendung

python3 remove-duplicates.py < input.txt > output.txt

Das Programm kann mit Spaltenüberschriften nicht umgehen, deshalb müssen Sie diese entfernen:

tail -n +2 < input.txt | python3 remove-duplicates.py > output.txt

Wenn Sie sie wieder zum Ergebnis hinzufügen möchten:

{ read -r header && printf '%s\n' "$header" && python3 remove-duplicates.py; } < input.txt > output.txt

1 Dies ist ein großer Vorteil gegenüberWaltinatorsUndStahltreiber Ansätze für Datensätze, die nicht in den Hauptspeicher passen.

Antwort2

Wenn Sie sie sortiert hätten inabnehmendReihenfolge des 4. Feldes, Sie hätten einfach das erste Vorkommen jedes 2. Feldwertes mithilfe eines assoziativen Arrays oder Hashs nehmen können, z. B. awk -F, '!seen[$2]++' fileoderperl -F, -ne 'print $_ unless $seen{$F[1]}++'

Bei den Werten in aufsteigender Reihenfolge ist es etwas schwieriger, dies in einem effizienten Einzeldurchgang zu tun. Sie können dies (mit ein wenig Einrichtung) tun, indem Sie bei jeder Änderung des Schlüsselwerts die vorherige Zeile drucken:

awk -F, '
  NR==1 {print; next}        # print the header line
  NR==2 {key=$2; next}       # initialize the comparison
  $2 != key {
    print lastval; key = $2  # print the last (largest) value of the previous key group
  } 
  {lastval = $0}             # save the current line
  END {print lastval}        # clean up
' file
storm_id,Cell_id,Windspeed,Storm_Surge,-1
2,10482422,45,0.4,-1
2,10482423,45,0.43,-1
2,10482424,45,0.49,-1
2,10482425,45,0.52,-1
2,10482426,45,0.64,-1
2,10482427,45,0.73,-1

Antwort3

Wenn Sie nicht zu viele eindeutige Cell_ids haben, können Sie die bereits gesehenen in einem assoziativen Perl-Array nachverfolgen. Wenn Sie zu viele haben (und mein Perl-Skript nicht genügend Speicher hat), schreiben Sie ein CProgramm, um eindeutige in einem Bitfeld zu speichern. Hier ist das Perl.

#!/usr/bin/perl -w
use strict;
my %seen = ();          # key=Cell_ID, value=1
my @cols=();            # for splitting input

while( <> ) {           # read STDIN
  @cols = split ',',$_;
  next if ( defined $seen{$cols[1]}); # skip if we already saw this Cell_Id
  $seen{$cols[1]} = 1;
  print;
}

Hier ist mein Test:

walt@bat:~(0)$ cat u.dat
storm_id,Cell_id,Windspeed,Storm_Surge,-1
2,10482422,45,0.06,-1
2,10482422,45,0.18,-1
2,10482422,45,0.4,-1
2,10482423,45,0.15,-1
2,10482423,45,0.43,-1
2,10482424,45,0.18,-1
2,10482424,45,0.49,-1
2,10482425,45,0.21,-1
2,10482425,45,0.52,-1
2,10482426,45,0.27,-1
2,10482426,45,0.64,-1
2,10482427,45,0.09,-1
2,10482427,45,0.34,-1
2,10482427,45,0.73,-1
walt@bat:~(0)$ perl ./unique.pl u.dat
storm_id,Cell_id,Windspeed,Storm_Surge,-1
2,10482422,45,0.06,-1
2,10482423,45,0.15,-1
2,10482424,45,0.18,-1
2,10482425,45,0.21,-1
2,10482426,45,0.27,-1
2,10482427,45,0.09,-1

verwandte Informationen