Je travaille avec pointcloud 3D de Lidar. Les points sont donnés par un tableau numpy qui ressemble à ceci:
points = np.array([[61651921, 416326074, 39805], [61605255, 416360555, 41124], [61664810, 416313743, 39900], [61664837, 416313749, 39910], [61674456, 416316663, 39503], [61651933, 416326074, 39802], [61679969, 416318049, 39500], [61674494, 416316677, 39508], [61651908, 416326079, 39800], [61651908, 416326087, 39802], [61664845, 416313738, 39913], [61674480, 416316668, 39503], [61679996, 416318047, 39510], [61605290, 416360572, 41118], [61605270, 416360565, 41122], [61683939, 416313004, 41052], [61683936, 416313033, 41060], [61679976, 416318044, 39509], [61605279, 416360555, 41109], [61664837, 416313739, 39915], [61674487, 416316666, 39505], [61679961, 416318035, 39503], [61683943, 416313004, 41054], [61683930, 416313042, 41059]])
Je voudrais garder mes données groupées en cubes de taille de 50*50*50
sorte que chaque cube conserve un indice hashable et indices numpy de mon points
qu'il contient . Pour obtenir le fractionnement, j'attribue les cubes = points \\ 50
sorties à:
cubes = np.array([[1233038, 8326521, 796], [1232105, 8327211, 822], [1233296, 8326274, 798], [1233296, 8326274, 798], [1233489, 8326333, 790], [1233038, 8326521, 796], [1233599, 8326360, 790], [1233489, 8326333, 790], [1233038, 8326521, 796], [1233038, 8326521, 796], [1233296, 8326274, 798], [1233489, 8326333, 790], [1233599, 8326360, 790], [1232105, 8327211, 822], [1232105, 8327211, 822], [1233678, 8326260, 821], [1233678, 8326260, 821], [1233599, 8326360, 790], [1232105, 8327211, 822], [1233296, 8326274, 798], [1233489, 8326333, 790], [1233599, 8326360, 790], [1233678, 8326260, 821], [1233678, 8326260, 821]])
Ma sortie souhaitée ressemble à ceci:
{(1232105, 8327211, 822): [1, 13, 14, 18]),
(1233038, 8326521, 796): [0, 5, 8, 9],
(1233296, 8326274, 798): [2, 3, 10, 19],
(1233489, 8326333, 790): [4, 7, 11, 20],
(1233599, 8326360, 790): [6, 12, 17, 21],
(1233678, 8326260, 821): [15, 16, 22, 23]}
Mon vrai pointcloud contient jusqu'à quelques centaines de millions de points 3D. Quel est le moyen le plus rapide pour effectuer ce type de regroupement?
J'ai essayé une majorité de solutions différentes. Voici une comparaison de la consommation de temps en supposant que la taille des points est d'environ 20 millions et la taille des cubes distincts est d'environ 1 million:
Pandas [tuple (elem) -> np.array (dtype = int64)]
import pandas as pd
print(pd.DataFrame(cubes).groupby([0,1,2]).indices)
#takes 9sec
Defauldict [elem.tobytes () ou tuple -> list]
#thanks @abc:
result = defaultdict(list)
for idx, elem in enumerate(cubes):
result[elem.tobytes()].append(idx) # takes 20.5sec
# result[elem[0], elem[1], elem[2]].append(idx) #takes 27sec
# result[tuple(elem)].append(idx) # takes 50sec
numpy_indexed [int -> np.array]
# thanks @Eelco Hoogendoorn for his library
values = npi.group_by(cubes).split(np.arange(len(cubes)))
result = dict(enumerate(values))
# takes 9.8sec
Pandas + réduction de la dimensionnalité [int -> np.array (dtype = int64)]
# thanks @Divakar for showing numexpr library:
import numexpr as ne
def dimensionality_reduction(cubes):
#cubes = cubes - np.min(cubes, axis=0) #in case some coords are negative
cubes = cubes.astype(np.int64)
s0, s1 = cubes[:,0].max()+1, cubes[:,1].max()+1
d = {'s0':s0,'s1':s1,'c0':cubes[:,0],'c1':cubes[:,1],'c2':cubes[:,2]}
c1D = ne.evaluate('c0+c1*s0+c2*s0*s1',d)
return c1D
cubes = dimensionality_reduction(cubes)
result = pd.DataFrame(cubes).groupby([0]).indices
# takes 2.5 seconds
Il est possible de télécharger le cubes.npz
fichier ici et d'utiliser une commande
cubes = np.load('cubes.npz')['array']
pour vérifier le temps de performance.
numpy_indexed
ne s'en approche que trop. Je suppose que c'est vrai. J'utilise actuellementpandas
pour mes processus de classification.Réponses:
Nombre constant d'indices par groupe
Approche n ° 1
Nous pouvons effectuer
dimensionality-reduction
pour réduirecubes
à un tableau 1D. Ceci est basé sur un mappage des données de cubes données sur une grille n-dim pour calculer les équivalents d'indice linéaire, discuté en détailhere
. Ensuite, en fonction de l'unicité de ces indices linéaires, nous pouvons séparer les groupes uniques et leurs indices correspondants. Par conséquent, en suivant ces stratégies, nous aurions une solution, comme ceci -Alternative n ° 1: si les valeurs entières dans
cubes
sont trop grandes, nous pourrions vouloir faire endimensionality-reduction
sorte que les dimensions avec une extension plus courte soient choisies comme axes principaux. Par conséquent, pour ces cas, nous pouvons modifier l'étape de réduction pour obtenirc1D
, comme ceci -Approche n ° 2
Ensuite, nous pouvons utiliser
Cython-powered kd-tree
pour une recherche rapide du plus proche voisin pour obtenir les indices voisins les plus proches et donc résoudre notre cas comme ça -Cas générique: nombre variable d'indices par groupe
Nous allons étendre la méthode basée sur argsort avec un certain fractionnement pour obtenir notre sortie souhaitée, comme ceci -
Utilisation de versions 1D de groupes de
cubes
clés asNous allons étendre la méthode listée précédemment avec les groupes de
cubes
clés as pour simplifier le processus de création de dictionnaire et aussi le rendre efficace avec, comme ceci -Ensuite, nous utiliserons
numba
package pour itérer et arriver à la sortie finale du dictionnaire lavable. Pour aller avec, il y aurait deux solutions - L'une qui obtient les clés et les valeurs séparément en utilisantnumba
et l'appel principal sera compressé et converti en dict, tandis que l'autre créera unnumba-supported
type de dict et donc aucun travail supplémentaire requis par la fonction d'appel principale .Ainsi, nous aurions une première
numba
solution:Et deuxième
numba
solution comme:Horaires avec
cubes.npz
données -Alternative n ° 1: nous pouvons accélérer davantage le
numexpr
calcul pour les grands tableauxc1D
, comme ceci -Ce serait applicable à tous les endroits qui le nécessitent
c1D
.la source
dtypes
int32
etint64
number of indices per group would be a constant number
que j'ai rassemblé les commentaires. Serait-ce une hypothèse sûre? De plus, testez-vouscubes.npz
la sortie de915791
?cubes.npz
uniquement et c'était983234
pour les autres approches que j'ai suggérées.Approach #3
ce cas générique de nombre variable d'indices.Vous pouvez simplement parcourir et ajouter l'index de chaque élément à la liste correspondante.
L'exécution peut être encore améliorée en utilisant tobytes () au lieu de convertir la clé en tuple.
la source
res[tuple(elem)].append(idx)
pris 50 secondes contre son éditionres[elem[0], elem[1], elem[2]].append(idx)
qui a pris 30 secondes.Vous pouvez utiliser Cython:
mais cela ne vous rendra pas plus rapide que ce que Pandas fait, bien qu'il soit le plus rapide après cela (et peut-être la
numpy_index
solution basée), et ne vient pas avec la pénalité de mémoire de celui-ci. Une collection de ce qui a été proposé jusqu'à présent est ici .Dans la machine OP, le temps d'exécution devrait atteindre près de 12 secondes.
la source