单个文件中的多个数据块和单个图+每个块的标记

Multiple data blocks in a single file and a single plot + Markers for each block

我有一个如下所示的数据文件:

"curve 0"
0 0.7800
10 0.333
12 0.5136
24 0.2096
26 -0.066
40 -0.674
42 -1.123


"curve 1"
0 0.876
2 0.73
4 0.693
6 0.672
10 0.70
12 0.88
16 0.95
148 -0.75


"curve 2"
8 2.2305
10 2.144
12 2.13
76 1.26
78 0.39
98 -0.97

我想使用 gnuplot 独立于其他数据块绘制每个数据块。这是我为此目的使用的代码:

plot 'file' i 0 u 1:2 w lines title columnheader(1),\
'file' i 1 u 1:2 w lines title columnheader(1),\
'file' i 2 u 1:2 w lines title columnheader(1),\
'file' i 3 u 1:2 w lines title columnheader(1)

它工作正常。

现在,我想在每个数据块中确定具有最大值y-value的点(x,y),并用与对应的曲线颜色相同的标记绘制它数据块。我尝试使用

max_y = GPVAL_DATA_Y_MAX
replot 'file' u ( == max_y ?  : 1/0):1

在前面的代码之后,但这似乎找到了包括所有块在内的整个第二列的最大值。

我想做的第二件事是:对于每个数据块和一个形状不同但颜色(曲线的颜色)与最大值标记相同的标记,绘制第一行那个街区。

这两项任务是否可以通过 gnuplot 和我绘制曲线的方式(列标题)实现?

这是可以做到的。它将广泛使用 stats 命令和一个临时文件。在 gnuplot 5 中,可以使用命名数据块在内存中创建临时文件(参见 help datablocks)。

此外,由于您的 plot 命令在很大程度上是重复的,因此您可以使用 plot 语法

plot for[in=0:2] 'file' i in u 1:2 w lines t columnheader(1)

这将使用 0 到 2 中的变量值重复绘图命令(您提供的命令使用四个数据块,但您提供的数据文件只有 3 个)。

以下脚本将完成您想要的:

stats 'file' u 1:2 nooutput
blocks = STATS_blocks

set print 'tempfile'

first_y = ""
first_x = ""
do for[i=0:blocks-1] {
    stats 'file' index i u (first_x=([=11=]==1)?sprintf("%s %f",first_x,):first_x,first_y=([=11=]==1)?sprintf("%s %f",first_y,):first_y,):2 nooutput
    print sprintf("%f %f",STATS_pos_max_y,STATS_max_y) 
}

print ""
print ""
do for[i=1:blocks] {
    print sprintf("%s %s",word(first_x,i),word(first_y,i))
}
set print

plot for[i=0:blocks-1] 'file' i i u 1:2 w lines title columnheader(1),\
     for[i=0:1] 'tempfile' i i u 1:2:([=11=]+1) w points pt (i==0?7:9) lc variable not

这会产生(使用您提供的数据文件)

在曲线0和2的情况下,第一个点和最大点相同,所以符号被遮挡了。

重新绘制此图,但更改规范以将第一个点标记向上移动 0.1,我们可以看到它们出现在应有的位置。


这部分会很长,但我会分解代码并详细解释,尽可能接近一行,因为这里有一些微妙的东西。

前两行

stats 'file' u 1:2 nooutput
blocks = STATS_blocks

运行 文件的统计命令。由于命名列 headers,如果我们不指定 using 规范,统计功能将失败,因此我们给它指定 u 1:2 规范。 nooutput 选项告诉 stats 命令捕获结果,但不输出它们。这里我们只关心获取块的数量。我们将其存储在变量 blocks 中(因为稍后的统计命令将覆盖该变量)。我们本可以给出一个命名前缀,但这会保存所有变量,没有理由这样做。代替这两个命令,在恰好 3 个块的情况下,我们可以将值 3 替换为下面所有出现的 blocks,但这样块的数量不是 hard-coded.

接下来,我们使用set print 'tempfile' 将打印命令重定向到一个临时文件。我们将建立一个包含最大点和第一个点的新数据文件。

下一段代码

first_y = ""
first_x = ""
do for[i=0:blocks-1] {
    stats 'file' index i u (first_x=([=13=]==1)?sprintf("%s %f",first_x,):first_x,first_y=([=13=]==1)?sprintf("%s %f",first_y,):first_y,):2 nooutput
    print sprintf("%f %f",STATS_pos_max_y,STATS_max_y) 
}

是最困难的,也是最神奇的地方。我们将创建我们的临时文件以包含两个数据块。第一个是最大值,第二个是第一个值。我们将计算内存中的第一个点,并在创建第一个数据块后添加它们。 x 坐标和 y 坐标将存储在 space 分隔的字符串变量中。

我们遍历所有数据块并为其计算统计命令。表达式

(first_x=([=14=]==1)?sprintf("%s %f",first_x,):first_x,first_y=([=14=]==1)?sprintf("%s %f",first_y,):first_y,)

为读入的每个点重新分配两个字符串变量。为此,它首先检查该点是否是系列中的第一个($0 的值将为 1,因为 0 值对应于 header 行)。如果是,它将通过将第一列的值添加到它来重建字符串变量(对于 y 坐标也是如此)。否则,它只是将相同的东西重新分配给变量。最后,它 returns 第一列中的值。当表达式像这样放在括号和逗号分隔时,依次计算每个表达式,并返回最终值。

因此 stats 命令的行为就像以前一样

stats 'file' index i u 1:2 nooutput

但是这个小技巧允许我们读取第一行值并在它们进来时存储它们。最后打印出具有最大y值的点。这将进入临时文件。

现在我们需要将第一个点作为一个新的数据块添加到临时文件中。所以首先我们打印两个空行然后我们再次迭代块数 运行ning

print sprintf("%s %s",word(first_x,i),word(first_y,i))

对于每个块(其中 i 是块的编号)。 word 函数将字符串变量视为 space 分隔的单词列表并提取请求的单词。此时我们的字符串变量看起来像

 0.000000 0.000000 8.000000 # first_x
 0.780000 0.876000 2.230500 # first_y

最后,我们发出 set print 恢复打印命令以打印到控制台。我们现在已经建立了一个看起来像

的临时文件
0.000000 0.780000
16.000000 0.950000
8.000000 2.230500


0.000000 0.780000
0.000000 0.876000
8.000000 2.230500

其中第一个数据块是具有最大值的点y-value,第二个数据块是第一个点。

最后,我们用

作图
plot for[i=0:blocks-1] 'file' i i u 1:2 w lines title columnheader(1),\
     for[i=0:1] 'tempfile' i i u 1:2:([=19=]+1) w points pt (i==0?7:9) lc variable not

第一部分与之前相同,只是使用了块变量而不是 hard-coding 块数。

接下来我们用索引 0 和索引 1 绘制临时文件两次。线条颜色根据行号(在本例中为 0 到 2)而变化。我们加一以强制通常基于 0 的行号为 1 到 3。这将与之前的数据块相对应。我们绘制点和 select 基于我们正在绘制的数据块的点类型。在这种情况下,它要么是实心圆(对于最大值)要么是实心三角形(对于第一个点)。