У меня есть текстовый файл размера приблизительно 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
Так как вход, кажется уже, группируется/сортируется 2-м столбцом, это должно быть довольно просто и не требует, чтобы сохранить и отсортировать весь набор данных в памяти, только двух записях во время 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 и steeldriver для наборов данных, которые не вписываются в оперативную память.
При сортировке их в порядке убывания 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
Если у Вас нет слишком многих уникальных Cell_ids, Вы могли бы отслеживать уже замеченные в ассоциативном массиве Perl. Если у Вас действительно есть слишком многие (и мой сценарий Perl исчерпывает память), запишите a 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