I'm very new to Mathematica, and I'm sure my question is trivial.

Suppose a have a (long) list whose elements are of the form {{x, y}, n}, where x and y are Real, and n is an Integer. I'd like to plot the convex hull of the corresponding points {x, y} in the plane. Moreover, for each point {x, y} that is a vertex of the convex hull should appear with its corresponding label n. Finally I'd like the coordinate axes to be displayed with the plot.



Contrive some data.

With[{n = 42}, 
  data = Transpose[{RandomReal[1., {n, 2}], RandomSample[Range[100], n]}]];
Short[data, 3]

{{{0.425905, 0.391023}, 51}, <<40>>, {{0.359445, 0.00772549},24}}

Separating the points from the labels.

pts = data[[All, 1]];
lbls = data[[All, 2]];

Generating the convex hull.

ch = ConvexHullMesh[pts];

Extracting the boundary point labels.

bpts = MeshCoordinates[RegionBoundary[ch]];
blbls = Extract[lbls, Flatten[Position[pts, #] & /@ bpts, 1]]

{94, 36, 21, 1, 88, 2, 14, 24}

Making the labels into graphics elements.

labels = MapThread[Text[#1, #2] &, {blbls, bpts}];

Showing the combined graphics.

Show[ch, Graphics[{Point[pts], labels}], Axes -> True]


Of course, the labels can be fancied up by using Inset rather than Text

labels = 
      Graphics[{FaceForm[White], EdgeForm[Black], Disk[], Text[Style[#1, 9]]}],
      #2, Automatic, Scaled[.0475]] &, 
    {blbls, bpts}];
Show[ch, Graphics[{Point[pts], labels}], 
  Method -> {"AxesInFront" -> False}, Axes -> True]


  • $\begingroup$ My label is not necessarily the index. $\endgroup$ Jul 4 '17 at 23:35
  • $\begingroup$ @JairoBochi. I have revised my answer in a way that I hope you find better attuned to your needs. $\endgroup$
    – m_goldberg
    Jul 5 '17 at 2:19
  • $\begingroup$ @MichaelE2 Right, I should have provided sample data, sorry. $\endgroup$ Jul 5 '17 at 22:39
  • $\begingroup$ @JairoBochi. Your nagging me about the indices motivated me to improve my answer, so I'm glad you did it. $\endgroup$
    – m_goldberg
    Jul 6 '17 at 5:02
data = Transpose[{RandomReal[1, {10, 2}], RandomInteger[100, 10]}];

xy = data[[All,1]];
labels = data[[All,2]]; 
rule = Thread[xy -> labels];

ConvexHullMesh[xy,  Prolog -> Point[xy], Frame -> True,
   MeshCellStyle -> {2 -> Opacity[0.5, LightBlue]},
   MeshCellShapeFunction -> {0 -> ({Opacity[1, Yellow], Disk[#, .05], 
     Text[Style[# /. rule, Red, 16], #]} &)}]

Mathematica graphics


labeled = Labeled[#, # /. rule] & /@ MeshCoordinates[ConvexHullMesh[xy]];

Show[ConvexHullMesh[xy], ListPlot[labeled], Graphics[Point[xy]], Frame ->True]

Mathematica graphics

  • $\begingroup$ I understand that you generate the plot from two lists, "xy" and "labels". But my list is given of the form "data". So I would need to extract "xy" and "labels" from data to have a working solution... $\endgroup$ Jul 4 '17 at 23:48
  • $\begingroup$ @JairoBochi, please see the updated version of my answer. $\endgroup$
    – kglr
    Jul 4 '17 at 23:52

There are potential shortcomings in using Rule to map points to labels, which I have occasionally come across. The purpose in creating a NearestFunction using Nearest below to map coordinates {x, y} to a label is overcome these shortcoming, at only a small expense of computational time. Given the general nature of the question, this seemed the best approach.

Often in an application, one might arrive at coordinates in various ways. If we're using floating-point numbers, rounding error might make simple rules of the form {x, y} -> label fail, because the coordinates {x, y} of the rule might not exactly match the computed coordinates in the figure. Or one might start with exact coordinates, which at some point might be numericized (converted to floating point), and again the replacement rule will fail. Nearest takes care of those issues, unless rounding error is so great that one computed point ends up closer to a different point than the intended one (in such a case, the problem is completely different, a numerics one). Whether or not to numericized the points with N[] depends on whether data is already numericized; it's not strictly necessary in either case, but it should speed up nf, which might be an issue with a very long list data.

points = data[[All, 1]];
nf = Nearest[N@points -> data[[All, 2]]]; (* map coordinates to labels in data *)
hull = ConvexHullMesh[data[[All, 1]]];

With[{coords = MeshCoordinates[hull]},
   MeshCells[hull, 2],
   MeshCellLabel -> Table[{0, i} -> First@nf[coords[[i]]], {i, Length@coords}]
  Axes -> True] (* or Frame -> True *)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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