2

I have a CSV from which I'm trying to draw dots using \node within a tikzpicture. I can get it to read the data and draw something, but it only draws the last row, and multiple times.

I know that normally foreach loops only get handled inside the axis environment at the end, and so I have tried this both with pgfplotsinvokeforeach and the datatool package, but there is no difference.

Below is a MWE, with \DTLforeach commented out but otherwise working identically to the pgfplotsinvokeforeach loop.

Note I've added a manual dot with the "a" label as well to show the difference in thickness between that and the looped ones, showing that it's drawing it multiple times in the loop.

What am I doing wrong here? I should see three dots with labels placed according to the data in filecontents (not including the manual one).

\documentclass{article}

\usepackage{datatool}
\usepackage{tikz}
\usepackage{pgfplots}
\usepackage{filecontents}

\begin{document}

\begin{filecontents*}{formants.csv}
label,f1,f2
"i",1800,275
"o",235,487
"a",650,810
\end{filecontents*}

% using datatool
\DTLloaddb[keys={formantlabel,formantone,formanttwo}]{formants}{formants.csv}
\DTLforeach*{formants}{\formantlabel=formantlabel,\formantone=formantone,\formanttwo=formanttwo}{%
  \formantlabel, \formantone, \formanttwo \\
}

% using pgfplots functions
\pgfplotstableread[col sep=comma]{formants.csv}{\formants}
\pgfplotstablegetrowsof\formants
\pgfmathsetmacro\numberofrows{\pgfplotsretval-1}

\begin{figure}[h]
  \begin{tikzpicture}
    \begin{axis}[
      xmin = 0,
      xmax = 2000,
      ymin = 0,
      ymax = 1000
      ]
      
% this works but only shows the last one
      \pgfplotsinvokeforeach{0,...,\numberofrows}{
        \pgfplotstablegetelem{#1}{label}\of\formants
        \pgfmathsetmacro{\flabel}{\pgfplotsretval}
        
        \pgfplotstablegetelem{#1}{f1}\of\formants
        \pgfmathsetmacro{\fone}{\pgfplotsretval}
        
        \pgfplotstablegetelem{#1}{f2}\of\formants
        \pgfmathsetmacro{\ftwo}{\pgfplotsretval}
        
        \node[label={180:{\flabel}},circle,fill,inner sep=2pt] at (axis cs:\fone, \ftwo) {}; %
      }

% this also works but again only shows the last one
%      \DTLforeach*{formants}{
%        \formantlabel=formantlabel,\formantone=formantone,\formanttwo=formanttwo}{%
%        \node[label={180:{\formantlabel}},circle,fill,inner sep=2pt] at (axis cs:\formantone, \formanttwo) {}; %
%      }

      \node[label={180:{a}},circle,fill,inner sep=2pt] at (axis cs:650.886, 910.603) {}; % test
    \end{axis}
  \end{tikzpicture}
\end{figure}

  
\end{document}

Also note this is XeLaTeX, in case that matters.

Here's how that looks on my end, with the lower "a" dot being the looped one, and darker than the higher one. The text above the plot is a loop showing that the data is being properly read.

enter image description here

1 Answer 1

2

You have to expand \flabel, \fone, and \ftwo, inside of the loop, before passing it to \node.

\documentclass{article}

\usepackage{datatool}
\usepackage{tikz}
\usepackage{pgfplots}
\usepackage{filecontents}

\begin{document}

\begin{filecontents*}{formants.csv}
label,f1,f2
"i",1800,275
"o",235,487
"a",650,810
\end{filecontents*}

% using datatool
\DTLloaddb[keys={formantlabel,formantone,formanttwo}]{formants}{formants.csv}
\DTLforeach*{formants}{\formantlabel=formantlabel,\formantone=formantone,\formanttwo=formanttwo}{%
  \formantlabel, \formantone, \formanttwo \\
}

% using pgfplots functions
\pgfplotstableread[col sep=comma]{formants.csv}{\formants}
\pgfplotstablegetrowsof\formants
\pgfmathsetmacro\numberofrows{\pgfplotsretval-1}

\begin{figure}[h]
  \begin{tikzpicture}
    \begin{axis}[
      xmin = 0,
      xmax = 2000,
      ymin = 0,
      ymax = 1000
      ]
      
% this works but only shows the last one
      \pgfplotsinvokeforeach{0,...,\numberofrows}{
        \pgfplotstablegetelem{#1}{label}\of\formants
        \pgfmathsetmacro{\flabel}{\pgfplotsretval}
        
        \pgfplotstablegetelem{#1}{f1}\of\formants
        \pgfmathsetmacro{\fone}{\pgfplotsretval}
        
        \pgfplotstablegetelem{#1}{f2}\of\formants
        \pgfmathsetmacro{\ftwo}{\pgfplotsretval}
        
        \edef\tmp{[label={180:{\flabel}},circle,fill,inner sep=2pt]
           at (axis cs:\fone, \ftwo) {};} %
        \expandafter\node\tmp
      }

% this also works but again only shows the last one
%      \DTLforeach*{formants}{
%        \formantlabel=formantlabel,\formantone=formantone,\formanttwo=formanttwo}{%
%        \node[label={180:{\formantlabel}},circle,fill,inner sep=2pt] at (axis cs:\formantone, \formanttwo) {}; %
%      }

      \node[label={180:{a}},circle,fill,inner sep=2pt] at (axis cs:650.886, 910.603) {}; % test
    \end{axis}
  \end{tikzpicture}
\end{figure}

  
\end{document}

enter image description here

1
  • Legend! Thank you. Marked as the correct solution.
    – Yhilan Ko
    Commented Jun 15, 2021 at 12:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .