Python 减少使用numpy的代码行的内存使用

Python 减少使用numpy的代码行的内存使用,python,numpy,memory,Python,Numpy,Memory,我正在使用python库: 用于规范化我的数据集(scRNAseq)和库文件utils.py中的最后一行: 使用太多内存: np.percentile(matrix[np.any(matrix > 0, axis=1)], p, axis=0) 有没有重写这行代码以提高内存使用率的好方法?我的意思是,我可以在集群上访问200GbRAM,使用20Gb之类的matrix,这行代码不起作用,但我相信应该有办法让它起作用。我将此作为一个答案,因为注释中包含的内容可能不完整。有两件可疑的事情-

我正在使用
python
库:

用于规范化我的数据集(
scRNAseq
)和库文件
utils.py
中的最后一行:

使用太多内存:

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

有没有重写这行代码以提高内存使用率的好方法?我的意思是,我可以在集群上访问
200Gb
RAM
,使用
20Gb
之类的
matrix
,这行代码不起作用,但我相信应该有办法让它起作用。

我将此作为一个答案,因为注释中包含的内容可能不完整。有两件可疑的事情-如果您的机器有200Gb的可用ram,那么第一个百分比在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(矩阵>0,轴=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])
该数组用于索引
矩阵
,它只选择不全为零的行并返回这些行。 如果你不熟悉这种索引方法,你应该仔细阅读

需要大量内存的计算-
matrix>0
创建一个与matrix维度相同的布尔数组,然后索引创建一个可能包含大多数行的
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
中的值(即
matrix[matrix<0]=np.nan
),然后调用该值。

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

np.percentile(matrix[np.any(matrix, axis = 1)], p, axis = 0)
这使用了一个事实,即当作为布尔值查看时,
0
以外的任何浮点或整数都被解释为
True
(这是
np.any
内部执行的)。避免了单独构建大型布尔矩阵

由于您在
矩阵[…]中进行布尔索引
,因此您正在创建一个临时副本,而您并不真正关心它是否在
百分位数
过程中被覆盖。因此,您可以使用
overwrite\u 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

最后,根据您的其他架构,我建议您考虑制作
matrix
某种风格的
scipy.sparse
,这将再次显著降低内存使用率(尽管根据您使用的类型会有一些缺点)。

我最近回答的一个问题与此类似(使用
np.median
,它在内部使用百分位)。基本上,您需要采用每列百分位来防止其占用内存,类似于“[np.percentile(c,p)for c in cmatrix[np.any(矩阵>0,轴=1)]].T”()好的,那么什么是
cmatrix
?仅仅是
matrix.T
?还是仅仅是一个打字错误?我实际上将行更改为
[np.百分位(c,p)表示矩阵中的c[np.any(矩阵>0,轴=1)]].T
但它没有解决内存问题。可能应该进一步改进。是的,这是一个打字错误。您如何测量内存使用率?我还需要将其更改为
np.array([np.percentile(c,p)表示矩阵中的c[np.any(矩阵>0,轴=1)]).T
因为
列表
没有
.T
功能。但它仍然不工作:我收到内存错误。仍然无法修复。错误仍然存在。我尝试使用
sacct--format="JobID,expead,MaxVMSize
,它为作业提供了
205Gb
的内存。对,所以这一行可能不是导致内存问题的那一行。它只是因为进程的某个部分占用内存而导致最终失败的那一行。您运行了
top
吗?它显示了什么?我运行了,但它是SLURM集群,确实如此不显示任何相关内容我认为答案不错,但我不确定为什么要比较
mat
matrix
。即使它们对某些输入比较为真,但在使用
overwrite\u input=true
@user2699时也不能保证。你能给出一个不起作用的示例吗?我找不到任何示例,以及
matrix
应该是一个副本,而不是原始
matrix
的视图。因此,如果它被更改,它不应该影响
matrix
本身。你是对的。因为它在副本上运行,所以原始版本不会更改,我错过了。