터미널 환경에서 gnuplot 도구를 이용한 자료 시각화

터미널 환경에서 gnuplot 도구를 이용한 자료 시각화
gnuplot
멋진 도구입니다.
쉘 프롬프트에서 gnuplot 명령을 실행했는데, 그런 명령어가 없다면, 설치가 안된 것입니다. 설치하세요.
구글 이미지 검색에서 "gnuplot dumb" 키워드로 검색한 결과를 살펴보세요.
이 글에서 그런 것들을 직접 내 터미널 환경에서 할 수 있는 방법을 소개합니다.
set terminal dumb
gnuplot 도구는 기본적으로 해당 OS의 모니터를 기본 출력으로 합니다. 즉, Windows라면 화면 창이 될 것이고, Linux라면, X-Window 화면 창이 됩니다.
윈도우즈 GUI 환경도 X-Window GUI 환경도 아닌 터미널 응용 프로그램을 이용해서 원격 접속한 텍스트 모드 상황에서는 그 원격 호스트에 설치된 gnuplot 버전에 따라 기본 출력이 결정됩니다. 다음은 한 리눅스 호스트로 ssh 원격 접속을 하고, gnuplot 명령을 실행한 결과입니다.
$ gnuplot G N U P L O T Version 4.6 patchlevel 2 last modified 2013-03-14 Build System: Linux x86_64 Copyright (C) 1986-1993, 1998, 2004, 2007-2013 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type "help FAQ" immediate help: type "help" (plot window: hit 'h') Terminal type set to 'x11' gnuplot>
여기서 맨 밑줄에 있는 'x11' 이 바로 기본 출력을 의미합니다. 즉 어떤 차트를 출력한다면, 그 결과는 x-window 창이 하나 생겨 그곳에 보일 것입니다.
다음은 이 상태에서 사인 곡선을 그려라고 명령을 내린 결과입니다.
gnuplot> plot sin(x) gnuplot: unable to open display '' gnuplot: X11 aborted. gnuplot>
출력으로 사용할 x-window display 설정이 없어 출력할 수 없다는 오류를 냅니다.
이 사인 곡선을 자신의 터미널 화면에서 보려면, 기본 출력을 자신이 접속한 가상 터미널로 바꿔야합니다.
gnuplot 은 이 경우를 대비해서 텍스트 모드 환경에서도 차트를 그릴 수 있습니다!
그 설정이 이 단락의 제목인 set terminal dumb 입니다.
gnuplot> set terminal dumb Terminal type set to 'dumb' Options are 'feed size 79, 24' gnuplot> plot sin(x) 1 ++---------------***---------------+---**-----------+--------**-----++ + * * + * ** + sin(x) ****** + 0.8 ++ * * * * * * ++ | * * * * * * | 0.6 *+ * * * * * * ++ |* * * * * * * | 0.4 +* * * * * * * ++ |* * * * * * * | 0.2 +* * * * * * *++ 0 ++* * * * * * *++ | * * * * * * *| -0.2 ++ * * * * * * *+ | * * * * * * *| -0.4 ++ * * * * * * *+ | * * * * * * * -0.6 ++ * * * * * * +* | * * * * * * | -0.8 ++ * * * * * * ++ + * * + ** * + * * + -1 ++-----**--------+-----------**----+--------------***---------------++ -10 -5 0 5 10 gnuplot>
이 작업에서 유심히 보아야 할 부분은 set 작업 결과로 이 화면 크기는 "79, 24"로 설정되었다는 부분이 나옵니다. 79는 화면의 가로 크기고, 24는 세로 크기입니다. 내가 사용하고 있는 터미널의 창 크기 만큼 전 화면을 모두 차트를 보여주는 공간으로 사용하려면, 해당 쉘의 COLUMNS, LINES 환경 변수를 이용해서 그 크기를 미리 알아야 합니다.
$ echo $COLUMNS 130 $ echo $LINES 35 $ gnuplot gnuplot> set terminal dumb size 130, 35 Terminal type set to 'dumb' Options are 'feed size 130, 35' gnuplot> plot sin(x) 1 ++----------------------------****--------------------------+-------****------------------+---------------***----------++ + *+ * + * * + *sin(x) ****** + | * * ** * * * | 0.8 ++ * * * * * * ++ | * * * * * * | | * * * * * * | 0.6 ++ * * * * * * ++ * * * * * * * | * * * * * * * | 0.4 +* * * * * * * ++ |* * * * * * * | | * * * * * * * | 0.2 ++* * * * * * * ++ | * * * * * * * | | * * * * * * * | 0 ++ * * * * * * * ++ | * * * * * * * | | * * * * * * * | -0.2 ++ * * * * * * *++ | * * * * * * *| | * * * * * * *| -0.4 ++ * * * * * * *+ | * * * * * * * | * * * * * * * -0.6 ++ * * * * * * ++ | * * * * * * | | * * * * * * | -0.8 ++ * * * ** * * ++ | * * * * * * | + * ** + ** * + * +* + -1 ++----------**----------------+-------------------***-------+--------------------------****----------------------------++ -10 -5 0 5 10 gnuplot>
시계열 자료처리
터미널로 원격 접속을 하고, 어떤 자료를 분석하는 경우 그 자료는 대부분 시계열 자료일 것입니다. 하루 동안 CPU 사용률 추이가 어떠했는지 이런 것을 급하게 살펴보아야 할 경우죠.
gnuplot 도구를 사용해서 시각화할 자료의 x 축은 대부분 시간값일 경우일 것입니다.
이런 시계열 자료를 처리할 때는 x 축 값이 시간이라고 지정해야하며, 그 날짜 양식은 이러하다는 것을 지정해야 바르게 처리합니다. 즉, 그 시간값은 일정 양식으로 정형화 되어있어야합니다.
리눅스 시스템 정보를 수집하는 sar 결과를 예로 들겠습니다.
$ LANG=C sar | egrep '^[0-9][0-9]' | tail -n +3 09:20:01 all 1.17 0.09 0.43 13.72 0.00 84.60 09:30:01 all 3.76 0.03 0.62 2.15 0.00 93.43 09:40:01 all 5.89 0.03 1.17 1.66 0.00 91.26 09:50:01 all 2.67 0.01 0.66 0.69 0.00 95.96 10:00:01 all 2.78 0.02 0.61 0.63 0.00 95.97 10:10:01 all 2.59 0.02 0.58 0.52 0.00 96.30 10:20:01 all 2.16 0.01 0.49 1.21 0.00 96.13 10:30:01 all 1.20 0.03 0.34 1.15 0.00 97.28 10:40:01 all 1.04 0.00 0.20 0.27 0.00 98.49 10:50:01 all 2.07 0.01 0.47 0.29 0.00 97.16 11:00:01 all 0.64 0.00 0.12 0.15 0.00 99.10 11:10:01 all 0.57 0.00 0.11 0.25 0.00 99.07 11:20:01 all 0.58 0.00 0.11 0.16 0.00 99.15 11:30:01 all 1.06 0.01 0.24 0.25 0.00 98.45 11:40:01 all 3.84 0.03 0.84 1.11 0.00 94.19 11:50:01 all 0.11 0.00 0.04 0.16 0.00 99.69 12:00:02 all 0.13 0.00 0.05 0.14 0.00 99.68 12:10:01 all 0.11 0.00 0.04 0.13 0.00 99.72 12:20:01 all 0.17 0.00 0.06 0.20 0.00 99.57 12:30:01 all 0.30 0.00 0.08 0.16 0.00 99.46 12:40:01 all 1.23 0.00 0.29 0.28 0.00 98.20 12:50:01 all 0.33 0.00 0.10 0.20 0.00 99.38 13:00:01 all 2.84 0.02 0.50 0.59 0.00 96.06 13:10:01 all 1.26 0.01 0.29 0.26 0.00 98.18 13:20:01 all 0.43 0.00 0.17 0.27 0.00 99.14 13:30:01 all 0.68 0.00 0.20 0.14 0.00 98.98 13:40:01 all 1.02 0.00 0.28 0.15 0.00 98.54 13:50:01 all 0.63 0.00 0.16 0.19 0.00 99.02 14:00:01 all 0.08 0.00 0.04 0.12 0.00 99.76 14:10:01 all 0.84 0.00 0.22 0.18 0.00 98.77 14:20:01 all 4.20 0.00 0.90 0.15 0.00 94.75 14:30:01 all 1.23 0.00 0.32 0.19 0.00 98.27 14:40:01 all 1.41 0.00 0.35 0.16 0.00 98.08 14:50:01 all 0.66 0.00 0.18 0.20 0.00 98.96 15:00:01 all 2.16 0.01 0.52 2.48 0.00 94.83 15:10:01 all 1.16 0.01 0.30 0.31 0.00 98.22
명령어가 길어졌습니다. gnuplot 쪽에서 사용할 자료는 sar 에서 보여주는 자료를 정형화 하기 위해서 조금 가공했습니다.
gnuplot 에서는 칼럼 구분 문자의 기본값이 whitespace (공백과 tab의 연속)이기 때문에, 윗 sar 자료를 대상으로 하면, 굳이 칼럼 구분 문자를 따로 지정하지 않아도 됩니다.
이 자료에서 첫번째 칼럼 자료가 x 축 값으로 사용될 예정인데, 이렇게 하려면, 먼저,
set xdata time
set timefmt '%H:%M:%S'
set format x '%H:%M'
이 세 설정이 되어야 정상적으로 처리됩니다.
첫번째는 x 축을 시간값으로 처리함을,
두번째는 그 값이 %H:%M:%S 양식으로 되어있음을
세번째는 차트를 그릴 때, 보일 x 축 값 출력 양식입니다.
<cat
대부분의 유닉스 명령들은 서로 조합해서 사용합니다.
앞 명령어의 표준 출력을 다음 명령어의 표준 입력으로 받는 파이프 기능을 이용하면, gnuplot에서 따로 자료파일을 준비하고, 차트를 그리기 위한 스크립트를 만들고, 꽤 복잡한 과정을 그냥 한 줄 명령어로 처리할 수 있습니다. gnuplot 명령어 -e 옵션의 설정값으로 이런 작업을 처리합니다.
$ LANG=C sar | egrep '^[0-9][0-9]' | tail -n +3 | \ gnuplot -e "set terminal dumb $COLUMNS,$LINES;set xdata time;set timefmt '%H:%M:%S'; \ set format x '%H:%M'; set grid ytics;plot '<cat' using 1:3 with line" 6 ++------+--*----+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+------++ + * + + + + + '<cat' using 1:3 ****** + | ** | | ** | 5 ++........*.*..................................................................................................++ | * * | | * * | | * * * | 4 ++......*...*........................................................................*.........................++ | * * * * | | * * * ** | | * * * * * | 3 ++.....*.....*............................**........................................*.*........................++ | * * **** * * * * * | | * ** * * * * * * | | * * * * * * * * | | * * * * * * * * * * | 2 ++....*...............*......**..........*..*..................*..*.................*..*........**.............++ | * * * * * * * * * * * * * | | * * * * * * * * * * ** * * * | | * **** * * * * * * * ** * * * | 1 ++.........................*....*.......*....*............*.*.*.....*.....***......*........*.*................++ | *** ** * * * * * *** * * * | | *** * * * ** * * | + + + * **** ***** * + *+* + + 0 ++------+-------+-------+-------+-------+----**-+--*----+-------+-------+-------*-------+-------+-------+------++ 09:00 10:00 11:00 12:00 13:00 14:00 15:00 16:00
gnuplot의 표준 입력은 gnuplot에서 사용할 명령들로 처리하기 때문에, gnuplot로 넘길 자료(가공된 sar 자료)를 표준 입력으로 보내면 해석하지 못합니다. 이 문제를 해결하기 위해서, plot 명령 (차트를 그리는 명령 - 맨 뒤 명령) 에서 사용할 데이터 파일은 cat 으로 받을 것이다고 '<cat' 이렇게 지정했습니다.
그외 y축 안내선을 그리기 위해서, set grid ytics를 명령에 포함했구요,
plot 명령은 다른 gnuplot 설명서를 참조해서 익히시면 될 듯합니다. 자세한 설명은 생략합니다. 제게는 저 정도면 충분했기 때문에.
DB 분석에서의 활용
PostgreSQL 홈페이지니, 이 gnuplot 를 이용한 데이터베이스 이야기를 빼 놓을 수는 없겠지만, 별로 할 말이 없습니다.
쓰는 사람 마음대로 쓰면 될 것이기 때문에.
postgresql.conf 에 log_min_duration 설정을 하고, 여러 텍스트 조작 도구를 이용해서 gnuplot 쪽으로 넘겨줄 수 있도록 가공하고 위와 같이 차트를 그리면, 원격 텍스트 밖에 지원하지 않는 환경에서도 얼마든지, 느린 쿼리 분포도 같은 것을 실시간으로 살펴볼 수 있을 것입니다.
보너스
다음은 기상청에서 제공하는 2017년 지역별 1시간 단위 1년 기온 자료(총 83만건)를 대상으로 제 화면으로 제일 큰 터미널 창 설정하고 서울과 제주를 함께 비교한 ascii art 입니다. :)
$ grep 서울 2017temp.csv | awk -F ',' '{print substr($3, 0, 8), $4}' | awk 'NR>1{ arr[$1] += $2 count[$1] += 1 } END{ for (a in arr) { print a, arr[a] / count[a] } } ' | sort > /tmp/seoul.dat $ grep 제주 2017temp.csv | awk -F ',' '{print substr($3, 0, 8), $4}' | awk 'NR>1{ arr[$1] += $2 count[$1] += 1 } END{ for (a in arr) { print a, arr[a] / count[a] } } ' | sort > /tmp/jeju.dat $ gnuplot -e "set title '2017 기온변화';set terminal dumb $COLUMNS,$LINES;set xdata time;set timefmt '%Y%m%d'; set format x '%y-%m-%d'; set grid ytics;plot '/tmp/seoul.dat' using 1:2 with line title '서울', '/tmp/jeju.dat' using 1:2 with line title '제주'" 2017 기온변화 35 ++----+----+----+-----+----+--+----+-----+----+----+-----+---+----+----+-----+----+----+-----+----+----+----+----+----+-----+----+----+----+----+----+-----+----+----+----+----+----+-----+----+----+----++ |+ + + + + + + + + + + + + 서울 ****** + | 제주 ###### | | # # | | # #### # *# # | 30 ++...........................................................................................#...#..##*#.#.##***#......##................................................................................++ | # # # #** * # * *# # ### | | ##### * * * *# * *#### # ## # | | # ** #**** **#*** *** ## * ## # | | * *# ***#**** * *** ** * **## # | 25 ++...................................................................................*.******.*..*.*......*......*.*.**.*..##.####...#...#...............................................................++ | # ** ** #* * * ** * * ##* *### # ## # | | # # ** * ## * # #**## ** * * ***** ##*###** ## | | # # # ** * #***### # # * * * **** ***#* *### *# | | ##** # *** *#***#***# ** ** *****#* *#* * * | | ##** ## ** *** *#** ## ** * ** * *#* * | 20 ++..........................................................#*#*#.#*..*..***...#**.##.....................................**.........*....***.*.*#.####..#....#..........................................++ | # #* # #*#*##** * ** ** ***** *###### ## # | | ## #* # * # ****#* *# * **** *# # *#### ## | | ## **# ##* **** * * * * ****#** ### | | ## *#**# *#* *## * * ** *#** *## # | 15 ++..............................................##**#**##****...*...............................................................................*.*...****#*##.###.......................................++ | # ***#*** #* ** * * ****#**#*#### # | | # #***#** ** * *#**#* ### # | | # # # ## # #*** #* * * * **** #### # ### | |#### # # # ## *# # # #* * * ** ** *# ## ##### # ## | 10 ###.#..........#..#.....#..#..#.#..#.#.**####*.*.................................................................................................*.......**.**.*...####.##...#.#.....##..#...............++ |## # # # #### # # # ##**#*# *** * ** * ## # ###### ## # | | # ## # ######### ## *##* *** ** * ** ** ## ###### ## ## | | ## ## # # # ##### # ##** * * ** * ** ###### ## ### # | | *## ## ## # # ## # ## # ##* ** * ** *### ## ####*### | 5 +*.**#.#.##..#.#.#.##.#.*#.##.*.*##*.*..........................................................................................................................*.*......*.*#..#.###.*.##................++ |****# ## ## # ### # # *# * * * * * ** * ### **# | *** * ## ## # ## * * ** * * * ** * # # ** | ** * # # # ** * * ** ** ***** ** ** * | | * ** # *** ** ** ** *** * *** * ** ** | | * * ** ****** ** ** ** **** * * * ** | 0 ++...*...*.....*.*.*..*..****....**................................................................................................................................**.*...******....*.*.**...............++ | ** ** ** * * * **** * ** * **** * * * * | | ** ** *** * ** * * * * **** ** * * * | | ** * * * *** * * * ** * ** ** * | | ** * * * ** ** * ** **** ** | -5 ++....*.*..*.*..*...**.........................................................................................................................................................*.**....*.................++ | ** * * * * ** * | | * ** * ** * | | * * ** | |+ * + + + + + + + + + + + * + + -10 ++----+----+----+-----+----+--+----+-----+----+----+-----+---+----+----+-----+----+----+-----+----+----+----+----+----+-----+----+----+----+----+----+-----+----+----+----+----+----+-----+----+----+----++ 17-01-01 17-02-01 17-03-01 17-04-01 17-05-01 17-06-01 17-07-01 17-08-01 17-09-01 17-10-01 17-11-01 17-12-01 18-01-01 18-02-01