3

I would like to conditionally print something in a report, based on whether there was a non-zero data point or not (for this example, \DepthOne and \DepthThree are to be tested).

I start by reading values in from a csv table using \csvreader, and then I assign these a label using \newcommand. Then I apply a test on the label using \ifnum. The behaviour in the implementation below is not what I hoped for. For instance, I get missing number, treated as zero errors and weird output preceding the sentence. Also, oddly one of the labels I assigned (\SampleName) doesn't seem to persist.

Is it possible to do what I am attempting, and what is the right way to go about this?

\documentclass[10pt]{report}

\usepackage{csvsimple}
\usepackage{filecontents}

\newcommand*{\ThisRock}{examplecsv}

% Make up some data in a CSV file called examplecsv.csv
\begin{filecontents*}{\ThisRock.csv}
    SampleName,Depth1,DepthTwo,Depth3
    ROCK001,48,52,0
\end{filecontents*}

% Pick out specific data in the file and create labels for them to be read by latex
\newcommand*{\SampleName}{\csvreader{\ThisRock.csv}{1=\SampleName}{\SampleName}}
\newcommand*{\DepthOne}{\csvreader{\ThisRock.csv}{2=\Depth1}{\Depth1}}
\newcommand*{\DepthTwo}{\csvreader{\ThisRock.csv}{3=\DepthTwo}{\DepthTwo}}
\newcommand*{\DepthThree}{\csvreader{\ThisRock.csv}{4=\Depth3}{\Depth3}}

\begin{document}
% Show the data (normally this is underwater)
\csvautotabular{examplecsv.csv} \\

% Expect this to print the sentence twice fully (first time is okay, second time incomplete?)
The rock sampled was called \SampleName. \\
The rock sampled was called \SampleName. \\

% Because there is some non-zero data for the first sample, expect this to print the sentence
\ifnum\ifnum\DepthOne>0 1\else0\fi
=1 %
\noindent The first sample hole was drilled to \DepthOne~mm\\
\else
\\
\fi

% Because the data is zero for the third sample, expect this not to print the sentence
\ifnum\ifnum\DepthThree>0 1\else0\fi
=1 %
\noindent The third sample hole was drilled to \DepthThree~mm\\
\else
\\
\fi

\end{document}
  • 1
    \DepthOne etc. aren't expandable apparently, so \ifnum\Depth... will fail -- it can't evaluate the test,\cvsreader is not expandable since it has an optional first argument, so \DepthOne etc. can't be expanded as well – user31729 Feb 9 '17 at 22:55
  • Perhaps the macros \ifcvsstrequal etc. (see the manual please) are more useful? I can't test right now... it's late and I am tired! – user31729 Feb 9 '17 at 22:59
  • Perhaps have a look at the readarray package, as an alternative. – Steven B. Segletes Feb 10 '17 at 2:45
2

This answer actually refers to your comment to egreg's answer:

What happens when I wish to access the \samplename label outside the \csvreader context?

You can use the \csvreader context for making global assignments after sufficiently expanding local "variables"/macros—in order to obtain the values, the "variables" need to be expanded twice:

% Make up some data in a CSV file
\begin{filecontents*}{\jobname.csv}
SampleName,Depth1,DepthTwo,Depth3
ROCK001,48,52,0
ROCK002,1,2,3
\end{filecontents*}

\documentclass[10pt]{report}
\usepackage{csvsimple}

\newcount\Samplenumber
\newcommand*\GlobalSamplename{}%
\newcommand*\GlobalDepthone{}%
\newcommand*\GlobalDepthtwo{}%
\newcommand*\GlobalDepththree{}%
%
\newcommand*\GlobalSamplenamederrorefault{\texttt{??}}%
\newcommand*\GlobalDepthoneerrorefault{0}%
\newcommand*\GlobalDepthtwoerrorefault{0}%
\newcommand*\GlobalDepththreeerrorefault{0}%
%
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\expandtwice{}%
\long\def\expandtwice#1#{\innerexpandtwice{#1}}%
\newcommand\innerexpandtwice[2]{%
  \expandafter\expandafter\expandafter\PassFirstToSecond
  \expandafter\expandafter\expandafter{#2}{#1}%
}%

\makeatletter

\newcommand\ReadToMacrosSampleDataRowNumber[1]{%
  \@bsphack
  \begingroup
  \let\GlobalSamplename=\UndEfINeD
  \Samplenumber=0 %
  \csvreader{\jobname.csv}{%
    SampleName=\InsideCsvreaderSamplename,
    Depth1=\InsideCsvreaderDepthone,
    DepthTwo=\InsideCsvreaderDepthtwo,
    Depth3=\InsideCsvreaderDepththree,
  }{%
    \advance\Samplenumber by 1\relax
    %
    \ifnum#1=\Samplenumber\relax
      \expandtwice\gdef\GlobalSamplename{\InsideCsvreaderSamplename}%
      \expandtwice\gdef\GlobalDepthone{\InsideCsvreaderDepthone}%
      \expandtwice\gdef\GlobalDepthtwo{\InsideCsvreaderDepthtwo}%
      \expandtwice\gdef\GlobalDepththree{\InsideCsvreaderDepththree}%
    \fi
  }%
  \ifx\GlobalSamplename\UndEfINeD
    \@latex@error{Row #1 not available}{Seems your sheet of csv data does not have #1 rows.}%
    \global\let\GlobalSamplename=\GlobalSamplenamederrorefault
    \global\let\GlobalDepthone=\GlobalDepthoneerrorefault
    \global\let\GlobalDepthtwo=\GlobalDepthtwoerrorefault
    \global\let\GlobalDepththree=\GlobalDepththreeerrorefault
  \fi
  \endgroup
  \@esphack
}%
\makeatother

\begin{document}

\noindent\csvautotabular{\jobname.csv}

\bigskip

Reading data row number 2 via
\verb|\ReadToMacrosSampleDataRowNumber{2}| and
using placeholder-macros outside
\verb|\csvreader|:\ReadToMacrosSampleDataRowNumber{2}

The rock sampled was called \GlobalSamplename.

The rock sampled was called \GlobalSamplename.

\ifnum\GlobalDepthone>0 %
  The first sample hole was drilled to \GlobalDepthone~mm.
\fi

\ifnum\GlobalDepthtwo>0 %
  The second sample hole was drilled to \GlobalDepthtwo~mm.
\fi

\ifnum\GlobalDepththree>0 %
  The third sample hole was drilled to \GlobalDepththree~mm.
\fi

\noindent\hrulefill\null

Reading data row number 1 via
\verb|\ReadToMacrosSampleDataRowNumber{1}| and
using placeholder-macros outside
\verb|\csvreader|:\ReadToMacrosSampleDataRowNumber{1}

The rock sampled was called \GlobalSamplename.

The rock sampled was called \GlobalSamplename.

\ifnum\GlobalDepthone>0 %
  The first sample hole was drilled to \GlobalDepthone~mm.
\fi

\ifnum\GlobalDepthtwo>0 %
  The second sample hole was drilled to \GlobalDepthtwo~mm.
\fi

\ifnum\GlobalDepththree>0 %
  The third sample hole was drilled to \GlobalDepththree~mm.
\fi

\noindent\hrulefill\null

Reading non-existing data row number 3 via
\verb|\ReadToMacrosSampleDataRowNumber{3}| and
using placeholder-macros outside
\verb|\csvreader|---this yields an error-message and
errordefault-values:\ReadToMacrosSampleDataRowNumber{3}

The rock sampled was called \GlobalSamplename.

The rock sampled was called \GlobalSamplename.

\verb|\GlobalSamplename| is assigned the errordefault value \GlobalSamplename.

\verb|\GlobalDepthone| is assigned the errordefault value \GlobalDepthone.

\verb|\GlobalDepthtwo| is assigned the errordefault value \GlobalDepthtwo.

\verb|\GlobalDepththree| is assigned the errordefault value \GlobalDepththree.

\ifnum\GlobalDepthone>0 %
  The first sample hole was drilled to \GlobalDepthone~mm.
\fi

\ifnum\GlobalDepthtwo>0 %
  The second sample hole was drilled to \GlobalDepthtwo~mm.
\fi

\ifnum\GlobalDepththree>0 %
  The third sample hole was drilled to \GlobalDepththree~mm.
\fi

\end{document}

enter image description here

1

I did some more research based on what @egreg suggested and I figured I can use datatool to do what I want:

\begin{filecontents*}{rockdata.csv}
SampleName,Depth1,DepthTwo,Depth3
ROCK001,48,52,0
\end{filecontents*}

\documentclass[10pt]{report}
\usepackage{datatool}

\DTLloaddb{rocks}{rockdata.csv}

\begin{document}

% This replaces the \newcommand step
\DTLforeach*{rocks}% database label
{\SampleName=SampleName,\DepthOne=Depth1,\DepthTwo=DepthTwo,\DepthThree=Depth3}% Assignment
{}% Things can be done in here

% But things can also seemingly be done out here too
\noindent The rock sampled was called \SampleName.\\
\dtlifnumeq{\DepthOne}{0}{\\}{The first sample hole was drilled to \DepthOne~mm\\}
\dtlifnumeq{\DepthTwo}{0}{\\}{The second sample hole was drilled to \DepthTwo~mm\\}
\dtlifnumeq{\DepthThree}{0}{\\}{The third sample hole was drilled to \DepthThree~mm\\}

\end{document}

More guru way

Ultimately I can separate the ugly data-collecting machinery into a style file and use the labels created by datatools to run the conditional tests I want:

LaTex file:

\begin{filecontents*}{rockdata.csv}
SampleName,Depth1,DepthTwo,Depth3
ROCK001,48,52,0
\end{filecontents*}

\documentclass[10pt]{report}
\usepackage{datatool}

% User-defined package here
\usepackage{LoadRocks}

\begin{document}

% But things can also seemingly be done out here too
\noindent The rock sampled was called \SampleName.\\
\dtlifnumeq{\DepthOne}{0}{\\}{The first sample hole was drilled to \DepthOne~mm\\}
\dtlifnumeq{\DepthTwo}{0}{\\}{The second sample hole was drilled to \DepthTwo~mm\\}
\dtlifnumeq{\DepthThree}{0}{\\}{The third sample hole was drilled to \DepthThree~mm\\}

\end{document}

Style file LoadRocks.sty:

\ProvidesPackage{LoadRocks}[]
\DTLloaddb{rocks}{rockdata.csv}

\DTLforeach*{rocks}% database label
{\SampleName=SampleName,\DepthOne=Depth1,\DepthTwo=DepthTwo,\DepthThree=Depth3}% Assignment
{}

enter image description here

1

You're misunderstanding what \csvreader does.

% Make up some data in a CSV file
\begin{filecontents*}{\jobname.csv}
SampleName,Depth1,DepthTwo,Depth3
ROCK001,48,52,0
ROCK002,1,2,3
\end{filecontents*}

\documentclass[10pt]{report}
\usepackage{csvsimple}

\begin{document}

\csvautotabular{\jobname.csv}

\bigskip

\csvreader{\jobname.csv}{
    SampleName=\samplename,
    Depth1=\depthone,
    DepthTwo=\depthtwo,
    Depth3=\depththree,
  }
  {%
   The rock sampled was called \samplename.

   The rock sampled was called \samplename.

   \ifnum\depthone>0
     The first sample hole was drilled to \depthone~mm
   \fi

   \ifnum\depththree>0
     The third sample hole was drilled to \depththree~mm
   \fi

   \bigskip
  }

\end{document}

enter image description here

If you want to have all data available in any place of your document, you have to index the rows.

% Make up some data in a CSV file
\begin{filecontents*}{\jobname.csv}
SampleName,Depth1,DepthTwo,Depth3
ROCK001,48,52,0
ROCK002,1,2,3
\end{filecontents*}

\documentclass{article}
\usepackage{csvsimple}
\usepackage{xparse}
\usepackage{siunitx}

\ExplSyntaxOn
\NewDocumentCommand{\rocks}{m}
 {
  \csvreader{#1}{}
   {
    \int_gincr:N \g_fejoia_rocks_int
    \prop_new:c { g_fejoia_rocks_ \int_to_arabic:n { \g_fejoia_rocks_int } _prop }
    \prop_gput:cno
     { g_fejoia_rocks_ \int_to_arabic:n { \g_fejoia_rocks_int } _prop }
     { SampleName } { \csvcoli }
    \prop_gput:cno
     { g_fejoia_rocks_ \int_to_arabic:n { \g_fejoia_rocks_int } _prop }
     { DepthOne } { \csvcolii }
    \prop_gput:cno
     { g_fejoia_rocks_ \int_to_arabic:n { \g_fejoia_rocks_int } _prop }
     { DepthTwo } { \csvcoliii }
    \prop_gput:cno
     { g_fejoia_rocks_ \int_to_arabic:n { \g_fejoia_rocks_int } _prop }
     { DepthThree } { \csvcoliv }
   }
}
\int_new:N \g_fejoia_rocks_int
\DeclareExpandableDocumentCommand{\getrock}{mm}
 {
  \prop_item:cn { g_fejoia_rocks_#1_prop } { #2 }
 }
\ExplSyntaxOff

\rocks{\jobname.csv}

\begin{document}

\csvautotabular{\jobname.csv}

\bigskip

\section{First rock}
The rock sampled was called \getrock{1}{SampleName}.

\ifnum\getrock{1}{DepthOne}>0
  The first sample hole was drilled to \SI{\getrock{1}{DepthOne}}{\mm}
\fi

\ifnum\getrock{1}{DepthThree}>0
  The third sample hole was drilled to \SI{\getrock{1}{DepthThree}}{\mm}
\fi

\section{Second rock}
The rock sampled was called \getrock{2}{SampleName}.

\ifnum\getrock{2}{DepthOne}>0
  The first sample hole was drilled to \SI{\getrock{2}{DepthOne}}{\mm}
\fi

\ifnum\getrock{2}{DepthThree}>0
  The third sample hole was drilled to \SI{\getrock{2}{DepthThree}}{\mm}
\fi

\end{document}

enter image description here

  • What happens when I wish to access the \samplename label outside the \csvreader context? I have the \csvreader in a separate style file and I create labels for each data point, which I intend to access throughout the document. – fejoa Feb 9 '17 at 23:43
  • @wryan Probably you want to look at datatool. Do you have different files for each sample? – egreg Feb 9 '17 at 23:48
  • I can cause my system to look at one csv file per sample rather than store everything in one csv. This post on using datatool with csv really helped – fejoa Feb 10 '17 at 1:52

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.