Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I've been struggling to create a DotPlot like the one shown in Cleveland's The Elements of Graphing Data.

enter image description here

Using the following dataset

data = Sort[{#, 
 WolframAlpha[
  StringJoin["Number of native speakers ", #], {{"Result", 1}, 
   "ComputableData"}]} & /@ {"Mandarin", "French", "English", 
"Spanish", "German", "Hindi", "Malay", "Arabic", "Portuguese", 
"Russian", "Korean", "Italian", "Cantonese", "Telugu", 
"Urdu"}, #1[[2]] < #2[[2]] &]

What is the best approach to replicate this chart with its two axis, dot, dashed lines , etc?

share|improve this question
add comment

3 Answers

up vote 24 down vote accepted

Something like this :

data = Sort[{#, 
  WolframAlpha[
   StringJoin["Number of native speakers ", #], {{"Result", 1}, 
   "ComputableData"}]} & /@ {"Mandarin", "French", "English", 
   "Spanish", "German", "Hindi", "Malay", "Arabic", "Portuguese", 
   "Russian", "Korean", "Italian", "Cantonese", "Telugu", 
   "Urdu"}, #1[[2]] < #2[[2]] &]

sorted = SortBy[data, #[[2]] &] ;
len= Length[sorted] ;

ListPlot[Transpose[{Log[2, sorted[[All, 2]]/10^6], Range[len]}], 
 PlotRange -> {All, All}, Frame -> True,
 FrameTicks -> {{{#, sorted[[#, 1]]} & /@ Range[len], None}, {{#, 2^#} & /@ Range[len], {#, #} & /@ Range[len]}},
 GridLines -> {None, {#, Dotted} & /@ Range[len]},
 FrameLabel -> {"Number of Speakers (millions)", ""}, 
 PlotLabel -> "Log Number of Speakers (\!\(\*SubscriptBox[\(log\), \(2\)]\) \ \ millions)", 
 AxesOrigin -> {0, Log[2, 1.5]}]

data

share|improve this answer
1  
you just need the top ticks and you're all set. Use a frame with the 4 element list version for the ticks –  rm -rf Jul 2 '12 at 18:23
    
+1 for the username –  alancalvitti Jul 2 '12 at 18:35
    
@R.M Thanks, I totally overlooked that. –  b.gatessucks Jul 2 '12 at 19:28
    
speakers are now stored in "people" units so You need sorted = MapAt[QuantityMagnitude, #, {2}] & /@ SortBy[data, #[[2]] &] but I'm not very familiar with Units so maybe it could be done simpler. –  Kuba Jul 10 '13 at 6:42
add comment

It is a Chart so one may want to use BarChart:

(* speakers are stored with [people] units so we have to get rid of it, 
   I'm also deleting Malavian since there is no data now*)
sorted = MapAt[QuantityMagnitude, #, {2}] & /@SortBy[data, #[[2]] &] // Rest
len = Length[sorted]

mark[{{xmin_, xmax_}, {ymin_, ymax_}}, ___] := {Black, AbsolutePointSize@7, 
                                              Point[{Scaled[{0, -.02}, {xmax, ymax}]}]}
topt = Table[{i, 2^i}, {i, 0, 9}];

BarChart[Log[2, sorted[[ ;; , 2]]/10^6], 
         ChartLabels -> sorted[[ ;; , 1]], BarOrigin -> Left, BarSpacing -> Large, 
         GridLines -> {None, Range@len}, GridLinesStyle -> Dotted, 
         ChartElementFunction -> mark, Frame -> {{False, True}, {True, True}},
         AxesOrigin -> {0, -1}, FrameTicks -> {{False, False}, {Automatic, topt}}]

enter image description here

share|improve this answer
add comment

Except for the simplest graphics, you almost always have more flexibility and control if you build the graphics expressions bottom-up, eg try a variation on this:

Graphics[
 {{Opacity[0.3], Dashed, Line[{{0, First@#2}, {900, First@#2}}]}, 
    Text[First@#, {0, First@#2}, {1, 0}],
    Blue, Disk[{Last@#/10^6, First@#2}, 5*{1, 0.03}]} &~ MapIndexed~ 
  data,
 AspectRatio -> 1/2,
 PlotRange -> All,
 ImageSize -> 500,
 Frame -> {True, False, False, False},
 BaseStyle -> FontFamily -> "Helvetica"
 ]

Note there's some hacks here (bad programming practice, but faster to implement):

  • The '900' as parameter to Line instead of extracting the Max coordinate

  • The 5*{1,0.03} as parameter to Disk (effecting ellipses) which must be tuned to AspectRatio, instead of using Epilog and Inset to decouple from AspectRatio. I've bugged WRI about this 'feature'.

enter image description here

share|improve this answer
    
If you use Point (with either PointSize or AbsolutePointSize) instead of Disk you don't have to worry about aspect ratios. –  Brett Champion Jul 3 '12 at 3:39
    
That works for simple elements like Point and Line, but doesn't work for most other icons, eg, if you want a Rectangle, a Star, or any other shape-coded elements which are common in visualization nowadays. Do you know of a way to avoid Epilog? –  alancalvitti Jul 3 '12 at 3:43
    
Inset is one possibility: –  Brett Champion Jul 3 '12 at 3:51
    
star = Graphics[ Polygon[Table[{Cos[2 Pi (2 i + 1)/5], Sin[2 Pi (2 i + 1)/5]}, {i, 5}]]] –  Brett Champion Jul 3 '12 at 3:51
    
Graphics[Table[ Inset[star, RandomReal[1, 2], Center, Offset[25]], {20}], AspectRatio -> 2] –  Brett Champion Jul 3 '12 at 3:51
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.