Plotting labeled intervals in matplotlib/gnuplot

后端 未结 3 1422
死守一世寂寞
死守一世寂寞 2020-12-01 06:40

I have a data sample which looks like this:

a 10:15:22 10:15:30 OK
b 10:15:23 10:15:28 OK
c 10:16:00 10:17:10 FAILED
b 10:16:30 10:16:50 OK

相关标签:
3条回答
  • 2020-12-01 06:57

    Updated: Now includes handling the data sample and uses mpl dates functionality.

    import matplotlib.pyplot as plt
    from matplotlib.dates import DateFormatter, MinuteLocator, SecondLocator
    import numpy as np
    from StringIO import StringIO
    import datetime as dt
    
    ### The example data
    a=StringIO("""a 10:15:22 10:15:30 OK
    b 10:15:23 10:15:28 OK
    c 10:16:00 10:17:10 FAILED
    b 10:16:30 10:16:50 OK
    """)
    
    #Converts str into a datetime object.
    conv = lambda s: dt.datetime.strptime(s, '%H:%M:%S')
    
    #Use numpy to read the data in. 
    data = np.genfromtxt(a, converters={1: conv, 2: conv},
                         names=['caption', 'start', 'stop', 'state'], dtype=None)
    cap, start, stop = data['caption'], data['start'], data['stop']
    
    #Check the status, because we paint all lines with the same color 
    #together
    is_ok = (data['state'] == 'OK')
    not_ok = np.logical_not(is_ok)
    
    #Get unique captions and there indices and the inverse mapping
    captions, unique_idx, caption_inv = np.unique(cap, 1, 1)
    
    #Build y values from the number of unique captions.
    y = (caption_inv + 1) / float(len(captions) + 1)
    
    #Plot function
    def timelines(y, xstart, xstop, color='b'):
        """Plot timelines at y from xstart to xstop with given color."""   
        plt.hlines(y, xstart, xstop, color, lw=4)
        plt.vlines(xstart, y+0.03, y-0.03, color, lw=2)
        plt.vlines(xstop, y+0.03, y-0.03, color, lw=2)
    
    #Plot ok tl black    
    timelines(y[is_ok], start[is_ok], stop[is_ok], 'k')
    #Plot fail tl red
    timelines(y[not_ok], start[not_ok], stop[not_ok], 'r')
    
    #Setup the plot
    ax = plt.gca()
    ax.xaxis_date()
    myFmt = DateFormatter('%H:%M:%S')
    ax.xaxis.set_major_formatter(myFmt)
    ax.xaxis.set_major_locator(SecondLocator(interval=20)) # used to be SecondLocator(0, interval=20)
    
    #To adjust the xlimits a timedelta is needed.
    delta = (stop.max() - start.min())/10
    
    plt.yticks(y[unique_idx], captions)
    plt.ylim(0,1)
    plt.xlim(start.min()-delta, stop.max()+delta)
    plt.xlabel('Time')
    plt.show()
    

    Resulting image

    0 讨论(0)
  • 2020-12-01 07:00

    gnuplot 5.2 version with creating a unique key list

    The main difference to @CiroSantilli's solution is that a list of unique keys is created automatically from column 1 and the index can be accessed via the defined function Lookup(). The referenced gnuplot demo already uses a list of unique items, however, in the OP's case there are duplicates.

    Creating such a list of unique items does not exist in gnuplot right away, so you have to implement it yourself. The code requires gnuplot >=5.2. It is probably difficult to get a solution which works under gnuplot 4.4 (the time of OP's question) because a few useful features were not implemented at that time: do for-loops, summation, datablocks, ... (a version for gnuplot 4.6 might be possible with some workarounds).

    Edit: the earlier version used with vectors and linewidth 20 to plot the bars, however, linewidth 20 also extends in x-direction which is not desired here. Therefore, with boxxyerror is now used.

    Code:

    ### Time chart
    reset session
    
    $Data <<EOD
    # category        start    end      status
    "event 1"         10:15:22 10:15:30 OK
    "event 2"         10:15:23 10:15:28 OK
    pause             10:16:00 10:17:10 FAILED
    "something else"  10:16:30 10:17:50 OK
    unknown           10:17:30 10:18:50 OK
    "event 3"         10:18:30 10:19:50 FAILED
    pause             10:19:30 10:20:50 OK
    "event 1"         10:17:30 10:19:20 FAILED
    EOD
    
    # create list of keys
    List = ''
    set table $Dummy
        plot $Data u (List=List.'"'.strcol(1).'" ',NaN) w table
    unset table
    
    # create list of unique keys
    UniqueList = ''
    do for [i=1:words(List)] {
        item = word(List,i)
        found = 0
        do for [j=1:words(UniqueList)] {
            if (item eq word(UniqueList,j)) { found=1; break }
        }
        if (!found) { UniqueList = UniqueList.'"'.item.'" '}
    }
    print UniqueList
    
    # define functions for lookup and color
    Lookup(s) = (Index = NaN, sum [i=1:words(UniqueList)] \
        (Index = s eq word(UniqueList,i) ? i : Index,0), Index)
    Color(s) = s eq "OK" ? 0x00cc00 : 0xff0000
    
    set xdata time
    set timefmt  "%H:%M:%S"
    set format x "%M'".'%S"'
    set yrange [0.5:words(UniqueList)+0.5]
    plot $Data u (timecolumn(2)):(Idx=Lookup(strcol(1))): \
        (timecolumn(3)):(timecolumn(2)):(Idx-0.3):(Idx+0.3): \
        (Color(strcol(4))):ytic(strcol(1)) \
        w boxxyerror fill solid 1.0 lc rgb var notitle
    ### end of code
    

    Result:

    0 讨论(0)
  • 2020-12-01 07:01

    gnuplot with vector solution

    Minimized from: http://gnuplot.sourceforge.net/demo_5.2/gantt.html

    main.gnuplot

    #!/usr/bin/env gnuplot
    
    $DATA << EOD
    1 1 5
    1 11 13
    2 3 10
    3 4 8
    4 7 13
    5 6 15
    EOD
    
    set terminal png size 512,512
    set output "main.png"
    set xrange [-1:]
    set yrange [0:]
    unset key
    set border 3
    set xtics nomirror
    set ytics nomirror
    set style arrow 1 nohead linewidth 3
    plot $DATA using 2 : 1 : ($3-$2) : (0.0) with vector as 1, \
         $DATA using 2 : 1 : 1 with labels right offset -2
    

    GitHub upstream.

    Output:

    You can remove the labels by removing the second plot command line, I added them because they are useful in many applications to more easily identify the intervals.

    The Gantt example I linked to shows how to handle date formats instead of integers.

    Tested in gnuplot 5.2 patchlevel 2, Ubuntu 18.04.

    0 讨论(0)
提交回复
热议问题