numpy数组:快速填充和提取数据

M K*_*atz 4 python arrays performance numpy loading

请参阅此问题底部的重要说明.

我正在使用numpy来加速经度/纬度坐标的处理.不幸的是,我的numpy"优化"使我的代码运行速度比没有使用numpy时运行的速度快5倍.

瓶颈似乎是用我的数据填充numpy数组,然后在完成数学转换后提取出数据.为了填充数组,我基本上有一个循环:

point_list = GetMyPoints() # returns a long list of ( lon, lat ) coordinate pairs
n = len( point_list )
point_buffer = numpy.empty( ( n, 2 ), numpy.float32 )

for point_index in xrange( 0, n ):
    point_buffer[ point_index ] = point_list[ point_index ]
Run Code Online (Sandbox Code Playgroud)

那个循环,只是在操作它之前填充numpy数组,非常慢,比整个计算没有numpy慢得多.(也就是说,它不仅仅是python循环本身的缓慢,而且实际上将每个小块数据从python转移到numpy显然是一些巨大的开销.)另一端有类似的缓慢; 在我处理了numpy数组之后,我再次访问循环中的每个修改过的坐标对

some_python_tuple = point_buffer[ index ]
Run Code Online (Sandbox Code Playgroud)

再次将数据拉出的循环比没有numpy的整个原始计算要慢得多.那么,我如何实际填充numpy数组并从numpy数组中提取数据的方式不会破坏首先使用numpy的目的?

我正在使用C库从形状文件中读取数据,该库将数据作为常规python列表提供给我.据我所知,如果图书馆将坐标传递给一个numpy数组,那么就不会"填充"必要的numpy数组.但不幸的是,数据的起点是常规的python列表.更重要的是,一般来说,我想了解如何使用python中的数据快速填充numpy数组.

澄清

上面显示的循环实际上过于简单了.我在这个问题中就是这样写的,因为我想专注于我试图在循环中慢慢填充numpy数组的问题.我现在明白这样做很慢.

在我的实际应用中,我所拥有的是坐标点的形状文件,我有一个API来检索给定对象的点.有200,000个对象.所以我反复调用一个函数GetShapeCoords( i )来获取对象i的坐标.这将返回一个列表列表,其中每个子列表是lon/lat对的列表,并且它是列表列表的原因是某些对象是多部分的(即多边形).然后,在我的原始代码中,当我读入每个对象的点时,我通过调用常规python函数对每个点进行转换,然后使用PIL绘制转换后的点.整个过程花了大约20秒来绘制所有200,000个多边形.并不可怕,但还有很大的提升空间.我注意到这20秒中至少有一半用于转换逻辑,所以我想我会在numpy中做到这一点.我最初的实现只是一次读取一个对象,并继续将子列表中的所有点附加到一个大的numpy数组中,然后我可以在numpy中进行数学运算.

所以,我现在明白,简单地将整个python列表传递给numpy是设置大数组的正确方法.但在我的情况下,我一次只读一个对象.所以我能做的一件事就是在一个列表列表的大蟒蛇列表中继续追加点.然后当我以这种方式编译大量对象的点(比如10000个对象)时,我可以简单地将该怪物列表分配给numpy.

所以现在我的问题是三个部分:

(a)numpy是否可以采用那些大的,形状不规则的列表清单,并且可以快速地将其剔除?

(b)然后我希望能够转换那棵怪物树的叶子中的所有点.例如,"进入每个子列表,然后进入每个子子列表,然后对于在这些子列表中找到的每个坐标对,将第一个(lon坐标)乘以0.5",表达式是什么?我能这样做吗?

(c)最后,我需要将这些变换的坐标取回以绘制它们.

Winston在下面的回答似乎暗示了我如何使用itertools完成所有这些工作.我想要做的就像温斯顿所做的那样,将列表弄平.但我不能完全把它弄平.当我去绘制数据时,我需要能够知道一个多边形停止和下一个多边形何时开始.所以,我认为如果有一种方法可以快速标记每个多边形(即每个子子列表)的末尾,并使用特殊的坐标对(如(-1000,-1000)或类似的东西),我可以使它工作.然后我可以像在温斯顿的回答中那样使用itertools压扁,然后在numpy中进行变换.然后我需要实际使用PIL从点到点绘制,在这里我想我需要将修改后的numpy数组重新分配回python列表,然后在常规python循环中遍历该列表以进行绘制.这似乎是我最好的选择,只是编写一个C模块,只需一步即可处理所有的阅读和绘图?

Win*_*ert 5

您将数据描述为"坐标列表列表".从这里我猜你的提取看起来像这样:

for x in points:
   for y in x:
       for Z in y:
           # z is a tuple with GPS coordinates
Run Code Online (Sandbox Code Playgroud)

做这个:

# initially, points is a list of lists of lists
points = itertools.chain.from_iterable(points)
# now points is an iterable producing lists
points = itertools.chain.from_iterable(points)
# now points is an iterable producing coordinates
points = itertools.chain.from_iterable(points)
# now points is an iterable producing individual floating points values
data = numpy.fromiter(points, float)
# data is a numpy array containing all the coordinates
data = data.reshape( data.size/2,2)
# data has now been reshaped to be an nx2 array
Run Code Online (Sandbox Code Playgroud)

itertools和numpy.fromiter都是用c实现的,效率很高.因此,这应该很快进行转换.

问题的第二部分并没有真正说明您想要对数据做什么.索引numpy数组比索引python列表要慢.通过对数据执行大量操作,您可以获得速度.如果不了解您正在使用该数据做什么,很难建议如何解决它.

更新:

我已经使用itertools和numpy完成了所有事情.我对因试图理解此代码而导致的任何脑损伤概不负责.

# firstly, we use imap to call GetMyPoints a bunch of times
objects = itertools.imap(GetMyPoints, xrange(100))
# next, we use itertools.chain to flatten it into all of the polygons
polygons = itertools.chain.from_iterable(objects)
# tee gives us two iterators over the polygons
polygons_a, polygons_b = itertools.tee(polygons)
# the lengths will be the length of each polygon
polygon_lengths = itertools.imap(len, polygons_a)
# for the actual points, we'll flatten the polygons into points
points = itertools.chain.from_iterable(polygons_b)
# then we'll flatten the points into values
values = itertools.chain.from_iterable(points)

# package all of that into a numpy array
all_points = numpy.fromiter(values, float)
# reshape the numpy array so we have two values for each coordinate
all_points = all_points.reshape(all_points.size // 2, 2)

# produce an iterator of lengths, but put a zero in front
polygon_positions = itertools.chain([0], polygon_lengths)
# produce another numpy array from this
# however, we take the cumulative sum
# so that each index will be the starting index of a polygon
polygon_positions = numpy.cumsum( numpy.fromiter(polygon_positions, int) )

# now for the transformation
# multiply the first coordinate of every point by *.5
all_points[:,0] *= .5

# now to get it out

# polygon_positions is all of the starting positions
# polygon_postions[1:] is the same, but shifted on forward,
# thus it gives us the end of each slice
# slice makes these all slice objects
slices = itertools.starmap(slice, itertools.izip(polygon_positions, polygon_positions[1:]))
# polygons produces an iterator which uses the slices to fetch
# each polygon
polygons = itertools.imap(all_points.__getitem__, slices)

# just iterate over the polygon normally
# each one will be a slice of the numpy array
for polygon in polygons:
    draw_polygon(polygon)
Run Code Online (Sandbox Code Playgroud)

您可能会发现最好一次处理一个多边形.将每个多边形转换为numpy数组并对其执行向量运算.这样做你可能会获得显着的速度优势.将所有数据放入numpy可能有点困难.

由于你形状奇特的数据,这比大多数numpy东西更难.Numpy几乎假设一个统一形状数据的世界.