The following example code has three for loops:
import numpy as np
import pandas as pd
#Generating a sample (ndarray) of 25 particles with 3 random coordinates in the range between 0 and 3.
#Maybe think of the particles as contained in a cube of 3 x 3 x 3 units.
sample_data = np.random.uniform(0, 3, (25,3))
#Converting the narray into a dataframe
df = pd.DataFrame(data = sample_data, columns = ['A', 'B', 'C'])
print(df)
#Generating another narray which will store the number of particles in each cell of the cube
#Each cell has dimentions 1 x 1 x 1; total cells = 27
counts_in_cells = np.empty((3, 3, 3))
counts_in_cells[:] = np.NaN
#Three nested loops to count the number of particles in each cell
for i, x_low in enumerate(np.arange(0, 3, 1)):
for j, y_low in enumerate(np.arange(0, 3, 1)):
for k, z_low in enumerate(np.arange(0, 3, 1)):
#Specifying filtering conditions for three dimentions of the cells
x_condition = (df['A'] >= x_low) & (df['A'] < (x_low + 1))
y_condition = (df['B'] >= y_low) & (df['B'] < (y_low + 1))
z_condition = (df['C'] >= z_low) & (df['C'] < (z_low + 1))
#Applying the filtering conditions
df_select = df[x_condition & y_condition & z_condition]
#Counting the particles in cells (desired outcome)
counts_in_cells[i][j][k] = len(df_select)
#Paricles in each cell
print(counts_in_cells)
Sample input
Desired outcome
Quick run
This example code may be run right away at Kaggle: https://www.kaggle.com/awaismirza/counting-particles-in-each-cell-of-a-cube.
Problem
I want to avoid three loops because the actual version of this code takes a few minutes to run. (It has 90k particles and a much bigger cube.) Further, I have to run the actual code 6k times which will take many days.
Is there a way (Pandas functionality or NumPy masking or something) I can avoid the loops and run the code faster?
Original code
The actual version of the code is available here but the above example code should be sufficient to understand the problem.

