为了账号安全,请及时绑定邮箱和手机立即绑定

减少使用 numpy 的一行代码的内存使用量

减少使用 numpy 的一行代码的内存使用量

ibeautiful 2021-06-13 18:45:11
我正在使用python图书馆:https://github.com/ficusss/PyGMNormalize用于规范化我的数据集 ( scRNAseq) 和库文件中的最后一行utils.py:https://github.com/ficusss/PyGMNormalize/blob/master/pygmnormalize/utils.py使用太多内存:np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0)有没有什么好办法重写这行代码来提高内存使用率?我的意思是我200Gb RAM可以在集群上访问,并且matrix像20Gb这条线这样的东西无法正常工作,但我相信应该有办法让它工作。
查看完整描述

2 回答

?
qq_遁去的一_1

TA贡献1725条经验 获得超7个赞

如果的所有元素matrix都 >=0,那么您可以执行以下操作:


np.percentile(matrix[np.any(matrix, axis = 1)], p, axis = 0)

这使用了这样一个事实,即任何浮点数或整数都0被解释为True当被视为布尔值时(np.any在内部执行)。使您无需单独构建那个大的布尔矩阵。


由于您在 中进行布尔索引matrix[...],因此您正在创建一个临时副本,您并不真正关心它是否在此percentile过程中被覆盖。因此,您可以使用它overwrite_input = True来节省更多内存。


mat = matrix.copy()

perc = np.percentile(matrix[np.any(matrix, axis = 1)], p, axis = 0, overwrite_input = True)

np.array_equals(mat, matrix) # is `matrix` still the same?


True

最后,这取决于你的其他archetecture,我建议你寻找到做matrix的一些味道scipy.sparse,(虽然有一些缺点,这取决于你所使用的类型),这应该siginficantly再次减少内存的使用情况。


查看完整回答
反对 回复 2021-06-15
?
摇曳的蔷薇

TA贡献1793条经验 获得超6个赞

我将其作为答案,因为评论中包含的内容太多了,尽管它可能不完整。有两个可疑的事情 - 如果您的机器有 200Gb 的可用内存,那么第一个百分位数应该在 20Gb 矩阵上运行良好。那是很多内存,所以开始研究还有什么可能会使用它。从top- 是否有其他进程或您的 python 程序使用所有这些?


第二个可疑的事情是文档utils.percentile与它的实际行为不匹配。这是您链接到的代码中的相关位:


def percentile(matrix, p):

    """

    Estimation of percentile without zeros.

    ....

    Returns

    -------

    float

        Calculated percentile.

    """

    return np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0)

它实际做的是返回为不全为零的行计算的(按列)百分位数。编辑那是包含至少一个正元素的行。如果值是非负的,那是一样的,但总的来说,这将是一个非常不同的结果。


np.any(matrix > 0, axis=1)返回一个布尔数组来索引不全为零的行。例如


>>> np.any(array([[3, 4], [0, 0]]) > 0, axis=1)

    array([ True, False])


>>> np.any(array([[3, 4], [1, 0]]) > 0, axis=1)

    array([ True,  True])


>>> np.any(array([[3, 0], [1, 0]]) > 0, axis=1)

    array([ True,  True])

该数组用于 index matrix,它仅选择不全为零的行并返回那些行。如果您不熟悉这种索引方式,您应该阅读用于索引的 numpy 文档。


计算需要大量内存 -matrix > 0创建一个与矩阵维度相同的布尔数组,然后索引创建一个matrix可能包含大部分行的副本。

因此,布尔数组可能为 2-4Gb,副本可能接近 20Gb。


可以减少,


## Find rows with all zeros, one row at a time to reduce memory

mask = [np.any(r > 0) for r in matrix]  

 ## Find percentile for each column, excluding rows with all zeros

perc = [np.percentile(c[mask], p) for c in matrix.T] 

但是,如前所述,这与功能文档不匹配。


这种逻辑可能是有原因的,但很奇怪。如果您不知道原因,您可能可以np.percentile直接调用- 只需检查它是否为较小的数据子集返回了一个接近值。还有nanpercentile, 可以以相同的方式使用,但忽略nan值。

您可以使用布尔索引来替换您不想包含在nan(ie matrix[matrix < 0] = np.nan) 中的值,然后调用它。


查看完整回答
反对 回复 2021-06-15
  • 2 回答
  • 0 关注
  • 87 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信