我有一个类别为粒子p = [Particle, Particle, ...]
的 N 个对象的小菜,其中每个对象都有一个属性pos
和vel
,每个都有一个数组{{1 }},所有的浮点数。我想知道更新数组中所有[x,y]
和pos
属性值的最有效方法是什么。
我想对大量粒子进行以下更新,其中vel
是2D数组, N 很多acc
:
[x,y]
我想知道是否存在一种更有效的方法来更改p元素的每个位置和速度属性,而不是使用逐个设置每个for i,body in enumerate(p):
body.vel -= acc[i] * timestep
body.pos += body.vel * timestep
和pos
元素的值相应的加速元件。似乎必须有一种Python方式才能做到这一点。
我希望像vel
这样的东西可以访问p[:].pos
值的数组。
答案 0 :(得分:0)
也许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
视为浮点数,如果它们像[x, y]
那样浮点数,就是这样。
答案 1 :(得分:0)
我建议使用pandas.DataFrame
。
我做了一些实现来比较速度。现在,您的选择几乎与使用带有Particle
对象的简单Python列表相同。
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
当然,我的示例实现与您描述的不完全相同,但是它表明有一种更快的方法可以实现您的预期目标。