Замена строк файла строками другого файла - PullRequest
2 голосов
/ 08 октября 2019

У меня есть два файла облака точек (Scene и Green) в формате .txt. Облако точек сцены обычно содержит более 100000 линий, а зеленое - 20000 строк. Эти два файла имеют одинаковые линии для зеленых точек, за исключением последнего числа, которое является меткой для каждой точки.

Сцена:

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

Зеленый:

-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

Я хочу заменить всю строку в зеленой точке сцены на ее равную линию в файле Green или изменить номер метки с 5 на 3, если обе линии равны. Окончательный результат будет выглядеть следующим образом: Сцена:

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

Я написал два типа кода, чтобы сделать это, но оба они загружаются в течение значительного количества времени, что не хорошовообще, из-за того, что у меня есть много файлов для изменения. Первый код:

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)

Второй код:

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)

Оба они отлично работают с небольшим количеством точек, но когда я использую свой файл оригинальных облаков точек,затем требуется более 30 минут или даже больше, чтобы закончить работу. Я на самом деле вижу проблему в FOR LOOP, когда я в основном использую NESTED LOOPS, что означает, что у меня будет 100000 * 20000 циклов для изменения зеленых точек.

Есть ли эффективный способ, используя массивы numpy или любые другие методы?

Ответы [ 3 ]

3 голосов
/ 09 октября 2019

У меня есть решение, которое должно быть адекватным, но прежде, чем я перейду к этому, откажу от ответственности: невозможно будет найти правильное решение без дополнительной информации от вас. Нам нужен контекст для этой проблемы, и гораздо более точная и подробная информация о формате данных и о том, что вы пытаетесь сделать.

Например, сравнивать поплавки на равенство нехорошо, а манипулирование числами в целом всегда несет определенный риск с точки зрения точности и т. Д. Так как эти точки, по-видимому, происходят из одного и того же места, было быбыло бы замечательно, если бы у каждого был какой-то уникальный идентификатор, который можно было бы использовать для проверки на равенство.


Как и некоторые другие люди здесь, мой первый рефлекс заключался в том, чтобы схватить numpy и панд. Это была ошибка с моей стороны, так как эта задача совсем не связана с манипулированием данными или их преобразованием

Вот самая простая реализация, о которой я могу подумать прямо сейчас:

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)

Код довольно прост. Наборы создаются для зеленой и черной точек, мы проверяем членство и меняем метку соответствующим образом.

Я создал несколько обучающих данных для себя: сцена с 1 000 000 точек, 125 000 зеленых точек и 125 000 черных точек,Время выполнения было менее 7 секунд (надеюсь, я не допустил каких-либо серьезных ошибок!), Использование памяти должно быть низким.

2 голосов
/ 08 октября 2019

Решение «грубой силы» с использованием numba JIT-компиляции. Просто для удовольствия лучше использовать frozenset -подход . Самая дорогая операция - ввод-вывод памяти во время mod_arr[j,:] = mod[i,:].

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 голосов
/ 08 октября 2019

Я думаю, вам следует задать себе несколько важных вопросов о ваших данных:

  1. Сохранен ли порядок в файлах? Я имею в виду, вам нужно постоянно искать по всему файлу или, найдя зеленую точку в каком-то месте, вы можете пропустить сравнения по какой-то части файла?
  2. 100000 записей - это не так уж много. Будет ли это когда-нибудь в 1000 раз больше? Можете ли вы прочитать весь файл в память (массив Numpy или DataFrame) один раз, чтобы можно было использовать кэш-память ОЗУ и ЦП вместо чтения с диска несколько раз? Тогда установка смещения на недавно найденную зеленую точку сделает возможным использование.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...