How do I to average irregularly spaced x & y coordinate tensor into a grid with a specific cell size?

Viewed 70

I have an algorithm that generates a tensor of irregularly spaced x and y coordinates (ex: torch.size([3600, 2])), and I need to average the points into grid cells of a specific size (ex: 8 by 8). The resulting grid needs to be either an array or tensor.

It's not required, but I would also like to be able to determine if any of the resulting cells have less than a specified number of points in them.

For example I can graph the tensor using matplotlib's plt.scatter, and it looks like this:

enter image description here

In the above example, 100,000 points exist but the number of points can sometimes be in the tens of millions.

I've tried using histogram approaches, and most of them use a specific number of cells vs a specific cell size. Matplotlib can seemingly do it in a graph, but that doesn't help me get an array or tensor.

Edit:

This code might work, if it can be made to work properly.

def grid_torch(x_coords, y_coords, grid_size=(8,8), x_extent=(0., 1.), y_extent=(0., 1.)):
    x_coords = ((x_coords - x_extent[0]) / (x_extent[1] - x_extent[0])) * grid_size[0]
    y_coords = ((y_coords - y_extent[0]) / (y_extent[1] - y_extent[0])) * grid_size[1]

    x_list = []
    for x in range(grid_size[0]):
        x = torch.ones_like(x_coords) * x
        y_list = []
        for y in range(grid_size[1]):
            y = torch.ones_like(y_coords) * y
            in_bounds_x = torch.logical_and(x <= x_coords, x_coords <= x + 1)
            in_bounds_y = torch.logical_and(y <= y_coords, y_coords <= y + 1)
            in_bounds = torch.logical_and(in_bounds_x, in_bounds_y)

            in_bounds_indices = torch.where(in_bounds)
            print(in_bounds_indices)
            y_list.append(in_bounds_indices)
        x_list.append(torch.stack(y_list))
    return torch.stack(x_list)



out = grid_torch(xy_tensor[:,0], xy_tensor[:,1])

print(out.shape)

def create_grid(grid_layout, activ, grid_size=(8,8), min_density=8):
    cells = []
    for x in range(grid_size[0]):
        for y in range(grid_size[1]):
            indices = grid_layout[x, y]
            if len(indices) > min_density:
                average_activation = torch.mean(activ[indices])
                cells.append((average_activation, x, y))
                print(average_activation, x, y)
    return torch.stack(cells)



grid_test = create_grid(out, xy_tensor, grid_size=(8,8))
1 Answers

I think this code would give you a good starting point.

def grid_torch(x_coords, y_coords, grid_size=(8,8), x_extent=(0., 1.), y_extent=(0., 1.)):
    # This part converts coordinates to bin numbers (like (2,5), (7,7) etc)
    x_bin = (((x_coords - x_extent[0]) / (x_extent[1] - x_extent[0])) * grid_size[0]).int()
    y_bin = (((y_coords - y_extent[0]) / (y_extent[1] - y_extent[0])) * grid_size[1]).int()
    
    counts = torch.zeros(grid_size)
    means = torch.zeros(list(grid_size) + [2])
    for x in range(grid_size[0]):
        for y in range(grid_size[1]):
            # these tensors are 1 where (x_bin == x and y_bin == y), 0 else where
            x_where = 1 * (x_bin == x)
            y_where = 1 * (y_bin == y)
            p_where = (x_where * y_where)
            cnt = p_where.sum()
            counts[x, y] = cnt
            
            # we'll average both x and y coords seperately.
            # you can embed min_density logic here. 
            if cnt > 0:
                means[x, y, 0] = (x_coords * p_where).sum() / p_where.sum()
                means[x, y, 1] = (y_coords * p_where).sum() / p_where.sum()
    
    return counts, means


# Generate sample points
points = torch.tensor(np.concatenate([
    np.random.normal(loc=0.2, scale=0.1, size=(1000, 2)),
    np.random.normal(loc=0.6, scale=0.1, size=(1000, 2))
]).clip(0,1)).float()
# plt.scatter(points[:,0], points[:,1])
# plt.grid()

counts, means = grid_torch(points[:,0], points[:,1])
counts

>>>
tensor([[ 47., 114.,  75.,  10.,   0.,   0.,   0.,   0.],
        [102., 204., 141.,  27.,   0.,   0.,   0.,   0.],
        [ 60., 101.,  74.,  16.,   7.,   4.,   1.,   0.],
        [  5.,  17.,   9.,  23.,  72.,  51.,  10.,   0.],
        [  1.,   1.,   4.,  54., 186., 141.,  28.,   3.],
        [  0.,   0.,   3.,  47., 154., 117.,  14.,   0.],
        [  0.,   0.,   0.,   9.,  37.,  24.,   4.,   0.],
        [  0.,   0.,   0.,   2.,   0.,   1.,   0.,   0.]])

Related