у нас есть некоторые данные, такие как:

12 0
13 0 
20 0
25 1
64 4
77 1
89 100
1201 204

Я хотел бы получить этот вывод:

3 0
5 1
6 4
7 100
8 204

объяснение: у нас есть 3 AS(автономные системы), которые имеют степень 0, а затем у нас есть 5 AS, у которых степень равна 1 или меньше 1, и у нас есть 6 AS, у которых степень составляет 4 или меньше 4 и ...

Я думаю, что есть много строк (100 000), это CDF дистрибутивов, это часть парсинга данных bgpdump, и я хочу вычислить эти числа. И снова tnx для вашей помощи.

2 ответа2

1

Вот быстрый Perl-скрипт, который должен сделать всю работу за вас:

#!/usr/bin/perl
use strict;
my %result;
my @data;
my %data;
my @degrees;
my $infile = shift() || die "Usage: $0 <file>\n";

# Read source data from input file
open IN, '<', $infile
    or die "Couldn't open data file: $!\n";
while (my $line = <IN>) { chomp $line; push @data, $line; };
close IN;

# Convert data lines to hash
foreach my $line (@data) {
    my ($count, $degree) = split(/\s+/, $line);
    $data{$degree}++;
};

# Get sorted degrees for count-up iteration
@degrees = sort { $a <=> $b } keys %data;

# Iterate degrees, adding each one's system count to result for this degree
# and all higher degrees
for (my $i = 0; $i < scalar(@degrees); $i++) {
    my $degree = $degrees[$i];
    my $count = $data{$degree};
    for (my $j = $i; $j < scalar(@degrees); $j++) {
        $result{$degrees[$j]} += $count;
    };
};

# Output result counts
foreach my $degree (sort { $a <=> $b } keys %result) {
    print "$result{$degree} $degree\n";
};

Этот сценарий потребует значительной памяти для больших входных наборов данных; он обрабатывает весь входной файл перед тем, как с ним работать, потому что он не выглядит так, как будто входной файл отсортирован, и перед операцией необходимо отсортировать данные по степени. Тем не менее, это должно сделать работу для вас довольно хорошо - дайте мне знать, если это не так!

1

Вот быстрый 100% скрипт bash, который сделает эту работу:

a=()
while read _ n; do
    [[ -n $n ]] && ((++a[n]))
done < datafile.txt
c=0
for i in ${!a[@]}; do
    echo "$((c+=a[i])) $i"
done

Если вам нужен скрипт, который вы можете вызвать из командной строки:

#!/bin/bash

a=()
while read _ n; do
    [[ -n $n ]] && ((++a[n]))
done < "$1"
c=0
for i in ${!a[@]}; do
    echo "$((c+=a[i])) $i"
done

Или, если вы предпочитаете однострочку, чтобы произвести впечатление на вашу бабушку:

a=(); while read _ n; do [[ -n $n ]] && ((++a[n])); done < datafile.txt; c=0; for i in ${!a[@]}; do echo "$((c+=a[i])) $i"; done

Он работает примерно за 2-3 секунды на двухъядерном процессоре Pentium с частотой 2,6 ГГц в файле с 100000 строк.

редактировать

Пояснения:

Первый цикл:

  • Мы инициализируем a как пустой массив: a=()
  • Читаем файл datafile.txt построчно. В каждой строке два поля, только второе добавляется в имена переменных n
  • Если n не пусто (это тест [[ -n $n ]] мы увеличиваем значение n ключа массива a ; это то, что делает строка ((++a[n])) . ((...)) является арифметическим контекстом bash.
  • После прочтения всех файлов у нас есть массив a , а k поле - это ровно количество автономных систем, степень которых равна k .

Тогда второй цикл:

  • Перед циклом переменная c устанавливается в 0.
  • for i in ${!a[@]}; do будет перебирать все ключи массива a .
  • $((c+=a[i])) добавит значение a[i] к c и расширится до этого значения. Это значение echo со значением ключа, который i добавил к нему.

Надеюсь это поможет!

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .