From: Luci Ellis on
Dear MathGroupers,
I am trying to set up some custom functions/ option settings to
replicate the standard look and feel of charts as used by my employer.
These are currently done in DeltaGraph, but I would like to be able to
generate charts directly in Mathematica so that I don't have to worry
about the additional (Export to Excel -> Import into DeltaGraph ->
bother a research assistant ) steps.

Most of our charts are of time series, and I have been able to
replicate the look for line graphs by customising DateListPlot[]. But I
can't replicate the effect for bar charts. Our standard is to put a
tick every X periods (say, to mark off every year or every quarter) and
then centre the date label in that region. I am having serious problems
putting the ticks in exactly the right place for bar charts. Adding up
bar widths and (prespecified) BarSpacings never quite seems to work.
(If you need an example of what I'm trying to do, have a look at the
two graphs on the left-hand side of the first page in
http://www.rba.gov.au/ChartPack/output_expenditure_activity_fincon.pdf
-- I have the equivalent of the bottom right-hand graph on that page
working.)

I tried using a Thickness style for Filling of a non-joined
DateListPlot, but it doesn't work because the thickness also applies to
the ends of the line -- ie the "rectangle" doesn't actually stop at the
axis but goes past it by an amount equal to the value of Thickness.

There is also the issue that I will need to combine Line and Bar charts
for certain applications. We often plot a year-ended percentage change
as a line and then the quarterly or monthly percentage change as small
bars on the same axis.

Can anyone offer any guidance on placement of ticks in BarChart, and
combining them with DateListPlots? Or, failing that, making
DateListPlots look like BarCharts?

Thanks in advance,
Luci


From: Armand Tamzarian on
On Nov 1, 3:03 am, Luci Ellis <l...(a)verbeia.com> wrote:
> Dear MathGroupers,
> I am trying to set up some custom functions/ option settings to
> replicate the standard look and feel of charts as used by my employer.
> These are currently done in DeltaGraph, but I would like to be able to
> generate charts directly in Mathematica so that I don't have to worry
> about the additional (Export to Excel -> Import into DeltaGraph ->
> bother a research assistant ) steps.
>
> Most of our charts are of time series, and I have been able to
> replicate the look for line graphs by customising DateListPlot[]. But I
> can't replicate the effect for bar charts. Our standard is to put a
> tick every X periods (say, to mark off every year or every quarter) and
> then centre the date label in that region. I am having serious problems
> putting the ticks in exactly the right place for bar charts. Adding up
> bar widths and (prespecified) BarSpacings never quite seems to work.
> (If you need an example of what I'm trying to do, have a look at the
> two graphs on the left-hand side of the first page inhttp://www.rba.gov.a=
u/ChartPack/output_expenditure_activity_fincon.pdf
> -- I have the equivalent of the bottom right-hand graph on that page
> working.)
>
> I tried using a Thickness style for Filling of a non-joined
> DateListPlot, but it doesn't work because the thickness also applies to
> the ends of the line -- ie the "rectangle" doesn't actually stop at the
> axis but goes past it by an amount equal to the value of Thickness.
>
> There is also the issue that I will need to combine Line and Bar charts
> for certain applications. We often plot a year-ended percentage change
> as a line and then the quarterly or monthly percentage change as small
> bars on the same axis.
>
> Can anyone offer any guidance on placement of ticks in BarChart, and
> combining them with DateListPlots? Or, failing that, making
> DateListPlots look like BarCharts?
>
> Thanks in advance,
> Luci

This requiring some further work but seems to do what you want.

Make some sample data ...I guess it might come in this sort of format

someData = Flatten[Table[{ToString[i] <> "q" <> ToString[j], RandomReal
[{-10, 10}]}, {i, 1999, 2008}, {j, 1, 4}], 1];

You will be able to convert it to this format easily enough:

data1 = Flatten[Table[{{i, j}, RandomReal[{-5, 5}]}, {i, 1999, 2008},
{j, 1, 4}], 1];
data2 = Flatten[Table[{{i, j*3}, RandomReal[{8, 20}]}, {i, 1999,
2008}, {j, 1, 4}],1];

(* need to tweek this for leap years *)

dates[{year_, quarter_}] := Which[
quarter == 1, DatePlus[{year, 1, 0}, #] & /@ Range[90],
quarter == 2, DatePlus[{year, 4, 0}, #] & /@ Range[91],
quarter == 3, DatePlus[{year, 7, 0}, #] & /@ Range[92],
quarter == 4, DatePlus[{year, 10, 0}, #] & /@ Range[92]
];

barData = Thread[{dates[#[[1]]], #[[2]]}] & /@ data1;

p1 = DateListPlot[barData, Joined -> False, Filling -> 0];
p1 = DeleteCases[p1, _Point, \[Infinity]];
p2 = DateListPlot[data2, Joined -> True, PlotStyle -> Thick];

Show[p2, p1, PlotRange -> All]

I don't know how to get the year labels centred. Maybe someone else
can offer a suggestion.

Mike

From: Luci Ellis on
First, thanks to Armand/Mike for his suggestion. It gets me a long way
to what I need. The other suggestions to use add-on products are
well-taken, but aren't likely to be accepted by my employer. Thanks
anyway.

On 2009-11-03 18:55:08 +1100, Armand Tamzarian
<mike.honeychurch(a)gmail.com> said:

> On Nov 1, 3:03 am, Luci Ellis <l...(a)verbeia.com> wrote:
>>
>> Can anyone offer any guidance on placement of ticks in BarChart, and
>> combining them with DateListPlots? Or, failing that, making
>> DateListPlots look like BarCharts?
>>
>> Thanks in advance,
>> Luci
>
> This requiring some further work but seems to do what you want.
>
> Make some sample data ...I guess it might come in this sort of format
>
> someData = Flatten[Table[{ToString[i] <> "q" <> ToString[j], RandomRe=
al
> [{-10, 10}]}, {i, 1999, 2008}, {j, 1, 4}], 1];
>
> You will be able to convert it to this format easily enough:
>
> data1 = Flatten[Table[{{i, j}, RandomReal[{-5, 5}]}, {i, 1999, 2008},
> {j, 1, 4}], 1];
> data2 = Flatten[Table[{{i, j*3}, RandomReal[{8, 20}]}, {i, 1999,
> 2008}, {j, 1, 4}],1];
>
> (* need to tweek this for leap years *)

Create the daily dates as a flat list, then use GatherBy?

>
> dates[{year_, quarter_}] := Which[
> quarter == 1, DatePlus[{year, 1, 0}, #] & /@ Range[90],
> quarter == 2, DatePlus[{year, 4, 0}, #] & /@ Range[91],
> quarter == 3, DatePlus[{year, 7, 0}, #] & /@ Range[92],
> quarter == 4, DatePlus[{year, 10, 0}, #] & /@ Range[92]
> ];
>
> barData = Thread[{dates[#[[1]]], #[[2]]}] & /@ data1;
>
> p1 = DateListPlot[barData, Joined -> False, Filling -> 0];
> p1 = DeleteCases[p1, _Point, \[Infinity]];
> p2 = DateListPlot[data2, Joined -> True, PlotStyle -> Thick];
>
> Show[p2, p1, PlotRange -> All]

This is a very helpful clue, thanks. In fact, as long as you are only
exporting graphics to PDF, GIF, PNG etc, there is no need to
DeleteCases the Points. This works:

DateListPlot[{barData1, barData1a}, PlotStyle -> Directive[Opacity[0]],
=E2=80=A8 Filling -> {1 -> {0, Blue}, 2 -> {{1}, Red}}]
(try this for barData1 = Flatten[Most /@ barData, 1]; and
data1a = {0, 1.*Sign[#[[2]]]} + # & /@ data1
barData1a = Flatten[Most /@ (Thread[{dates[#[[1]]], #[[2]]}] & /@
data1a), 1]; )

It's not even necessary to create two plots and use Show.

But it is incredibly slow (creating daily data out of non-daily data),
and will only work for stacked bars (see below) not side by side bars,
unless one fakes the date positions for the second series.

i will keep working on it. In the meantime, any other suggestions are
greatly appreciated.

> I don't know how to get the year labels centred. Maybe someone else
> can offer a suggestion.

This bit I already have working -- just not TickPlacement in BarChart.
It involves AdjustmentBox constructs like this.

RBADateTicks[min_, max_, opts___Rule] :==E2=80=A8 Module[{seq},=E2=80=A8=

With[{dateformat = DateLabelFormat /. {opts} /. Options[RBADateTicks],=E2=
=80=A8
datelabelspacing = DateLabelSpacing /. {opts} /.
Options[RBADateTicks],=E2=80=A8 vertshift = DateLabelVerticalShift /=
.. {opts}
/. Options[RBADateTicks],=E2=80=A8 afac = With[{afacopt = DateLabe=
lOffset /.
{opts} /. Options[RBADateTicks]},=E2=80=A8 Switch[afacopt, Automati=
c, =E2=80=A8
AutoDateLabelOffset[max - min + 1], _?NumericQ, afacopt]]},=E2=80=A8 =
seq
= If[datelabelspacing == Automatic, AutoDateLabel[max - min + 1], =E2=
=80=A8
datelabelspacing];=E2=80=A8 Table[{{i, 1, 1}, =E2=80=A8 If[Mod[max =
- i, seq] ==
0, =E2=80=A8 DisplayForm[=E2=80=A8 AdjustmentBox[DateString[{i=
, 1, 1},
{dateformat}], =E2=80=A8 BoxBaselineShift -> 0.1, BoxMargins -> {{=
afac,
0}, {0, 0}}]], =E2=80=A8 ""], {0.015, 0}}, {i, Range[min, max]}]=E2=80=
=A8 ]]

where

AutoDateLabelOffset[num_Integer?Positive] := =E2=80=A8 Switch[num, 1, 3=
2, 2,
16, 3, 10.5,=E2=80=A8 4, 8.,=E2=80=A8 5, 6.8,=E2=80=A8 6, 5.2, 7, 4.7,=
8, 4.0, 9, 3.7, 10,
3.5, 11, 3.25, 12, 2.9, 13, 2.65, 14, =E2=80=A8 2.35, 15, 2.3, 16, 2.15,=
17,
2.05, 18, 2., 19, 1.9, 20, 1.8, _, 1.5]

and

AutoDateLabel[num_Integer?Positive] := =E2=80=A8 Piecewise[{{1, num < 6=
}, {2, 5
<= num <= 10}, {3, 10 < num <= 15}, {4, =E2=80=A8 15 < num < 20}=
, {5, 20 <=
num <= 45}, {10, num > 45}}, 5]=E2=80=A8


From: Luci Ellis on
An update:
This seems to work nicely in DateListPlot:
Epilog -> ({ Blue, =E2=80=A8 Rectangle[Offset[{-.2, 0}, {#[[1]], 0}],
Offset[{1.1, 0}, #]] & /@ =E2=80=A8 data]})

(where the data are in the form {{1999,12,1},number}..} )

The exact amount of offset depends on the number of bars, but this is
not a big issue.

Stacked bars will need MapThread or something, presumably.

This solution also renders much faster than Mike's.

I hope it is useful to others.

Regards,
Luci

On 2009-11-04 17:34:26 +1100, Luci Ellis <luci(a)verbeia.com> said:

> First, thanks to Armand/Mike for his suggestion. It gets me a long way
> to what I need. The other suggestions to use add-on products are
> well-taken, but aren't likely to be accepted by my employer. Thanks
> anyway.
>
> On 2009-11-03 18:55:08 +1100, Armand Tamzarian
> <mike.honeychurch(a)gmail.com> said:
>
>> On Nov 1, 3:03 am, Luci Ellis <l...(a)verbeia.com> wrote:
>>>
>>> Can anyone offer any guidance on placement of ticks in BarChart, and
>>> combining them with DateListPlots? Or, failing that, making
>>> DateListPlots look like BarCharts?
>>>
>>> Thanks in advance,
>>> Luci
>>
>> This requiring some further work but seems to do what you want.
>>
>> Make some sample data ...I guess it might come in this sort of format
>>
>> someData = Flatten[Table[{ToString[i] <> "q" <> ToString[j], RandomReal
>> [{-10, 10}]}, {i, 1999, 2008}, {j, 1, 4}], 1];
>>
>> You will be able to convert it to this format easily enough:
>>
>> data1 = Flatten[Table[{{i, j}, RandomReal[{-5, 5}]}, {i, 1999, 2008},
>> {j, 1, 4}], 1];
>> data2 = Flatten[Table[{{i, j*3}, RandomReal[{8, 20}]}, {i, 1999,
>> 2008}, {j, 1, 4}],1];
>>
>> (* need to tweek this for leap years *)
>
> Create the daily dates as a flat list, then use GatherBy?
>
>>
>> dates[{year_, quarter_}] := Which[
>> quarter == 1, DatePlus[{year, 1, 0}, #] & /@ Range[90],
>> quarter == 2, DatePlus[{year, 4, 0}, #] & /@ Range[91],
>> quarter == 3, DatePlus[{year, 7, 0}, #] & /@ Range[92],
>> quarter == 4, DatePlus[{year, 10, 0}, #] & /@ Range[92]
>> ];
>>
>> barData = Thread[{dates[#[[1]]], #[[2]]}] & /@ data1;
>>
>> p1 = DateListPlot[barData, Joined -> False, Filling -> 0];
>> p1 = DeleteCases[p1, _Point, \[Infinity]];
>> p2 = DateListPlot[data2, Joined -> True, PlotStyle -> Thick];
>>
>> Show[p2, p1, PlotRange -> All]
>
> This is a very helpful clue, thanks. In fact, as long as you are only
> exporting graphics to PDF, GIF, PNG etc, there is no need to
> DeleteCases the Points. This works:
>
> DateListPlot[{barData1, barData1a}, PlotStyle -> Directive[Opacity[0]],
> =E2=80=A8 Filling -> {1 -> {0, Blue}, 2 -> {{1}, Red}}]
> (try this for barData1 = Flatten[Most /@ barData, 1]; and
> data1a = {0, 1.*Sign[#[[2]]]} + # & /@ data1
> barData1a = Flatten[Most /@ (Thread[{dates[#[[1]]], #[[2]]}] & /@
> data1a), 1]; )
>
> It's not even necessary to create two plots and use Show.
>
> But it is incredibly slow (creating daily data out of non-daily data),
> and will only work for stacked bars (see below) not side by side bars,
> unless one fakes the date positions for the second series.
>
> i will keep working on it. In the meantime, any other suggestions are
> greatly appreciated.
>
>> I don't know how to get the year labels centred. Maybe someone else
>> can offer a suggestion.
>
> This bit I already have working -- just not TickPlacement in BarChart.
> It involves AdjustmentBox constructs like this.
>
> RBADateTicks[min_, max_, opts___Rule] :==E2=80=A8 Module[{seq},=
=E2=80=A8=
>
> With[{dateformat = DateLabelFormat /. {opts} /. Options[RBADateTicks]=
,=E2=
> =80=A8
> datelabelspacing = DateLabelSpacing /. {opts} /.
> Options[RBADateTicks],=E2=80=A8 vertshift = DateLabelVertica=
lShift /=
> . {opts}
> /. Options[RBADateTicks],=E2=80=A8 afac = With[{afacopt = =
DateLabe=
> lOffset /.
> {opts} /. Options[RBADateTicks]},=E2=80=A8 Switch[afacopt, =
Automati=
> c, =E2=80=A8
> AutoDateLabelOffset[max - min + 1], _?NumericQ, afacopt]]},=E2==
80=A8 =
> seq
> = If[datelabelspacing == Automatic, AutoDateLabel[max - min + 1],=
=E2=
> =80=A8
> datelabelspacing];=E2=80=A8 Table[{{i, 1, 1}, =E2=80=A8 =
If[Mod[max =
> - i, seq] ==
> 0, =E2=80=A8 DisplayForm[=E2=80=A8 AdjustmentBox=
[DateString[{i=
> , 1, 1},
> {dateformat}], =E2=80=A8 BoxBaselineShift -> 0.1, BoxMargi=
ns -> {{=
> afac,
> 0}, {0, 0}}]], =E2=80=A8 ""], {0.015, 0}}, {i, Range[min, ma=
x]}]=E2=80=
> =A8 ]]
>
> where
>
> AutoDateLabelOffset[num_Integer?Positive] := =E2=80=A8 Switch[n=
um, 1, 3=
> 2, 2,
> 16, 3, 10.5,=E2=80=A8 4, 8.,=E2=80=A8 5, 6.8,=E2=80==
A8 6, 5.2, 7, 4.7,=
> 8, 4.0, 9, 3.7, 10,
> 3.5, 11, 3.25, 12, 2.9, 13, 2.65, 14, =E2=80=A8 2.35, 15, 2.3, 1=
6, 2.15,=
> 17,
> 2.05, 18, 2., 19, 1.9, 20, 1.8, _, 1.5]
>
> and
>
> AutoDateLabel[num_Integer?Positive] := =E2=80=A8 Piecewise[{{1,=
num < 6=
> }, {2, 5
> <= num <= 10}, {3, 10 < num <= 15}, {4, =E2=80=A8 15 < n=
um < 20}=
> , {5, 20 <=
> num <= 45}, {10, num > 45}}, 5]=E2=80=A8