【问题标题】:How to bin a matrix如何对矩阵进行装箱
【发布时间】:2014-01-28 00:27:30
【问题描述】:

numpy.histogram(data, bins) 是一种非常快速有效的方法来计算数据数组中有多少元素落入由数组 bins 定义的 bin 中。是否有等效功能可以解决以下问题?我有一个 R 行乘以 C 列的矩阵。我想使用 bins 给出的定义对矩阵的每一行进行 bin 处理。结果应该是一个包含 R 行且列数等于 bin 数的矩阵。

我尝试使用函数 numpy.histogram(data, bins) 作为输入矩阵,但我发现矩阵被视为具有 R*C 元素的数组。然后,结果是一个包含 Nbins 个元素的数组。

【问题讨论】:

  • 也;我们可以退后一步吗?很可能你并不真的需要 np.histogram,但更简单但更快的计算就足够了。你的更大目标是什么?
  • 您好 Eelco,感谢您的提问。我正在尝试编写一个非常快速的代码来管理数据系列。我的矩阵中每一行的每个系列。我在到达最终系列之前进行了一些操作。与我的预期相反,我发现在循环中为每一行执行所有这些操作比在整个矩阵上逐个执行操作要慢得多。现在,在这些操作结束时,我想对每个数据系列进行分类。尽管如此,我还没有找到避免for 循环将它们装箱的方法。
  • 目前,我的代码运行时间为 8 秒,其中 5 秒用于分箱。如果我可以将分箱时间减少到其他单个操作的时间(np.histogram 正是我现在使用的方式,我惊讶地发现它将矩阵视为平面数组,而不是保持其结构。当然,正如你所说,如果有更简单但更快的方法,那就太好了。
  • 如果您真的需要分箱,那并不能真正回答;但正如我在下面问的,你确定循环是问题吗?也许分箱本身就是那么昂贵;毕竟,它比大多数标准算术数组操作要昂贵得多。 Bi Rico 的解决方案显然更慢的事实表明可能是这种情况,因为该代码对我来说似乎非常优化。我唯一能想到的是,也许你有很多非常短的行,在这种情况下可能存在更优化的解决方案。
  • 我刚刚在下面回答了你的问题。目前,我正在处理每个具有 ~10E4 个元素的数据系列。

标签: python arrays numpy matrix binning


【解决方案1】:

如果您将其应用于具有许多行的数组,此函数将以一些临时内存为代价为您提供一些加速。

def hist_per_row(data, bins):

    data = np.asarray(data)

    assert np.all(bins[:-1] <= bins[1:])
    r, c = data.shape
    idx = bins.searchsorted(data)
    step = len(bins) + 1
    last = step * r
    idx += np.arange(0, last, step).reshape((r, 1))
    res = np.bincount(idx.ravel(), minlength=last)
    res = res.reshape((r, step))
    return res[:, 1:-1]

最后一行的res[:, 1:-1] 与 numpy.histogram 一致,后者返回一个数组,其长度为len(bins) - 1,但如果你想计算小于和大于bins[0] 的值,可以删除它和bins[-1]

【讨论】:

  • 谢谢。我复制了您的函数,但收到以下消息:res = np.bincount(idx.ravel(), minlength=last) ValueError: object too deep for desired array
  • @user3242568,我猜你使用的是 numpy.matrix 类型而不是 numpy.ndarray 类型。您可以阅读有关here 的两种数据类型。如果是这种情况,修复很容易,只需将data 包装在对np.asarray 的调用中。我已经更新了答案。在旁注中,我几乎总是建议使用ndarray 而不是matrix,因为matrix 只会破坏东西。但你知道,有些人喜欢让他们的生活变得更加艰难:)。
  • 使用新行代码现在可以工作,但我很遗憾地说它比for 循环还要慢。我正在使用time.clock() 计算 CPU 时间。不知道有没有更好的办法。
  • 您可以使用timeit module,或者如果您碰巧使用ipython,您可以使用%timeit magic
【解决方案2】:

感谢大家的回答和cmets。最后,我找到了一种加快分箱过程的方法。我没有使用np.searchsorted(data),而是使用np.array(data*nbins, dtype=int)。将这一行替换为 Bi Rico 发布的代码,我发现它的速度提高了 3 倍。下面我将 Bi Rico 的功能与我的修改一起发布,以便其他用户可以轻松使用它。

def hist_per_row(data, bins):

    data = np.asarray(data)
    assert np.all(bins[:-1] <= bins[1:])
    r, c = data.shape

    nbins = len(bins)-1
    data = data/bins[-1]
    idx = array(data*nbins, dtype=int)+1

    step = len(bins) + 1
    last = step * r
    idx += np.arange(0, last, step).reshape((r, 1))
    res = np.bincount(idx.ravel(), minlength=last)
    res = res.reshape((r, step))
    return res[:, 1:-1]

【讨论】:

  • 那是我要求的更多上下文;显然你的垃圾箱都是等距的,而你的问题的措辞恰恰相反。作为对其他用户的说明(如果您不知道,可能是您自己),直接映射到整数并使用 bincount 仅适用于等间距的 bin
  • 是的,当然。很抱歉在我使用等间距的垃圾箱之前没有提到
【解决方案3】:

类似的东西?

import numpy as np
data = np.random.rand(10,20)
print np.apply_along_axis(lambda x: np.histogram(x)[0], 1, data)

【讨论】:

  • 好的,感谢您的提示。以下表格完全符合我的需求:print np.apply_along_axis(lambda x: np.histogram(x, bins)[0], 1, data)
  • 无论如何,用我在代码中的矩阵行上的循环替换这一行,我发现 CPU 时间没有改变。我希望比“for”循环更快。
  • @user3242568 np.apply_along_axis 仍然在 Python 中完成所有循环,因此它本质上只是一种在语法上更简洁的传统 for 循环方式
  • 确实,看来你 (ali_m) 是完全正确的。但是,我注意到,例如两个矩阵之间的乘法执行写入matrixA*matrixB 比在列和行上使用显式for 循环执行相同的操作要快得多。我想知道对于分箱是否有与上面示例中的乘法类似的运算。
  • 您确定循环是您的问题,而不是 np.histogram 本身吗? python 循环可能很慢,但除非你的行真的很短,否则这个成本很容易摊销。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
相关资源
最近更新 更多