5

I am new to TikZ and trying to recreate the following image (which was created in R): enter image description here (Translation: correlation = +1, correlation = -1, two clusters, circle, normal distribution)

Each "column" displays the same data, using a scatter plot and parallel coordinates. The graphics are of course fairly simple, so it would be possible to construct them by hand with the basic \draw -- and \fill circle commands. I am however looking for the best way to create, loop through, and draw the data, kind of like it is done in the R source.

(Note: The image doesn't have to look exactly like the above, that is the number of dots does not really matter, the dots in the clusters and normal distribution can have different coordinates and it would be nice if the circle was actually round and the font was the normal font used in my LaTeX document)

5
  • If I'm understanding correctly these are the projections of 3D data. So if you have the data you can do this in pgfplots
    – percusse
    Sep 23, 2012 at 19:20
  • In this example, the data is actually only 2D.
    – jhenninger
    Sep 23, 2012 at 21:18
  • Yes I should have said 3D plots e.g. XY plane at the bottom and YZ plane at the top
    – percusse
    Sep 23, 2012 at 21:21
  • No, both diagrams in a column are showing the same 2 dimensions. In parallel coordinates (bottom) every point is represented as line. the X axis is at the left, the Y axis is at the right, a point is a line between both axes.
    – jhenninger
    Sep 23, 2012 at 21:52
  • Have a look at my answer, it's not the only way to interpret as such.
    – percusse
    Sep 24, 2012 at 11:02

2 Answers 2

8

You can do this using PGFPlots. You can generate the x and y components for the different plots in a table, and then use them either in a normal scatterplot, or using the quiver style -- which is ordinarily used for drawing vector fields -- for the parallel plots.

\documentclass[landscape]{article}

\usepackage{pgfplots, pgfplotstable}
\begin{document}


% Create a function for generating inverse normally distributed numbers using the Box–Muller transform
\pgfmathdeclarefunction{invgauss}{2}{%
  \pgfmathparse{sqrt(-2*ln(#1))*cos(deg(2*pi*#2))}%
}


% Create the data columns. \pgfplotstablerow is the row index
\pgfplotstablenew[
    create on use/index/.style={
        create col/expr={\pgfplotstablerow}
    },
    create on use/negcorr/.style={
        create col/expr={24-\pgfplotstablerow}
    },
    create on use/clusterx/.style={ % Offset half the data by 4
        create col/expr={rand+(\pgfplotstablerow<12)*4}
    },
    create on use/clustery/.style={
        create col/expr={rand+(\pgfplotstablerow<12)*4}
    },
    create on use/circlex/.style={
        create col/expr={cos(\pgfplotstablerow*360/24)}
    },
    create on use/circley/.style={
        create col/expr={sin(\pgfplotstablerow*360/24)}
    },
    create on use/normalx/.style={
        create col/expr={invgauss(rnd,rnd)}
    },
    create on use/normaly/.style={
        create col/expr={invgauss(rnd,rnd)}
    },
    columns={index,negcorr,clusterx,clustery,circlex,circley,normalx,normaly}
]{24}\datatable

% Save the datatable to a file so it can be used with the quiver style
\pgfplotstablesave{\datatable}{data.csv}

% Create plot styles for the scatter and parallel plots
\pgfplotsset{
    scatterplot/.style args={#1,#2}{
        width=4cm, height=4cm,
        only marks, mark size=1,
        xtick=\empty, ytick=\empty,
        enlargelimits=false,
        table/x=#1,
        table/y=#2
    },
    parallel/.style args={#1,#2}{
        width=4cm, height=4cm,
        no markers,
        xtick=\empty, ytick=\empty,
        enlargelimits=false,
        table/x expr=24,
        table/y=#1,
        quiver={u=24,v=\thisrow{#2}-y} % The quiver plots use relative coordinates, so we'll have to subtract the y coordinate
    }
}

% Create commands for making the plots easier
\newcommand{\scatterplot}[2]{
    \begin{tikzpicture}[trim axis left, trim axis right]
        \begin{axis}[scatterplot={#1,#2}]
            \addplot [black] table {data.csv};
        \end{axis}
    \end{tikzpicture}
}

\newcommand{\parallelplot}[2]{
    \begin{tikzpicture}[trim axis left, trim axis right]
        \begin{axis}[parallel={#1,#2}]
            \addplot [black] table {data.csv};
        \end{axis}
    \end{tikzpicture}
}



\scatterplot{index}{index}%
\scatterplot{index}{negcorr}%
\scatterplot{clusterx}{clustery}%
\scatterplot{circlex}{circley}%
\scatterplot{normalx}{normaly}%

\parallelplot{index}{index}%
\parallelplot{index}{negcorr}%
\parallelplot{clusterx}{clustery}%
\parallelplot{circlex}{circley}%
\parallelplot{normalx}{normaly}%

\end{document}
2
  • Aargh, 1 minute more .... :P
    – percusse
    Sep 23, 2012 at 20:28
  • @percusse: Hehe, sorry! Let's see yours anyway, please!
    – Jake
    Sep 23, 2012 at 20:33
4

Jake just went one step further and generated the data tables beautifully. So this is now obsolete as I wanted to give an example and finish off with saying This would be much better with actual data but nevermind :)

\documentclass[border=3mm]{standalone}
\usepackage{pgfplots}
\usepgfplotslibrary{groupplots}
\pgfplotsset{compat=1.6}
\begin{document}
\begin{tikzpicture}
\begin{groupplot}[group style={group size=5 by 2},view={0}{0},xtick=\empty,ytick=\empty,ztick=\empty,enlargelimits=false]
    \nextgroupplot[view={90}{0}]
        \pgfplotsinvokeforeach{-10,...,10}{
        \addplot3[only marks] coordinates {(0,{#1},{-#1} ) (3,{#1},{-#1} )};
        }
    \nextgroupplot[view={90}{0}]
        \pgfplotsinvokeforeach{-10,...,10}{
        \addplot3[only marks] coordinates {(0,{#1},{-#1} ) (3,{-#1},{#1} )};
        }
    \nextgroupplot[view={90}{0},enlargelimits]
        \pgfplotsinvokeforeach{1,...,15}{
        \addplot3[only marks] coordinates {(0,{3+rand},{3+rand})};
        \addplot3[only marks] coordinates {(0,{-3+rand},{-3+rand}) (3,{-3+rand},{-3+rand})};
        }
    \nextgroupplot[view={90}{0},enlargelimits]
        \pgfplotsinvokeforeach{0,15,...,345}{
        \addplot3[only marks] coordinates {(0,{sin(#1+5)},{cos(#1+5)} ) (3,{sin(#1+95)},{cos(#1+95)} ) };
        }
    \nextgroupplot[view={90}{0}]
        \pgfplotsinvokeforeach{1,...,25}{
        \addplot3[only marks] coordinates {(0,{5*rand},{5*rand} ) (3,{5*rand},{5*rand})};
        }
    \nextgroupplot
        \pgfplotsinvokeforeach{-10,...,10}{
        \addplot3[no marks] coordinates {(0,{#1},{-#1} ) (3,{#1},{-#1} )};
        }
    \nextgroupplot
        \pgfplotsinvokeforeach{-10,...,10}{
        \addplot3[no marks] coordinates {(0,{#1},{-#1} ) (3,{#1},{#1} )};
        }
    \nextgroupplot[enlarge x limits=false]
        \pgfplotsinvokeforeach{1,...,15}{
        \addplot3[no marks] coordinates {(0,{3+rand},{3+rand}) (3,{3+rand},{3+rand})};
        \addplot3[no marks] coordinates {(0,{-3+rand},{-3+rand}) (3,{-3+rand},{-3+rand})};
        }
    \nextgroupplot
        \pgfplotsinvokeforeach{0,15,...,345}{
        \addplot3[no marks] coordinates {(0,{sin(#1+5)},{cos(#1+5)} ) (3,{sin(#1+95)},{cos(#1+95)} ) };
        }
        \nextgroupplot
        \pgfplotsinvokeforeach{1,...,25}{
        \addplot3[no marks] coordinates {(0,{5*rand},{5*rand} ) (3,{5*rand},{5*rand})};
        }
    \end{groupplot}
\end{tikzpicture}

\end{document}

enter image description here

3
  • Ah, interesting approach with the 3D plots! But couldn't you could achieve the same result with normal 2D ones (\addplot[only marks] coordinates {({#1},{-#1} ) ({#1},{-#1} )}; and \addplot[no marks] coordinates {(0,{#1} ) (3,{-#1} )}; for the negative correlation one, for instance)?
    – Jake
    Sep 23, 2012 at 21:02
  • 1
    @Jake Yes you're right. But I was thinking about fixed externally provided data sets. Then you would just rotate the same plot without those rather ugly \foreach loops but your answer is just nice for those too.
    – percusse
    Sep 23, 2012 at 21:15
  • Ah yes, good point with the external datasets!
    – Jake
    Sep 23, 2012 at 21:18

You must log in to answer this question.

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