19

I'm trying to label a bar chart with rotated labels

see this simple example:

BarChart[RandomReal[1, 10], BarSpacing -> Large, 
 ChartLabels -> 
  Placed[ToString /@ RandomReal[100000, 10], Axis, Rotate[#, Pi/4] &]]

In this case the center of the label is aligned with the center of the bar.

Mathematica graphics

I want the right/top of the label to be aligned with the center of the bar instead, basically shift the labels over a bit to the left. How can this be done?

Also, what if all the labels have different lengths:

Mathematica graphics

s0rce
  • 9,632
  • 4
  • 45
  • 78
  • You could try including an AdjustmentBox in your Placed function. I haven't time to write this up as a full answer just now but someone else might like to try. – Verbeia Mar 06 '12 at 22:08
  • 1
    s0rce, you may wish to revisit this question. belisarius's method no longer works in version 10, while Brett's does, and also for other plots as well. – Mr.Wizard Nov 11 '14 at 02:57

5 Answers5

24

By default the Axis position that you're using centers the label under the bar along the axis. For long text that's rotated at a slant, it generally looks bad. (It works fine for horizontal and vertical text, however.)


Here's one approach, using a different second argument for Placed:

Module[{
    labels = {"Learning focused", "Positively oriented", "Continuous", 
              "Timely", "Clear criteria", "Flexible", "Suited to student level"}, 
    data = {8, 6, 4, 5, 5, 9, 9}
    }, 
    BarChart[data, 
        ChartLabels -> Placed[
            labels, 
            {{0.5, 0}, {0.9, 1}}, 
            Rotate[#, (2/7) Pi] &
            ], 
        PlotRange -> {Automatic, {0, 10}}, Ticks -> {None, Range[0, 10, 2]}, 
        ImagePadding -> {{20, 0}, {95, 0}}
    ]
]

enter image description here

The first part of the argument, {0.5, 0}, says to place the label halfway across the bar, at the bottom.

The second part of the argument, {0.9, 1}, says what part of the label at the first part. In this case it's near, but not quite at, the right-top corner.

It's not exactly at the corner, because I think it looks better if the top of the "ink" is centered below the bars, and that won't be the actual corner unless your label ends with ">" or something similar.


Here's a second approach, using a different third argument of Placed:

Module[{
    labels = {"Learning focused", "Positively oriented", "Continuous", 
              "Timely", "Clear criteria", "Flexible", "Suited to student level"}, 
    data = {8, 6, 4, 5, 5, 9, 9}
    }, 
    BarChart[data, 
        ChartLabels -> Placed[
            labels, 
            Axis, 
            Block[{text = Rotate[#, (2/7) Pi]}, 
                Row[{text, Invisible[text]}, 
                    "\[NegativeMediumSpace]"]
            ]&
            ], 
        PlotRange -> {Automatic, {0, 10}}, Ticks -> {None, Range[0, 10, 2]}, 
        ImagePadding -> {{20, 0}, {95, 0}}
    ]
]

This uses the normal axes positioning, but for each label, it creates an object that has the label and an invisible copy of the label side-by-side so that the center of the object is the end of the visible label. (I used a negative space between the objects, because again I think it looks better.) The nice thing about this approach is that the basic idea can be used for labels for ticks on axes that don't otherwise allow specific placements.

enter image description here

Brett Champion
  • 20,779
  • 2
  • 64
  • 121
12

It can be done using the pos parameter of Placed:

BarChart[RandomReal[1, 10], BarSpacing -> Large, 
 ChartLabels -> 
  Placed[(ToString /@ 10^RandomInteger[10, 10]), 
         {{1, 0}, {1, 1}} 
         (* pos: bar position: lower right corner (1,0), 
                 text box:     upper right corner (1,1)  *), 
         Rotate[#, Pi/4] &
  ]
]

Mathematica graphics

Use {0.5,0} instead of {1,0} to align with the bottom center of the bar (which I find slightly less esthetical pleasing, but YMMV):

Mathematica graphics

Sjoerd C. de Vries
  • 65,815
  • 14
  • 188
  • 323
  • NIce approach. I find {0.75, 0} more pleasing to the eye than {0.5, 0}. – DavidC Mar 06 '12 at 23:02
  • I also like {0.75,0} however, the labels are slightly closer to the Axis here then they are with Axis, can this be solved? edit: {{0.75, 0}, {1, 1.1}} looks a bit better but I think it makes the top uneven. – s0rce Mar 07 '12 at 02:49
12

Something like this could also do:

BarChart[RandomReal[1, 10], BarSpacing -> Large, 
  ChartLabels -> 
   Placed[ToString /@ 
     RandomChoice[ExampleData[{"Text", "DeclarationOfIndependence"}, "Words"], 10], 
     Axis, Rotate[#, Pi/4] &]] /. 
 ImageScaled[{1/2, 1}] -> ImageScaled[{0.9, 1}]

enter image description here

István Zachar
  • 47,032
  • 20
  • 143
  • 291
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
11

Here is a function I had lying around to deal with the same problem. Note you need to specify your font and size somewhere first, or your could adapt the following function to take options.

The issue is that Rotate rotates around the center of the item, not around some point corresponding to a top edge or corner. This code works out where the top corner should be, and adds margins to the box so that the top corner stays centred on the bar. Depending on where you actually want the labels to be, you might need to adapt this a little. This function was designed for the case where the labels were always at the bottom of a framed chart.

font = "Arial"; 
fsize = 12;     
rotatelabel[xlist : {(_String | _Style) ..}, rads_]/; Length[xlist]>2 := 
 With[{len = Length[xlist], cosdeg = Cos[rads]}, 
  DisplayForm@AdjustmentBox[
      Style[Rotate[#, rads, If[Sign[rads] == 1, {Left, Bottom}, {Right, Top}]], 
       FontFamily -> font, FontSize -> fsize], 
      BoxMargins -> {{If[rads < 0,  Max[0, cosdeg*(Plus @@ 
         ImageDimensions[Rasterize[Style[#, FontFamily -> font, FontSize -> fsize]]])/(fsize) 
         - 1.5 ((len-1)/len) * Abs@cosdeg], 0], 
         If[cosdeg == 0 || cosdeg == 1 || rads < 0, 0, 
          Max[0, cosdeg*(Plus @@ 
          ImageDimensions[Rasterize[Style[#, FontFamily -> font, FontSize -> fsize]]])/(fsize)     
         - 1.5 ((len-1)/len)* Abs@cosdeg]]}, {0.3, 0.25}}] & /@ xlist]

Example using your data:

BarChart[data,  TicksStyle -> Directive[FontFamily -> "Arial", FontSize -> 12], 
 ChartLabels -> Placed[rotatelabel[labels, 45 Degree], Axis],   ImageSize -> 500]

enter image description here

The function handles the opposite slant, too, if that is what is desired.

BarChart[data, TicksStyle -> Directive[FontFamily -> "Arial", FontSize -> 12], 
 ChartLabels -> Placed[rotatelabel[labels, -45 Degree], Axis], ImageSize -> 500]

enter image description here

Another example

 BarChart[data, TicksStyle -> Directive[FontFamily -> "Arial", FontSize -> 12], 
 ChartLabels -> Placed[rotatelabel[labels, -90 Degree], Axis], ImageSize -> 500]

enter image description here

Verbeia
  • 34,233
  • 9
  • 109
  • 224
8

See if this works.

b = BarChart[RandomReal[1, 10], BarSpacing -> Large, 
 ChartLabels ->  Placed[ToString /@ RandomReal[100000, 10], Axis, 
     Rotate[#, Pi/4] &]];

b /. {Text[p_, Offset[ _, pos_], is_] -> Text[p, Offset[{-10, -2}, pos], is]}

chart

I used AbsoluteOptions[b // InputForm] to inspect the code and identify the Text commands that place the chart labels. I then offset the labels leftward by 10 printer's points.

DavidC
  • 16,724
  • 1
  • 42
  • 94