Малювання гістограми з виводу команди 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, яка дозволила б мені це зробити?


1
bashplotlib - приємне рішення
Майкл Міор

Це дійсно один із ризиків надання посилань замість самостійних відповідей. Якщо видалена відповідь 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)'Це виглядає справді чудово :) дякую
Natim

12

В perl:

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

В bash(натхненний цією відповіддю ):

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Версія не працює тут, наскільки я можу судити.
Натим

@Natim тут, де?
муру

@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");}'

Маленький сценарій вище передбачає, що дані містяться у файлі, образно названому "дані".

Я не надто задоволений лінією "запустити її через сортування та сортування" - це було б зайвим, якби у місяця та дня у місяці завжди було 2 цифри, але це життя.

Також, як історична примітка, традиційні Unixes використовувались з утилітою для побудови командного рядка, яка могла робити досить потворні графіки та графіки ASCII. Я не можу згадати ім'я, але схоже, що GNU plotutils замінює стару традиційну утиліту.


Чи не повинно бути це if ($1 in count) ...?
muru

1
@muru - здається, працює в будь-якому випадку. Однак я знайшов друкарський помилок у пункті "else". Спасибі.
Брюс Едігер

1

Приємні вправи тут. Я скидав дані у файл під назвою "дані", тому що я дуже творчий.

Ну, ти просив це в басі ... ось воно в чистому басі.

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, і я не хотів продовжувати копіювати та вставляти файл даних, тому що я лінивий.
Неправдиві

1
Насправді я просто перечитав питання, пов'язуючи це з колегою ... Ви сказали, що у вас "вихід", а не файл даних. Таким чином, ви можете просто запустити все, що створює цей звіт, а потім передати його пробудити, і ви закінчили. Труби просто прямий вихід останньої команди як джерело введення для наступної команди.
Неправдиві

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.