The following steps is one method to achieve your result. I used Wolfram Language but the method can be applied by any language with the right libraries.
FindClusters
for category A data (dataA
),
- calculate the
ConvexHullMesh
for each of these clusters,
- for each point in category B (
dataB
)
calculate the
RegionDistance
to each of the category A hulls,
- and
Pick
the
dataB
points by their nearest category A hull.
We can collect related 3D example data from "AdministrativeDivision"
Entity
object properties.
dataA =
Select[FreeQ[_Missing]]@
EntityValue[
EntityClass["AdministrativeDivision", {"ParentRegion" -> Entity["Country", "UnitedStates"]}]
, {"GiniIndex", "TotalVotingRate", "HomeOwnershipRate"}];
First@dataA
{0.4776, 56.3712%, 70.7%}
I used FindClusters
with the "MeanShift"
method to cluster. Two clusters were found.
clusters = FindClusters[dataA, Method -> "MeanShift"];
Length@clusters
2
The list of ConvexHullMesh
for each cluster is obtained by
hulls = ConvexHullMesh /@ clusters

These can be visualised with their internal points by combining aListPointPlot3D
of clusters
with a Graphics3D
of hulls
(with low Opacity
to make them transparent) with Show
.
cp =
Show[
ListPointPlot3D[
clusters
, PlotStyle -> ColorData[110]
, PlotTheme -> {"Web", "FrameGrid"}
, BoxRatios -> Automatic]
, Graphics3D[
{Opacity[.1]
, MapIndexed[
{ColorData[110] @@ #2, EdgeForm[{Thin, Opacity[.1], ColorData[110] @@ #2}], #1} &
, hulls]}]
]

For category B example data we need points outside of the hulls of the clusters. We can create a Cuboid
around the RegionUnion
of hulls
and hollow out the volume of hulls
by taking the RegionDifference
. This region can be visualised with RegionPlot3D
.
With[
{ru = RegionUnion[hulls]}
, rd =
RegionDifference[
Cuboid @@ Transpose[
MapAt[Ceiling[#, 0.01] &, {All, 2}]@
MapAt[Floor[#, 0.01] &, {All, 1}]@
RegionBounds@ru]
, ru]
];
RegionPlot3D[rd
, PlotStyle -> Opacity[.1]
, Axes -> True]

Then we can generate RandomPoint
s inside this region for dataB
.
SeedRandom[19283745]
dataB = RandomPoint[DiscretizeRegion@rd, 20];
The dataB
points can be combined with the dataA
cluster plot with Show
. All of the dataB
points are outside of the dataA
hulls.
Show[
cp
, ListPointPlot3D[dataB
, PlotStyle -> Black]
]

Now that we have example category B data (dataB
) we can calculate the RegionDistance
of each point to each of category A's hulls
. Then by Ordering
these distances the First
entry gives the hull the point is closest to.
nc =
First /@
Ordering /@
Transpose@
Through[
Function[r, RegionDistance[r, #] &, Listable][hulls][dataB]
]
{2, 1, 2, 1, 2, 2, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1}
Lastly we Pick
the dataB
points by their closest hull and combine their plot with the dataA
cluster plot with Show
. dataB
points have been coloured to indicate their closest dataA
cluster.
pncB = Pick[dataB, nc, #] & /@ Range@Length@hulls;
Show[
cp
, ListPointPlot3D[
pncB
, PlotStyle -> ColorData[104]
, BoxRatios -> Automatic
]
]

Hope this helps.