水平瀑布图,每个条形均有标签

水平瀑布图,每个条形均有标签

我看过类似的问题这里这里,但我仍然卡住了。基本上我想实现的是这个加上条形标签:

在此处输入图片描述

我有两个问题:

1) 我的代码很烂。目前,y 轴标签和瀑布连接器都是手动放置的,这不是最理想的,因为我将制作 15 个左右版本的图表。

2) 我无法让条形标签显示在正确的位置。我以前使用过 Jake 的 Centered Nodes Near Coords 解决方案(目前找不到参考 stack exchange 问题),但出于某种原因,它们拒绝在本例中出现在正确的位置。

如有任何关于如何改进或使标签发挥作用的建议,我们将不胜感激。

[编辑] 我真的需要一个使用 PGFplots 的答案。这需要快速完成,我没有时间学习不同的 .cvs 到 latex 实现。

梅威瑟:

\documentclass{article}
\usepackage{pgfplots, pgfplotstable}
\usepackage{filecontents}

\pgfdeclareplotmark{waterfall bridge}{\pgfpathmoveto{\pgfpoint{0pt}{33pt}}\pgfpathlineto{\pgfpoint{0pt}{9pt}}\pgfusepathqstroke}
\pgfdeclareplotmark{waterfall bridge 2}{\pgfpathmoveto{\pgfpoint{0pt}{19pt}}\pgfpathlineto{\pgfpoint{0pt}{-5pt}}\pgfusepathqstroke}
\pgfdeclareplotmark{waterfall bridge 3}{\pgfpathmoveto{\pgfpoint{0pt}{5pt}}\pgfpathlineto{\pgfpoint{0pt}{-19pt}}\pgfusepathqstroke}
\pgfdeclareplotmark{waterfall bridge 4}{\pgfpathmoveto{\pgfpoint{0pt}{-9pt}}\pgfpathlineto{\pgfpoint{0pt}{-33pt}}\pgfusepathqstroke}
%~~ALL OF THE MARK POSITIONING IS UGLY~~
% I would like them to use relative, rather than absolute, positioning

\begin{filecontents}{datatable.csv}
2 3 2 -6 -3
\end{filecontents}

\makeatletter
\pgfplotsset{
    calculate xoffset/.code={
        \pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}
        \pgfmathsetmacro\labelshift{(\pgfplotspointmeta*10^\pgfplots@data@scale@trafo@EXPONENT@x)/2)*\pgfplots@x@veclength)}
        \pgfkeys{/pgf/fpu=false}
        },
    nodes near coords horizontally centered/.style={
        every node near coord/.append style={
            /pgfplots/calculate xoffset,
            xshift=-\labelshift,yshift=-.25pt
            },
        nodes near coords align=center
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}
\begin{axis}[
        xbar stacked,
        point meta=explicit,
        nodes near coords horizontally centered, %~~THIS ISN'T WORKING~~
        bar width=10pt,
        axis x line*=bottom,
        axis on top=true,
        ytick style={opacity=0},
       yticklabel style={font=\small, text width=2cm, align=center},
       x axis line style={opacity=0},
       y axis line style={opacity=0},
        y dir=reverse,
        ytick={-.35,-.175,0,.175,.35}, %~~THIS IS UGLY!~~
        yticklabels={
                label1,label2,label3,label4,label5
            },
    ]
      \addplot[
        fill=cyan,
        draw=none,
        bar shift=28pt,
        mark options={
            lightgray,
            thick,
        },
        mark=waterfall bridge,
      ] table [y expr=\coordindex, x index=0, meta index=0] {datatable.csv};
      \addplot[
        fill=orange,
        draw=none,
        bar shift=14pt,
        mark options={
            lightgray,
            thick,
        },
        mark=waterfall bridge 2,
      ] table [y expr=\coordindex, x index=1, meta index=1] {datatable.csv};
      \addplot[
        fill=blue,
        draw=none,
        bar shift=0pt,
        mark options={
            lightgray,
            thick,
        },
        mark=waterfall bridge 3,
      ] table [y expr=\coordindex, x index=2, meta index=2] {datatable.csv};
      \addplot[
        fill=red,
        draw=none,
        bar shift=-14pt,
        mark options={
            lightgray,
            thick,
        },
        mark=waterfall bridge 4,
      ] table [y expr=\coordindex, x index=3, meta index=3] {datatable.csv};
      \addplot[
        fill=yellow,
        draw=none,
        bar shift=-28pt,
      ] table [y expr=\coordindex, x index=4, meta index=4] {datatable.csv};
    \draw[ultra thin,lightgray] (axis cs:0,\pgfkeysvalueof{/pgfplots/ymin}) -- (axis cs:0,\pgfkeysvalueof{/pgfplots/ymax}); % adds x=0 line
\end{axis}
\end{tikzpicture}


\end{document}

答案1

好吧,只是为了好玩(并且为了更好地学习新工具)我在 lua 中做了一个实现,可以通过 LuaLatex 运行。

lua 代码从外部读取.cvs数据,其中数据预计位于不同的行中(每行一个数字),并生成一个 tikz 图形,该图形会根据读取的数据自适应其轴。此外,生成的 tikz 定义了一组名为 的坐标row1row2等等,位于每个条形的中心,以及一个名为 的坐标,min位于最左侧条形的 x 位置。这些坐标可用于在图表中放置标签,要么位于条形的中心,要么位于图形的左侧。

图形中使用的颜色可由用户定义。如果颜色数量少于条数,则颜色将循环使用。

这是 LaTeX 代码:

\documentclass{book}
\usepackage{xcolor}
\usepackage{tikz}
\usepackage{filecontents}
\directlua{dofile("luaFunctions.lua")}

%create a pair of datafiles
\begin{filecontents*}{datafile1.csv}
  2
  3
  2
  -6
  -3
\end{filecontents*}
\begin{filecontents*}{datafile2.csv}
  3 
  4 
  2 
  3 
  -4   
  -6
  2 
  -5
\end{filecontents*}

% latex commands to execute the lua functions
\def\waterfallChart#1{\directlua{waterfallChart("#1")}}
\def\setColors#1{%
   \directlua{emptyColors()}%
   \foreach \c in {#1} {\directlua{addColor("\c")}}
}

% set some styles
\tikzset{bar connection/.style = {black!50, thick}}
\setColors{cyan!80!black!50, orange, blue!80!black, red, yellow}

\begin{document}
\begin{tikzpicture} % First graph
  \waterfallChart{datafile1.csv}  % This draws the chart
  % Now, adding labels, centered at each bar
  \foreach \label [count=\n from 1] in {foo, bar, bad, foobar, spam}
    \node at (row\n) {\label};
\end{tikzpicture}

\vskip 2cm

\begin{tikzpicture} % Second graph
  \setColors{brown,red,orange}   % Different colors for this one
  \waterfallChart{datafile2.csv} % Draw the chart
  % Put labels (at the left of the figure in this case)
  \foreach \label [count=\n from 1] in {foo, bar, bad, foobar, spam, eggs, lorem, ipsum}
    \node[left] at (row\n-|min) {\label};
\end{tikzpicture}
\end{document}

结果如下:

结果

这是该文件的内容luaFunctions.lua

colors = {"blue","green"}  -- Default colors

function readDataFile(filename)
    local input = io.open(filename, 'r')
    local dataTable = {}
    local n
    for line in input:lines() do
       table.insert(dataTable, line)
    end
    input:close()
    return dataTable
end

function emptyColors()
    colors = {}
end

function addColor(c)
    table.insert(colors,c)
end

function computeExtremes(dataTable)
    local max, min, x
    x = 0.0
    min = 0.0
    max = 0.0
    for i,p in ipairs(dataTable) do
        x = x + p
        if (x<min) then min = x end
        if (x>max) then max = x end
    end
    return min, max
end

function waterfallChart(filename)
    local data = readDataFile(filename)
    local min, max, n_steps, step, color, xpos, ypos, barwidth, aux, spread

    -- Configure here as required
    barwidth = 0.5  -- Height of each bar in the chart
    spread = 1.4    -- Distance among baselines of the bars (in barwidth units)
    n_ticks = 6     -- Number of ticks in the x-axis

    min, max = computeExtremes(data)
    step = (max-min)/n_ticks
    max = min + n_ticks*step
    xpos = 0.0
    ypos = 0.0
    aux = 0
    -- Draw axes
    -- Vertical axis
    tex.print(string.format("\\draw (0,%f) -- (0, %f);",
               1.1*barwidth, -#data*spread*barwidth))
    -- Horizontal axis
    tex.print(string.format("\\foreach \\tick in {%d, %d, ..., %d}",
              min, min+step, max))
    tex.print(string.format("  \\draw (\\tick, %f) -- +(0, -2mm) node[below] {\\tick};",
              -#data*spread*barwidth))
    tex.print(string.format("\\coordinate (min) at (%f,%f);",
                  min+0.0, -#data*spread*barwidth))

    -- Draw the bars
    color = 1
    for i,p in ipairs(data) do
        tex.print(string.format("\\fill[%s] (%f,%f) rectangle +(%f, %f) coordinate[midway] (row%d);",
                  colors[color], xpos, ypos, p+0.0, barwidth, i))
        tex.print(string.format("\\draw[bar connection] (%f, %f) -- +(0,%f);",
                  xpos, ypos, spread*aux*barwidth))
        aux =   1
        ypos = ypos - spread*barwidth
        xpos = xpos + 1.0 * p
        color = color + 1
        if (color > #colors) then color = 1; end
    end
end

答案2

我不知道pgfplotsnor pgfplotstable,所以我看不懂你的代码。但是看了你的图,我写了一个纯 tikz 解决方案,也许可以更简单。主要缺点是你必须在循环中指定数据点\foreach(而不是从 读取它们.csv),还要指定条形的颜色。你会决定这是否对你有用。

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
  \xdef\myypos{0}
  \xdef\myxpos{0}
  \xdef\mywidth{0.5}
  \xdef\myyprev{0.0}
  % Axes (ugly, some hardcoded numbers)
  \draw (0,1.1*\mywidth) -- (0, -5*2.2*\mywidth);
  \foreach \tick in {-2,0,...,8}
    \draw (\tick, -5*2.2*\mywidth) -- +(0,-2mm) node[below] {\tick};

  % bars and labels
  \foreach \mylen/\mylab/\mycolor in
  {2/label1/cyan!80!black!50,
   3/label2/orange,
   2/label3/blue!80!black,
  -6/label4/red,
  -3/label5/yellow}
  {
    \fill[\mycolor] (\myxpos,\myypos) rectangle +(\mylen, \mywidth) node[midway,black] {\mylab};
    \draw[black!40,thick] (\myxpos, \myypos) -- +(0, 2.2*\myyprev*\mywidth);
    \pgfmathparse{\myypos - 1.2*\mywidth}\xdef\myypos{\pgfmathresult}
    \pgfmathparse{\myxpos + \mylen}\xdef\myxpos{\pgfmathresult}
    \xdef\myyprev{1.0}
  }
\end{tikzpicture}
\end{document}

结果:

图形

相关内容