Tengo un archivo de texto de tamaño aprox. 25 GB. Quiero eliminar las filas duplicadas según el valor de la segunda columna. Si se encuentran duplicados en un archivo, quiero eliminar todas las filas con ese valor en la columna y mantener solo una fila con el valor más alto en la cuarta columna. El archivo está en formato CSV y ya está ordenado.
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
En el ejemplo anterior, solo quiero un valor de aumento máximo para cada uno Cell_Id
eliminando otras filas duplicadas.
El resultado esperado es:
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
Respuesta1
Dado que la entrada parece estar agrupada/ordenada por la segunda columna, esto debería ser bastante simple ynorequieren mantener y ordenar todo el conjunto de datos en la memoria, solo dos registros a la vez. 1
Primero pensé en una solución Awk, pero me resultó demasiado complicado lidiar con matrices y delimitadores de campos que no estaban en blanco. Luego me decidí por un programa Python corto:
#!/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()
En mi sistema tiene un rendimiento de ~250.000 registros o 5 MB por segundo de CPU.
Uso
python3 remove-duplicates.py < input.txt > output.txt
El programa no puede manejar encabezados de columna, por lo que debes eliminarlos:
tail -n +2 < input.txt | python3 remove-duplicates.py > output.txt
Si desea volver a agregarlos al resultado:
{ read -r header && printf '%s\n' "$header" && python3 remove-duplicates.py; } < input.txt > output.txt
1 Esta es una gran ventaja sobreWaltinatoryconductor de acero enfoques para conjuntos de datos que no caben en la memoria principal.
Respuesta2
Si los hubieras ordenadodecrecienteorden del cuarto campo, simplemente podría haber tomado la primera aparición de cada valor del segundo campo usando una matriz asociativa o hash, por ejemplo, awk -F, '!seen[$2]++' file
operl -F, -ne 'print $_ unless $seen{$F[1]}++'
Con los valores en orden creciente, es un poco más complicado hacerlo en una sola pasada eficiente; puede hacerlo (con un poco de configuración) imprimiendo la línea anterior cada vez que cambia el valor clave:
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
Respuesta3
Si no tiene demasiados Cell_ids únicos, puede realizar un seguimiento de los ya vistos en una matriz asociativa de Perl. Si tiene demasiados (y mi script Perl se queda sin memoria), escriba un C
programa para mantener los únicos en un campo de bits. Aquí está el 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;
}
Aquí está mi prueba:
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