10
$\begingroup$

In this chart the areas of the voronoi cells have been apportioned so that they represent the fraction of total spending.

enter image description here

How can I make a chart like this? Suppose we have the data:

data = {{"๐Ÿ‡บ๐Ÿ‡ธ", 860000}, {"๐Ÿ‡ฉ๐Ÿ‡ช", 68080}, {"๐Ÿ‡ฌ๐Ÿ‡ง", 65763}, {"๐Ÿ‡ซ๐Ÿ‡ท", 56649}, {"๐Ÿ‡ฎ๐Ÿ‡น",
     31585}, {"๐Ÿ‡ต๐Ÿ‡ฑ", 29105}, {"๐Ÿ‡จ๐Ÿ‡ฆ", 28950}, {"๐Ÿ‡ช๐Ÿ‡ธ", 19179}, {"๐Ÿ‡ณ๐Ÿ‡ฑ", 
    16741}, {"๐Ÿ‡น๐Ÿ‡ท", 15842}, {"๐Ÿ‡ณ๐Ÿ‡ด", 8814}, {"๐Ÿ‡ท๐Ÿ‡ด", 8481}, {"๐Ÿ‡ซ๐Ÿ‡ฎ", 7325}, {"๐Ÿ‡ฌ๐Ÿ‡ท",
     7125}, {"๐Ÿ‡ง๐Ÿ‡ช", 7076}, {"๐Ÿ‡ฉ๐Ÿ‡ฐ", 6775}, {"๐Ÿ‡ญ๐Ÿ‡บ", 5036}, {"๐Ÿ‡จ๐Ÿ‡ฟ", 5033}, {"๐Ÿ‡ต๐Ÿ‡น", 
    4167}, {"๐ŸŒ", 12400}};
totalSpending = Total[data[[All, 2]]];

We'll have Length[data] many points, and we'll form a voronoi diagram but clipped inside a circle. The positions of the points need to be optimized such that the cell areas in the (circle) diagram get closer to the fraction of total spending. The first problem to solve would be how to clip the voronoi diagram in a circle.

$\endgroup$
5
  • 4
    $\begingroup$ I believe this kind of diagram is called a Voronoi treemap (in case anyone will look for existing implementations online) :) $\endgroup$
    – Domen
    Commented Feb 27 at 12:49
  • 5
    $\begingroup$ Useful link about Voronoi treemaps. $\endgroup$
    – Syed
    Commented Feb 27 at 15:10
  • 4
    $\begingroup$ Clipping could be done like this: vm = VoronoiMesh[RandomReal[{-1, 1}, {25, 2}]]; circle = BoundaryDiscretizeGraphics[Disk[]]; pieces = RegionIntersection[circle, #] & /@ MeshPrimitives[vm, 2]; Show[DeleteCases[pieces, _EmptyRegion]] $\endgroup$
    – halmir
    Commented Feb 27 at 15:39
  • 1
    $\begingroup$ I'm trying to make this work, but unfortunately the following code crashes quite often in (quitting the kernel): P = RandomPoint[Disk[], 20]; V = VoronoiMesh[P, {{-1, 1}, {-1, 1}}]; c = MeshCoordinates[V]; MeshCells[V, 2] /. Polygon[L_] :> RegionIntersection[Region[Polygon[c[[L]]]], Disk[]] $\endgroup$
    – Roman
    Commented Feb 29 at 9:44
  • $\begingroup$ @Roman yes I've had a go myself and get similar crashes too. There's some kind of weird bug when calling VoronoiMesh repeatedly, so I might look at a custom voronoi implementation or call out to a library. $\endgroup$
    – flinty
    Commented Feb 29 at 15:46

1 Answer 1

2
$\begingroup$

This is only a partial answer and workaround for the kernel crash problems seen by @Roman in the comments concerning generating a circle masked Voronoi.

I was able to get a Voronoi diagram without using the builtin VoronoiMesh. This is a naive implementation (nearest point for every grid point) using OpenCL. Getting the cell proportions is then an easy matter of counting the pixel colours.

Remove["Global`*"];
Needs["OpenCLLink`"];
source = "
__kernel void voronoi(
__global float2 * points,
__global mint * grid,
__global mint * mask,
mint n, 
mint gridRows,
mint gridColumns,
float realX0,
float realX1,
float realY0,
float realY1)
{
    int gridCol = get_global_id(0);
    int gridRow = get_global_id(1);
    int gridIndex = gridCol + gridRow * gridColumns;

    if(mask[gridIndex] < 1)
    {
        grid[gridIndex] = -1;
        return;
    }

    float xfrac = gridRow / ((float)gridRows);
    float yfrac = gridCol / ((float)gridColumns);
    float2 realPos;
    realPos.x = mix(realX0, realX1, xfrac);
    realPos.y = mix(realY0, realY1, yfrac);

    float mindist = HUGE_VALF;
    int minN = 0;
    for(int i = 0; i < n; ++i)
    {
        float2 p = points[i];
        float d = distance(p,realPos);
        if(d < mindist)
        {
            minN = i;
            mindist = d;
        }
    }
    grid[gridIndex] = minN;
}
";
voronoi = 
  OpenCLFunctionLoad[source, 
   "voronoi", {{"Float[2]", 1, "Input"}, {_Integer, 1, 
     "Output"}, {_Integer, 1, "Input"}, _Integer, _Integer, _Integer, 
    "Float", "Float", "Float", "Float"}, {16, 16}];
generateVoronoi[points_, gridDim_, mask_] :=
 ArrayReshape[voronoi[
   Flatten[points],
   Flatten@ConstantArray[0, gridDim],
   Flatten@mask,
   Length[points], gridDim[[1]], gridDim[[2]], -1., 1., -1., 1.,
   gridDim
   ], gridDim]

gridDim = {512, 512};
mask = ArrayResample[DiskMatrix[Round[gridDim[[1]]/2]], gridDim, 
   Resampling -> "Nearest"];
maskArea = Total[mask, 2];

n = 20;
points = RandomPoint[Disk[], n];
vor = generateVoronoi[points, gridDim, mask];
vorProportions = 
  N@Values[KeySort[KeyDrop[Counts[Flatten@vor], -1]/maskArea]];
Colorize[vor]

Circle masked voronoi diagram

$\endgroup$

Your Answer

By clicking โ€œPost Your Answerโ€, you agree to our terms of service and acknowledge you have read our privacy policy.

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