Numpy跨多个轴进行聚合

huangapple go评论140阅读模式
英文:

Numpy aggregate across multiple axes

问题

让我们假设我有一个形状为(27, 27, 27)的3D numpy数组。我想通过同时对每个轴上的每3个元素求平均值来将其压缩为(9, 9, 9)(例如,将3x3x3像素变成1x1x1)。目标是通过一个整数在三个轴上同时有效地压缩(假设任何数组的每个轴的形状都是该整数的倍数)。

我的初始尝试是使用np.apply_over_axes,尽管我担心它不是在所有3个轴上获取立方均值,而是依次平均每个轴。

  1. def mean_over(arr, axis):
  2. return np.average(arr.reshape(-1, 3), axis=axis)
  3. the_array_small = np.apply_over_axes(mean_over, the_array, [0,1,2])

然而,这会返回一个错误:

  1. Traceback (most recent call last):
  2. File "<stdin>", line 1, in <module>
  3. File "<__array_function__ internals>", line 180, in apply_over_axes
  4. File "/opt/homebrew/Caskroom/mambaforge/base/envs/seaborn/lib/python3.10/site-packages/numpy/lib/shape_base.py", line 496, in apply_over_axes
  5. if res.ndim == val.ndim:
  6. AttributeError: 'NoneType' object has no attribute 'ndim'

我不确定我的apply_over_axes解决方案是否得到我想要的聚合。理想情况下,应返回每个(3,3,3)组件的平均值。

英文:

Let's say I have a 3d numpy array.shape of (27,27,27). I want to compress this to (9,9,9) by averaging every 3 elements across every axis simultaneously (e.g. make 3x3x3 pixels into 1x1x1).  The objective is to effectively compress by a single integer across all three axes simultaneously (with the assumption that any array will have a multiple of that integer for the shape of each axes).

My initial attempt was to use np.apply_over_axes, though I'm worried it is not getting the cubic mean of all 3 axes but instead averaging each sequentially.

  1. def mean_over(arr, axis):
  2. &#160; &#160; np.average(arr.reshape(-1, 3), axis=axis)
  3. the_array_small = np.apply_over_axes(mean_over, the_array, \[0,1,2\])

However this returns an error:

  1. Traceback (most recent call last):
  2. &#160; File &quot;\&lt;stdin\&gt;&quot;, line 1, in \&lt;module\&gt;
  3. &#160; File &quot;\&lt;\__array_function_\_ internals\&gt;&quot;, line 180, in apply_over_axes
  4. &#160; File &quot;/opt/homebrew/Caskroom/mambaforge/base/envs/seaborn/lib/python3.10/site-packages/numpy/lib/shape_base.py&quot;, line 496, in apply_over_axes
  5. &#160; &#160; if res.ndim == val.ndim:
  6. AttributeError: &#39;NoneType&#39; object has no attribute &#39;ndim&#39;

I'm not convinced my apply_over_axes solution gets the aggregation I'm aiming for though. Ideally the mean of each (3,3,3) component is returned.

答案1

得分: 4

  1. arr=np.random.rand(27,27,27)
  2. the_array_small = arr.reshape(9,3,9,3,9,3).mean(axis=(1,3,5))
英文:

Just a first answer (but again, depending on your answer to my comment, it could be better to rely on some convolution)

  1. arr=np.randon.rand(27,27,27)
  2. the_array_small = arr.reshape(9,3,9,3,9,3).mean(axis=(1,3,5))

答案2

得分: 3

另一种解决方案,但利用了 as_strided

  1. a = np.arange(27**3).reshape(27, 27, 27)
  2. tile_size = (3, 3, 3)
  3. tile_shape = tuple(np.array(a.shape) // np.array(tile_size))
  4. tile_strides = tuple(np.array(a.strides) * np.array(tile_size)) + tuple(a.strides)
  5. tile_view = np.lib.stride_tricks.as_strided(
  6. a,
  7. shape=tile_shape + tile_size,
  8. strides=tile_strides,
  9. writeable=False,
  10. )
  11. result = np.mean(tile_view, axis=(-3, -2, -1))
英文:

Another solution but take advantage of as_strided

  1. a = np.arange(27**3).reshape(27, 27, 27)
  2. tile_size = (3, 3, 3)
  3. tile_shape = tuple(np.array(a.shape) // np.array(tile_size))
  4. tile_strides = tuple(np.array(a.strides) * np.array(tile_size)) + tuple(a.strides)
  5. tile_view = np.lib.stride_tricks.as_strided(
  6. a,
  7. shape=tile_shape + tile_size,
  8. strides=tile_strides,
  9. writeable=False,
  10. )
  11. result = np.mean(tile_view, axis=(-3, -2, -1))

答案3

得分: 1

使用来自此答案中的 cubify 函数,您可以将数组分成立方体。然后,您可以使用 apply_over_axes 来获取平均值并为您所需的结果进行重塑。这里我使用一个9x9x9的立方体的示例,因为这样更容易看到结果。

  1. import numpy as np
  2. def cubify(arr, newshape):
  3. &quot;&quot;&quot;https://stackoverflow.com/a/42298440/12131013&quot;&quot;&quot;
  4. oldshape = np.array(arr.shape)
  5. repeats = (oldshape / newshape).astype(int)
  6. tmpshape = np.column_stack([repeats, newshape]).ravel()
  7. order = np.arange(len(tmpshape))
  8. order = np.concatenate([order[::2], order[1::2]])
  9. # newshape must divide oldshape evenly or else ValueError will be raised
  10. return arr.reshape(tmpshape).transpose(order).reshape(-1, *newshape)
  11. a = np.arange(9**3).reshape(9,9,9)
  12. c = cubify(a, (3,3,3))
  13. res = np.apply_over_axes(np.mean, c, [1,2,3]).reshape(3,3,3)

结果:

  1. array([[[ 91., 94., 97.],
  2. [118., 121., 124.],
  3. [145., 148., 151.]],
  4. [[334., 337., 340.],
  5. [361., 364., 367.],
  6. [388., 391., 394.]],
  7. [[577., 580., 583.],
  8. [604., 607., 610.],
  9. [631., 634., 637.]]])
英文:

Using the cubify function from this answer, you can break your array into cubes. With that result, you can then use apply_over_axes to get the averages and reshape for your desired result. Here I use an example of a 9x9x9 cube since it's easier to see the result that way.

  1. import numpy as np
  2. def cubify(arr, newshape):
  3. &quot;&quot;&quot;https://stackoverflow.com/a/42298440/12131013&quot;&quot;&quot;
  4. oldshape = np.array(arr.shape)
  5. repeats = (oldshape / newshape).astype(int)
  6. tmpshape = np.column_stack([repeats, newshape]).ravel()
  7. order = np.arange(len(tmpshape))
  8. order = np.concatenate([order[::2], order[1::2]])
  9. # newshape must divide oldshape evenly or else ValueError will be raised
  10. return arr.reshape(tmpshape).transpose(order).reshape(-1, *newshape)
  11. a = np.arange(9**3).reshape(9,9,9)
  12. c = cubify(a, (3,3,3))
  13. res = np.apply_over_axes(np.mean, c, [1,2,3]).reshape(3,3,3)

Result:

  1. array([[[ 91., 94., 97.],
  2. [118., 121., 124.],
  3. [145., 148., 151.]],
  4. [[334., 337., 340.],
  5. [361., 364., 367.],
  6. [388., 391., 394.]],
  7. [[577., 580., 583.],
  8. [604., 607., 610.],
  9. [631., 634., 637.]]])

huangapple
  • 本文由 发表于 2023年6月16日 02:45:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76484652.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定