3

How to extend tikzpicture with data under diagram?

I've written the diagram below, which displays nicely in LaTeX. However, the points in the diagram are very close, so that it is difficult to see which graph point is higher than the other.

enter image description here

What I need is a table with the data underneath the graph, like I do in Excel. How do I do that in LaTeX?

enter image description here

\begin{figure}[htbp]
\centering
\begin{tikzpicture}
\begin{axis}[
    title={\textbf{All-Pairs Shortest Path}},
    scaled y ticks = false,
    y tick label style={/pgf/number format/fixed,
    /pgf/number format/1000 sep = \thinspace},
    xlabel={Matrix Dimension},
    ylabel={Gas},
    xmin=0, xmax=25,
    ymin=0, ymax=10500000,
    xtick={1,4,8,12,16,20,24},
    %ytick={},
    legend pos=north west,
    ymajorgrids=true,
    grid style=dashed,
    width = \linewidth
]

\addplot[
    color=blue,
    mark=*,
    mark options={scale=1.50}
    ]
    coordinates {
    (1,23902)(4,78508)(8,426081)(12,1338366)(16,3086445)(20,5945874)(24,10182149)
    };

\addplot[
    color=red,
    mark=triangle*,
    mark options={scale=1.50},
    ]
    coordinates {
    (1,107391)(4,154467)(8,306647)(12,563926)(16,928604)(20,1411996)(24,2025096)
    };
    \legend{Local, Oraclize}    

\end{axis}
\end{tikzpicture}
\caption{Gas cost of computing all-pairs shortest path (APSP) using Floyd-Warshall algorithm. (Source: Project)}
\label{fig:apsp}
\end{figure}

If I use Torbjørn's answer for a diagram with ten datapoints,

\pgfplotsset{compat=1.14}
% read in data
\pgfplotstableread{
x v nv
1 22147 107477
5 26844 120709
10 32674 137975
25 60060 188721
50 101523 274672
100 205331 445946
250 542985 982947
500 1208542 1914372
750 1956327 2901016
1000 2634826 3928101
}\datatable

% get number of rows
\pgfplotstablegetrowsof{\datatable}
% subtract 1 because table indices start at 0
\pgfmathsetmacro{\Nrows}{\pgfplotsretval-1}
% for convenience, macro to store width of axis
\pgfmathsetlengthmacro{\MyAxisW}{10cm}

\begin{figure}[htbp]
\centering
\begin{tikzpicture}[
  cell/.style={ % style used for "table" cells
    draw,
    minimum width={\MyAxisW/(\Nrows+1)}, % +1 because -1 above
    minimum height=4ex,
    inner sep=0pt,
    outer sep=0pt,
    anchor=north west,
    font=\sffamily\scriptsize
  }]
\begin{axis}[
   name=ax,
   % so axis labels and ticklabels are not accounted for in size settings   
   scale only axis,
   width=\MyAxisW,
   height=4cm,
   % we add the ticklabels as part of the table, so no xticks needed
   xtick=\empty,
   % and add grid lines
   grid=major,
   % only need left y-axis line
   axis y line=left,
   x axis line style={draw=none},
   % and for that we need to make sure that the distance from the first/last tick
   % to the axis edge is the same, so that there is a half a bar width of space
   enlarge x limits={abs={\MyAxisW/(2*\Nrows+2)}},
   ymin=0,
   title={Array Sorting},
   title style={font=\bfseries},
   ylabel={Gas},
   xlabel={\# matrix dimension},
   % move xlabel to below table
   xlabel shift=12ex,
   % set yticks as sans serif
   tick label style={
    font=\sansmath\sffamily\small,
    % and remove comma in 1,000
    /pgf/number format/set thousands separator=},
   % set axis labels as sans serif
   label style={font=\sansmath\sffamily}
  ]
  % because the x-values are not evenly spaced, used index as x-value instead
  \addplot +[black!60] table[x expr=\coordindex,y=nv] {\datatable};
  \label{dataNV}

  \addplot +[black!20] table[x expr=\coordindex,y=v] {\datatable};
  \label{dataV}
\end{axis}

% define a starter coordinate at the lower left corner of the axis
\coordinate (c-0-0) at (ax.south west);

% loop over the table
\foreach [count=\j from 1] \i in {0,...,\Nrows}
  {
  % get element \i from the x-column, stored in \pgfplotsretval
  \pgfplotstablegetelem{\i}{x}\of\datatable
  % add node with value
  \node [cell] (c-0-\j) at (c-0-\i.north east) {\pgfplotsretval};
  % repeat for other two columns
  \pgfplotstablegetelem{\i}{v}\of\datatable
  \node [cell] (c-1-\j) at (c-0-\j.south west) {\pgfplotsretval};
  \pgfplotstablegetelem{\i}{nv}\of\datatable
  \node [cell] (c-2-\j) at (c-1-\j.south west) {\pgfplotsretval};
  }

% add "legend" on the left
\matrix [draw,nodes={cell,minimum width=0pt,draw=none},anchor=north east,row sep=0pt,column sep=5pt,outer sep=0pt,inner ysep=0pt] (m) at (c-1-1.north west)
{
 \node {\ref{dataV}};  & \node{Gas: Oraclize}; \\
 \node {\ref{dataNV}}; & \node{Gas: local}; \\
};

% draw center line of legend
\draw (m.west) -- (m.east);

\end{tikzpicture}
\end{figure}

enter image description here

I've the problem with "dots" (data points) lying on the x-axis and columns too small for the data?

2
2

Take the code in Stacked bar chart above table, and do a few things:

  • Replace the data in \pgfplotstableread with your data. x-values in the first column, then two columns with the y-data.
  • Remove from the axis options:
    • ybar stacked (don't want bar plot)
    • bar width={...} (not needed, not a bar plot)
    • ytick distance=200 (ridiculous when the y-axis span 10^7)
  • Change ylabel/xlabel in the axis options, and add a title.
  • In the options to the \matrix that generates the legend:
    • change nodes={cell,draw=none} to nodes={cell,minimum width=0pt,draw=none}
    • add column sep=5pt next to row sep.
  • Remove +[black!60] from the first \addplot and +[black!20] from the second.

If the numbers are too wide for the cells, there are a couple of things you can do. Either

  • Change from \scriptstyle to \tiny in the cell style, to reduce the font size. That is, below you'll find font=\sffamily\scriptsize, change that to font=\sffamily\tiny.
  • Increase the width of the axis. The width is defined in the \MyAxisW macro, so change from \pgfmathsetlengthmacro{\MyAxisW}{10cm} to for example \pgfmathsetlengthmacro{\MyAxisW}{12cm}.

Or do both of these, it depends on the numbers, the number of data points, etc.

Because of ymin=0, the points with the lowest numbers lie on the x-axis. To fix that you can do one of two things:

  • Change the ymin value to for example ymin=-1e5.
  • Replace ymin=0 with enlarge y limits.

To add a third plot, you need to

  • add a new column in the table
  • add a new \addplot
  • assuming the new column is named nnv, add

    \pgfplotstablegetelem{\i}{nnv}\of\datatable
    \node [cell] (c-3-\j) at (c-2-\j.south west) {\pgfplotsretval};
    

    to the loop that builds the table

  • modify the method for drawing the horizontal lines in the legend
  • increase the xlabel shift, to account for the new table row

output of code

\documentclass[border=5mm]{standalone}

% for sans serif ticks (https://tex.stackexchange.com/questions/33325/)
\usepackage[eulergreek]{sansmath}

\usepackage{pgfplots,pgfplotstable}
\pgfplotsset{compat=1.14}
% read in data
\pgfplotstableread{
x v nv nnv
1 23902 107391 100391
4 78508 154467 254467
8 426081 306647 406647
12 1338366 563926 550926
16 3086445 928604 1028604
20 5945874 1411996 1511996
24 10182149 2025096 2525096
}\datatable

% get number of rows
\pgfplotstablegetrowsof{\datatable}
% subtract 1 because table indices start at 0
\pgfmathsetmacro{\Nrows}{\pgfplotsretval-1}
% for convenience, macro to store width of axis
\pgfmathsetlengthmacro{\MyAxisW}{10cm}

\begin{document}%
\begin{tikzpicture}[
  cell/.style={ % style used for "table" cells
    draw,
    minimum width={\MyAxisW/(\Nrows+1)}, % +1 because -1 above
    minimum height=4ex,
    inner sep=0pt,
    outer sep=0pt,
    anchor=north west,
    font=\sffamily\scriptsize
  }]
\begin{axis}[
   name=ax,
   % so axis labels and ticklabels are not accounted for in size settings   
   scale only axis,
   width=\MyAxisW,
   height=4cm,
   % we add the ticklabels as part of the table, so no xticks needed
   xtick=\empty,
   % and add grid lines
   grid=major,
   % only need left y-axis line
   axis y line=left,
   x axis line style={draw=none},
   % and for that we need to make sure that the distance from the first/last tick
   % to the axis edge is the same, so that there is a half a bar width of space
   enlarge x limits={abs={\MyAxisW/(2*\Nrows+2)}},
   ymin=0,
   title={All-Pairs Shortest Path},
   title style={font=\bfseries},
   ylabel={Gas},
   xlabel={\# matrix dimension},
   % move xlabel to below table
   xlabel shift=16ex,
   % set yticks as sans serif
   tick label style={
    font=\sansmath\sffamily\small,
    % and remove comma in 1,000
    /pgf/number format/set thousands separator=},
   % set axis labels as sans serif
   label style={font=\sansmath\sffamily}
  ]
  % because the x-values are not evenly spaced, used index as x-value instead
  \addplot table[x expr=\coordindex,y=nnv] {\datatable};
  \label{dataNNV}

  \addplot table[x expr=\coordindex,y=nv] {\datatable};
  \label{dataNV}

  \addplot table[x expr=\coordindex,y=v] {\datatable};
  \label{dataV}
\end{axis}

% define a starter coordinate at the lower left corner of the axis
\coordinate (c-0-0) at (ax.south west);

% loop over the table
\foreach [count=\j from 1] \i in {0,...,\Nrows}
  {
  % get element \i from the x-column, stored in \pgfplotsretval
  \pgfplotstablegetelem{\i}{x}\of\datatable
  % add node with value
  \node [cell] (c-0-\j) at (c-0-\i.north east) {\pgfplotsretval};
  % repeat for other two columns
  \pgfplotstablegetelem{\i}{v}\of\datatable
  \node [cell] (c-1-\j) at (c-0-\j.south west) {\pgfplotsretval};
  \pgfplotstablegetelem{\i}{nv}\of\datatable
  \node [cell] (c-2-\j) at (c-1-\j.south west) {\pgfplotsretval};
  \pgfplotstablegetelem{\i}{nnv}\of\datatable
  \node [cell] (c-3-\j) at (c-2-\j.south west) {\pgfplotsretval};
  }

% add "legend" on the left
\matrix [draw,nodes={cell,minimum width=0pt,draw=none},anchor=north east,row sep=0pt,column sep=5pt,outer sep=0pt,inner ysep=0pt] (m) at (c-1-1.north west)
{
 \node {\ref{dataV}};  & \node{Gas: Oraclize}; \\
 \node {\ref{dataNV}}; & \node{Gas: local}; \\
 \node {\ref{dataNNV}}; & \node{Foobar}; \\
};

% draw horizontal lines in legend
\draw (m.west |- c-1-1.south west) -- (c-1-1.south west);
\draw (m.west |- c-2-1.south west) -- (c-2-1.south west);


\end{tikzpicture}
\end{document}
10
  • I've a problem with your solution - see my question. – Shuzheng Nov 28 '17 at 16:45
  • @Shuzheng If I use your code with \documentclass{article} \usepackage[eulergreek]{sansmath} \usepackage{pgfplotstable} I do not get numbers that are too wide for the cells. But try changing the value for \MyAxisW, e.g. from \pgfmathsetlengthmacro{\MyAxisW}{10cm} to \pgfmathsetlengthmacro{\MyAxisW}{12cm}. For the other problem, you could for example set ymin=-1e5 or something, instead of ymin=0. – Torbjørn T. Nov 28 '17 at 16:58
  • @Shuzheng See also the update to my answer. – Torbjørn T. Nov 28 '17 at 17:07
  • Thanks once again. Last question, how do I set the line color of the "red" marker line to some other color than the color used for the lineseparators? In each case, the color is lightgray. – Shuzheng Nov 28 '17 at 17:08
  • @Shuzheng I don't actually understand exactly what you mean, but try removing +[black!20] and +[black!60] from the \addplots. (Had forgotten to remove those.) – Torbjørn T. Nov 28 '17 at 17:10

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.