<data><y>9</y><y>15</y><y>11</y><y>6</y><y>5</y><y>10</y><y>8</y> <y>8</y><y>3</y><y>12</y><y>14</y><y>9</y><y>16</y><y>14</y></data>

And I want to look at the data as a histogram.

Uses SVG

100×100 space

There are n values, so each rectangle is 100/n wide.

The range of the data is max - min

The vertical space is therefore divided over 100/range, which we'll call `vscale`

The height of the rectangle for each value v is `v × vscale`

.

The SVG will look something like this:

<svg ...> <xf:repeatbind="values"> <rect width="{...}" height="{...}" x="{...}" y="{...}" /> </xf:repeat> </svg>

Note: SVG coordinate system is 'upside down'. (0,0) is top left, and goes downwards and to the right.

We load the data:

<instance id="data" src="data.xml"/>

Identify the values:

<bind ref="instance('data')/y" id="values"/>

The first thing we need is how many data values there are:

<bind ref="n" calculate="count(bind('values'))"/>

The width of each rectangle is:

<bind ref="width" calculate="100 div ../n" type="double"/>

Now we can fill in the horizontal values:

<svg ...> <xf:repeat bind="values"> <rect width="{width}" height="{...}" x="{(position()-1) * width}" y="{...}" /> </xf:repeat> </svg>

Get the minimum and maximum values:

<bind ref="min" calculate="min(bind('values'))"/> <bind ref="max" calculate="max(bind('values'))"/>

Calculate the range minimum and maximum, and the range:

<bind ref="rmin" calculate="min(0, ../min)"/> <bind ref="rmax" calculate="max(0, ../max)"/> <bind ref="range" calculate="../rmax - ../rmin"/>

And so the vertical scale is

<bind ref="vscale" calculate="100 div ../range" type="double"/>

Except if the range is zero (i.e. all values are zero), so we actually need

calculate="if(../range = 0, 1, 100 div ../range)"

Ideally we would just say

height="{. * vscale}"

Incredibly, although SVG allows lines to have negative length, rectangles are not allowed to have negative height.

So we have to do some extra work.

height="{abs(.) * vscale}"

So now we have a row of rectangles, of the correct height, of the correct width, at the correct horizontal position:

Now to fix the vertical position.

Currently all rectangles start at SVG's vertical zero, and the other end of the longest (positive) bar is where zero really ought to be.

That value is `rmax`

, so all we have to do is push all the bars
down by the difference between its value and `rmax`

. All negative
values just have to start at `rmax`

:

y="{vscale * if(. < 0, rmax, rmax - .) }"

Giving the final result:

<svg ...> <xf:repeat bind="values"> <rect width="{width}"height="{vscale * abs(.)}"x="{(position()-1) * width}"y="{vscale * if(. < 0, rmax, rmax - .) }"/> </xf:repeat> </svg>