Как обновить значения атрибута множества объектов в массиве - PullRequest
2 голосов
/ 27 марта 2019

У меня есть числовой массив из N объектов класса Particle p = [Particle, Particle, ...], где каждый объект имеет атрибут pos и vel, каждый массив [x,y], все с плавающей точкой.Мне было интересно, какой самый эффективный способ обновить значения всех атрибутов pos и vel в массиве.Я хочу запустить следующее обновление для большого массива частиц, где acc - это двумерный массив, N много [x,y]:

for i,body in enumerate(p):
    body.vel -= acc[i] * timestep
    body.pos += body.vel * timestep

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

Я бы хотел, чтобы что-то вроде p[:].pos имело доступ к массиву значений pos.

Ответы [ 2 ]

0 голосов
/ 27 марта 2019

Я предлагаю использовать pandas.DataFrame.

Я сделал несколько реализаций для сравнения скорости. Ваш вариант сейчас почти такой же, как простой список Python с Particle объектами.

import random
import timeit
import numpy as np
import pandas as pd

class Particle:
    def __init__(self, pos, vel):
        self.pos = pos
        self.vel = vel

    def __repr__(self):
        return 'Particle({:.2f} | {:.2f})'.format(self.pos, self.vel)

def f1(data, acc, time_step=2):
    for i, p in enumerate(data):
        p.vel -= acc[i] * time_step
        p.pos += p.vel * time_step

    return data

def f2(data, acc, time_step=2):
    df['vel'] -= acc * time_step
    df['pos'] += df['vel'] * time_step

    return data

if __name__ == '__main__':
    for n1 in (10**1, 10**3, 10**5):
        particle_list = [
            [random.random(), random.random()]
            for _ in range(n1)]
        acceleration_arr = np.random.random((n1, ))
        acceleration_arr_2 = acceleration_arr.reshape((n1, 1))

        # option 1
        particle_list_1 = [
            Particle(pos, vel)
            for pos, vel in particle_list]

        # option 2
        df = pd.DataFrame(
            data=particle_list,
            columns=['pos', 'vel'])

        # assure results are equal
        ret_1 = f1(particle_list_1, acceleration_arr)
        ret_2 = f2(df, acceleration_arr)
        # convert to lists
        ret_1 = [(p.pos, p.vel) for p in ret_1]
        ret_2 = [(p['pos'], p['vel']) for _, p in ret_2.iterrows()]
        # print('ret_1', ret_1)
        # print('ret_2', ret_2)
        assert ret_1 == ret_2

        # compare duration
        repetitions = 100
        t1 = timeit.timeit(
            'f1(particle_list_1, acceleration_arr)',
            'from __main__ import f1, acceleration_arr, particle_list_1',
            number=repetitions)
        t2 = timeit.timeit(
            'f2(df, acceleration_arr)',
            'from __main__ import f2, acceleration_arr, df',
            number=repetitions)
        print('n={:10d} | {:30s} {:.6f}'.format(n1, 'list with for-loop', t1))
        print('n={:10d} | {:30s} {:.6f}'.format(n1, 'pandas.DataFrame', t2))
        print()

Выполнение этого кода дает мне этот вывод, показывая, что версия pandas намного быстрее с ростом размера данных:

n=        10 | list with for-loop             0.001032
n=        10 | pandas.DataFrame               0.064379     # pandas is slower

n=      1000 | list with for-loop             0.106632
n=      1000 | pandas.DataFrame               0.067613     # pandas is faster

n=    100000 | list with for-loop             9.986003
n=    100000 | pandas.DataFrame               0.115627     # pandas is a lot faster

Конечно, мой пример реализации не совсем то, что вы описали, но он показывает, что есть более быстрый способ сделать то, что вы собираетесь.

0 голосов
/ 27 марта 2019

Может быть numpy.recarray поможет вам здесь.Используйте recarray вместо ndarray больше, чтобы удовлетворить ваши требования.recarray разрешает доступ к полям с использованием атрибутов.Некоторые примеры кодов здесь:

acc = numpy.array([1, 2, 3])
timestep = 1
paticle_num = len(acc)

arr = numpy.recarray(paticle_num, dtype=[('pos', float), ('vel', float)])
# initialize
arr.pos = numpy.arange(paticle_num)
arr.vel = numpy.arange(paticle_num)

# operate in the whole dimension easily, I think that is what you want.
arr.vel -= acc * timestep
arr.pos += arr.vel * timestep

Я просто трактую pos и vel как float, чтобы упростить проблему, если они такие как float как [x, y], это просто что-то вроде этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...