Python numpy数组扩展效率问题

时间:2022-11-18 21:25:58

Numpy库的ndarray数组可以方便地进行各种多维数据处理工作


可是它最大的缺点就是不可动态扩展——“NumPy的数组没有这种动态改变大小的功能,numpy.append()函数每次都会重新分配整个数组,并把原来的数组复制到新数组中。”(引用自http://blog.chinaunix.net/uid-23100982-id-3164530.html


场景:

今天用ndarray处理 42000 条数据时,就遇到了数组扩展的效率问题

文件名:train.csv(后附下载)

文件大小:73.2MB

文件格式:.csv

文件数据:42001 * 785

文件说明:第一行为标题栏,忽略;第一列为样本标签栏

目标:读取所有数据,样本值存入一个矩阵,样本标签存入另一个矩阵


方式1:

我首先想到的是 ndarray.vstack() 方法,它的作用是合并两个矩阵。思路是,先新建一个只有一行的ndarray,然后每次按行读取文件,将读到的数据与原矩阵合并。最后构成42000行的矩阵数据。


代码:

from numpy import *
import time

DIM = 28


def img2matrix(filename):
start_time = time.time()

fr = open(filename)

#drop the header
fr.readline()

return_mat = array(fr.readline().strip().split(','))[1::]
labels = array(['vector[0]'])

training_num = 0

for line in fr.readlines():
vector = line.strip().split(',')
labels = hstack((labels, array([vector[0]])))
return_mat = vstack((return_mat, vector[1::]))
training_num += 1
print training_num

end_time = time.time()
print end_time - start_time

return return_mat, labels, training_num


结果:

1096.56099987 #约合18分钟


原因分析:

经过检查,ndarray.vstack() 是本程序的性能瓶颈。这时由于该函数的工作原理是将所有return_mat的数据与vector的数据复制出来,重新创建一个新的矩阵写入,因此相当地耗费时间。运行时也容易观察到随着return_mat的扩大,程序运行越来越慢(将training_num输出,一开始上千地跳,后来几百,后来几十,后来几个……)。


下面是github上 vstack的源代码,_nx.concatenate的源代码没找到、、、哪位大神找到了告诉一下~

def vstack(tup):
"""
Stack arrays in sequence vertically (row wise).

Take a sequence of arrays and stack them vertically to make a single
array. Rebuild arrays divided by `vsplit`.

Parameters
----------
tup : sequence of ndarrays
Tuple containing arrays to be stacked. The arrays must have the same
shape along all but the first axis.

Returns
-------
stacked : ndarray
The array formed by stacking the given arrays.

See Also
--------
hstack : Stack arrays in sequence horizontally (column wise).
dstack : Stack arrays in sequence depth wise (along third dimension).
concatenate : Join a sequence of arrays together.
vsplit : Split array into a list of multiple sub-arrays vertically.

Notes
-----
Equivalent to ``np.concatenate(tup, axis=0)`` if `tup` contains arrays that
are at least 2-dimensional.

Examples
--------
>>> a = np.array([1, 2, 3])
>>> b = np.array([2, 3, 4])
>>> np.vstack((a,b))
array([[1, 2, 3],
[2, 3, 4]])

>>> a = np.array([[1], [2], [3]])
>>> b = np.array([[2], [3], [4]])
>>> np.vstack((a,b))
array([[1],
[2],
[3],
[2],
[3],
[4]])

"""
return _nx.concatenate([atleast_2d(_m) for _m in tup], 0)


方式2:

既然一行行扩展太慢,那干脆来点儿暴力的,一次性把 return_mat 大小建好,再改改里面的数据即可。


代码:

from numpy import *
import time

DIM = 28


def img2matrix(filename):
start_time = time.time()

fr = open(filename)

training_num = len(fr.readlines())-1
return_mat = zeros((training_num, DIM*DIM))
labels = array(['vector[0]'])

index = 0
fr.seek(0, 0)
# drop the header
fr.readline()
for line in fr.readlines():
vector = line.strip().split(',')
labels = hstack((labels, array([vector[0]])))
return_mat[index, :] = vector[1::]
index += 1

end_time = time.time()
print end_time - start_time

return return_mat, labels, training_num


结果:

7.63100004196 #约合 7.6秒


原因分析:

可以看出,ndarray的“肚量”还是挺大,一个 42000 * 784 的矩阵创建起来毫无压力。至于具体有多大,以及超过这个大小是怎么处理?可以参考*的帖子: Very large matrices using Python and NumPy



测试数据下载地址:https://www.kaggle.com/c/digit-recognizer/data  欢迎同志加入我的 Kaggle 项目组 :)