Making the Most of Information Visualization

MinHsuan Peng

WolframResearch

Table of Content

Clear[globaloptions];
globaloptions[size_:16]:=options=Sequence[BaseStyle->{FontSize->size,FontFamily->"Helvetica"}];
basic=Style[#,EdgeForm[Directive[AbsoluteThickness[3],Blue]]]&;
advanced=Style[#,EdgeForm[Directive[AbsoluteThickness[3],Orange]]]&;
d1={basic@1,basic@1,basic@1,basic@1,advanced@1,advanced@1,advanced@1,9};
BarChart[Reverse@d1,BarOrigin->Left,AxesLabel->{"number of slides",None},ImageSize->800,BarSpacing->1,Axes->{True,False},ChartLabels->Placed[Reverse@{"Styling","Labeling","Legending","Layout & Spacing","Wrappers","ChartElements","ChartElementFunction","Application"},After],LabelStyle->20,globaloptions[25],ColorFunction->"FallColors",ChartElementFunction->"FadingRectangle",GridLines->{Range[9],None},GridLinesStyle->LightGray]

Styling

ChartStyle->{rowstyle,columnstyle}

{BarChart[{{1,2},{1,2,3}},ChartStyle->{{Red,Blue},None},PlotLabel->"Row Style",globaloptions[16]],BarChart[{{1,2},{1,2,3}},ChartStyle->{None,"Rainbow"},PlotLabel->"Column Style",globaloptions[16]]}

ColorFunction->cf

d12=Range[0,2,0.2];{BarChart[d12,ColorFunction->"Rainbow",globaloptions[16]],BarChart[d12,ColorFunction->"Rainbow",ColorFunctionScaling->False,globaloptions[16]]}

ChartBaseStyle->style

d13={Range[2],Range[3]};BarChart[d13,
ChartBaseStyle->EdgeForm[Directive[Red,Thick]],
ChartStyle->{{EdgeForm[Black],EdgeForm[Thin]},None}]

Example - CandlestickChart

Table[CandlestickChart[{"GOOG", {{2009,12,15},{2010,1,15}}},BarSpacing->0.2,ImageSize->Medium,globaloptions[16],o],{o,{ColorFunction->None,ColorFunction->(ColorData["RedGreenSplit"][#6]&)}}]

Example - BubbleChart

data=Table[{
100CountryData[country,"LiteracyFraction"],
CountryData[country,"LifeExpectancy"],
CountryData[country,"Population"]
},{country,CountryData[]}];

Table[BubbleChart[data,ChartLabels->Placed[CountryData[],Tooltip],FrameLabel->{"LiteracyFraction","LifeExpectancy"},ImageSize->Medium,ColorFunction->Function[{lit,life,ppl},Evaluate[ColorData["Rainbow"][i]]],globaloptions[16]],{i,{lit,life,ppl}}]

Labeling

ChartLabels -> {rowlabel, columnlabel}

LabelingFunction -> f[value_, index_, label_]

BarChart[{Range[3],Range[4]},ChartLabels->{{"r1","r2"},{"c1","c2","c3","c4"}},LabelingFunction->(Placed[Style[Grid@#3,16],Tooltip]&),ImageSize->Medium,globaloptions[14]]

Example - RectangleChart

Display product of population and birth rate in tooltip

d31=Transpose[{CountryData["G8","Population"],CountryData["G8","BirthRateFraction"]}];

base[v_]:=Floor[Log10[v],3]
unit[base_]:=Switch[base,0," people",3," thousand",6," million",9," billion"]
labeling[v__]:=Row[{Round[(Times@@v)/10^base[Times@@v],0.1], unit[base[Times@@v]]}]
RectangleChart[d31,
(*labels*)
ChartLabels->Placed[CountryData["G8"],Center,(Rotate[#,90Degree]&)],LabelingFunction->(Placed[{Style[labeling[#1],24]},{Tooltip}]&),
AxesLabel->{"population","birth rate"},PlotLabel->"Annual Births",
(*styles*)
ImageSize->Large,globaloptions[20],ChartStyle->27]

Example - PieChart

Use any expression as chart labels

d32={{"Ca",11},{"Fe",0.55},{"Mg",22},{"P",283},{"K",429},{"Na",50},{"Zn",0.47},{"Cu",0.055}};

gd32=Reverse@GatherBy[d32,Last[#]<50&]
{{{"P",283},{"K",429},{"Na",50}},{{"Ca",11},{"Fe",0.55`},{"Mg",22},{"Zn",0.47`},{"Cu",0.055`}}}
largedata=Transpose[First[gd32]]
{{"P","K","Na"},{283,429,50}}
smalldata=Transpose[Reverse@SortBy[Last[gd32],Last]]
{{"Mg","Ca","Fe","Zn","Cu"},{22,11,0.55`,0.47`,0.055`}}
d32label=Append[First[largedata],Row[Column/@Transpose[smalldata],">"]]
{"P","K","Na",(Row[({{"Mg"}, {22}}), ({{"Ca"}, {11}}), ({{"Fe"}, {0.55`}}), ({{"Zn"}, {0.47`}}), ({{"Cu"}, {0.055`}})])}
salmonnutrients=Append[Last[largedata],Total[Last[smalldata]]]
{283,429,50,34.074999999999996`}
PieChart[salmonnutrients,
(*labels*)
ChartLabels->Placed[d32label,"RadialCallout"],
LabelingFunction->"RadialCenter",
PlotLabel->"Salmon Mineral Content (mg)",
(*layout*)
ImageSize->Large,PlotRange->All,globaloptions[16],
SectorOrigin->{{Pi/6,"Counterclockwise"},0.5}]

Legending

ChartLegends->{rowlegend,columnlegend}

{BarChart[{Range[3],Range[5]},ChartStyle->{{Red,Blue},None},ChartLegends->{{"g1","g2"},None},globaloptions[16]],BarChart[{Range[3],Range[5]},ChartStyle->{None,"Rainbow"},ChartLegends->{None,{"A","B","C","D","E"}},globaloptions[16]]}

Example - PairedHistogram

d41=Table[CountryData[#,p]&/@CountryData[],{p,{"MaleLifeExpectancy","FemaleLifeExpectancy"}}];

PairedHistogram[Sequence@@d41,{Range[25,90,2.5]},ChartStyle->{{Lighter@Blue,Lighter@Red},None},PlotLabel->Style["World Life Expectancy","Title",24],ChartLegends->{"Male","Female"},globaloptions[14]]

Example - PairedBarChart

countries=CountryData["G8"];
{dataLeft,dataRight}=Table[1*^-9CountryData[#,s]&/@countries,{s,{"ExportValue","ImportValue"}}];

d42options=Sequence[ChartLabels->{Placed[{"Import", "Export"}, Above],None,None},BarSpacing->{0, 0,1},GridLines->{Range[-2500,2500,500],None},GridLinesStyle->Directive[AbsoluteThickness[3],LightGray],globaloptions[16],PlotLabel->"G8 Import Export (in billion)"];
PairedBarChart[{dataLeft},{dataRight}, ChartLegends->countries,ChartStyle->{{Opacity[1],Opacity[0.3]},None,53},ImageSize->Large,d42options]

Layout & Spacing

Layout

d61=RandomInteger[{1,10},{3,3}];

Row[Table[BarChart[{{3,2,4},{2,5,4},{3,4,7},{3,5,6}},ChartLayout->layout,globaloptions[18],PlotLabel->layout,ChartLegends->{"Q n-1","Q n-2","Q n-3"},ChartLabels->{{"Q1","Q2","Q3","Q4"},None},ImageSize->300,ChartStyle->11],{layout,{"Stepped","Percentile"}}],"  "]

Spacing

BarSpacing->{individual, group}

Row[Table[BarChart[{{3,2,4},{2,5,4},{3,4,7},{3,5,6}},BarSpacing->sp,ChartLegends->{"Q n-1","Q n-2","Q n-3"},ChartLabels->{{"Q1","Q2","Q3","Q4"},None},ImageSize->300,ChartStyle->11,globaloptions[18],PlotLabel->sp],{sp,{{0.2,1},{-0.5,0}}}],"  "]

Wrappers

Style[data, style]

BarChart[{Style[1,Red],2,3},globaloptions[16]]

Tooltip[data, label]

BarChart[{1,Tooltip[2,"Here!"],3},globaloptions[16]]

Button[data, action]

a=1;Dynamic[BarChart[{Labeled[Button[a,a+=0.1],a,Above],2,3},globaloptions[16]]]

Labeled[data, label]

PieChart[{1,Labeled[2,"label","VerticalCallout"],3}]

EventHandler, Legended, Mouseover, PopupWindow, Hyperlink, StatusArea, Annotation...

Example - BarChart

ExampleData["Statistics"]

d61=CountryData["G8","HIVAIDSPopulation"]/CountryData["G8","Population"];

average=Mean[d61];

outlier=Part[Quartiles[d61],3]+1.5InterquartileRange[d61];

HIVdata=Which[
#>outlier, Style[#,Red],
#>average, Style[#,Yellow],
True,#]&/@d61;

BarChart[HIVdata,
(*labels*)
PlotLabel->"G8 HIV Population Rate",
ChartLabels->Placed[CountryData["G8"],{{0.5,0},{0.8,1}},Rotate[#,65Degree]&],
(*others*)
Epilog->{Line[{{0.5,average},{8.5,average}}],Text["average line",{2,average},{Center,Bottom}]},
globaloptions[16]]

Example - CandlestickChart

d62=FinancialData["RIMM","OHLC",{{2010,1},{2010,2,28}}];

stockdata=(
{date,{o,h,l,c}}=#;
Which[
(h-l)/o>0.035&&(c>o),Labeled[Tooltip[#,"Bullish"],"↑"],
(h-l)/o>0.035&&(c<o),Labeled[Tooltip[#,"Bearish"],"↓",Above],
Abs[o-c]/o<0.001,Labeled[#,"Doji"],
True,#
])&/@d62;

CandlestickChart[stockdata,ImageSize->400,globaloptions[16]]

ChartElements

ChartElements->graphics

ChartElements->{graphics,spec}

Table[BarChart[Range[4],ChartElements->{Graphics[Disk[],AspectRatio->Full],i},BarSpacing->0,AspectRatio->Automatic,PlotLabel->i,globaloptions[16],ImageSize->300],{i,{{1,1},{Scaled[0.5],Scaled[0.5]}}}]

Example - BarChart

d61label=Row[{"US Car Sales","( ",," = 1 Million)"}];

carsales={1.6696*^7,1.6579*^7,1.5725*^7,1.0129*^7,1.106*^7,1.2456*^7}/10^6;

BarChart[carsales,ChartElements->{,{Scaled[1],1}},BarSpacing->0.6,AspectRatio->1, PlotLabel->d61label,ChartLabels->{2005,2006,2007,2008,2009,2010},globaloptions[16]]

ChartElementFunction

Pre-defined

BarChart[RandomSample[Range[6]],ChartStyle->10,ChartElementFunction->"GlassRectangle"]

DistributionChart[RandomVariate[NormalDistribution[],{7,100}],ChartStyle->10,ChartElementFunction->ChartElementDataFunction["GlassQuantile","GradientOrigin"->Automatic,"Quantile"->8,"QuantileShading"->True,"QuantileStyle"->GrayLevel[0.9`]]]

Function

genericf[info___]:=Print[{info}]

BarChart[{1->0.1,2->0.2,3->0.3}->"g1",BarSpacing->0,ChartElementFunction->genericf];

genericf[{{x0_, x1_}, {y0_, y1_}}, value_, meta_] := {...}

Example - ErrorBarChart

errordata={3->(3/5),5->(1/10),4->(1/5),2->(1/2),1->(2/5),6->(3/10)};

errorbar[{{x0_,x1_},{y0_,y1_}},data_,meta_]:={
(*main bar*)
Rectangle[{x0,y0},{x1,y1}],
(*error marker*)
{Gray,Opacity[0.3],
Tooltip[
(*marker bar*)
Rectangle[{x0,y1-First[meta]},{(2x0+x1)/3,y1+First[meta]}],
(*tooltip label*)
Grid[{
{"Upper value:", y1+First[meta]},
{"Central value:", y1},
{"Lower value:", y1-First[meta]}},
Alignment->{".",Center}
]
]}
}

BarChart[errordata,ChartElementFunction->errorbar,globaloptions[16],LabelStyle->16,BarSpacing->0.5,ChartStyle->10]

Example - VariantBaseChart

revenuedata={5.2->2.07,3.6->0.57,3.5->2.32,-1.6->0.54,-3.5->1.18,5.9->0.61};

referencebar[{{x0_,x1_},{y0_,y1_}},value_,meta_]:=
Tooltip[{
If[Part[meta,1]>y1,Red,Green],
Rectangle[{(3x0+x1)/4,Part[meta,1]},{(x0+3x1)/4,y1}]
,
{Black,AbsoluteThickness[3],
Line[{{x0,Part[meta,1]},{x1,Part[meta,1]}}]
}
},
Grid[{
Style[#,16]&/@{"Revenue: ", y1},
Style[#,16]&/@{"Monthly spent: ", Part[meta, 1]},
Style[#,16]&/@{"Net value: ", y1-Part[meta,1]}
},
Alignment->{".",Center}
]
]

BarChart[revenuedata,ChartElementFunction->referencebar,Axes->{False,True},Frame->True,FrameTicks->{{All,None},{{{1,"Jan"},{2,"Feb"},{3,"Mar"},{4,"Apr"},{5,"May"},{6,"Jun"}},Automatic}}]

Visualize Data - Simple Data

exe1data=ExampleData[{"Statistics","AnimalWeights"}]

Dimensions[exe1data]

ExampleData[{"Statistics","AnimalWeights"},"ColumnDescriptions"]

💡 Create a bubble chart which uses the percentage of brain weight in the body as bubble size.
💡 Show animal image on tooltip. (Labeling)

{animals, weight,brain}=Transpose[exe1data];

findimages[names_]:=Module[{result},
Table[
result=WolframAlpha[ToString[i]<>" Image",{{"Image:SpeciesData",1},"Content"}];
If[Head[ result] === Missing,
result=WolframAlpha[ToString[i]<>" Image",{{"Image:DinosaurData",1},"Content"}],
result],
{i,names}]]
animalimages=findimages[animals];
First[animalimages]

BubbleChart

exe1bubbles=Transpose[{weight,brain,N[brain/weight/1000]}];

exe1bubbles=MapThread[Tooltip[#1,Column[#2]]&,{exe1bubbles,Transpose[{Style[#,16]&/@animals,animalimages}]}];

exe1title=ExampleData[{"Statistics","AnimalWeights"},"Description"];
{exe1xlabel,exe1ylabel}=Rest[ExampleData[{"Statistics","AnimalWeights"},"ColumnDescriptions"]];
BubbleChart[exe1bubbles,
(*label*)
PlotLabel->exe1title,FrameLabel->{exe1xlabel,exe1ylabel},
(*style*)
ColorFunction->"Rainbow",
(*others*)
ScalingFunctions->{"Log","Log"},ImageSize->Large,globaloptions[12]]

Visualize Data - Complicated Data

d111=ExampleData[{"Statistics","USCars1993"}]/."Chrylser"->"Chrysler";

Dimensions[d111]

d111[[1]]

categories=ExampleData[{"Statistics","USCars1993"},"ColumnDescriptions"]

💡 Count how many cars each manufacturer has made in different size. (Data processing)
💡 Create a stacked bar chart which groups the data by car size. (ChartLayout)
💡 Use different color to separate car manufacturers. (ChartStyle)

dataBySize=GatherBy[d111,#[[3]]&];

First[dataBySize]

makers=DeleteDuplicates[d111[[All,1]]]

Count how many cars were made by each maker in each size group

Count[dataBySize[[1]],{#,___}]&/@makers

chartdata[names_]:=Table[Count[dataBySize[[i]],{#,___}]&/@names,{i,Length[dataBySize]}]

Chart the data

BarChart[chartdata[makers],ChartStyle->10,AspectRatio->1,ChartLayout->"Stacked",globaloptions[14]]

Complicated Data - Labeling

💡 Show group labels on axis and individual labels on tooltip. (ChartLabels)
💡 Show title of the chart. (PlotLabel)

allSizes=Part[First[#],3]&/@dataBySize

makers

BarChart[chartdata[makers],
(*style*)
ChartStyle->10,
(*layout*)
ChartLayout->"Stacked",AspectRatio->1,
(*labeling*)
ChartLabels->{allSizes,Placed[Style[#,16]&/@makers,Tooltip]},PlotLabel->Style["US Cars 1993",25],
(*others*)
globaloptions[12]]

Complicated Data - Meta Data

💡 Use car information as meta data. (Meta data)
💡 Make each data as a button to set the car manufacturer as the current selection. (Button wrapper)
💡 Show a sub-plot with only the cars that are made by current selected manufacturer. (Dynamic interactivity)

Add meta data

chartdata[names_]:=Table[(Count[dataBySize[[i]],{#,___}]->Cases[dataBySize[[i]],d:{#,___}:>d])&/@names,{i,Length[dataBySize]}]

First[chartdata[makers]]

Create buttons

Clear[currentbrand]

buttondata[count_->meta_]:=Button[count->meta,currentbrand=meta[[1,1]]]

finaldata=Map[buttondata,chartdata[makers],{2}];

Chart the data

mainchart=BarChart[finaldata,
ChartStyle->10,AspectRatio->1,
ChartLayout->"Stacked",ImageSize->350,
ChartLabels->{allSizes,Placed[Style[#,16]&/@makers,Tooltip]},PlotLabel->Style["US Cars 1993",25],
globaloptions[14]];

subchart=Dynamic[
BarChart[chartdata[{currentbrand}],
ChartStyle->{16,None},
AspectRatio->1/3,ImageSize->350,
ChartLabels->{allSizes,None},PlotLabel->ToString[currentbrand],
globaloptions[14]]];

Column[{mainchart,subchart}]

Complicated Data - ChartElementFunction

💡 Create a chart element function that can show each car as a marker. (ChartElementFunction)
💡 The size of cars can be compared with each other. (Data processing)
💡 Creat a button for each car marker to call for detail information. (Dynamic interactivity)

Create a rich ChartElementFunction

rescalewidth[v_]:=Rescale[v,Through[{Min,Max}[d111[[All,21]]]],{0.3,0.6}]

rescalelength[v_]:=Rescale[v,Through[{Min,Max}[d111[[All,19]]]],{0.2,0.8}]

Clear[buttoncarbar];buttoncarbar[{{x0_,x1_},{y0_,y1_}},v_,meta_]:=Module[{points,prim,size,model,linewidth,linelength,cardata,scalingfunction,currentdynamicdata},
cardata=Part[meta,1,All];
(*tooltip*)
model=Part[cardata,All,1;;2];
size=Part[cardata,All,3];
(*rescale car dimensions*)
linewidth=Part[cardata,All,21];
linelength=Part[cardata,All,19];
{linewidth,linelength}=MapThread[#1/@#2&,{{rescalewidth,rescalelength},{linewidth,linelength}}];
linelength=Rescale[#,{0,1},{0,x1-x0}]&/@linelength;
(*number of cars to be drawn*)
points={(x0+x1)/2,#}&/@(Mean/@Partition[Range[y0,Round[y1]],2,1]);
(*create a list of button rectangle*)
prim=MapThread[
Button[Rectangle[#1+{-#3/2,-#2/2},#1+{#3/2,#2/2}],{dynamicSize=#4;dynamicModelData=#5}]&,{points,linewidth, linelength,size,cardata}];
{{Opacity[0.3],Rectangle[{x0,y0},{x1,y1}]},MapThread[Tooltip[#1,Style[#2,16]]&,{prim,Row[#,": "]&/@model}]
}
]

Chart the data

Row[{Style["A",16],Style["A",16]}]

mainchart=BarChart[finaldata,
ChartStyle->10,AspectRatio->1,
ChartElementFunction->buttoncarbar,
ChartLayout->"Stacked",ImageSize->350,Axes->{True,False},
ChartLabels->{allSizes,Placed[Style[#,16]&/@makers,Tooltip]},PlotLabel->Style["US Cars 1993",25],
globaloptions[14]];

subchart=Dynamic[
BarChart[chartdata[{currentbrand}],
ChartElementFunction->buttoncarbar,
ChartStyle->{16,None},AspectRatio->1/3,
PlotRange->{All,{-1,2.5}},ImageSize->350,Axes->{True,False},
ChartLabels->{Placed[allSizes,Axis],None},PlotLabel->ToString[currentbrand],globaloptions[14]]];

Column[{mainchart,subchart}]

Dynamic[{dynamicSize,dynamicModelData}]

Complicated Data - Comparison

💡 Find out numerical spec column. (Data processing)
💡 Create a distribution chart of each column. (DistributionChart)

Group data

Clear[databysize];databysize[size_]:=Cases[d111,d:{_,_,size,__}:>d]

databysize["Small"]

Find out numeric spec

numericpos=Position[NumericQ/@d111[[1]],True]

numericdescription=Extract[categories,numericpos]

dataretrieve[data_,column_String]:=Part[Transpose[data],First@Flatten[Position[categories,column]]]

dataretrieve[databysize["Small"],"Minimum Price (in $1,000): price for a basic version"]

Chart the data

DistributionChart[dataretrieve[databysize["Small"],"Minimum Price (in $1,000): price for a basic version"],BarOrigin->Left,BarSpacing->0.3,ImageSize->400,PerformanceGoal->"Speed"]

Complicated Data - Detail

💡 Create a chart element function that can mark the selection.

Clear[linedensity];
linedensity[c:{{x0_,x1_},{y0_,y1_}},v_,meta__]:=Module[{marker},
marker=If[!MatchQ[meta,{_?NumericQ}],Missing[],Last[meta]];
{ChartElementData["LineDensity"][c,v,_],
(*{White,Line[{{#1,y0},{#1,y1}}&/@v]},*)
If[marker===Missing[],{},
{Red,Tooltip[Line[{{marker,y0},{marker,y1}}],marker]}
]}
]

distdata=dataretrieve[databysize["Small"],"Minimum Price (in $1,000): price for a basic version"]->12.9

DistributionChart[
(*data*)
distdata,
(*layout*)
BarOrigin->Left,BarSpacing->0.3,
(*style*)
ChartStyle->Darker@Green,ChartBaseStyle->{AbsoluteThickness[3],EdgeForm[]},
(*Elements*)
ChartElementFunction->linedensity,
(*labeling*)
PlotLabel->"Minimum Price (in $1,000): price for a basic version",
(*others*)
ImageSize->400,AspectRatio->1/20,PerformanceGoal->"Speed",globaloptions[14]]

Complicated Data - Detail

💡 Group car information into Prices, Performances and Specs.
💡 Create a tab view of those three groups of information.

Group car info

grouppos[list_]:=(#+{1,0})&/@Partition[FoldList[Plus,0,list],2,1]

groupedcolumn=Take[numericdescription,#]&/@grouppos[{3,7,9}]

Create a list of distribution charts for each group

Clear[carspec]

d111[[1]]

carspec[size_,list_]:=
Module[{data},
(*scrub data*)
data=Take[Extract[list,numericpos],#]&/@grouppos[{3,7,9}];
(*create a list of DistributionChart for specs*)
Table[Column[
MapThread[
DistributionChart[
(*data*)dataretrieve[databysize[size],#1]->#2,
(*layout*)BarOrigin->Left,BarSpacing->0.3,
(*style*)ChartStyle->Darker@Green,ChartBaseStyle->{AbsoluteThickness[3],EdgeForm[]},
(*Elements*)ChartElementFunction->linedensity,
(*others*)ImageSize->400,AspectRatio->1/20,PerformanceGoal->"Speed",globaloptions[10],PlotLabel->#1]&,
{groupedcolumn[[i]],data[[i]]}],
(*alignment and spacing for Column*)
Left,6/Length[groupedcolumn[[i]]]
]
,{i,Length[groupedcolumn]}]
];

tabdata=carspec["Small",d111[[1]]]

Create a tab view

Clear[tabdetail]

tabdetail[size_,data_]:=TabView[MapThread[(#1->#2)&,{{"Prices","Vehicle Performances", "Specs"},carspec[size,data]}],2]

tabdetail["Small",d111[[1]]]

Complicated Data - Combine Everything

Panel[
Row[{
(*mainchart and subchart*)
Column[{mainchart,subchart}],
Dynamic[Labeled[
(*tabview of car detail*)
tabdetail[dynamicSize,dynamicModelData],
(*use selected car model as label*)
Style[Row[Take[dynamicModelData,2],": "],20],Top]]}]]

References

Mathematica 8 Documentations

Information Graphics - Robert L. Harris

The Grammar of Graphics - Leland Wikinson