如何使用GNUPlot根据列中的值绘制单个/多个线条

6

我在使用gnuplot时遇到了一些小问题,这是我的数据文件:

From Time Packets Jitter  
127.0.0.1:53091 1 0 274  
127.0.0.1:53091 2 0 417  
127.0.0.1:53091 3 36 53  
127.0.0.1:53091 4 215 55  
127.0.0.1:53090 4 215 55  
127.0.0.1:53091 5 215 33  
127.0.0.1:53090 6 256 78

我为测试设置了一个“时间”字段,但是在它正常工作后,它将被datetime替换。

我想要绘制两个不同的图形,其中第一个图形的y轴是“Packets”列,第二个图形的y轴是“Jitter”列。这两个图形都需要在x轴上使用“Time”列。但是,正如您所看到的,我不知道“From”列中会有多少不同的值(最少1个,但我不知道最大值,数据文件每隔几秒钟就会刷新并添加一些值)。
因此,我的问题是我想在两个图形上为每个不同的“From”值创建另一条线。
实际上,我希望在线的标题中包含“From”值(例如:“127.0.0.1:53091”)。

如果可以更改列的顺序,则需要说明。

我尝试过:

plot 'data.log' using 3:xtic(2) title 'Packets' with lines, \
     'data.log' using 4:xtic(2) title 'Jitter' with lines

但它位于同一个图形上(我还没有使用multiplot,我试图在使多条线工作之前)。

这是否可能? 如果是这样,如何在gnuplot中绘制这两个图形?
如果不行,我们可以删除Jitter图形,并仅在具有不同From值的单个图形上绘制Packets列。

3个回答

5

这里有一个解决方案,不需要外部文件。首先,我会提取第一列中的所有不同来源,并将它们存储在 gnuplot 变量中:

filename = 'data.log'
from=system('tail -n +2 '.filename. '| cut -f 1 -d " " | sort | uniq')

在绘图过程中,我使用awk进行过滤,并定义了一个gnuplot函数。

select_source(w) = sprintf('< awk ''{if ($1 == "%s") print }'' %s', w, filename)

现在你可以遍历存储在from中的所有源代码。完整的gnuplot脚本如下所示:
filename = 'data.log'
from=system('tail -n +2 '.filename. '| cut -f 1 -d " " | sort | uniq')
select_source(w) = sprintf('< awk ''{if ($1 == "%s") print }'' %s', w, filename)

set style data linespoints
set multiplot layout 1,2

set title 'Packets'
plot for [f in from] select_source(f) using 2:3 title f

set title 'Jitter'
plot for [f in from] select_source(f) using 2:4 title f

unset multiplot

enter image description here


哦,谢谢你的回复。现在我有选择的余地来决定哪个脚本更适合我的使用(而且将这个脚本放入C++中可能也很容易)。 - DJunior
非常好,只使用gnuplot来完成(几乎)所有操作的想法很有趣,您的解决方案还避免了我的“plot 0”命令。 - kebs

2
这是一个基于几个标准工具的解决方案,应该适用于任何标准 Linux 系统,并且主要基于 bash。让我们从你提供的数据文件开始,不包括第一行步骤 1:将数据分割成每个字段 1 一个文件:awk -f split.awk < data.log,其中 split.awk 的内容如下:
#!/usr/bin/awk -f
# erase previous files
BEGIN { system("rm file_*.dat"); }

# print each line in a specific file
 { print $0 >>( "file_" $1 ".dat") }

步骤2:复制每个生成的数据文件的第一行(因为在gnuplot中使用一个字段作为标题会忽略这一行,导致无法绘图):

for f in `ls file_*.dat`; do 
    head -n 1 $f > tmp.dat
    cat $f >> tmp.dat
    mv tmp.dat $f
done;

步骤三:生成一个包含“plot”命令的gnuplot脚本,用于绘制不同的文件(请参见下面的完整脚本)。
 echo "plot \\" >> plot.plt
 for f in `ls file_*.dat`; do 
     echo "   '$f' using 2:3 title columnheader(1) with linespoints lw 2, \\" >> plot.plt
done;
echo "    0 notitle" >> plot.plt

顺带一提,最后一个“0”仅是为了将多个文件绘制在同一张图上,gnuplot在行末需要有一个反斜杠。如果有这个反斜杠,但下一行没有要绘制的内容,则会生成错误。所以我只能用这种愚蠢的方法使它工作...

步骤4:调用生成的gnuplot脚本。

使用提供的数据,以下脚本的结果如下图所示: enter image description here

也许可以更短,但我喜欢保持代码可读性。

完整脚本:

#!/bin/bash

# 1 - split data into one file per field 1
awk -f split.awk < data.log

# 2 - duplicate first line (useful for gnuplot)
for f in `ls file_*.dat`; do 
    head -n 1 $f > tmp.dat
    cat $f >> tmp.dat
    mv tmp.dat $f
done;

# 3 - generate gnuplot script
echo "set terminal pngcairo size 800,500" > plot.plt
echo "set output 'b.png'" >> plot.plt
echo "set multiplot layout 1,2" >> plot.plt

echo "set title 'Packets'" >> plot.plt
echo "plot \\" >> plot.plt
for f in `ls file_*.dat`; do 
    echo "   '$f' using 2:3 title columnheader(1) with linespoints lw 2, \\" >> plot.plt
done;
echo "    0 notitle" >> plot.plt

echo "set title 'Jitter'" >> plot.plt
echo "plot \\" >> plot.plt
for f in `ls file_*.dat`; do 
    echo "   '$f' using 2:4 title columnheader(1) with linespoints lw 2, \\" >> plot.plt
done;
echo "    0 notitle" >> plot.plt

echo "unset multiplot" >> plot.plt

# 4 - call gnuplot script
gnuplot plot.plt

在等待您的帮助时,我使用了以下命令:string commands << "cd '/tmp'\n" << "set terminal X11\n"; << "set multiplot layout 1,2\n" << "set title ' Packets Lost '\n" << "plot \"< tail -n +3 gstServer.log | sort -t\\\" \\\" -k 1,1 -k 2,2n | awk -F \\\" \\\" '{ if (x != $1 && x != NULL) print \\\"\\\\n\\\"$0; else print; };{x=$1}'\" using 2:3 notitle with lines\n" << "set autoscale x\n" ... Jitter也是一样,但是评论里没有足够的空间 << "unset multiplot\n";但那并不完全是我想要的。 - DJunior
非常感谢您的帮助,这对我的工作非常有用。 我将尝试使用C++编写此脚本,但使用popen命令可能会很容易。 再次感谢您花费时间解决我的问题。 - DJunior

-1

我不确定我理解你的问题,但至少我可以回答 "如何在gnuplot中绘制这两个图形?":

set multiplot layout 1,2
plot 'data.log' using 3:xtic(2) title 'Packets' with lines
plot 'data.log' using 4:xtic(2) title 'Jitter' with lines
unset multiplot

会产生这个:

enter image description here


首先,感谢您的回复。 这就是我想做的事情,但是,正如您在我的“示例”数据中所看到的,可能会有不同的IP和不同的端口,但我不知道可能会有多少不同的IP:端口(例如,在我的示例中有两个不同的IP:127.0.0.1:53091和127.0.0.1:53090)。 因此,我的目标是每次在该字段中有另一个值时在每个图形上绘制另一条线。 是否更清楚了?如果不是很清楚,请原谅。 - DJunior
我明白了。问题的确在于绘图中的每一行必须对应一个“plot”命令(或者在绘图命令中的一个“sequence”)。因此,我认为需要进行一些数据预处理以便将不同的地址分开,然后为每个地址生成一个绘图命令。这并不容易,但我会考虑一下,可能需要使用一些awk处理。如果你想感谢我,可以给答案点个赞...;-) - kebs
哦,还有一个问题:只有两个端口号吗?还是可能有其他几个?因为我上面的评论考虑了可能有其他几个的假设。如果只有两个,那么就会更简单。 - kebs
不,可能会有一个或多个(两个、三个,我不知道具体数量)不同的IP:端口(IP和端口可能会变化)。在您的多图中,第一列没有区别(例如,x轴上有两个4值,但IP:端口不同)。所以,是的,也许需要使用awk进行处理。感谢您花时间解决我的问题。我还要补充一点:我的目标是每隔x秒重新绘制这个图形,并且在两个循环之间可能会添加或删除不同的IP:端口值。(我的声望低于15,所以我不能点赞 :( ) - DJunior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接