14

I have this type of data set:

Time          State
22:54:14.716    5
22:54:14.716    5
22:54:14.716    5
22:54:14.716    5
22:54:14.729    4
22:54:14.919    1
22:54:14.919    1
22:54:15.128    5

I would like to plot a graph with pgfplots but the package can not read properly the time format.

lockstep
  • 250,273
user21431
  • 685

2 Answers2

15

You'll have to tell PGFPlots how to handle these times, which can be done using a x coord trafo/.code key. I've defined a new style timeplot that takes care of that.

Note that because you're using a numerically disadvantageous data format (you need to handle 8 significant digits), you should centre your data by defining a zero point close to your actual data range (in your example, you could subtract 22 hours). I've defined a key timeplot zero that takes an hour value for that.

\documentclass[tikz,border=2mm]{article}
\usepackage{pgfplots}
\usepgfplotslibrary{dateplot}

\def\transformtime#1:#2:#3!{
    \pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
    \pgfmathparse{#1*3600-\pgfkeysvalueof{/pgfplots/timeplot zero}*3600+#2*60+#3}
    \pgfkeys{/pgf/fpu=false}
}

\pgfplotsset{
    timeplot zero/.initial=0,
    timeplot/.style={
        x coord trafo/.code={\expandafter\transformtime##1!},
        x coord inv trafo/.code={%
            \pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
            \pgfmathsetmacro\hours{floor(##1/3600)+\pgfkeysvalueof{/pgfplots/timeplot zero}}
            \pgfmathsetmacro\minutes{floor((##1-(\hours-\pgfkeysvalueof{/pgfplots/timeplot zero})*3600)/60)}
            \pgfmathsetmacro\seconds{##1-floor(##1/60)*60}
            \def\pgfmathresult{\pgfmathprintnumber{\hours}:\pgfmathprintnumber{\minutes}:\pgfmathprintnumber[fixed zerofill]{\seconds}}
            \pgfkeys{/pgf/fpu=false}
        },
    scaled x ticks=false,
    xticklabel=\tick
    }
}

\begin{document}
\begin{tikzpicture}
\begin{axis}[
    timeplot, timeplot zero=22,
    xtick={22:54:14.75,22:54:15}]
\addplot table {
Time          State
22:54:14.716    5
22:54:14.716    5
22:54:14.716    5
22:54:14.716    5
22:54:14.729    4
22:54:14.919    1
22:54:14.919    1
22:54:15.128    5
};
\end{axis}
\end{tikzpicture}
\end{document}
Jake
  • 232,450
  • thanks for the reply. Is possible to draw the line state changes in horizontal?

    ie to any changes, a horizontal line maintains the new state, rather than an oblique line that connects two points ?

    – user21431 Oct 29 '12 at 08:45
  • @user21431: Sure, just use \addplot +[const plot] .... – Jake Oct 29 '12 at 11:01
  • 1
    @user21431: Glad it helped! If you consider your question solved, you can accept the answer by clicking the tick mark next to it (and upvoting) – Jake Oct 29 '12 at 22:53
  • 1
    This code unfortunately fails if, in the data, 22 is replaced by 08 (or 09) with the error ! Package PGF Math Error: Digit '8' invalid for base 8 (in '08*3600-08*3600+54*60+14.75'). As pointed out here, "removing the leading zeros eliminated the error, so it looks like pgfplots thinks that a leading zero means a number in an octal basis, so any digits larger than 7 generates an error". Manually remove these zeros is not always possible. Is there a way to improve this answer in this respect? – Denis Bitouzé Oct 19 '14 at 09:44
  • 1
    @DenisBitouzé I have copied Jake's approach here and modified it such that it solves the base-8-problem (and handles sub-second parts). See http://tex.stackexchange.com/questions/249923/how-to-escape-any-csv-file-automatically-for-pgfplots/249944#249944 – Christian Feuersänger Jun 12 '15 at 15:37
  • 1
    ... and I failed to read the answer of @ema which also solved the base-8-problem... :( – Christian Feuersänger Jun 12 '15 at 15:41
7

thanks for the answer, I made some additional modification, especially to prevent the the 08 problem.

remove leading zero:

\def\removeleadingzeros#1{\if0#1 \expandafter\else#1\fi}

new definitions: (mostly adding leading 0 to x label and no floating point for seconds)

\def\transformtime#1:#2:#3!{
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
%\pgfmathparse{#1*3600-\pgfkeysvalueof{/pgfplots/timeplot zero}*3600+#2*60+#3}
\pgfmathparse{\removeleadingzeros#1*3600-\pgfkeysvalueof{/pgfplots/timeplot zero}*3600+\removeleadingzeros#2*60+\removeleadingzeros#3}
\pgfkeys{/pgf/fpu=false}
}


\pgfplotsset{
timeplot zero/.initial=0,
timeplot/.style={
    x coord trafo/.code={\expandafter\transformtime##1!},
    x coord inv trafo/.code={%
        \pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
        \pgfmathsetmacro\hours{floor(##1/3600)+\pgfkeysvalueof{/pgfplots/timeplot zero}}
        \pgfmathsetmacro\minutes{floor((##1-(\hours-\pgfkeysvalueof{/pgfplots/timeplot zero})*3600)/60)}
        \pgfmathsetmacro\seconds{##1-floor(##1/60)*60}
        \def\pgfmathresult{\pgfmathparse{mod(\hours,60)<10?"0":{},int(mod(\hours,60))}\pgfmathresult:\pgfmathparse{mod(\minutes,60)<10?"0":{},int(mod(\minutes,60))}\pgfmathresult:\pgfmathparse{mod(\seconds,60)<10?"0":{},int(mod(\seconds,60))}\pgfmathresult}
        \pgfkeys{/pgf/fpu=false}
    },
scaled x ticks=false,
xticklabel=\tick
}
}

Further I removed the floating point for the seconds I did not need. But it should be easy to add.

Example:

\begin{tikzpicture}
\begin{axis}[
    timeplot, timeplot zero=0,
    xtick={02:08:08,11:30:15}]
\addplot table {
Time          State
02:08:00    1
03:54:02    2
04:54:03    3
05:54:04    4
06:54:05    5
07:54:06    6
08:54:07    5
09:54:08    4
10:54:09    3
11:54:10    2
12:54:11    1
};
\end{axis}
\end{tikzpicture}

Cheers

Ema
  • 71