20

I have highly dimensional data for which I'd like to draw a plot where I vary the line-width, somewhat similar to the famous Minard chart:

Minard Napoléon losses chart

I suspect this can be done with some combination of point meta and line width={\pgfplotspointmetatransformed/1000 * 2pt} somewhere, but I can't quite figure out how to do this. For scatter-plots with markers I found how to do this (pgfplots manual, version 1.5, page 84), and varying the colour can be done using a 1-D coloured mesh plot. Linked below is a way to fix this for quiver plots, and below I linked a question how to do this in 'pure' tikz. Maybe it would be possible with a clever decoration (fill a rectangle perpendicular to the plot?) but I suspect this would not look very nice (I haven't tried this yet). I could fill the area between y-dy and y+dy, but this is not the same as varying the line width, because the area that is filled is estimated into ±y, not perpendicular to the local line direction.

Close, but not quite what I seek.

My data are discrete, so the varying line width can be, too (NOTE: see update below!). Maybe it can be done with a for-loop. The question is:

  • How do I vary the line width of a pgfplots-plot according to a particular function?

Some sample data, where I would plot column 1 as a function of column 0, and seek to map column 2 to the line-width:

2006 5213 48
2007 5112 47
2008 5260 49
2009 5161 53
2010 4647 57
2011 4413 62
2012 4309 65

Bonus question: If this succeeds, it would be very neat if I could draw a very thin grid through this line, so that the line width can be more easily seen. Not even Minard has done this :)

EDIT 2012-06-01: contrary to an earlier answer I gave to a comment, I realise now that I'm really looking for a continuously changing line width, e.g. an appropriately calculated and filled polygon. Using calc and intersections I can get the result below, but I haven't yet managed to get it into a plot (though this question on using a macro as a coordinate or this one on consistency with tikz and pgfplots might help), and of course I should automate it with my input data (with pgfplotstable). I like the geometric calculations with calc, but otherwise I'm beginning to suspect it may be easier for me to calculate the corners of the polygon externally and then filling them with pgfplots later. OTOH it would be cool to have a solution where I can plug in any series of numbers (particularly since my final plot will contain many such lines).

\documentclass{standalone}
\usepackage{pgfplots,pgfplotstable}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}
\begin{document}%
\begin{tikzpicture}
\begin{axis}
\coordinate (a) at (0, 0);
\coordinate (b) at (1, 3);
\coordinate (c) at (2, 1);
\coordinate (aba) at ($(a)!.01cm!90:(b)$);
\coordinate (abb) at ($(a)!.01cm!-90:(b)$);
\coordinate (baa) at ($(b)!.04cm!90:(a)$);
\coordinate (bab) at ($(b)!.04cm!-90:(a)$);
\coordinate (bca) at ($(b)!.04cm!90:(c)$);
\coordinate (bcb) at ($(b)!.04cm!-90:(c)$);
\coordinate (cba) at ($(c)!.1cm!90:(b)$);
\coordinate (cbb) at ($(c)!.1cm!-90:(b)$);

\coordinate (lowsect) at (intersection of abb--baa and cba--bcb);%a_1--a_2 and b_1--b_2);
\coordinate (hisect) at (intersection of aba--bab and cbb--bca);%a_1--a_2 and b_1--b_2);

\addplot[fill=black] coordinates {(abb) (lowsect) (cba) (cbb) (hisect) (aba)} -- cycle;
%\path[fill=black] (abb) -- (lowsect) -- (cba) -- (cbb) -- (hisect) -- (aba) -- cycle;
\end{axis}
\end{tikzpicture}
\end{document}

beginning of attempt to plot with variable line width

Related questions:

12
  • Could you provide a bit of dummy data?
    – Jake
    May 30, 2012 at 15:34
  • Do you want to vary the line width continuously, or (like Minard did) discretely?
    – Jake
    May 30, 2012 at 15:41
  • Thanks Jake. I added some sample data. My data are discrete. One way would be to plot two lines with two y-axes, but since I will have quite many lines within the same plot, I'd like to condense the information into a single line.
    – gerrit
    May 30, 2012 at 15:54
  • To be honest, I'm seeing a polygon on this chart. Wouldn't constructing a filled path be an easier approach than the variable-linewidth thing? May 30, 2012 at 16:10
  • 1
    No, that wouldn't quite do it. But it'd be close. I realised that I was wrong when I said the line-width should vary discretely, because then there will inevitably be discontinuous joins...
    – gerrit
    May 31, 2012 at 12:47

1 Answer 1

19

Well, here's a start, using a quiver plot. This approach requires that you have a "difference" column for the x and y values. This could also be created on the fly using pgfplotstable, but for the moment, I did it by hand. The joins aren't very pretty, but I can't really think of a way to fix this. Using line cap=round helps somewhat, but of course introduces artifacts at the start and end of the plot.

Here's Napoleon's main group of soldiers marching from Kowno to Moscow and back (data from http://www.datavis.ca/gallery/re-minard.php)

And here's a plot of your sample data, with line cap=rect.


Code for plot with Napoleon's data

\documentclass{article}

\usepackage{pgfplots,pgfplotstable}


\begin{document}

\begin{tikzpicture}
\begin{axis}[
    width=10cm,
    height=4cm,
    xlabel=Longitude,
    ylabel=Latitude
]
\addplot[
    quiver={
        u=\thisrow{U},
        v=\thisrow{V},
        every arrow/.append style={
            line width=1pt+\pgfplotspointmetatransformed/1000 * 9pt,
            line cap=round,
            color=brown!\dir!black
        }
    },
    point meta=explicit,
    visualization depends on=\thisrow{Dir}*100\as\dir
] table [meta index=2]{
X   Y   N   U   V   Dir Group
24  54.9    340000  0.5 0.1 1   1
24.5    55  340000  1   -0.5    1   1
25.5    54.5    340000  0.5 0.2 1   1
26  54.7    320000  1   0.1 1   1
27  54.8    300000  1   0.1 1   1
28  54.9    280000  0.5 0.1 1   1
28.5    55  240000  0.5 0.1 1   1
29  55.1    210000  1   0.1 1   1
30  55.2    180000  0.3 0.1 1   1
30.3    55.3    175000  1.7 -0.5    1   1
32  54.8    145000  1.2 0.1 1   1
33.2    54.9    140000  1.2 0.6 1   1
34.4    55.5    127100  1.1 -0.1    1   1
35.5    55.4    100000  0.5 0.1 1   1
36  55.5    100000  1.6 0.3 1   1
37.6    55.8    100000  0.1 -0.1    1   1
37.7    55.7    100000  -0.2    0   0   1
37.5    55.7    98000   -0.5    -0.7    0   1
37  55  97000   -0.2    0   0   1
36.8    55  96000   -1.4    0.3 0   1
35.4    55.3    87000   -1.1    -0.1    0   1
34.3    55.2    55000   -1  -0.4    0   1
33.3    54.8    37000   -1.3    -0.2    0   1
32  54.6    24000   -1.6    -0.2    0   1
30.4    54.4    20000   -1.2    -0.1    0   1
29.2    54.3    20000   -0.7    -0.1    0   1
28.5    54.2    20000   -0.2    0.1 0   1
28.3    54.3    20000   -0.8    0.2 0   1
27.5    54.5    20000   -0.7    -0.2    0   1
26.8    54.3    12000   -0.4    0.1 0   1
26.4    54.4    14000   -1.4    0   0   1
25  54.4    8000    -0.6    0   0   1
24.4    54.4    4000    -0.2    0   0   1
24.2    54.4    4000    -0.1    0   0   1
};
\end{axis}
\end{tikzpicture}

\end{document}

Code for plot with gerrit's data

\documentclass{article}

\usepackage{pgfplots,pgfplotstable}


\begin{document}

\begin{tikzpicture}
\begin{axis}
\addplot[
    quiver={
        u=\thisrow{U},
        v=\thisrow{V},
        every arrow/.append style={
            line width=1pt+\pgfplotspointmetatransformed/1000 * 5pt,
            line cap=rect
        }
    },
    point meta=explicit
] table [meta index=2]{
X   Y   N   U   V
2006 5213 48 1 -101
2007 5112 47 1 148
2008 5260 49 1 -99
2009 5161 53 1 -514
2010 4647 57 1 -234
2011 4413 62 1 -104
2012 4309 62 0.01 -1
};
\end{axis}
\end{tikzpicture}

\end{document}
4
  • 1
    Looks quite close, but it'd be quite nice to find a solution for the joins. Maybe I should reconsider the question if I want discrete or continuously changing line-width. My data are discrete, but a solution where the line width are interpolated would avoid the joins. I still wonder whether a clever decoration couldn't fix the job... I might post another, related question.
    – gerrit
    May 31, 2012 at 10:07
  • 2
    Actually, this is the best possible with discretely changing line-widths. I don't think it's theoretically possible to avoid the problem of the joins. I realise now that I need continuous changing line-widths, and probably the polygon suggested by @StephanLemke would be the way to go. A little meta-question: since this actually changes my question, should I post a new question (identical except line-width changes continuously) or adapt the current question, which would mean Jake's reply doesn't answer it anymore?
    – gerrit
    May 31, 2012 at 12:50
  • 1
    Actually this might be possible if you declare a decoration such that the start/end point thickness is given by the point meta. But it's a challenging task. The idea is to read off the initial/final thickness and fill a trapezoid along the input segment and the next one starts off with the final thickness. But I am not sure exactly if the meta info fits the bill to achieve that.
    – percusse
    Jun 1, 2012 at 13:12
  • 1
    Just an interesting side-note learned from tex.stackexchange.com/questions/65751/… : the task of discretely changing line width can also be accomplished if you replace your quiver=... entry by mesh,line width=1pt+\pgfplotspointmetatransformed/1000 * 9pt, color=brown!\dir!black . You may need to define \pgfplotspointmetatransformed and dir globally to avoid compilation errors (see that question for details). Aug 3, 2012 at 20:39

You must log in to answer this question.

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