Как добавить элемент в массив NumPy в функции Python - PullRequest
4 голосов
/ 04 ноября 2019

Я пытаюсь создать серию массивов NumPy из текстового файла, используя пул рабочих с многопроцессорным модулем.

def process_line(line, x,y,z,t):
    sl = line.split()
    x = np.append(x,float(sl[0].replace(',','')))
    y = np.append(y,float(sl[1].replace(',','')))
    z = np.append(z,float(sl[2].replace(',','')))
    t = np.append(t,float(sl[3].replace(',','')))

def txt_to_HDF_converter(name, path_file):

    #init objects
    x = np.empty(0)
    y = np.empty(0)
    z = np.empty(0)
    t = np.empty(0)
    pool = mp.Pool(4)
    jobs = []

with open(path_file) as f:
    for line in f:
        jobs.append(pool.apply_async(process_line,(line,x,y,z,t)))

#wait for all jobs to finish
for job in jobs:
    job.get()
#clean up
pool.close()

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

1 Ответ

1 голос
/ 05 ноября 2019

Вы передаете значения как часть кортежа в коде здесь:

        jobs.append(pool.apply_async(process_line,(line,x,y,z,t)))

Затем вы неявно распаковываете этот кортеж в функции:

def process_line(line, x,y,z,t):

Тогда вы не делаетеизмените существующие значения, но вместо этого создайте новые с этими строками:

    x = np.append(x,float(sl[0].replace(',','')))
    y = np.append(y,float(sl[1].replace(',','')))
    z = np.append(z,float(sl[2].replace(',','')))
    t = np.append(t,float(sl[3].replace(',','')))

Позвольте мне повторить это: вы не изменяете исходные массивы (как вы ожидаете). Вместо этого вы просто используете старые значения для создания новых значений, которые вы затем назначаете локальным переменным x, y, z и t. Затем вы покидаете функцию и забываете о новых значениях. Я бы сказал, что это никогда не будет иметь никакого эффекта (также не для последнего значения) вне функции.

У вас есть несколько вариантов обхода этого.

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

  2. Верните ваши значения. После создания новых значений верните их как-нибудь и убедитесь, что следующий вызов снова получит ранее возвращенные значения в качестве входных данных. Это функциональный подход.

  3. Передайте свои значения по ссылке. Вы можете сделать это, не передавая x создать одноэлементный список. Смотрите код ниже о том, как это сделать. Передача ссылок является типичным программированием в стиле C и не очень Pythonic (но это работает). Многие IDE предупредят вас о том, как вы поступите таким образом, и типичному разработчику Python будет трудно понять, что вы там делаете. Более хорошим вариантом этого является не использование простых списков, а помещение ваших данных в некоторый объект, который будет передан по ссылке.

x_ref = [x]
y_ref = [y]
y_ref = [y]
t_ref = [t]

with open(path_file) as f:
    for line in f:
        jobs.append(pool.apply_async(process_line,(line,x_ref,y_ref,z_ref,t_ref)))

Тогда process_line необходимобыть настроен так, чтобы ожидать ссылки:

def process_line(line, x_ref,y_ref,z_ref,t_ref):
    sl = line.split()
    x_ref[0] = np.append(x_ref[0],float(sl[0].replace(',','')))
    y_ref[0] = np.append(y_ref[0],float(sl[1].replace(',','')))
    z_ref[0] = np.append(z_ref[0],float(sl[2].replace(',','')))
    t_ref[0] = np.append(t_ref[0],float(sl[3].replace(',','')))
...