Рисование гистограммы из вывода команды bash


31

У меня есть следующий вывод:

2015/1/7    8
2015/1/8    49
2015/1/9    40
2015/1/10   337
2015/1/11   11
2015/1/12   3
2015/1/13   9
2015/1/14   102
2015/1/15   62
2015/1/16   10
2015/1/17   30
2015/1/18   30
2015/1/19   1
2015/1/20   3
2015/1/21   23
2015/1/22   12
2015/1/24   6
2015/1/25   3
2015/1/27   2
2015/1/28   16
2015/1/29   1
2015/2/1    12
2015/2/2    2
2015/2/3    1
2015/2/4    10
2015/2/5    13
2015/2/6    2
2015/2/9    2
2015/2/10   25
2015/2/11   1
2015/2/12   6
2015/2/13   12
2015/2/14   2
2015/2/16   8
2015/2/17   8
2015/2/20   1
2015/2/23   1
2015/2/27   1
2015/3/2    3
2015/3/3    2

И я хотел бы нарисовать гистограмму

2015/1/7  ===
2015/1/8  ===========
2015/1/9  ==========
2015/1/10 ====================================================================
2015/1/11 ===
2015/1/11 =
...

Вы знаете, есть ли команда bash, которая позволила бы мне это сделать?



Это действительно один из рисков предоставления ссылок, а не отдельных ответов. Если удаленный SO-ответ полезен, пожалуйста, опубликуйте его как ответ здесь.
Джефф Шаллер

Ответы:


12

Попробуйте это в :

perl -lane 'print $F[0], "\t", "=" x ($F[1] / 5)' file

ПОЯСНЕНИЯ:

  • -aявляется явным split()в @Fмассиве, мы получаем значения с$F[n]
  • x это сказать Perl, чтобы напечатать символ N раз
  • ($F[1] / 5) : здесь мы получаем число и делим его на 5 для получения красивого вывода на печать

1
perl -lane 'print $F[0], "\t", $F[1], "\t", "=" x ($F[1] / 3 + 1)'Это выглядит действительно здорово :) спасибо
Натим

12

В perl:

perl -pe 's/ (\d+)$/"="x$1/e' file
  • eвызывает выражение для оценки, поэтому я =повторяюсь, используя значение $1(число соответствует (\d+)).
  • Вы могли бы сделать "="x($1\/3)вместо того, "="x$1чтобы получить более короткие строки. (Символ /экранирован, поскольку мы находимся в середине команды замещения.)

В bash(вдохновленный этим SO ответом ):

while read d n 
do 
    printf "%s\t%${n}s\n" "$d" = | tr ' ' '=' 
done < test.txt
  • printfдополняет вторую строку пробелами, чтобы получить ширину $n ( %${n}s), и я заменяю пробелы на =.
  • Столбцы разделяются с помощью табуляции ( \t), но вы можете сделать их более красивыми, добавив в них ссылку column -ts'\t'.
  • Вы можете использовать $((n/3))вместо того, ${n}чтобы получить более короткие строки.

Другая версия:

unset IFS; printf "%s\t%*s\n" $(sed 's/$/ =/' test.txt) | tr ' ' =

Единственный недостаток, который я вижу, это то, что вам нужно будет направить sedвывод во что-то, если вы хотите уменьшить масштаб, иначе это самый чистый вариант. Если есть вероятность, что ваш входной файл содержит один из [?*вас, следует ввести команду w / set -f;.


2
Браво за показ решения оболочки тоже. Ваше решение Perl также очень чисто.
цыплята

@mikeserv Замечательно! Я всегда забываю, %*sхотя это был первый printfтрюк, связанный с программированием на Си.
Муру

printf(sed) | trВерсия не работает здесь, насколько я могу судить.
Натим

@ Натим здесь, где?
Муру

@mikeserv ограничения в длине аргумента возможно?
Муру

6

Легко с awk

awk '{$2=sprintf("%-*s", $2, ""); gsub(" ", "=", $2); printf("%-10s%s\n", $1, $2)}' file

2015/1/7 ========
2015/1/8 =================================================
2015/1/9 ========================================
..
..

Или с моим любимым языком программирования

python3 -c 'import sys
for line in sys.stdin:
  data, width = line.split()
  print("{:<10}{:=<{width}}".format(data, "", width=width))' <file

3

Как насчет:

#! /bin/bash
histo="======================================================================+"

read datewd value

while [ -n "$datewd" ] ; do
   # Use a default width of 70 for the histogram
   echo -n "$datewd      "
   echo ${histo:0:$value}

   read datewd value
done

Который производит:

~/bash $./histogram.sh < histdata.txt
2015/1/7    ========
2015/1/8    =================================================
2015/1/9    ========================================
2015/1/10   ======================================================================+
2015/1/11   ===========
2015/1/12   ===
2015/1/13   =========
2015/1/14   ======================================================================+
2015/1/15   ==============================================================
2015/1/16   ==========
2015/1/17   ==============================
2015/1/18   ==============================
2015/1/19   =
2015/1/20   ===
2015/1/21   =======================
2015/1/22   ============
2015/1/24   ======
2015/1/25   ===
2015/1/27   ==
2015/1/28   ================
2015/1/29   =
2015/2/1    ============
2015/2/2    ==
2015/2/3    =
2015/2/4    ==========
2015/2/5    =============
2015/2/6    ==
2015/2/9    ==
2015/2/10   =========================
2015/2/11   =
2015/2/12   ======
2015/2/13   ============
2015/2/14   ==
2015/2/16   ========
2015/2/17   ========
2015/2/20   =
2015/2/23   =
2015/2/27   =
2015/3/2    ===
2015/3/3    ==
~/bash $

1

Это показалось мне забавной традиционной проблемой командной строки. Вот мое bashрешение сценария:

awk '{if (count[$1]){count[$1] += $2} else {count[$1] = $2}} \
        END{for (year in count) {print year, count[year];}}' data |
sed -e 's/\// /g' | sort -k1,1n -k2,2n -k3,3n |
awk '{printf("%d/%d/%d\t", $1,$2,$3); for (i=0;i<$4;++i) {printf("=")}; printf("\n");}'

В приведенном выше небольшом сценарии предполагается, что данные находятся в файле с оригинальным названием «данные».

Я не слишком доволен строкой «пропусти через sed и sort» - было бы ненужно, если бы в вашем месяце и дне месяца всегда было 2 цифры, но это жизнь.

Кроме того, как историческое примечание, традиционные Unixes раньше поставлялись с утилитой построения графиков командной строки, которая могла делать довольно уродливые графики и графики ASCII. Я не могу вспомнить название, но похоже, что GNU plotutils заменяет старую традиционную утилиту.


Не должно ли это быть if ($1 in count) ...?
Муру

1
@muru - похоже, работает в любом случае. Тем не менее, я нашел опечатку в предложении "else". Спасибо.
Брюс Эдигер

1

Хорошее упражнение здесь. Я поместил данные в файл под названием «данные», потому что я очень изобретательный.

Ну, вы просили об этом в bash ... здесь это в чистом bash.

cat data | while read date i; do printf "%-10s " $date; for x in $(seq 1 $i); do echo -n "="; done; echo; done

awk - лучший вариант.

awk '{ s=" ";while ($2-->0) s=s"=";printf "%-10s %s\n",$1,s }' data

Можете ли вы передать данные через awk вместо файла?
Натим

Да, в любом случае это одно и то же. Просто добавьте «данные кошки» | в начале, как у меня для битов bash, или «<данные» в конце. Или вы можете просто добавить часть awk без указания файла, вставить данные и нажать Ctrl-D в конце. Указание файла просто обрабатывает этот файл как stdin, и я не хотел продолжать копировать и вставлять файл данных, потому что я ленивый.
Falsenames

1
На самом деле, я просто перечитал вопрос, связывая это с коллегой ... вы сказали, что у вас есть "выходные данные", а не файл данных. Таким образом, вы можете просто запустить все, что создает этот отчет, затем направить его в awk, и все готово. Каналы просто направляют вывод последней команды в качестве источника ввода для следующей команды.
Falsenames

0

Попробуй это:

while read value count; do
    printf '%s:\t%s\n' "${value}" "$(printf "%${count}s" | tr ' ' '=')"
done <path/to/my-output

Единственная сложная часть - это строительство бара. Я делаю это здесь, делегируя printfи мне trнравится этот ТАК ответ .

В качестве бонуса, это POSIX- shсовместимый.

Ссылки:

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.