1

I have \points array and I want to add xtick and ytick on my axis automatically from it. How can I do that?

\documentclass{article}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture} \def\points{{{-9,0.30},{-6,0.44},{-2,0.59},{3,1}}} \pgfmathsetmacro{\len}{dim(\points)-1}

\begin{axis} [ axis lines = center, axis line style = thick, % xtick= loop here for \points[\i][0] % ytick= loop here for \points[\i][1] where \i=0,...,\len clip=false ] \addplot ({x},{x^2}); \end{axis}

\end{tikzpicture} \end{document}

antshar
  • 4,238
  • 9
  • 30

3 Answers3

2

No need to loop.

The problem is that the points of your MWE have nothing to do with your curve (leave out opacity=0 to see them), they are neither on the curve nor near the curve, so the graphical representation is not good.
I think it would be better with your actual points and curve.

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}

\begin{document}

\begin{tikzpicture} \def\points{(-9,0.30) (-6,0.44) (-2,0.59) (3,1)} \begin{axis}[scale=1.5, xtick=data, ytick=data, axis lines = center, axis line style = thick, clip=false ] \addplot[only marks, opacity=0] coordinates {\points}; \addplot ({x},{x^2}); \end{axis} \end{tikzpicture} \end{document}

enter image description here

CarLaTeX
  • 62,716
  • But I need to display labels only for those points that are in \points array – antshar Nov 07 '21 at 11:06
  • @antshar See my renewed answer, please. – CarLaTeX Nov 08 '21 at 06:28
  • That's an interesting workaround. If you fake ticks with drawings inside axis I don't see a reason to give up the initial array. I still don't know a way of converting this one into mine that can be iterated with an access to a specific index. So \foreach \i [evaluate={\ix=\points[\i][0]; \iy=\points[\i][1];}] in {0,...,\len}{ \addplot [only marks, opacity=0] coordinates {(\ix,0) (0,\iy)}; } will do. – antshar Nov 08 '21 at 15:24
  • @antshar Sorry, I don't understand what do you mean by "give up the initial array" – CarLaTeX Nov 08 '21 at 15:41
  • my array of points has {{{x1,y1},{x2,x2},...}} format, but yours is {(x1,y1),(x2,y2),...} – antshar Nov 08 '21 at 15:44
  • @antshar Ah, ok. It is because that is the syntax accepted by coordinates. If you can't change that, my solution is useless – CarLaTeX Nov 08 '21 at 15:48
  • your workaround with faking ticks is clever. It might be useful for someone else. Personally, I often come across with an interesting approach in posts that don't really relate to my search request, but still contain useful information. – antshar Nov 08 '21 at 15:53
  • @antshar Thank you! You're always very kind – CarLaTeX Nov 08 '21 at 15:54
1

Here I use a \foreach to loop through \points and construct the list of x- and y-coords stored in \pointsX and \pointsY, respectively. Then just set xtick=\pointsX in axis environment.

Wrapping all these into a new pgfkeys handler would be neat, but the difference between a \foreach-list and a pgfmath-array makes the generalization very hard.

\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture} \def\points{{{-9,0.30},{-6,0.44},{-2,0.59},{3,1}}} \pgfmathsetmacro{\len}{dim(\points)-1}

\def\pointsX{} \def\pointsY{} \foreach \i in {0,...,\len} { \pgfmathsetmacro{\x}{\points[\i][0]} \pgfmathsetmacro{\y}{\points[\i][1]} \ifnum\i=0 \xappto\pointsX{\x} \xappto\pointsY{\y} \else \xappto\pointsX{,\x} \xappto\pointsY{,\y} \fi }

\begin{axis} [ axis lines = center, axis line style = thick, xtick=\pointsX, ytick=\pointsY, clip=false ] \addplot ({x},{x^2}); \end{axis}

\end{tikzpicture} \end{document}

enter image description here

muzimuzhi Z
  • 26,474
  • I see, etoolbox provides macro that appends elements. That's a neat solution that doesn't require much effort. Thank you for your answer. – antshar Nov 08 '21 at 15:26
  • @antshar The dependence on etoolbox is soft. You can easily define your own \xappto by \def\myXappto#1#2{\xdef#1{\unexpanded\expandafter{#1}#2}} (this differs from the \xappto that it doesn't check the existance of #1). – muzimuzhi Z Nov 08 '21 at 16:30
0

Basically the problem was about generating this kind of array {<num1>, <num2>, <num3>, ...}. It occurred to be quite tricky, but I managed to find the solution.

The key to solve the problem is an expansion. \xdef(equivalent to \global\edef) allows to define an expanded macro so that I it can access itself in its definition. For example

\edef\foo{-3}     % -> -3
\edef\foo{\foo,6} % -> -3,6

First solution based on \push macros that I found in this post. And the following code forms the desired array

\def\push#1#2{\expandafter\xdef\csname #1\endcsname{\expandafter\ifx\csname #1\endcsname\empty\else\csname #1\endcsname,\fi#2}}

\def\xarr{} \def\yarr{} \foreach \i [evaluate={\x=\points[\i][0]; \y=\points[\i][1]}] in {0,...,\len}{ \push{xarr}{\x} \push{yarr}{\y} }

Second solution doesn't require that macros, but still based on expanded definition

\def\xarr{}
\def\yarr{}
\foreach \i [evaluate={\x=\points[\i][0]; \y=\points[\i][1]; \c=ifthenelse(\i<\len,",",)}] in {0,...,\len}{
    \xdef\xarr{\xarr\x\c}
    \xdef\yarr{\yarr\y\c}
}

MWE

\documentclass{article}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture} \def\points{{{-9,0.30},{-6,0.44},{-2,0.59},{3,1}}} \pgfmathsetmacro{\len}{dim(\points)-1}

\def\push#1#2{\expandafter\xdef\csname #1\endcsname{\expandafter\ifx\csname #1\endcsname\empty\else\csname #1\endcsname,\fi#2}}

\def\xarr{} \def\yarr{}

% First method %\foreach \i [evaluate={\x=\points[\i][0]; \y=\points[\i][1]}] in {0,...,\len}{ % \push{xarr}{\x} % \push{yarr}{\y} %}

% Second method \foreach \i [evaluate={\x=\points[\i][0]; \y=\points[\i][1]; \c=ifthenelse(\i<\len,",",)}] in {0,...,\len}{ \xdef\xarr{\xarr\x\c} \xdef\yarr{\yarr\y\c} }

\begin{axis} [ axis lines = center, axis line style = thick, % xtick= loop here for \points[\i][0] % ytick= loop here for \points[\i][1] where \i=0,...,\len clip=false ] \addplot ({x},{x^2}); \end{axis}

\end{tikzpicture} \end{document}

Note that if you have babel package added, the second method might throw an error. That's all because of " inside ifthenelse. In order to fix that, use ifthenelse(\i<\len,\string",\string",), or use \shorthandoff{"}.

antshar
  • 4,238
  • 9
  • 30