Как группировать переменные по временному интервалу? [закрыто]

Мой fb.csv файл находится как показано ниже:

"Source","Time"  
"192.168.137.174","120025"
"10.0.138.163","120525"
"157.240.10.13","121036"
"157.240.10.13","122536"
"157.240.10.23","123041"
"157.240.10.23","123241"
"10.0.138.163","123352"
"192.168.137.174","123952"
"157.240.10.18","124152"
"157.240.10.18","124252"
"157.240.10.23","125653"
"157.240.10.23","130053"
"192.168.137.174","130102"
"10.0.138.163","130302"
"192.168.137.174","131007"
"192.168.137.174","131352"
"157.240.10.18","132552"
"157.240.10.18","132752"
"157.240.10.23","132953"
"157.240.10.23","133253"
"192.168.137.174","133502"
"10.0.138.163","134002"
"192.168.137.174","134507"
"192.168.137.174","135752"
"157.240.10.18","140052"
"157.240.10.18","140552"
"157.240.10.23","140653"
"157.240.10.23","141053"
"192.168.137.174","141402"
"10.0.138.163","141702"
"192.168.137.174","142707"

Я хотел бы сгруппировать (подсчитать) «Источники» для каждого временного интервала 003000 (30 минут).

Пример желаемого результата:

"Time Interval","Count of Sources"
"120000","4"
"123000","7"
"130000","8"
"133000","5"
"140000","7"

Есть ли какое-нибудь возможное решение этой проблемы? {{1} } Спасибо.

1
задан 14 September 2017 в 23:04

2 ответа

Мы можем сделать это полностью использование только awk:

awk -F, 'BEGIN{print"\"Time Interval\",\"Count of Sources\""}NR>1{gsub(/"/,"",$2);h=int($2/10000)*10000;m=int(($2-h)/3000)*3000;ctr[h+m]++}END{n=asorti(ctr,idx);for(i=1;i<=n;i++){print "\""idx[i]"\",\""ctr[idx[i]]"\""}}' fb.csv

Для Вашего данного входного файла fb.csv это приводит к выводу

"120000","4"
"123000","7"
"130000","8"
"133000","5"
"140000","7"

Важный: Это требует GNU AWK (gawk) работать, потому что это использует asorti(...) функция для сортировки ассоциативных массивов их индексами. Это не работает с mawk. Можно узнать значение по умолчанию awk версия с использованием awk -Wv.


Объяснение команды:

Мы работаем awk как это здесь, устанавливая разделителя полей, который разграничивает столбцы к , и использование файла fb.csv как введено:

awk -F, '<COMMAND>' fb.csv

awk команда (заполнитель <COMMAND> выше), это, после надлежащего форматирования:

BEGIN {
    print "\"Time Interval\",\"Count of Sources\""
}
NR>1 {
    gsub(/"/, "", $2)
    h = int($2 / 10000) * 10000
    m = int(($2-h) / 3000) * 3000
    ctr[h+m]++
}
END {
    n = asorti(ctr, idx)
    for(i=1; i<=n; i++) {
        print "\"" idx[i] "\",\"" ctr[idx[i]] "\""
    }
}

Это выглядит ужасно сложным (и я не могу отклонить это, требуется некоторая мысль для понимания), таким образом, я попытаюсь разбить его немного:

BEGIN { ... } блок кода будет выполняться однажды, первая строка входа из файла читается. Затем для каждого, но первой строки ("номер строки, больше, чем 1"), NR>1 { ... } блок выполняется. Наконец после того, как весь вход читается, END { ... } блок будет работать.

  • Теперь BEGIN блок довольно прост, он только печатает новую строку заголовка CSV.

  • Давайте посмотрим на NR>1 блок. Помните это awk разделения каждая строка в поля, которые были разделены разделителем полей (тот мы устанавливаем на , использование -F аргумент). Первый столбец/поле будет сохранен в переменной $1, второе в $2 и так далее. Мы только интересуемся значением второго поля, которое содержит время.

    Используя gsub(<PATTERN>, <REPLACEMENT>, <VARIABLE>) функция, мы заменяем все случаи <PATTERN> (регулярное выражение включило в наклонные черты, здесь оно просто соответствует только кавычкам) с a <REPLACEMENT> (пустой, поскольку мы хотим удалить их), строка в <VARIABLE> ($2 т.е. второй зарегистрированный, содержащий время здесь).

    Затем мы декодируем метку времени в целые целые часы h (умноженный с 10 000) и целое полчаса m (без целых часов; умноженный с 3 000). Мы используем ассоциативный массив ctr как счетчик как часто округленная метка времени h+m происходит во входе.

  • Наконец в END блок, мы распечатываем значения счетчика, отсортированные по округленным индексам метки времени.

1
ответ дан 15 September 2017 в 09:04

Я уверен, что есть более элегантный способ, но я предлагаю создать исполняемый файл, назовем его counter.bash, с таким содержанием скрипта:

#!/bin/bash

echo '"Time Interval","Count of Sources"'
FILTRED=$(tail -n +2 "$1" | sed -e 's/^.*\,//' -e 's/"//g' | sort)
T=1

while [ $T -lt 24 ]; do

        ((T++)); R1=0; R2=0

        for i in $FILTRED; do

                hour=${i::-4}; minute=${i:2:-2}

                if [ "$T" -lt "10" ]; then TT="0${T}"; else TT="${T}"; fi

                if [ "$minute" -lt "30" ]; then
                        if [ "$hour" == "$TT" ]; then ((R1++)); fi
                else
                        if [ "$hour" == "$TT" ]; then ((R2++)); fi
                fi
        done

        if [ "$R1" -ne "0" ]; then echo "\"${TT}0000\",\"$R1\""; fi
        if [ "$R2" -ne "0" ]; then echo "\"${TT}3000\",\"$R2\""; fi
done

Затем запустите:

./counter.bash fb.csv

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

./counter.bash fb.csv > fb.counted.csv

Я сравнил производительность ответа Byte Commander и этого сценария, оба применяются к одному и тому же не такой огромный файл. Они просто несопоставимы:

$ cat fb.csv | wc -l
3304


$ time awk -F, 'BEGIN{print"\"Time Interval\",\"Count of Sources\""}NR>1{gsub(/"/,"",$2);h=int($2/10000)*10000;m=int(($2-h)/3000)*3000;ctr[h+m]++}END{n=asorti(ctr,idx);for(i=1;i<=n;i++){print "\""idx[i]"\",\""ctr[idx[i]]"\""}}' fb.csv

"Time Interval","Count of Sources"
"120000","672"
"123000","672"
"130000","560"
"133000","560"
"140000","839"

real    0m0.017s
user    0m0.012s
sys     0m0.000s


$ time ./counter.bash fb.csv

"Time Interval","Count of Sources"
"120000","672"
"123000","672"
"130000","560"
"133000","560"
"140000","839"

real    0m2.374s
user    0m2.368s
sys     0m0.000s

Когда файл действительно огромен, мой скрипт просто вылетает (по разным строкам) через долгое время:

$ cat fb.csv | wc -l
9303745


$ time awk -F, 'BEGIN{print"\"Time Interval\",\"Count of Sources\""}NR>1{gsub(/"/,"",$2);h=int($2/10000)*10000;m=int(($2-h)/3000)*3000;ctr[h+m]++}END{n=asorti(ctr,idx);for(i=1;i<=n;i++){print "\""idx[i]"\",\""ctr[idx[i]]"\""}}' fb.csv

"Time Interval","Count of Sources"
"120000","1892288"
"123000","1892290"
"130000","1576904"
"133000","1576905"
"140000","2365357"

real    0m23.193s
user    0m23.080s
sys     0m0.096s


$ time ./counter.bash fb.csv

"Time Interval","Count of Sources"
./counter.bash: line 17: [: .0.138.1: integer expression expected
^C
real    2m27.992s
user    2m27.940s
sys     0m1.636s
0
ответ дан 14 September 2017 в 23:04

Другие вопросы по тегам:

Похожие вопросы: