如何在 gnuplot 中使用不同的点类型划线?

How to dash a line using different pointtypes in gnuplot?

我正在尝试用虚线绘制数据文件。以下是一些值:

2001 dic 21 5,9 9,2 3,8 0,2
2001 dic 22 6,8 8,7 4,8 4,2
2001 dic 23 6,3 8,1 6,5 6,8
2001 dic 24 3,1 4,1 3
2001 dic 25 -1, 3,5 -5
2001 dic 26 4,5 8 0,8 14,8
2001 dic 27 4 7 2,7 0,2
2001 dic 28 0,1 3,4 -3,5
2001 dic 29 7 10,7 2,9 6,2
2001 dic 30 11,2 12,9 8 1,4
2001 dic 31 5,2 6,3 5 3,4
2002 gen 1 2,9 6,9 0,8 
2002 gen 2 -0,8 5,4 -5,4 
2002 gen 3 2,0 8,0 -1,8 
2002 gen 4 2,0 5,2 0,1 
2002 gen 5 -0,8 5,5 -5,2 
2002 gen 6 0,3 6,7 -4,3 
2002 gen 7 0,5 5,6 -3,4 
2002 gen 8 1,2 7,9 -3,3 
2002 gen 9 1,9 8,4 -2,8 
2002 gen 10 1,8 8,1 -2,7 
2002 gen 11 3,6 7,7 -0,7 
2002 gen 12 6,0 10,2 3,5 

为了避免与其他图表混淆,我应该绘制的不是虚线而是珠子 ('°')。 Gnuplot 只允许“.-_”用于用户定义的虚线。使用用户定义的线点绘图可能是解决方案,但是您有线条和珠子,而不仅仅是 'beads',像这样(看中间的图):

仅使用用户定义的点进行绘图会在珠子之间留下很多空白空间。因此,我正在尝试创建一个新的数据文件,其中包含通过原始数据文件值的线性插值获得的更多值,以填充那些空白空间(前三列是“2022 年 2 月 28 日”等类型的时间戳数据):

set table $Temp
     plot "datafile.csv" u 1:4:5:6:7 skip 15 w table 
unset table

set print $Temp2  #this file is like $Temp with lots of values added by interpolation   
  do for [i = 1:|$Temp|-1] { 
  print $Temp[i:()],$Temp[i:()]
              do for [j = 1,10] { print $Temp[i:()]+($Temp[i+1:()]-$Temp[i:()])*j/11), $Temp[i:()]+($Temp[i+1:()]-$Temp[i:()])*j/11) }
              } 
  print $Temp[|$Temp|:()],$Temp[|$Temp|:()]  
    set print

显然这不起作用,因为我没有正确处理 $Temp 文件的列。在 gnuplot 手册中查找“数组”没有提供任何提示。最后,生成的文件应该用点 pt "°" 绘制。

编辑: 由于我将问题标题更改为“...different pointtypes...”,我应该展示如何用多列和不同的点类型来做。 多列数据沿路径插值并附加到数据块 $DashMultipleCols 作为(子)块,稍后可以通过 index 寻址。 solid/dash/dotted 和 plus/circle/triangle 哪个更容易区分,由你来决定。

以下示例创建一个数据块,该数据块沿给定路径具有等距点。

  • 为了说明一些随机测试数据是用 1 x-column 和 3 y-columns

    创建的
  • 为了比较,第一张图是实线、虚线和点线。我认为线条可以很好区分,但你要求用符号“破折号”。

  • 对于第二个图,第 2、3、4 列的数据分别以点类型 1、6、8 为“虚线”。

  • 符号之间的距离由变量设置,这里:Dist = 8

  • 为了获得视觉上的均匀分布,使用了从 x,y 到像素坐标和反向的坐标转换。为此,使用 gnuplot 变量 GPVAL... 在 绘图后可用。这就是为什么有必要绘制两次。命令pause -1可以去掉。

  • 这适用于线性轴(需要调整对数轴)

  • 您的数据是时间数据。代码需要做相应的调整。

代码:(使用wxt终端测试)

### "dashing" with symbols equidistantly along a path
reset session

# create some random test data
set print $Data
    x0 = 0
    y0 = y1 = y2 = 100
    do for [i=1:10] {
        print sprintf("%g %g %g %g", x0=x0+int(rand(0)*10)+5, y0=y0+int(rand(0)*10)-5, y1=y1+int(rand(0)*10)-5, y2=y2+int(rand(0)*10)-5)
    }
set print

set key top left Left reverse
# plot to get the GPVAL_ ... values
plot $Data u 1:2 w l dt 1 lc "black" ti "Column 2, solid", \
        '' u 1:3 w l dt 2 lc "black" ti "Column 3, dashed", \
        '' u 1:4 w l dt 3 lc "black" ti "Column 4, dotted"

# store GPVAL parameters after plot in variables
txmin = GPVAL_TERM_XMIN
txmax = GPVAL_TERM_XMAX
tymin = GPVAL_TERM_YMIN
tymax = GPVAL_TERM_YMAX
xmin  = GPVAL_X_MIN
xmax  = GPVAL_X_MAX
ymin  = GPVAL_Y_MIN
ymax  = GPVAL_Y_MAX

# x,y to pix and pix to x,y coordinate conversion
XtoPix(x)    = txmin + real(x-xmin)    *(txmax-txmin)/( xmax- xmin)
YtoPix(y)    = tymin + real(y-ymin)    *(tymax-tymin)/( ymax- ymin)
PixToX(scrx) =  xmin + real(scrx-txmin)*( xmax- xmin)/(txmax-txmin)
PixToY(scry) =  ymin + real(scry-tymin)*( ymax- ymin)/(tymax-tymin)

# get lengths and angles in pixel coordinates
set angle degrees
Length(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)
Angle(x0,y0,x1,y1)  = (_dx=x1-x0, _dy=y1-y0, _L=sqrt(_dx**2 + _dy**2), _L==0 ? NaN : \
                      (_dy>=0 ? acos(_dx/_L) : 360-acos(_dx/_L) ))

colX  = 1
colsY = "2 3 4"         # multiple y-columns
do for [colY in colsY] {
    set table $LaA      # table for length and angles
        Total = 0
        plot x1=y1=NaN $Data u (x0=x1,x1=XtoPix(column(colX)),x0):\
                               (y0=y1,y1=YtoPix(column(int(colY))),y0):\
                (L=Length(x0,y0,x1,y1)):(L==L ? Total=Total+L : 0, Angle(x0,y0,x1,y1)) w table
    unset table
    X0(n)        = real(word($LaA[n],1))
    Y0(n)        = real(word($LaA[n],2))
    SegLength(n) = real(word($LaA[n],3))
    SegAngle(n)  = real(word($LaA[n],4))

    # create equidistant datapoints along path
    set print $DashSingleCol
        Dist = 8                    # Distance between symbols
        N = floor(Total/Dist)
        idx = 2
        L0 = 0
        L = SegLength(idx)
        do for [i=0:N] {
            R = i*Dist
            while (L-R<0) {
                L0 = L
                idx = idx + 1
                L = L + SegLength(idx)
            }
            print sprintf("%g %g", PixToX(X0(idx)+(R-L0)*cos(SegAngle(idx))), \
                                   PixToY(Y0(idx)+(R-L0)*sin(SegAngle(idx))))
        }
    set print
    set print $DashMultipleCols append
        print $DashSingleCol
        print "\n\n"
    set print
}

pause -1 

mySymbol(n) = int(word("1 6 8",n))

plot for [i=1:words(colsY)] $DashMultipleCols u 1:2:(mySymbol(i)) index i-1 \
          w p pt mySymbol(i) ps 0.6 lc "black" ti sprintf("Column %s, Symbol %d",word(colsY,i),mySymbol(i))
### end of code

结果:

补充:时间格式适配代码

gnuplot 中的时间就是自 1970 年 1 月 1 日以来经过的秒数 00:00:00。如果您在 gnuplot 控制台中输入 print time(0)(这是当前时间),您将得到类似 1646757721 的信息,因此从那时到今天已经过去了大约 16 亿秒。

与上述代码相比的主要区别和注意事项:

  • 定义您的特定时间格式,例如myTimeFmt = "%Y %b %d"
  • 使用这种时间格式和中间的空格,您的数据将在第 4、5 和 6 列中。
  • 用于在您第一次使用时绘制数据,例如plot $Data u (timecolumn(1,myTimeFmt)):4 w l
  • 对于长度和角度 table ($LaA),您还必须使用第 4、5、6 列和 timecolumn(),即 plot x1=y1=NaN $Data u (x0=x1,x1=XtoPix(timecolumn(colX,myTimeFmt)),x0)
  • 为了创建数据块 $DashSingleCol,您必须强制时间格式为 "%.0f",即 print sprintf("%.0f %g", PixToX(X0(idx)+(R-L0)*cos(SegAngle(idx)))。否则,使用 "%g" gnuplot 会写一个带指数但只有 6 位数字的浮点数,例如1.64676e+09 这将是不需要的截断或舍入。
  • 为了绘制 $DashMultipleCols 你可以简单地使用 u 1:2 因为时间已经以秒为单位并且不必通过 timecolumn().
  • 进行更改

我希望这些额外的代码和解释能帮助您用不同的符号“划线”您的数据。

代码:

### "dashing" with symbols equidistantly along a path (with timedata)
reset session

myTimeFmt = "%Y %b %d"

# create some random test data
set print $Data
    t0 = time(0)
    y0 = y1 = y2 = 10
    SecsPerDay =  24*3600   # seconds per day
    do for [i=1:10] {
        t0=t0+int(rand(0)*10*SecsPerDay)+5*SecsPerDay
        print sprintf("%s %g %g %g", strftime(myTimeFmt,t0), \
                y0=y0+int(rand(0)*10)-5, y1=y1+int(rand(0)*10)-5, y2=y2+int(rand(0)*10)-5)
    }
set print

set key top left Left reverse
# plot to get the GPVAL_ ... values
set format x "%b %01d\n%Y" timedate
plot $Data u (timecolumn(1,myTimeFmt)):4 w l dt 1 lc "black" ti "Column 4, solid", \
        '' u (timecolumn(1,myTimeFmt)):5 w l dt 2 lc "black" ti "Column 5, dashed", \
        '' u (timecolumn(1,myTimeFmt)):6 w l dt 3 lc "black" ti "Column 6, dotted"

# store GPVAL parameters after plot in variables
txmin = GPVAL_TERM_XMIN
txmax = GPVAL_TERM_XMAX
tymin = GPVAL_TERM_YMIN
tymax = GPVAL_TERM_YMAX
xmin  = GPVAL_X_MIN
xmax  = GPVAL_X_MAX
ymin  = GPVAL_Y_MIN
ymax  = GPVAL_Y_MAX

# x,y to pix and pix to x,y coordinate conversion
XtoPix(x)    = txmin + real(x-xmin)    *(txmax-txmin)/( xmax- xmin)
YtoPix(y)    = tymin + real(y-ymin)    *(tymax-tymin)/( ymax- ymin)
PixToX(scrx) =  xmin + real(scrx-txmin)*( xmax- xmin)/(txmax-txmin)
PixToY(scry) =  ymin + real(scry-tymin)*( ymax- ymin)/(tymax-tymin)

# get lengths and angles in pixel coordinates
set angle degrees
Length(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)
Angle(x0,y0,x1,y1)  = (_dx=x1-x0, _dy=y1-y0, _L=sqrt(_dx**2 + _dy**2), _L==0 ? NaN : \
                      (_dy>=0 ? acos(_dx/_L) : 360-acos(_dx/_L) ))

colX  = 1
colsY = "4 5 6"         # multiple y-columns
do for [colY in colsY] {
    set table $LaA      # table for length and angles
        Total = 0
        plot x1=y1=NaN $Data u (x0=x1,x1=XtoPix(timecolumn(colX,myTimeFmt)),x0):\
                               (y0=y1,y1=YtoPix(column(int(colY))),y0):\
                (L=Length(x0,y0,x1,y1)):(L==L ? Total=Total+L : 0, Angle(x0,y0,x1,y1)) w table
    unset table
    X0(n)        = real(word($LaA[n],1))
    Y0(n)        = real(word($LaA[n],2))
    SegLength(n) = real(word($LaA[n],3))
    SegAngle(n)  = real(word($LaA[n],4))

    # create equidistant datapoints along path
    set print $DashSingleCol
        Dist = 8                    # Distance between symbols
        N = floor(Total/Dist)
        idx = 2
        L0 = 0
        L = SegLength(idx)
        do for [i=0:N] {
            R = i*Dist
            while (L-R<0) {
                L0 = L
                idx = idx + 1
                L = L + SegLength(idx)
            }
            print sprintf("%.0f %g", PixToX(X0(idx)+(R-L0)*cos(SegAngle(idx))), \
                                   PixToY(Y0(idx)+(R-L0)*sin(SegAngle(idx))))
        }
    set print
    set print $DashMultipleCols append
        print $DashSingleCol
        print "\n\n"
    set print
}

pause -1 

mySymbol(n) = int(word("1 6 8",n))

plot for [i=1:words(colsY)] $DashMultipleCols u 1:2:(mySymbol(i)) index i-1 \
          w p pt mySymbol(i) ps 0.6 lc "black" ti sprintf("Column %s, pointtype %d",word(colsY,i),mySymbol(i))
### end of code

结果:

set xdata time就好了。 set timefmt myTimeFmt 仅适用于使用相同的时间日期格式 设置 xrange 和 xtics 。然后,在绘图之前,必须根据要绘图的文件的时间日期格式修改set timefmt(即$DashMultipleCols),在本例中为秒;因此 set timefmt "%s".