[Tex/LaTex] Latent variable model with tikz

tikz-arrowstikz-pgf

I would like to code the below figure (a latent variable model) in tikz. However all my attempts to recreate the figure fail despite reading the tikz manual, see code below.

How can I align the nodes and arrows more to the left?

enter image description here

% latent variable model
\begin{tikzpicture}[transform shape, node distance=2cm,
roundnode/.style={circle, draw=black, very thick, minimum size=7mm},
squarednode/.style={rectangle, draw=black, very thick, minimum size=5mm},
arrow/.style = {semithick,-Stealth}
]

%Nodes
\node[roundnode](latent)[align=center]{T};
\node[squarednode](X2)[left=1.2cm of latent, align=center]{X2};
\node[squarednode](X1)[above= 0.5cm of X2, align=center]{X1};
\node[squarednode](X3)[below= 0.5cm of X2, align=center]{X3};

%Letters
\node[text width=3cm, left=1cm of X1]{e1};
\node[text width=3cm, left=1cm of X2]{e2};
\node[text width=3cm, left=1cm of X3]{e3};

%Arrows
\draw[arrow] (T) -- ["\lambda1"] (X1);
\draw[arrow] (T) -- ["\lambda2"] (X2);
\draw[arrow] (T) -- ["\lambda3"] (X3);

\draw[arrow] (e1) -- (X1);
\draw[arrow] (e2) -- (X2);
\draw[arrow] (e3) -- (X3);

\end{tikzpicture}

Best Answer

There are a couple of problems with your code.

First of all the syntax for specifying a node is

\node [<settings>] (<name>) at (<coordinate>) {<node text>};

The only thing that is mandatory is {<node text>}, so \node {foo}; is perfectly valid.

If you want to draw a line/arrow to/from a node, it is the <name> that you use as a coordinate, not the <node text>. You have

\node[roundnode](latent)[align=center]{T};

so in order to draw a line from the node containing the text T, you have to use \draw (latent) .... The same goes for the e1/e2/e3 nodes.

I would also say that it would be better to have all the settings in the same brackets, i.e.

\node[roundnode,align=center] (latent) {T};

Except that align=center doesn't really do anything here, as you haven't set the text width, so I would use just

\node[roundnode] (latent) {T};

Next point is \node[text width=3cm, left=1cm of X1]{e1};. I would remove the text width setting here, you don't want the node to be much wider than the text (e1), do you?

There is also a problem in the lines

\draw[arrow] (T) -- ["\lambda1"] (X1);

It looks like you're trying to use the syntax defined by the quotes library, but that is intended as a shorthand for labels, and labels belong to nodes. So I think you must do

\draw[arrow] (T) -- node["$\lambda_1$"] {} (X1);

Note I also used math mode, because \lambda only works in math mode, and added the number as a subscript.

From your image, you probably want $e_1$ and $X_1$ as well.

For the k nodes, I added a suggestion in the code below. I added the Xk node below X3, but note that I added yshift=0.5cm to the settings of the X2 node, to move it up a bit.

The code below also shows an example of how you can shorten the code by using a \foreach loop.

Complete code.

output of code

\documentclass[border=5mm]{standalone}
\usepackage{tikz} 
\usetikzlibrary{positioning,arrows.meta,quotes}
\begin{document}
% latent variable model
\begin{tikzpicture}[
  transform shape, node distance=2cm,
  roundnode/.style={circle, draw=black, very thick, minimum size=7mm},
  squarednode/.style={rectangle, draw=black, very thick, minimum size=5mm},
  arrow/.style = {semithick,-Stealth},
  dotnode/.style={fill,inner sep=0pt,minimum size=2pt,circle} % <- this is new
]

%Nodes
\node[roundnode] (latent) {$T$};
\node[squarednode, left=1.2cm of latent,yshift=0.5cm] (X2) {$X_2$}; % note addition of yshift
\node[squarednode, above=0.5cm of X2]    (X1) {$X_1$};
\node[squarednode, below=0.5cm of X2]    (X3) {$X_3$};
\node[squarednode, below=0.5cm of X3]    (Xk) {$X_k$};

%Letters
%\node[left=1cm of X1] (e1) {$e_1$};
%\node[left=1cm of X2] (e2) {$e_2$};
%\node[left=1cm of X3] (e3) {$e_3$};
%\node[left=1cm of Xk] (ek) {$e_k$};
%
%%Arrows
%\draw[arrow] (latent) -- node["$\lambda_1$"inner sep=1pt]{} (X1.east);
%\draw[arrow] (latent) -- node["$\lambda_2$"{inner sep=1pt,yshift=-3pt}]{} (X2.east);
%\draw[arrow] (latent) -- node["$\lambda_3$"{inner sep=1pt,yshift=-2pt}]{} (X3.east);
%\draw[arrow] (latent) -- node["$\lambda_k$"inner sep=1pt]{} (Xk.east);
%
%\draw[arrow] (e1) -- (X1);
%\draw[arrow] (e2) -- (X2);
%\draw[arrow] (e3) -- (X3);
%\draw[arrow] (ek) -- (Xk);

% the following loop does the same as the commented lines above
\foreach \i/\Yshift in {1/0,2/-3pt,3/-2pt,k/0}
{
   \node[left=1cm of X\i] (e\i) {$e_\i$};
   \draw[arrow] (latent) -- node["$\lambda_\i$"{inner sep=1pt,yshift=\Yshift}]{} (X\i.east);
   \draw[arrow] (e\i) -- (X\i);
}

% drawing the 
\path (X3) --
  node[dotnode,pos=0.2]{}
  node[dotnode,pos=0.5]{}
  node[dotnode,pos=0.8]{}
  (Xk);

\end{tikzpicture}
\end{document}