从numpy矩阵中提取子矩阵的快速方法

时间:2017-09-03 13:05:15

标签: python python-3.x numpy submatrix

我有一个巨大的.csv文件(~2GB),我在我的程序中使用read_csv导入,然后转换为带有as_matrix的numpy矩阵。生成的矩阵的形式类似于下面给出示例中的data_mat。我现在的问题是,我需要使用相同的uuid4(矩阵的第一列中的条目)提取块。然后子矩阵由另一个函数处理。看来我下面的例子不是最好的方法。欢迎更快的方法。

import numpy as np
data_mat = np.array([['f9f1dc71-9457-4d17-b5d1-e63b5a766f84', 4, 3, 1],\
                     ['f9f1dc71-9457-4d17-b5d1-e63b5a766f84', 3, 1, 1],\
                     ['f9f1dc71-9457-4d17-b5d1-e63b5a766f84', 3, 3, 1],\
                     ['f9f1dc71-9457-4d17-b5d1-e63b5a766f84', 6, 1, 1],\
                     ['f35fb25b-dddc-458a-9f71-0a9c2c202719', 3, 4, 1],\
                     ['f35fb25b-dddc-458a-9f71-0a9c2c202719', 3, 1, 1],\
                     ['a4cf92fc-0624-4a00-97f6-0d21547e3183', 3, 2, 1],\
                     ['a4cf92fc-0624-4a00-97f6-0d21547e3183', 3, 9, 0],\
                     ['a4cf92fc-0624-4a00-97f6-0d21547e3183', 3, 1, 0],\
                     ['a4cf92fc-0624-4a00-97f6-0d21547e3183', 5, 1, 1],\
                     ['a4cf92fc-0624-4a00-97f6-0d21547e3183', 3, 1, 1],\
                     ['d3a8a9d0-4380-42e3-b35f-733a9f9770da', 3, 6, 10]],dtype=object)

unique_ids, indices = np.unique(data_mat[:,0],return_index=True,axis=None)
length = len(data_mat)
i=0
for idd in unique_ids:
    index = indices[i]
    k=0
    while ((index+k)<length and idd == data_mat[index+k,0]):        
        k+=1
    tmp_mat=data_mat[index:(index+k),:]
    # do something with tmp_mat ...
    print(tmp_mat)
    i+=1

3 个答案:

答案 0 :(得分:2)

优化想法是在我们进入循环后最小化计算。因此,考虑到这一点,我们将重新排列数组的行,按第一列排序。然后,获取定义边界的索引。最后,启动我们的循环并简单地为每个组切片以在每次迭代时获得子矩阵。使用数组时切片几乎是免费的,这样可以帮助我们。

因此,一个实现将是 -

a0 = data_mat[:,0]
sidx = a0.argsort()
sd = data_mat[sidx] # sorted data_mat
idx = np.flatnonzero(np.concatenate(( [True], sd[1:,0] != sd[:-1,0], [True] )))
for i,j in zip(idx[:-1], idx[1:]):
    tmp_mat = sd[i:j]
    print tmp_mat

如果您希望将每个子矩阵存储为数组以将数组列表作为最终输出,只需执行 -

[sd[i:j] for i,j in zip(idx[:-1], idx[1:])]

对于已排序的data_mat

对于data_mat已经如示例中所示进行排序的情况,我们可以避免对整个数组进行排序并直接使用第一列,如下所示 -

a0 = data_mat[:,0]
idx = np.flatnonzero(np.concatenate(( [True], a0[1:] != a0[:-1], [True] )))

for i,j in zip(idx[:-1], idx[1:]):
    tmp_mat = data_mat[i:j]
    print(tmp_mat)

再次,要将所有这些子矩阵作为数组列表,请使用 -

[data_mat[i:j] for i,j in zip(idx[:-1], idx[1:])]

请注意,我们用这个子矩阵获得的子矩阵的顺序与前一种方法中的排序顺序不同。

排序data_mat

的基准测试

方法 -

# @Daniel F's soln-2
def split_app(data_mat):
    idx = np.flatnonzero(data_mat[1:, 0] != data_mat[:-1, 0]) + 1
    return np.split(data_mat, idx)

# Proposed in this post
def zip_app(data_mat):
    a0 = data_mat[:,0]
    idx = np.flatnonzero(np.concatenate(( [True], a0[1:] != a0[:-1], [True] )))
    return [data_mat[i:j] for i,j in zip(idx[:-1], idx[1:])]

计时 -

在样本中,我们有一个最大长度为6的子矩阵。所以,让我们扩展到一个更大的案例,保持相同的模式 -

In [442]: a = np.random.randint(0,100000,(6*100000,4)); a[:,0].sort()

In [443]: %timeit split_app(a)
10 loops, best of 3: 88.8 ms per loop

In [444]: %timeit zip_app(a)
10 loops, best of 3: 40.2 ms per loop

In [445]: a = np.random.randint(0,1000000,(6*1000000,4)); a[:,0].sort()

In [446]: %timeit split_app(a)
1 loop, best of 3: 917 ms per loop

In [447]: %timeit zip_app(a)
1 loop, best of 3: 414 ms per loop

答案 1 :(得分:2)

您可以使用布尔索引执行此操作。

unique_ids = np.unique(data_mat[:, 0])
masks = np.equal.outer(unique_ids, data_mat[:, 0])
for mask in masks:
    tmp_mat = data_mat[mask]
    # do something with tmp_mat ...
    print(tmp_mat)

答案 2 :(得分:0)

如果已对唯一ID进行了分组,则可以使用Accept-Language执行此操作,类似于@Divakar

_locale