Tengo dos archivos de nube de puntos (Escena y Verde) en formato .txt. La nube de puntos de escena generalmente contiene más de 100000 líneas y la verde contiene 20000 líneas, por ejemplo. Estos dos archivos tienen líneas iguales para los puntos verdes, excepto el último número que es una etiqueta para cada punto.

Escena:

0.805309, -3.43696, 6.85463, 0, 0, 0, 5
0.811636, -3.42248, 6.82576, 0, 0, 0, 5
-1.00663, 0.0985967, 3.02769, 42, 134, 83, 5
-1.00182, 0.098547, 3.02617, 43, 133, 83, 5
-0.997052, 0.0985018, 3.02478, 41, 133, 82, 5
0.811636, -3.42248, 6.82576, 0, 0, 0, 5

Verde:

-1.00663, 0.0985967, 3.02769, 42, 134, 83, 3
-1.00182, 0.098547, 3.02617, 43, 133, 83, 3
-0.997052, 0.0985018, 3.02478, 41, 133, 82, 3

Quiero reemplazar toda la línea en el punto verde de Scene con su línea igual en el archivo Green o solo cambiar el número de etiqueta de 5 a 3 donde las dos líneas sean iguales. El resultado final sería así: Escena:

0.805309, -3.43696, 6.85463, 0, 0, 0, 5
0.811636, -3.42248, 6.82576, 0, 0, 0, 5
-1.00663, 0.0985967, 3.02769, 42, 134, 83, 3
-1.00182, 0.098547, 3.02617, 43, 133, 83, 3
-0.997052, 0.0985018, 3.02478, 41, 133, 82, 3
0.811636, -3.42248, 6.82576, 0, 0, 0, 5

He escrito dos tipos de código para hacer esto, pero ambos se cargan durante una cantidad de tiempo significativa, lo que no es bueno en absoluto, porque tengo muchos archivos para modificar. Primer código:

import os
import fileinput
def main(scene, others):

    for file in others:
        other = open(file, "r+")
        for line in other:
            line1 = line[:-3]
            f=scene
            for sceneLine in fileinput.input(f,inplace=True):
                new = sceneLine
                sceneLine1 = sceneLine[:-3]
                if sceneLine1 == line1:
                    print(sceneLine.replace(new, line), end='')
                else:
                    print(sceneLine.replace(line,line), end='')
            fileinput.close()


others = []
for file in os.listdir("./"):
    if file.endswith(".txt"):
        if file.startswith("pointCloudScene9863Cl"):
            scene = file
        else:
            others.append(file)

main(scene,others)

Segundo código:

import os
import fileinput
import numpy

def main(scene1, others):

    pointcloud = []
    scene1 = open(scene1,"r+")
    scene = []
    for each_point in scene1:
        scene.append(each_point)

    for file in others:
        other = open(file, "r+")
        for line in other:
            pointcloud = []
            line1 = line[:-3]
            for sceneLine in scene:
                sceneLine1 = sceneLine[:-3]
                if sceneLine1 == line1:
                    pointcloud.append(line)
                else:
                    pointcloud.append(sceneLine)
            scene = pointcloud

    with open('pointcloud.txt', 'w') as points:
        for item in scene:
            points.write("%s" % item)


others = []
for file in os.listdir("./"):
    if file.endswith(".txt"):
        if file.startswith("pointCloudScene9863Cl"):
            scene = file
        else:
            others.append(file)

main(scene,others)

Ambos funcionan perfectamente con una pequeña cantidad de puntos, pero cuando uso mi archivo original de nube de puntos, me lleva más de 30 minutos o incluso más terminar el trabajo. De hecho, veo el problema en FOR LOOP cuando básicamente estoy usando NESTED LOOPS, lo que significa que tendré 100000 * 20000 bucles para cambiar los puntos verdes.

¿Existe una manera eficiente mediante el uso de matrices numpy o cualquier otro método?

2
Morti 8 oct. 2019 a las 15:22

3 respuestas

La mejor respuesta

Tengo una solución que debería ser adecuada, pero antes de pasar por eso, un descargo de responsabilidad: será imposible encontrar una solución adecuada sin más información tuya. Necesitamos contexto para este problema e información mucho más precisa y detallada sobre el formato de datos y lo que está tratando de hacer.

Por ejemplo, comparar flotadores para la igualdad no se siente bien, y la manipulación de los números en general siempre conlleva un cierto riesgo en términos de precisión, etc. Dado que estos puntos aparentemente provienen del mismo lugar, sería genial si cada uno tuviera algún tipo de identificación única que podría usarse para verificar la igualdad.


Como algunas de las otras personas aquí, mi primer reflejo fue agarrar numpy y pandas. Fue un error de mi parte, ya que esta tarea no implica mucha manipulación o transformación de datos.

Aquí, entonces, es la implementación más simple que puedo pensar en este momento:

def point_parse(line):
    line_point = line.split(", ")
    line_point[0] = float(line_point[0])
    line_point[1] = float(line_point[1])
    line_point[2] = float(line_point[2])
    line_point[3] = int(line_point[3])
    line_point[4] = int(line_point[4])
    line_point[5] = int(line_point[5])
    line_point[6] = int(line_point[6])
    return tuple(line_point)

green_points_set: frozenset
black_points_set: frozenset

with open("../resources/Green_long.txt", "r") as green_file:
    green_points_set = frozenset((point_parse(line)[:-1] for line in green_file))

with open("../resources/Black_long.txt", "r") as black_file:
    black_points_set = frozenset((point_parse(line)[:-1] for line in black_file))

def set_point_label(point):
    point_comp = point[:-1]
    if point_comp in green_points_set:
        point_comp += (3,)
    elif point_comp in black_points_set:
        point_comp += (4,)
    else:
        point_comp = point
    return point_comp

with open("../resources/Scene_long.txt", "r") as scene_file:
    scene_points_new = (set_point_label(point_parse(line)) for line in scene_file)
    form_lines = ((f"{res_line[0]}, {res_line[1]}, {res_line[2]}, {res_line[3]}, "
               f"{res_line[4]}, {res_line[5]}, {res_line[6]}\n") for res_line in scene_points_new)

    with open("../out/Scene_out.txt", "w") as scene_out:
        scene_out.writelines(form_lines)

El código es bastante sencillo. Los conjuntos se crean para los puntos verdes y negros, probamos la membresía y cambiamos la etiqueta adecuadamente.

Creé algunos datos de entrenamiento para mí: una escena con 1,000,000 de puntos en total, 125,000 puntos verdes y 125,000 puntos negros. El tiempo de ejecución fue inferior a 7 segundos (¡espero que no haya cometido ningún error crucial!), El uso de memoria debería ser bajo.

3
AMC 9 oct. 2019 a las 05:03

Creo que debería hacerse algunas preguntas esenciales sobre sus datos:

  1. ¿Se conserva el orden en los archivos? Quiero decir, ¿tiene que buscar en todo el archivo en todo momento, o después de encontrar el punto verde en algún lugar puede omitir las comparaciones en alguna parte del archivo?
  2. 100000 registros no es tanto. ¿Alguna vez será, como, 1000 veces más? ¿Puede leer el archivo completo en la memoria (matriz Numpy o DataFrame) una vez, para que pueda usar la memoria RAM y el caché de la CPU, en lugar de leer del disco varias veces? Establecer el desplazamiento en el punto verde encontrado recientemente sería una opción viable entonces.
2
LDTV 8 oct. 2019 a las 12:44

Una solución de 'fuerza bruta' que utiliza la compilación jit numba. Solo por diversión, mejor utilice el frozenset - enfoque. La operación más costosa es la memoria IO durante mod_arr[j,:] = mod[i,:] parece.

import timeit
import numpy as np
from numba import njit

### numba njit-ed version of nested loops
@njit
def modify(arr, mod, tol=0.000000001):
    mod_arr = arr[:]
    mask = np.ones(arr.shape[0]).astype(np.bool_)
    idx = np.arange(0, arr.shape[0], 1)
    for i in range(mod.shape[0]):
        for j in idx[mask]:
            if np.absolute(np.sum(arr[j,:-1]-mod[i,:-1])) < tol:
                mod_arr[j,:] = mod[i,:]
                mask[j] = False
    return mod_arr

# "scene":
a = np.array([[0.805309, -3.43696, 6.85463, 0, 0, 0, 5],
              [0.811636, -3.42248, 6.82576, 0, 0, 0, 5],
              [-1.00663, 0.0985967, 3.02769, 42, 134, 83, 5],
              [-1.00182, 0.098547, 3.02617, 43, 133, 83, 5],
              [-0.997052, 0.0985018, 3.02478, 41, 133, 82, 5],
              [0.811636, -3.42248, 6.82576, 0, 0, 0, 5]])
# "green":
m = np.array([[-1.00663, 0.0985967, 3.02769, 42, 134, 83, 3],
              [-1.00182, 0.098547, 3.02617, 43, 133, 83, 3],
              [-0.997052, 0.0985018, 3.02478, 41, 133, 82, 3]])
# desired output:
mod_arr_test = np.array([[0.805309, -3.43696, 6.85463, 0, 0, 0, 5],
                         [0.811636, -3.42248, 6.82576, 0, 0, 0, 5],
                         [-1.00663, 0.0985967, 3.02769, 42, 134, 83, 3],
                         [-1.00182, 0.098547, 3.02617, 43, 133, 83, 3],
                         [-0.997052, 0.0985018, 3.02478, 41, 133, 82, 3],
                         [0.811636, -3.42248, 6.82576, 0, 0, 0, 5]])
# check:
mod_arr = modify(a, m)
print([np.isclose(np.sum(mod_arr[i] - l), 0.) for i, l in enumerate(mod_arr_test)])
# -->
[True, True, True, True, True, True]

# now let's make the arrays big...
a = np.tile(a, (17000, 1)) # a.shape is (102000, 7)
m = np.tile(m, (7000, 1)) # m.shape is (21000, 7)

### performance check:
%timeit modify(a, m)
# -->
2min 55s ± 4.07 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
2
MrFuppes 9 oct. 2019 a las 12:13
58286285