4

I have monthly data, and I want to show ticks for, say, each quarter. Thus, instead of showing 12 ticks per year, I want to show 4. There is a very similar post here, but the solution is to write the coordinates manually. That is not helpful for me, since my dataset is long. I have tried using the xtick={0,4,...,100} option, but that doesn't skip dates (see plot below).

My MWE is:

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{pgfplots,tikz}
\RequirePackage{filecontents}

\begin{filecontents}{Data1.csv}
Period,spot
jun 15,1.93
jul 15,2.12
aug 15,2.23
sep 15,1.81
oct 15,1.76
nov 15,1.18
dec 15,1.12
jan 16,1.05
feb 16,1
mar 16,0.9
apr 16,0.75
may 16,1
jun 16,1.15

\end{filecontents}

\begin{document}

\begin{figure}
    \centering
    \pgfplotsset{table/col sep = comma}
    \begin{tikzpicture}
        \begin{axis}[
            xticklabels from table={Data1.csv}{Period},
            xtick={0,2,...,12},
            enlarge x limits=0.02,
            width=16cm,
            height=8cm,
            xticklabel style={rotate=45},
            ]
            \addplot+[smooth] table [x expr=\coordindex, y=spot, col sep = comma] {Data1.csv};
        \end{axis}
    \end{tikzpicture}
\end{figure}

\end{document}

enter image description here

  • If you had a longer dataset, the gap between ticks would increase automatically. – John Kormylo Dec 18 '15 at 4:18
5

xticklabels from table will always use all the tick labels in the order that it finds them in the table. It can't skip labels according to the tick value because there is no mapping from tick value to tick label (PGFPlots can only say "Give me the third label", not "give me the label for tick position 3.0"). You'll have to roll your own solution here, for example like this:

xtick={0,2,...,12},
xticklabel={
    \pgfmathparse{int(round(\tick))}
    \pgfplotstablegetelem{\pgfmathresult}{Period}\of{Data1.csv}\pgfplotsretval
}

The \pgfmathparse statement rounds the \tick value to the nearest integer, and that integer is then used as the row index for fetching the tick label from the table.

\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{pgfplots,tikz}
\RequirePackage{filecontents}

\begin{filecontents}{Data1.csv}
Period,spot
jun 15,1.93
jul 15,2.12
aug 15,2.23
sep 15,1.81
oct 15,1.76
nov 15,1.18
dec 15,1.12
jan 16,1.05
feb 16,1
mar 16,0.9
apr 16,0.75
may 16,1
jun 16,1.15

\end{filecontents}

\begin{document}

\begin{figure}
    \centering
    \pgfplotsset{table/col sep = comma}
    \begin{tikzpicture}
        \begin{axis}[
            xtick={0,2,...,12},
            xticklabel={
                \pgfmathparse{int(round(\tick))}
                \pgfplotstablegetelem{\pgfmathresult}{Period}\of{Data1.csv}\pgfplotsretval
            },
            enlarge x limits=0.02,
            width=16cm,
            height=8cm,
            xticklabel style={rotate=45, anchor=north east},
            ]
            \addplot+[smooth] table [x expr=\coordindex, y=spot, col sep = comma] {Data1.csv};
        \end{axis}
    \end{tikzpicture}
\end{figure}

\end{document}
  • This works, but now I need to manually define the exact number of rows that the dataset has. Otherwise, it gives an Package pgfplots Error: Sorry, row `NN' does not exist in table. I tried placing \pgfplotstablegetrowsof{Data1.csv} after the \pgfplotsset{table/col sep = comma} line in the code, and then replacing xtick={0,2,...,12} by xtick={0,2,...,\pgfplotsretval}, or by xtick={0,2,...,\pgfmathresult}, as page 60 of pgfplotstable manual indicates, but it the graph only shows two dates. Reading value of those two elements inside \caption shows they work. What's wrong? – luchonacho Dec 18 '15 at 18:05
  • I mean, for this example, the value of \pgfplotsretval is 13, as it should be. Yet, xtick={0,2,...,\pgfplotsretval} doesn't show all dates. Naturally, xtick={0,2,...,13} does show all dates. I fail to see the cause of the problem! – luchonacho Dec 18 '15 at 18:14
2

This is closely related to my previous answer. In short: I write a coord trafo and a coord inv trafo so that every quarter is represented by an integer. And since PgfPlots loves integers, it will end up with quarter-wise ticks.

Notes:

  • You need to reformat the input. Machines do not know "Oct", "Nov", or a "Dec" followed by a "Jan".
  • You need to setup try min ticks and max space between ticks really carefully, otherwise the interval would not be a quarter long.

\documentclass[border=1cm]{standalone}
\usepackage{pgfplots}
    \usepgfplotslibrary{dateplot}
\usepackage{filecontents}
\begin{filecontents}{Data1.csv}
Period,spot
2015-06-15,1.93
2015-07-15,2.12
2015-08-15,2.23
2015-09-15,1.81
2015-10-15,1.76
2015-11-15,1.18
2015-12-15,1.12
2016-01-16,1.05
2016-02-16,1
2016-03-16,0.9
2016-04-16,0.75
2016-05-16,1
2016-06-16,1.15
\end{filecontents}

\makeatletter
\pgfplotsset{
    /pgfplots/year coordinates in/.code={
        \pgfkeysalso{%
            #1 tick label style={/pgf/number format/1000 sep=}, % "2015" rather than "2,015"
            #1 tick label as interval
        }
        \pgfkeysdef{/pgfplots/#1 coord trafo}{
            \begingroup
            \edef\pgfplotstempjuliandate{##1}
            % check if we also have a TIME like '2006-01-01 11:21'
            \expandafter\pgfutil@in@\expandafter:\expandafter{\pgfplotstempjuliandate}
            \ifpgfutil@in@
                % we have a TIME!
                \expandafter\pgfplotslibdateplot@map@time\pgfplotstempjuliandate:\dateto\pgfplotstempjuliandate\timeto\pgfplotstemptime
            \else
                \let\pgfplotstemptime=\pgfutil@empty
            \fi
            \expandafter\pgfcalendardatetojulian\expandafter{\pgfplotstempjuliandate}\c@pgf@counta
            \expandafter\pgfcalendardatetojulian\expandafter{\year-1-0}\c@pgf@countb
            \expandafter\pgfcalendardatetojulian\expandafter{\year-12-31}\c@pgf@countc
            \advance\c@pgf@counta by-\c@pgf@countb % now a = #days from 1/1 to temp
            \advance\c@pgf@countc by-\c@pgf@countb % now b = #days of that year
            \ifx\pgfplotstemptime\pgfutil@empty
                % no time:
                \pgfmathparse{\year+\the\c@pgf@counta/\the\c@pgf@countc}
            \else
                % add time fraction (which should be in the range
                % [0,1]).
                \ifdim\pgfplotstemptime pt<1pt
                    % discard prefix '0.':
                    \expandafter\pgfplotslibdateplot@discard@zero@dot\pgfplotstemptime\to\pgfplotstemptime
                    \pgfmathparse{\year+(\the\c@pgf@counta.\pgfplotstemptime)/\the\c@pgf@countc}%
                \else
                    % assume \pgfplotstemptime=1pt :
                    \advance\c@pgf@counta by1
                    \pgfmathparse{\year+\the\c@pgf@counta/\the\c@pgf@countc}
                \fi
            \fi
            \pgfmathparse{\pgfmathresult*4}
            \pgfmath@smuggleone\pgfmathresult
            \endgroup
        }
        \pgfkeysdef{/pgfplots/#1 coord inv trafo}{
            \pgfmathsetmacro\temp@a{##1/4}
            \pgfmathsetmacro\temp@b{int(\temp@a)}
            \pgfmathsetmacro\temp@c{\temp@a-\temp@b}
            \ifdim\temp@c pt<.76pt\xdef\pgfmathresult{\temp@b\ Q4}\fi
            \ifdim\temp@c pt<.51pt\xdef\pgfmathresult{\temp@b\ Q3}\fi
            \ifdim\temp@c pt<.26pt\xdef\pgfmathresult{\temp@b\ Q2}\fi
            \ifdim\temp@c pt<.01pt\xdef\pgfmathresult{\temp@b\ Q1}\fi
        }
        \pgfkeys{
            #1ticklabel={\tick},
            scaled #1 ticks=false,
            plot coordinates/math parser=false
        }
    }
}

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[
            year coordinates in=x,
            width=16cm,
            height=8cm,
            try min ticks=5,
            max space between ticks=1000pt
            ]
            \addplot+[smooth]table[x=Period, y=spot,col sep=comma] {Data1.csv};
        \end{axis}
    \end{tikzpicture}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.