【发布时间】:2021-02-22 10:25:19
【问题描述】:
我想计算 pi,它总结了特定类中的几个标签/GaussianMixture 模型中的标签总数。
tr_y 是一个熊猫数据框
| index | labels |
|---|---|
| 0 | 6 |
| 1 | 5 |
| 2 | 6 |
| 3 | 5 |
| 4 | 6 |
1000 行 × 1 列。
然后我尝试比较两种方法:
- 第一个是使用列表:
%%timeit
y_list = tr_y.values.flatten().tolist()
>>> 12.3 µs ± 193 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%%timeit
sum([1 if y == 5 else 0 for y in y_list]) / len(y_list)
54.9 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
- 第二种方法是使用numpy数组:
%%timeit
arr = tr_y.to_numpy()
>>> 4.55 µs ± 92.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%%timeit
sum([1 for i in arr if i == 5 ])/arr.__len__()
>>> 883 µs ± 48 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
如果使用 tolist() 将 NumPy 转换为列表,则更新的方法比之前的两种方法快得多。
arr = tr_y.to_numpy().tolist()
%%timeit
sum([1 for i in arr if i == 5 ])/arr.__len__()
>>> 43.1 µs ± 410 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
source最后一种方法
所以使用列表比使用 NumPy 数组更快。
我已经搜索过了,我发现 NumPy 必须使用 python 类型(例如,在这种情况下为 NumPy.float64 或 NumPy.int64)包装返回的对象,如果您逐项迭代,这需要时间-项目1。迭代时进一步证明了这一点——我们看到我们在迭代数组时在 2 个单独的 ID 之间交替。这意味着python的内存分配器和垃圾收集器超时工作以创建新对象然后释放它们。
列表没有这种内存分配器/垃圾收集器开销。列表中的对象已经作为 python 对象存在(并且它们在迭代后仍然存在),因此它们在列表的迭代中都没有任何作用。
我的搜索得出的结论是,如果我们需要处理多维矩阵或进行一些向量化,我们必须更快地使用 NumPy 数组并占用更少的内存。这是真的吗?
我要计算的另一件事是 Numpy 数组和列表的内存消耗。尽管如此,我发现sys.sizeOf 并不可靠,它给了我们指针数组和容器头的大小,这是越来越多的考虑因素。有没有可靠的方法来计算内存消耗?
另一项调查是,当我将 NumPy 数组转换为列表时,我将其转换为行矩阵,该行矩阵会立即上传到 L1 缓存中,而不是 col 向量,这会在 L1 缓存中产生很多错误。 source
那么如果我们使用 Fortran 顺序的向量呢?
【问题讨论】:
-
像这样使用 NumPy 就像你走到商店时用手把车拖在身后 - 你实际上并没有使用你可以使用的工具的力量。
-
@user2357112supportsMonica,你能解释一下这是怎么回事吗?
-
您在 Python 级别进行迭代,而不是在 NumPy 代码中直接迭代机器整数。
-
我想如果你为你的 1000 个元素的数组做
%timeit np.count_nonzero(arr==5)/arr.size,你会有一个大发现。