Set-up
The data in this MSE question Finding the best way to visualize rather complicated data presents a good case for visualization with Chernoff faces. For that data, actually, the Chernoff faces work "out of the box" pretty well!
Here is the data:
data = {{7.5, 12.45, 12.45, 12.75, 12.75, 12.25, 12.25, 12.53, 12.53},
{8.5, 12.22, 12.22, 12.23, 12.23, 13, 13, 12.54, 12.54},
{9.5, 11.58, 11.53, 12.75, 13.48, 12.39, 12.52, 12.17, 13.56},
{10.5, 11.76, 11.82, 12.97, 13.55, 12.15, 11.88, 13.07, 12.79},
{11.5, 11.18, 11.85, 13.27, 13.02, 12.32, 13, 12.72, 12.63},
{12.5, 11.04, 11.61, 13.70, 14.17, 12.77, 12.79, 12.13, 11.78},
{13.5, 11.64, 10.68, 13.52, 14.03, 13.14, 13.21, 11.64, 12.13},
{14.5, 12.04, 12.12, 13.23, 13.67, 12.58, 13.02, 11.26, 12.05},
{15.5, 14.10, 14, 11.65, 11.68, 12.17, 12.36, 12.19, 11.85},
{16.5, 14.85, 14.54, 10.94, 11.62, 12.17, 11.72, 11.84, 12.31},
{17.5, 15.78, 15.78, 10.62, 10.62, 11.72, 11.72, 11.88, 11.88},
{18.5, 17.18, 17.18, 9.53, 9.53, 11.66, 11.66, 11.63, 11.63}};
The data in the question presents a good case for visualization with Chernoff faces. For that data, actually, the Chernoff faces work "out of the box" pretty well!
Make faces
Load Chernoff faces plotting package:
Import["https://raw.githubusercontent.com/antononcube/\
MathematicaForPrediction/master/ChernoffFaces.m"]
As it is explained in the question the first element of each row is a coordinate and the rest of the elements are percentages:
Total@*Rest /@ data
(* {99.96, 99.98, 99.98, 99.99, 99.99, 99.99, 99.99, 99.97, 100., 99.99, 100., 100.} *)
Using that data property we apply Chernoff faces in the following way.
The Chernoff faces are applied after each non-coordinate column is rescaled into [0,1].
The faces are colored according to how close the values of each row of data[[All,2;;-1]]
are to the Normal Distribution.
The rows with close to normally distributed percentages have faces that are more yellow and more smiling.
Here is a grid of the obtained Chernoff faces :
facesGrid =
Grid[ArrayReshape[#, {3, 4}, ""], Dividers -> All,
Alignment -> {Left, Top}] &@
MapThread[
(asc =
AssociationThread[
Take[Keys@ChernoffFace["FacePartsProperties"],
Length[#3] + 1] -> Append[#3, #4]];
Column[{
Row[{"row:", #1, ", x=", #2}],
ChernoffFace[
Join[asc, <|"FaceColor" -> Blend[{White, Lighter[Yellow]}, #4]|>],
ImageSize -> 150, AspectRatio -> Automatic]}]) &
, {Range[Length[data]], First /@ data,
Transpose[Rescale /@ Transpose[Rest /@ data]],
PearsonChiSquareTest[Standardize[Rest[#]],
NormalDistribution[0, 1]] & /@ data}]
Here are all face properties used in the Chernoff faces above:
props =
Take[Keys@ChernoffFace["FacePartsProperties"], Length[First@data]]
(* {"FaceLength", "ForheadShape", "EyesVerticalPosition", "EyeSize",\
"EyeSlant", "LeftEyebrowSlant", "LeftIris", "NoseLength", \
"MouthSmile"} *)
Discernibility and classification
The main motivation behind the introduction and use of Chernoff faces is that they would provide inherent visual discernibility and classification. With this data that claim is fulfilled.
We can easily see that the face of row 7 is a clear outlier which can be explained by looking at the columns "ForheadShape" and "EyeSlant" of the table of the data:
Also we can easily see from the faces that row 5 has rows 3, 4, 6 as nearest neighbors. This can be demonstrated with the following commands:
nf = Nearest[data[[All, 2 ;; -1]] -> Automatic];
nf[data[[5, 2 ;; -1]], 5]
(* {5, 4, 3, 6, 1} *)
(Of course other nearest neighbors can be easily found.)