我需要从numpy数组(或熊猫数据框)中列出对象列表。每行都包含对象的所有属性值(请参见示例)。
import numpy as np
class Dog:
def __init__(self, weight, height, width, girth):
self.weight = weight
self.height = height
self.width = width
self.girth = girth
dogs = np.array([[5, 100, 50, 80], [4, 80, 30, 70], [7, 120, 60, 90], [2, 50, 30, 50]])
# list comprehension with idexes
dog_list = [Dog(dogs[i][0], dogs[i][1], dogs[i][2], dogs[i][3]) for i in range(len(dogs))]
我的真实数据当然要大得多(多达5列的一百万行),因此逐行进行迭代并查找正确的索引会花费很多时间。有没有一种方法可以对此进行矢量化,或者通常使其更高效/更快?我尝试自己寻找方法,但至少在我的专业水平上,我找不到任何可翻译的内容。
但是保留行的顺序非常重要,因此,如果行不通,我想我将不得不忍受缓慢的操作。
干杯!
编辑-关于np.vectorize的问题:
这是我实际代码的一部分,以及一些实际数据:
将numpy导入为np
class Particle:
TrackID = 0
def __init__(self, uniq_ident, intensity, sigma, chi2, past_nn_ident, past_distance, aligned_x, aligned_y, NeNA):
self.uniq_ident = uniq_ident
self.intensity = intensity
self.sigma = sigma
self.chi2 = chi2
self.past_nn_ident = past_nn_ident
self.past_distance = past_distance
self.aligned_y = aligned_y
self.aligned_x = aligned_x
self.NeNA = NeNA
self.new_track_length = 1
self.quality_pass = True
self.re_seeder(self.NeNA)
def re_seeder(self, NeNA):
if np.isnan(self.past_nn_ident):
self.newseed = True
self.new_track_id = Particle.TrackID
print(self.new_track_id)
Particle.TrackID += 1
else:
self.newseed = False
self.new_track_id = None
data = np.array([[0.00000000e+00, 2.98863746e+03, 2.11794100e+02, 1.02241467e+04, np.NaN,np.NaN, 9.00081968e+02, 2.52456745e+04, 1.50000000e+01],
[1.00000000e+00, 2.80583577e+03, 4.66145720e+02, 6.05642671e+03, np.NaN, np.NaN, 8.27249728e+02, 2.26365501e+04, 1.50000000e+01],
[2.00000000e+00, 5.28702810e+02, 3.30889610e+02, 5.10632793e+03, np.NaN, np.NaN, 6.03337243e+03, 6.52702811e+04, 1.50000000e+01],
[3.00000000e+00, 3.56128350e+02, 1.38663730e+02, 3.37923885e+03, np.NaN, np.NaN, 6.43263261e+03, 6.14788766e+04, 1.50000000e+01],
[4.00000000e+00, 9.10148200e+01, 8.30057400e+01, 4.31205993e+03, np.NaN, np.NaN, 7.63955009e+03, 6.08925862e+04, 1.50000000e+01]])
Particle.TrackID = 0
particles = np.vectorize(Particle)(*data.transpose())
l = [p.new_track_id for p in particles]
对此感到奇怪的是,ree_seeder函数“ print(self.new_track_id)”中的print语句将打印0、1、2、3、4、5。
如果我然后取出粒子对象,并从它们的new_track_id属性“ l = [p中的p的p.new_track_id]”中列出来,则值为1、2、3、4、5。
所以在某个地方,第一个对象不知何故丢失,重写或其他我不理解的东西。
答案 0 :(得分:2)
只要坚持构建Python对象,您就不会获得很大的效率/速度提升。拥有这么多项目,将数据保留在numpy数组中将可以为您提供更好的服务。如果您希望更好的属性访问,可以将数组转换为记录数组(recarray
),这将允许您在仍保持命名的情况下命名列(如weight
,height
等)将数据保存在numpy数组中。
dog_t = np.dtype([
('weight', int),
('height', int),
('width', int),
('girth', int)
])
dogs = np.array([
(5, 100, 50, 80),
(4, 80, 30, 70),
(7, 120, 60, 90),
(2, 50, 30, 50),
], dtype=dog_t)
dogs_recarray = dogs.view(np.recarray)
print(dogs_recarray.weight)
print(dogs_recarray[2].height)
如果需要,您还可以混合和匹配数据类型(例如,如果某些列是整数,而另一些则是浮点数)。请注意,在使用此代码时,dogs
数组中的项需要在元组中指定(使用()
),而不是在列表中指定才能正确应用数据类型。
答案 1 :(得分:0)
Multiprocessing可能值得一看。
from multiprocessing import Pool
dog_list = []
将对象追加到列表的功能:
def append_dog(i):
dog_list.append(Dog(*dogs[i]))
让多个工作人员并行追加到此列表:
number_of_workers = 4
pool = Pool(processes=number_of_workers)
pool.map_async(append_dog, range(len(dogs)))
或更短的版本:
from multiprocessing import Pool
number_of_workers = 4
pool = Pool(processes=number_of_workers)
pool.map_async(lambda i: dog_list.append(Dog(*dogs[i])), range(len(dogs)))
答案 2 :(得分:0)
使用一个简单的类:
class Foo():
_id = 0
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.id = self._id
Foo._id += 1
def __repr__(self):
return '<Foo %s>'%self.id
In [23]: arr = np.arange(12).reshape(4,3)
直观的列表理解:
In [24]: [Foo(*xyz) for xyz in arr]
Out[24]: [<Foo 0>, <Foo 1>, <Foo 2>, <Foo 3>]
默认使用vectorize
:
In [26]: np.vectorize(Foo)(*arr.T)
Out[26]: array([<Foo 5>, <Foo 6>, <Foo 7>, <Foo 8>], dtype=object)
请注意,Foo 4
已被跳过。 vectorize
执行试算以确定返回的dtype(此处为object
)。 (这给其他用户带来了问题。)我们可以通过指定otypes
来解决此问题。还有一个cache
参数可能有用,但是我没有玩过。
In [27]: np.vectorize(Foo,otypes=[object])(*arr.T)
Out[27]: array([<Foo 9>, <Foo 10>, <Foo 11>, <Foo 12>], dtype=object)
内部vectorize
使用frompyfunc
,在这种情况下,效果也一样,并且根据我的经验,速度更快:
In [28]: np.frompyfunc(Foo, 3,1)(*arr.T)
Out[28]: array([<Foo 13>, <Foo 14>, <Foo 15>, <Foo 16>], dtype=object)
通常vectorize/frompyfunc
将“标量”值传递给函数,从而迭代2d数组的整个元素。但是使用*arr.T
是传递行的一种聪明方法-实际上是一维元组数组。
In [31]: list(zip(*arr.T))
Out[31]: [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]
一些比较时间:
In [32]: Foo._id=0
In [33]: timeit [Foo(*xyz) for xyz in arr]
14.2 µs ± 17.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [34]: Foo._id=0
In [35]: timeit np.vectorize(Foo,otypes=[object])(*arr.T)
44.9 µs ± 108 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [36]: Foo._id=0
In [37]: timeit np.frompyfunc(Foo, 3,1)(*arr.T)
15.6 µs ± 18.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
这与我过去的时间一致。 vectorize
很慢。 frompyfunc
在列表理解方面具有竞争力,有时甚至快2倍。将列表解析包装在数组中会减慢其速度,例如np.array([Foo(*xyz)...])
。
以及您的原始列表理解:
In [40]: timeit [Foo(arr[i][0],arr[i][1],arr[i][2]) for i in range(len(arr))]
10.1 µs ± 80 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
那甚至更快!因此,如果您的目标是列表而不是数组,那么我看不出使用numpy
工具的意义。
当然,在一个小例子中,这些计时需要谨慎对待。