У меня есть текстовый файл размером около 25 ГБ. Я хочу удалить дубликаты строк на основе значения во втором столбце. Если в файле будут найдены дубликаты, я хочу удалить все строки с этим значением в столбце и оставить только одну строку с самым высоким значением в четвертом столбце. Файл в формате CSV и уже отсортирован.
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
В приведенном выше примере мне нужно всего лишь одно максимальное значение всплеска для каждого, Cell_Id
удалив другие дубликаты строк.
Ожидаемый результат:
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
решение1
Поскольку входные данные, по-видимому, уже сгруппированы/отсортированы по второму столбцу, это должно быть довольно просто ине делаеттребуется хранить и сортировать весь набор данных в памяти, только две записи за раз. 1
Сначала я подумал о решении на Awk, но обнаружил, что оно слишком неуклюже для работы с массивами и непустыми разделителями полей. Тогда я решил использовать короткую программу на Python:
#!/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()
На моей системе пропускная способность составляет около 250 000 записей или 5 МБ в секунду ЦП.
Применение
python3 remove-duplicates.py < input.txt > output.txt
Программа не может работать с заголовками столбцов, поэтому их нужно удалить:
tail -n +2 < input.txt | python3 remove-duplicates.py > output.txt
Если вы хотите добавить их обратно в результат:
{ read -r header && printf '%s\n' "$header" && python3 remove-duplicates.py; } < input.txt > output.txt
1 Это одно из главных преимуществ по сравнению сwaltinator'sистальной водитель подходы для наборов данных, которые не помещаются в основную память.
решение2
Если бы вы их отсортировалиуменьшениепорядок 4-го поля, вы могли бы просто взять первое вхождение каждого значения 2-го поля, используя ассоциативный массив или хэш, например, awk -F, '!seen[$2]++' file
илиperl -F, -ne 'print $_ unless $seen{$F[1]}++'
При наличии значений в порядке возрастания сделать это за один эффективный проход немного сложнее — это можно сделать (с небольшой настройкой), печатая предыдущую строку каждый раз, когда изменяется значение ключа:
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
решение3
Если у вас не так много уникальных Cell_id, вы можете отслеживать уже просмотренные в ассоциативном массиве Perl. Если у вас их слишком много (и мой скрипт Perl исчерпывает память), напишите программу C
для сохранения уникальных в битовом поле. Вот 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;
}
Вот мой тест:
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