16

In my TeX document I would like to add a picture and in addition, zooming on a small part of the picture (e.g. inset in TeX or spy in TikZ).

My question, I have a histogram 2D (f(x,y) = z) date which I created from it a picture. I would like to know how could I create in TeX, something like spy or magnifying glass from a picture or raw data?

For example, I found in tikz spy library with pgfplots: opacity and grid nice example, but the person there uses \addplot and I would like to create histogram plot of my own data.

Is it possible to create 2D histogram in TikZ?

EDIT

here is an example of a 2D histogram that i would like to create: 2d histogram

7

2 Answers 2

18

Until someone gets around to implementing this directly in LaTeX, the easiest approach might be to use R and the brilliant hexbin library to generate the hexplot data and use pgfplots to do the actual plot:

Here's an R script for generating a hexplot of the distribution of cities with a population exceeding 40000:

# Load the data
library(maps)
data(world.cities)

# Load the hexbin package
library(hexbin)

# Generate hexbins, with the aspect ratio of the plot matching that of the data.
hbin<-hexbin(x=world.cities$long,y=world.cities$lat,xbins=100,shape=diff(range(world.cities$lat))/diff(range(world.cities$long)))

# Write the datafile. hcell2xy extracts the centroids of the hexagons
write.table(data.frame(hcell2xy(hbin),slot(hbin,"count")),row.name=F,file="testdata.csv")

This can then be plotted using pgfplots:

\documentclass[border=5mm]{article}
\usepackage{siunitx}
\usepackage{pgfplots, pgfplotstable}
\usetikzlibrary{spy}

\sisetup{round-mode=places,round-precision=0}

\pgfdeclareplotmark{hexagon}
{%
  \pgfpathmoveto{\pgfqpoint{0pt}{1.1547\pgfplotmarksize}}
  \pgfpathlineto{\pgfqpointpolar{150}{1.1547\pgfplotmarksize}}
  \pgfpathlineto{\pgfqpointpolar{210}{1.1547\pgfplotmarksize}}
  \pgfpathlineto{\pgfqpointpolar{270}{1.1547\pgfplotmarksize}}
  \pgfpathlineto{\pgfqpointpolar{330}{1.1547\pgfplotmarksize}}
  \pgfpathlineto{\pgfqpointpolar{30}{1.1547\pgfplotmarksize}}
  \pgfpathclose
\pgfusepathqfill
}

\pgfplotsset{
colormap={grayred}{color(0cm)=(black!10); color(1cm)=(red!75!black)}
}


\begin{document}
\begin{tikzpicture}[spy using outlines={circle, magnification=5, connect spies}]
\begin{axis}[
    enlargelimits=false,
    colorbar, colormap name=grayred,
           scale only axis,width=10cm,unit vector ratio*=1 1 1,
    enlarge x limits={abs=2},enlarge y limits={abs=2},
    xlabel=Longitude, ylabel=Latitude, xticklabel={\SI{\tick}{\degree}},yticklabel={\SI{\tick}{\degree}},
]
\addplot [
    scatter, scatter/use mapped color={draw=mapped color, fill=mapped color},
    scatter src=explicit,
    only marks,
    mark=hexagon,mark size=\pgfkeysvalueof{/pgfplots/width}/100/2
] table [meta index=2] {testdata.csv};
\coordinate (spynode) at (axis cs:5,45);
\begin{scope}[fill=white]
    \spy [size=5cm] on (spynode) in node [fill=white] at (3,7);
\end{scope}

\end{axis}
\end{tikzpicture}


\end{document}

If you just want to plot a series of values as a coloured matrix, this can be done directly with pgfplots, which is much faster than using arrays and looping over them "by hand":

\documentclass[border=5mm]{standalone}
\usepackage{pgfplots, pgfplotstable}

\pgfplotstableread[col sep=comma]{
5,-8,-10,-4,30,11,24,-9,13,22,5,23,-3,-8,12,-3,22,7,28,24,23,11,17,-4,24,11,19,15,-2,8,0,%
-6,26,27,-10,24,6,-6,10,12,17,29,-8,22,20,30,27,-1,0,27,8,13,4,10,8,30,-4,-6,6,26,11,0,-4,-2,17,27,17,%
-5,8,-7,-5,24,30,6,22,3,30,8,-10,8,11,28,-8,6,24,17,1,-2,26,-1,25,27,25,15,19,15,-7,2,29,14,21,25,-7,24,%
19,14,19,15,8,-6,-4,-7,28,15,7,3,6,2,-10,-9,22,18,-7,15,2,27,9,15,11,0,-7,-5,-8,24,-6,16,10,10,8,26,-4,%
6,-4,20,21,0,16,10,2,-4,3,-9,-3,21,16,-9,30,24,13,20,29,29,11,4,5,4,8,24,22,28,8,-1,1,6,30,-7,16,-6,30,%
12,-8,-4,-4,20,-4,-5,14,28,-4,28,15,14,7,7,9,14,-9,12,8,7,0,15,9,2,-10,28,22,3,-2,19,30,25,1,-9,26,12,%
25,11,15,16,-4,7,17,28,7
}\datatable
\pgfplotstabletranspose\newtable\datatable



\begin{document}
\begin{tikzpicture}
\begin{axis}[
    enlargelimits=false,
    colorbar, colormap/greenyellow,
    axis equal,
    scale only axis,
    width=6cm, height=6cm,
    xmin=-0.5,xmax=14.5
]
\addplot [
    scatter,
    scatter src=explicit,
    only marks,
    mark=square*,
    mark size=\pgfkeysvalueof{/pgfplots/width}/15/2
 ] table [x expr={mod(mod(\coordindex,15),15)}, %Use mod twice because of a bug in the fpu library
    y expr={round((\coordindex-7)/15)},
    meta=0] {\newtable};
\end{axis}
\end{tikzpicture}
\end{document}
9

In principle it is possible, I generated some random data in a TikZ suitable format:

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\pgfmathsetmacro{\minval}{-10}
\pgfmathsetmacro{\maxval}{30}
%\pgfmathsetmacro{\spanval}{\maxval-\minval}
\pgfmathsetmacro{\colstep}{100/(\maxval-\minval)}

\begin{tikzpicture}
\foreach \x/\y/\z in {
1/1/5,
1/2/-6,
1/3/-10,
1/4/12,
1/5/27,
1/6/17,
1/7/25,
1/8/-7,
1/9/14,
1/10/8,
1/11/21,
1/12/-6,
1/13/-4,
1/14/3,
1/15/0,
2/1/-2,
2/2/-9,
2/3/10,
2/4/7,
2/5/-6,
2/6/-4,
2/7/9,
2/8/15,
2/9/-4,
2/10/26,
2/11/5,
2/12/-3,
2/13/3,
2/14/27,
2/15/18,
3/1/4,
3/2/12,
3/3/-6,
3/4/22,
3/5/7,
3/6/23,
3/7/-8,
3/8/10,
3/9/-2,
3/10/12,
3/11/30,
3/12/30,
3/13/28,
3/14/28,
3/15/7,
4/1/13,
4/2/-2,
4/3/26,
4/4/-4,
4/5/4,
4/6/8,
4/7/-3,
4/8/-5,
4/9/22,
4/10/10,
4/11/25,
4/12/-6,
4/13/-5,
4/14/-3,
4/15/-9,
5/1/5,
5/2/10,
5/3/8,
5/4/23,
5/5/-3,
5/6/-1,
5/7/-2,
5/8/-8,
5/9/22,
5/10/8,
5/11/3,
5/12/-10,
5/13/29,
5/14/14,
5/15/-6,
6/1/28,
6/2/-2,
6/3/21,
6/4/-8,
6/5/14,
6/6/-4,
6/7/22,
6/8/25,
6/9/-9,
6/10/20,
6/11/-3,
6/12/3,
6/13/11,
6/14/18,
6/15/5,
7/1/19,
7/2/6,
7/3/-10,
7/4/-1,
7/5/10,
7/6/7,
7/7/0,
7/8/30,
7/9/-9,
7/10/25,
7/11/29,
7/12/12,
7/13/21,
7/14/-6,
7/15/13,
8/1/5,
8/2/27,
8/3/18,
8/4/-6,
8/5/5,
8/6/-8,
8/7/9,
8/8/8,
8/9/10,
8/10/24,
8/11/25,
8/12/0,
8/13/29,
8/14/15,
8/15/27,
9/1/21,
9/2/26,
9/3/14,
9/4/-8,
9/5/8,
9/6/-2,
9/7/28,
9/8/6,
9/9/22,
9/10/20,
9/11/13,
9/12/6,
9/13/18,
9/14/-1,
9/15/-3,
10/1/-3,
10/2/13,
10/3/28,
10/4/22,
10/5/30,
10/6/1,
10/7/0,
10/8/-2,
10/9/19,
10/10/-5,
10/11/11,
10/12/14,
10/13/-1,
10/14/27,
10/15/-8,
11/1/13,
11/2/-4,
11/3/20,
11/4/20,
11/5/26,
11/6/1,
11/7/11,
11/8/1,
11/9/11,
11/10/6,
11/11/14,
11/12/5,
11/13/-5,
11/14/-1,
11/15/18,
12/1/18,
12/2/27,
12/3/-9,
12/4/18,
12/5/10,
12/6/26,
12/7/-1,
12/8/-3,
12/9/25,
12/10/-8,
12/11/9,
12/12/11,
12/13/10,
12/14/16,
12/15/5,
13/1/18,
13/2/-2,
13/3/4,
13/4/-3,
13/5/21,
13/6/7,
13/7/5,
13/8/1,
13/9/21,
13/10/-8,
13/11/-4,
13/12/24,
13/13/4,
13/14/16,
13/15/20,
14/1/24,
14/2/25,
14/3/17,
14/4/0,
14/5/-9,
14/6/9,
14/7/-4,
14/8/13,
14/9/25,
14/10/23,
14/11/2,
14/12/-4,
14/13/11,
14/14/-2,
14/15/-7,
15/1/13,
15/2/25,
15/3/-6,
15/4/26,
15/5/-1,
15/6/18,
15/7/12,
15/8/-7,
15/9/9,
15/10/-3,
15/11/25,
15/12/14,
15/13/12,
15/14/12,
15/15/19}
{
\pgfmathsetmacro{\ptcol}{\colstep*(\z-\minval)}
\fill[orange!\ptcol!blue] (\x/4-0.25,\y/4-0.25) rectangle (\x/4,\y/4);
}
\end{tikzpicture}

\end{document}

Which produces:

enter image description here

The main problems would then be:

  • formatting the input data to x/y/z, shouldn't be too hard
  • multiple colors to cycle through, should be possible, at least I have an idea how to
  • hexagonal grid (if this is required); especially interesting would be the raw data format for this
  • possible TeX size restrictions; e.g. a plot with 200 x 200 points would have 40000 points, at some point capacity may be exceeded
  • the frames, ticks, scales and the "value bar" (or whatever you call it) should not be too problematic

So it would be nice if you specified your input format, and could go a lot more in detail if the hexagonal grid is requested / required.


Edit 1: Here is a little more advanced sample. You now can give your data as a comma separated list, everything except for the spy is then drawn automatically. I have to warn you though, it is really, really slow, apparently it's not a good idea looking up array indices:

\documentclass[parskip]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}
\usetikzlibrary{calc,shadings,spy}

\begin{document}

\pgfmathsetmacro{\minval}{-10}
\pgfmathsetmacro{\maxval}{30}
\pgfmathsetmacro{\zlsteps}{10}
\pgfmathsetmacro{\colstep}{100/(\maxval-\minval)}
\pgfmathsetmacro{\ptsize}{0.5}
\pgfmathsetmacro{\xsize}{15}
\pgfmathsetmacro{\ysize}{15}
\pgfmathsetmacro{\xlmin}{4}
\pgfmathsetmacro{\xlmax}{8}
\pgfmathsetmacro{\ylmin}{5}
\pgfmathsetmacro{\ylmax}{11}
\pgfmathsetmacro{\xlsteps}{5}
\pgfmathsetmacro{\ylsteps}{7}
\newcommand{\zmaxcol}{orange}
\newcommand{\zmincol}{gray}
\def\valarray{{5,-8,-10,-4,30,11,24,-9,13,22,5,23,-3,-8,12,-3,22,7,28,24,23,11,17,-4,24,11,19,15,-2,8,0,%
-6,26,27,-10,24,6,-6,10,12,17,29,-8,22,20,30,27,-1,0,27,8,13,4,10,8,30,-4,-6,6,26,11,0,-4,-2,17,27,17,%
-5,8,-7,-5,24,30,6,22,3,30,8,-10,8,11,28,-8,6,24,17,1,-2,26,-1,25,27,25,15,19,15,-7,2,29,14,21,25,-7,24,%
19,14,19,15,8,-6,-4,-7,28,15,7,3,6,2,-10,-9,22,18,-7,15,2,27,9,15,11,0,-7,-5,-8,24,-6,16,10,10,8,26,-4,%
6,-4,20,21,0,16,10,2,-4,3,-9,-3,21,16,-9,30,24,13,20,29,29,11,4,5,4,8,24,22,28,8,-1,1,6,30,-7,16,-6,30,%
12,-8,-4,-4,20,-4,-5,14,28,-4,28,15,14,7,7,9,14,-9,12,8,7,0,15,9,2,-10,28,22,3,-2,19,30,25,1,-9,26,12,%
25,11,15,16,-4,7,17,28,7}}

\begin{tikzpicture}[spy using outlines={circle, magnification=4, size=4cm, connect spies}]
\pgfmathsetmacro{\xdec}{\xsize-1}
\pgfmathsetmacro{\ydec}{\ysize-1}
\foreach \x in {0,...,\xdec}
  { \foreach \y in {0,...,\ydec}
    { \pgfmathsetmacro{\ptcol}{\colstep*(\valarray[\y*\xsize+\x]-\minval)}
      \fill[\zmaxcol!\ptcol!\zmincol] (\x*\ptsize,\y*\ptsize) rectangle (\x*\ptsize+\ptsize,\y*\ptsize+\ptsize);
    }
  }
\draw (0,0) rectangle (\xsize*\ptsize,\ysize*\ptsize);
\pgfkeys{/pgf/number format/.cd,fixed zerofill,precision=2}
\foreach \x in {0,...,\xlsteps}  
{ \pgfmathsetmacro{\xlval}{(\xlmax-\xlmin)/\xlsteps*\x+\xlmin}
  \draw (\xsize/\xlsteps*\x*\ptsize,0) -- (\xsize/\xlsteps*\x*\ptsize,-0.2) node[right,rotate=-90] {\pgfmathprintnumber{\xlval}};
}
\foreach \y in {0,...,\ylsteps}  
{ \pgfmathsetmacro{\ylval}{(\ylmax-\ylmin)/\ylsteps*\y+\ylmin}
  \draw (0,\ysize/\ylsteps*\y*\ptsize) -- (-0.2,\ysize/\ylsteps*\y*\ptsize) node[left] {\pgfmathprintnumber{\ylval}};
}
\draw[top color=\zmaxcol,bottom color=\zmincol] (\xsize*\ptsize+0.5,0) rectangle (\xsize*\ptsize+1.5,\ysize*\ptsize);
\foreach \z in {0,...,\zlsteps}  
{ \pgfmathsetmacro{\zlval}{(\maxval-\minval)/\zlsteps*\z+\minval}
  \draw (\xsize*\ptsize+1.5,\ysize*\ptsize/\zlsteps*\z) -- (\xsize*\ptsize+1.7,\ysize*\ptsize/\zlsteps*\z) node[right] {\pgfmathprintnumber{\zlval}};
}
\spy [blue, size=4cm] on (3,2) in node [right] at (4,9);
\end{tikzpicture}

\end{document}

Output:

enter image description here


The same with \pgfmathsetmacro{\xsize}{25} and \pgfmathsetmacro{\ysize}{9}:

enter image description here

4
  • If the computation of the images takes such a long time, you may want to take a look at the pgfmanual (p. 500 Ch. 63), there is at least a possibility to externalize (and hence precompute only when it changes) the graphics.
    – Ronny
    Commented Jan 27, 2012 at 8:25
  • Yes, I know about the externalize library, but as far as I know it doesn't redo the externalized graphichs upon change. When I tried it a while back, I had to delete the .pdf containining the externalized graphic to get it recomputed. So I think it is meant for "finished graphics you don't plan to work on anymore, or am I wrong? Commented Jan 27, 2012 at 9:29
  • @TomBombadil: You can force the external library to recreate an image by preceding it with \tikzset{external/remake next}, or to recreate all images starting with the next by setting \tikzset{external/force remake}.
    – Jake
    Commented Jan 27, 2012 at 10:07
  • Very interesting, didn't know that, but it will be very helpful. Thanks a lot! Commented Jan 27, 2012 at 10:23

You must log in to answer this question.

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