Labeling in Visualization

Min-Hsuan Peng, Wolfram

Join the Conversation #WolframTechConf

Outline

Where are labels used in visualizations?

Frameworks of labeling in Wolfram Language

Graphics low level

Visualization

Types of Implementation

List plots

Sampled plots

Charts & more

A world without labels

ListLogLogPlot[(EntityClass["James Bond", EntityClass["Movie","JamesBondFranchise"], "EntityClass[\"Movie\", \"JamesBondFranchise\"]", "movies"])[{(EntityProperty["production budget", EntityProperty["Movie","ProductionBudget"], "EntityProperty[\"Movie\", \"ProductionBudget\"]"]),(EntityProperty["US box office total receipts", EntityProperty["Movie","DomesticBoxOfficeGross"], "EntityProperty[\"Movie\", \"DomesticBoxOfficeGross\"]"])}],PlotStyle->PointSize[Large],LabelStyle->20,ImageSize->850,AxesLabel->{"budget","US box office"},PlotRangePadding->Scaled[.1],PlotLabel->(EntityClass["James Bond", EntityClass["Movie","JamesBondFranchise"], "EntityClass[\"Movie\", \"JamesBondFranchise\"]", "movies"]),GridLines->Automatic,Epilog->InfiniteLine[{{0,0},{1,1}}]]

ListLogLogPlot[poke=LinguisticAssistant[{"Speed","Weight"}],AxesLabel->{"Speed","Weight"},LabelStyle->20,ImageSize->800,PlotLabel->"Pokemon Stats",Epilog->{InfiniteLine[{{0,Log@First@Mean[poke[[All,2]]]},{1,Log@First@Mean[poke[[All,2]]]}}],InfiniteLine[{{Log@Mean[poke[[All,1]]],0},{Log@Mean[poke[[All,1]]],1}}]}]

Frameworks

Low level

Text[expr, coords, offset]

labelposition={2,3};labeloffset={-1,-1};

Graphics[{PointSize[0.02],Point[labelposition],Text[Style["label",20],labelposition,labeloffset]},Frame->True,GridLines->Automatic,GridLinesStyle->Directive[Dashed, LightGray,Thickness[0.0005]]]

Inset[object, pos, objectpos]

objectpos={0.5,0.5};

label

label=Graphics[{{Red,PointSize[0.03],Point[objectpos]},Circle[]},Frame->True]

Inset[label,labelposition,objectpos]

Graphics[{PointSize[0.03],Point[labelposition],Inset[label,labelposition,objectpos]},Frame->True,GridLines->Automatic,GridLinesStyle->Directive[Dashed, LightGray,Thickness[0.0005]]]

label positions

DynamicModule[{r={5,5},size={30,10}},Manipulate[Graphics[{
(*Rectangle[{0,0}-r/2,{0,0}+r/2]*)Disk[{0,0},If[!fivetoeight, Sqrt[2],1]r/2],FaceForm[],EdgeForm[Black],
If[!fivetoeight,{(*1*)Rectangle[{r[[1]]/2,r[[2]]/2},{r[[1]]/2+size[[1]],r[[2]]/2+size[[2]]}],Text[Style[1,18],{r[[1]]/2+size[[1]]/2,r[[2]]/2+size[[2]]/2}],
(*2*)Rectangle[{-r[[1]]/2,r[[2]]/2},{-r[[1]]/2-size[[1]],r[[2]]/2+size[[2]]}],
Text[Style[2,18],{-r[[1]]/2-size[[1]]/2,r[[2]]/2+size[[2]]/2}],
(*3*)Rectangle[{-r[[1]]/2,-r[[2]]/2},{-r[[1]]/2-size[[1]],-r[[2]]/2-size[[2]]}],
Text[Style[3,18],{-r[[1]]/2-size[[1]]/2,-r[[2]]/2-size[[2]]/2}],
(*4*)Rectangle[{r[[1]]/2,-r[[2]]/2},{r[[1]]/2+size[[1]],-r[[2]]/2-size[[2]]}],
Text[Style[4,18],{r[[1]]/2+size[[1]]/2,-r[[2]]/2-size[[2]]/2}]}],
If[fivetoeight,{
(*5*)Rectangle[{r[[1]]/2,-size[[2]]/2},{r[[1]]/2+size[[1]],size[[2]]/2}],
Text[Style[5,18],{r[[1]]/2+size[[1]]/2,0}],
(*6*)Rectangle[{-size[[1]]/2,r[[2]]/2},{size[[1]]/2,r[[2]]/2+size[[2]]}],
Text[Style[6,18],{0,r[[2]]/2+size[[2]]/2}],
(*7*)Rectangle[{-r[[1]]/2,-size[[2]]/2},{-r[[1]]/2-size[[1]],size[[2]]/2}],
Text[Style[7,18],{-r[[1]]/2-size[[1]]/2,0}],
(*8*)Rectangle[{-size[[1]]/2,-r[[2]]/2},{size[[1]]/2,-r[[2]]/2-size[[2]]}],
Text[Style[8,18],{0,-r[[2]]/2-size[[2]]/2}]
}
]
}

]
,
{{r,{5,5}},{1,1},{10,10}},{{fivetoeight,False,"5-8"},{False,True}}]]

Data

datalist -> labellist

ListPlot[RandomReal[1,30]->RandomColor[30]]

<|"key1" -> dataset1, "key2" -> dataset2, ...|>

ListLinePlot[<|"label1"->Accumulate@RandomReal[{-1,1},50],"label2"->Accumulate@RandomReal[{-1,1},50]|>,PlotLabels->Automatic,PlotLegends->None]

Wrappers

Labeled[data,label,pos]
Callout[data,label,pos,apos,...]

Plot[{Sin[x],Callout[Sin[x-Pi/4],"(π/4) delay",pos=Automatic,apos=Automatic]},{x,0,4Pi},PlotStyle->{Automatic,Dashed}]

Example

ClearAll[f,points,labels];

f[x_]:=x^3+2x+2

points[h_]:={PointSize[Large],Point[{1,f[1]}],Point[{1+h,f[1+h]}]}

labels[h_]:={Text[Style["(1, f(1))",16],{1,f[1]},{1,-1.5}], Text[Style["(1 + h, f(1 + h))",16],{1+h,f[1+h]},{-1.1,1}]}

Plot[{f[x],f[1]+(x-1) (f[2]-f[1])},{x,0,3},PlotRange->{{0,3},{0,20}},Epilog->{points[1],labels[1]}]

Use Labeled wrapper

Plot[{f[x],Labeled[f[1]+(x-1) (f[2]-f[1]),{"(1, f(1))","(1 + h, f(1 + h))"},{{1},{2}}]},{x,0,3},PlotRange->{{0,3},{0,20}},Epilog->{points[1]}]

Use Callout

Plot[{f[x],Callout[f[1]+(x-1) (f[2]-f[1]),{"(1, f(1))","(1 + h, f(1 + h))"},{{1},{{2,Below},{0,0.5}}}]},{x,0,3},PlotRange->{{0,3},{0,20}},Epilog->{points[1]}]

Options

PlotLabels -> {lbl1, lbl2, ...}
PlotLabels -> Placed/Callout[...]

ChartLabels -> {lbl1, lbl2, ...}

VertexLabels -> {(Subscript[v, 1]) -> (Subscript[lbl, 1]), (Subscript[v, 2]) -> (Subscript[lbl, 2]), ...}

LabelingFunction -> Function[{data, index, meta}, label = makelabel[data, index, meta]; Placed[label, pos]]
LabelingFunction -> Function[{data, index, meta}, label = makelabel[data, index, meta]; Callout[label, pos]]

Plot[{Sin[x],Cos[x]},{x,0,10},PlotLabels->"Expressions"]

Plot[{Sin[x],Cos[x]},{x,0,10},PlotLabels->Placed["Expressions",{Scaled[0.25],Scaled[0.5]}]]

Callout Appearance

Callout[data,"label",
LabelStyle->40,
LeaderSize->{{leaderlength,angle,gap},{necklength,angle}},
Appearance->appearance,
CalloutMarker->marker,
Background->backgroundstyle,
FrameMargins->framemargins,
RoundingRadius->radius,
CalloutStyle->Thickness[0.01]]

DynamicModule[{autonangle=True,angle=45,llen=25,nlen=10,nangle=45,gap=5,app="Frame",marker=None,bgrd=White,radius=0,margin=5},Row[{Framed@Dynamic[Graphics[{PointSize[Large],Point[{0,0}],
Charting`labelMaker[Style["label",40],,{{0,0}},{{-1,1},{-1,1}},LeaderSize->{{llen,angle Degree,gap},{nlen,If[autonangle,Automatic,nangle Degree]}},Appearance->app,CalloutMarker->marker,CalloutStyle->Thickness[0.01],Background->bgrd,RoundingRadius->radius,FrameMargins->margin]
},PlotRange->0.5,ImageSize->400]],Column[{
Labeled[Slider[Dynamic[angle],{0,360}],"angle",Left],
Labeled[Slider[Dynamic[llen],{10,60}],"leader length",Left],
Labeled[Slider[Dynamic[nlen],{0,30}],"neck length",Left],
Labeled[Slider[Dynamic[gap],{0,30}],"leader gap",Left],
Labeled[Slider[Dynamic[radius],{0,30}],"rounding radius",Left],
Labeled[Slider[Dynamic[margin],{0,30}],"frame margins",Left],
Labeled[ColorSlider[Dynamic[bgrd]],"background",Left],
Row@{Labeled[Checkbox[Dynamic[autonangle]],"auto neck angle",Left],
Labeled[Slider[Dynamic[nangle],{0,360},Enabled->Dynamic@Not@autonangle],"neck angle",Left]},
Labeled[PopupMenu[Dynamic[app],{"CurvedLeader","Balloon","LineBalloon","Corners","Frame","Line"}],"appearance",Left],Labeled[PopupMenu[Dynamic[marker],{None,"CirclePoint","Circle","BoxPoint", "Box","Star", "OpenStar","Arrow", "OpenArrow"}],"marker",Left]}]
},"   "]]

Plots

multiple curves

Plot[Evaluate@Table[BesselJ[n,x],{n,1,10}],{x,0,10},PlotLabels->Automatic]

automatic positions

Plot[{Exp[x],Log[x],x},{x,-3,3},PlotLabels->Placed[Automatic,Above],
PlotRange->3,PlotRangePadding->Scaled[0.05], AspectRatio->Automatic]

Plot[{Exp[x],Log[x],x},{x,-3,3},PlotLabels->Placed[Automatic,Scaled[0.85]],
PlotRange->3,PlotRangePadding->Scaled[0.05], AspectRatio->Automatic]

multi labels

Plot[{x Sin[1/x],Abs[x],-Abs[x]}, {x,-1/2,1/2}, PlotStyle->{Red,Directive[Dashed,Blue],Directive[Dashed,Purple]},
PlotLabels->{
x Sin[1/x],
Callout[{Abs[x],Abs[x]},{{Scaled[0.25],Above},{Scaled[0.75],Above}}],Callout[{-Abs[x],-Abs[x]},{{Scaled[0.2],Below},{Scaled[0.8],Below}}]
}
]

feature labeling

Plot[Callout[1/(x^3-x+1),x^3-x+1==0,{-1.5,2}],{x,-2,2},Exclusions->{x^3-x+1==0},ExclusionsStyle->Blue]

more...

Plot[Abs[Exp[I ω]/(1+(ω^2))],{ω,-10,10},PlotLabels->Placed[Automatic,Above],
PlotRangePadding->{Automatic,{None,Scaled[.2]}},Filling->Bottom]

Manipulate[Plot[Callout[1/(x^3-x+1),"label",pos],{x,-1,2},Epilog->Point[pos]],{{pos,{0.5,1}},{-1,-1},{2,2}}]

Manipulate[Plot[Callout[1/(x^3-x+1),"label",{1,2.5},pos],{x,-2,2},Exclusions->{x^3-x+1==0},ExclusionsStyle->Blue,Epilog->Point[{1,2.5}]],{{pos,0.5},-2,2}]

labels=Flatten[Last/@Quiet@Through[{FindMinimum,FindMaximum}[Sin[x]+x/2,{x,5,2,10}]]]

Plot[Sin[x]+x/2,{x,2,10},PlotLabels->Placed[labels,{Below,Above}]]

ListPlots

multiple curves

ticks={"AAPL","AMZN","GOOG"};

DateListPlot[Table[FinancialData[tick,{{2010,1,1},{2010,12,31}}],{tick,ticks}],PlotLabels->ticks]

point feature labeling problem

James Bond movie example

ListLogLogPlot[Callout[#[[1;;2]],#[[3]]]&/@(EntityClass["James Bond", EntityClass["Movie","JamesBondFranchise"], "EntityClass[\"Movie\", \"JamesBondFranchise\"]", "movies"])[{(EntityProperty["production budget", EntityProperty["Movie","ProductionBudget"], "EntityProperty[\"Movie\", \"ProductionBudget\"]"]),(EntityProperty["US box office total receipts", EntityProperty["Movie","DomesticBoxOfficeGross"], "EntityProperty[\"Movie\", \"DomesticBoxOfficeGross\"]"]),(EntityProperty["title", EntityProperty["Movie","Name"], "EntityProperty[\"Movie\", \"Name\"]"])}],PlotStyle->PointSize[Medium],LabelStyle->12,ImageSize->850,AxesLabel->{(EntityProperty["production budget", EntityProperty["Movie","ProductionBudget"], "EntityProperty[\"Movie\", \"ProductionBudget\"]"]),(EntityProperty["US box office total receipts", EntityProperty["Movie","DomesticBoxOfficeGross"], "EntityProperty[\"Movie\", \"DomesticBoxOfficeGross\"]"])},PlotRangePadding->Scaled[.1],GridLines->Automatic]

use images as labels

ListLogLogPlot[#[[1;;2]]->Tooltip[If[ImageQ[#[[3]]],ImageResize[#[[3]],30],#[[3]]],Magnify[#[[4]],2]]&/@(EntityClass["James Bond", EntityClass["Movie","JamesBondFranchise"], "EntityClass[\"Movie\", \"JamesBondFranchise\"]", "movies"])[{(EntityProperty["production budget", EntityProperty["Movie","ProductionBudget"], "EntityProperty[\"Movie\", \"ProductionBudget\"]"]),(EntityProperty["US box office total receipts", EntityProperty["Movie","DomesticBoxOfficeGross"], "EntityProperty[\"Movie\", \"DomesticBoxOfficeGross\"]"]),(EntityProperty["image", EntityProperty["Movie","Image"], "EntityProperty[\"Movie\", \"Image\"]"]),(EntityProperty["title", EntityProperty["Movie","Name"], "EntityProperty[\"Movie\", \"Name\"]"])}],PlotStyle->PointSize[Medium],ImageSize->850,AxesLabel->{(EntityProperty["production budget", EntityProperty["Movie","ProductionBudget"], "EntityProperty[\"Movie\", \"ProductionBudget\"]"]),(EntityProperty["US box office total receipts", EntityProperty["Movie","DomesticBoxOfficeGross"], "EntityProperty[\"Movie\", \"DomesticBoxOfficeGross\"]"])},PlotRangePadding->Scaled[.1],GridLines->Automatic]

Pokemon example

ListLogLogPlot[#[[1;;2]]->Tooltip[#[[3]],#[[4]]]&/@(LinguisticAssistant[{"Speed","Weight","Name","Image"}]),AxesLabel->{"Speed","Weight"},LabelStyle->10,AxesStyle->20,ImageSize->800]

Charts and more

Categorical Charts

eu=LinguisticAssistant["Name"];

eugdp=LinguisticAssistant["GDP"];

BarChart[eugdp,ChartLabels->Callout[eu]]

Enhance the plot with various label size

rescaleSize[v_]:=Rescale[Log@v,Log@QuantityMagnitude@MinMax[eugdp],{8,20}]

BarChart[eugdp,LabelingFunction->(Callout[Style[eu[[Last@#2]],rescaleSize[#1]],Appearance->"Line"]&),ScalingFunctions->"Log",AxesLabel->{None,"GDP"}]

Use PieChart

PieChart[eugdp,LabelingFunction->(Callout[Style[eu[[Last@#2]],rescaleSize[#1]],Appearance->"Line"]&),AxesLabel->{None,"GDP"},PlotRange->All]

World major petroleum production countries labeled in GeoBubbleChart

l=(EntityClass["all countries, dependencies, and territories", EntityClass["Country","Countries"], "EntityClass[\"Country\", \"Countries\"]", "countries"])["Position"];
c=(EntityClass["all countries, dependencies, and territories", EntityClass["Country","Countries"], "EntityClass[\"Country\", \"Countries\"]", "countries"])["Name"];
oil=(EntityClass["all countries, dependencies, and territories", EntityClass["Country","Countries"], "EntityClass[\"Country\", \"Countries\"]", "countries"])[];
major=Quantile[QuantityMagnitude@DeleteMissing[oil],0.75];
oildata=Select[Select[Transpose[{l,oil,c}],Head@#[[2]]=!=Missing&],QuantityMagnitude[#[[2]]]>major&];
GeoBubbleChart[oildata[[All,1;;2]],ColorFunction->"Rainbow",LabelingFunction->(Callout[oildata[[All,3]][[#2[[2]]]],LabelStyle->13]&),ImageSize->1200,PlotTheme->"Marketing",GeoProjection->"Albers"]

Init

$PlotTheme={"Vibrant","Presentation","Large","ThickLine"}
Internal`SetValueNoTrack[Charting`rasterizeLabels,True]

Graphics[{Inset[Magnify[Framed["callout label"],2],{0,0},{Left,Bottom},20],
Text[Magnify[{0,0},2],{0,0},{1,1}],
Text[Magnify[{1,0},2],{8.5,0},{0,1}],
Text[Magnify[{0,1},2],{0,3},{1,0}],
Text[Magnify[{1,1},2],{8.5,3},{0,0}]
}]

Graphics[{Inset[Magnify[Framed["expr"],2],{0,0},{Left,Bottom},20],
Text[Magnify[{0,0},2],{0,0},{1,1}],
Text[Magnify[{1,0},2],{4.5,0},{0,1}],
Text[Magnify[{0,1},2],{0,3},{1,0}],
Text[Magnify[{1,1},2],{4.5,3},{0,0}]
}]

Graphics[{Inset[Magnify[Framed["expr"],2],{0,0},{Left,Bottom},20],
Text[Magnify[{-1,-1},2],{0,0},{1,1}],
Text[Magnify[{1,-1},2],{5,0},{0,1}],
Text[Magnify[{-1,1},2],{0,3},{1,0}],
Text[Magnify[{1,1},2],{5,3},{0,0}]
}]