首页 > 解决方案 > 将具有特定结构的 1D 数组解码为 3D 或 4D 数组的最有效方法(使用 Python)

问题描述

我有一个由大约 63M 整数元素组成的 1D 数组,它代表一个 4D 数据集,轴为 x、y、通道、帧(所有正整数)。数据集的形状为 (512, 512, 4069, 239)。该数据集代表一个 X 射线光谱流,其中 x 和 y 是电子束光栅位置索引,每个通道代表一个 X 射线能量箱。样本被扫描多次,每一帧代表这样一个(x,y,通道)数据集。

为了能够轻松地操作这些数据并通过各个轴对其进行切片,最好将数据转换为 4D 数据集或能够动态创建 3D 切片。

一维数组中的数据结构如下:每个元素要么是一个非常大的整数x(大约 65000),要么是一个表示通道索引的整数(最多 4069)。存储在数组中的每个x对应一个扫描位置 (x,y),因此第一个x代表 (0,0),第二个 x 代表 (1,0),依此类推。扫描网格的形状为 512x512 像素,因此一旦达到 (511,0),下一个x对应于 (0, 1)。如果遇到除x以外的其他值,则表示与这些值对应的通道在与前一个x对应的波束位置 (x,y) 处接收到计数. 一旦超过索引 (511,511),下一帧开始。该阵列非常稀疏,因为在每一帧中总共只有大约 3000 个 X 射线计数,分布在 4069 个通道上。所以一维数组基本上是 239 帧,每帧由 512x512 + ~3000 个整数表示,全部串在一起。

为了说明,一维数据集可能看起来像

[65000, 65000, 1, 65000, 65000, 65000, 2, 3, 65000, 2, 2, 65000, 65000, ...]

这应该解释为

Frame 1

       | Channel
x y    | 1 2 3 ... 4069
_____________________________
0 0    | 0 0 0
1 0    | 1 0 0
2 0    | 0 0 0
3 0    | 0 0 0
4 0    | 0 1 1
5 0    | 0 2 0
6 0    | 0 0 0
7 0    | 0 0 0
...    |
511 511|

Frame 2
...

Frame 239
...

我想能够做什么:

我知道如何使用循环来做到这一点,但恐怕我会开始遇到内存或性能问题。即使我不为这个数据集,未来的数据集可能会更大,所以我想从一开始就以正确的方式去做。

我的问题是:有没有一种很好的 Pythonic、有效的方法来进行这种类型的数据转换?避免所有元素循环的东西?

我也很欣赏关于最佳实践的提示,以便在保持良好性能和管理内存的同时轻松处理数据。最好存储整个 4D 数据集并对其执行切片和操作,还是只存储 1D 数组并在需要进行某些计算时进行逐帧转换?鉴于数据非常稀疏,是否有另一种存储数据的方法,这样它会比 4D 数组占用更少的内存,但仍然可以轻松查看/访问/操作?

标签: pythonarraysmemorytransformationlarge-data

解决方案


我找到了一种方法,仍然有一个循环,但避免了所有元素的循环。我测量它比所有元素的循环快 5 倍,所以我想我会分享。关键是找到使用 numpy.argwhere 计数的索引,我发现它比遍历所有元素要快得多。查找这些计数应该映射到哪个像素索引也可以通过矢量化方式完成。

from scipy.sparse import dok_matrix
import numpy as np

def convert_stream_to_sparse(d1d: np.ndarray, dim: tuple, dv: int = 65535):

    #Initialize an array with the dimensions: total size * the number of channels
    temp = dok_matrix(dim, dtype=np.int16) 

    #find the indexes where counts are registered (!= the counting number)
    cinx = np.argwhere(d1d!=dv)[:,0] 
    #calc the pixel index to which these counts must be mapped
    pixind = cinx - np.arange(len(cinx)) - 1 

    #loop over the list of counts and put them in the right bin
    for i, j in zip(cinx, pixind): 
        chan = d1d[i] #the channel number = the value stored at the index
        temp[j, chan] += 1 #increment the right entry

这会产生一个稀疏矩阵,其中行表示位置(连同有关扫描区域和帧数的信息,每行表示唯一的 (x,y,frame))和表示通道的列。必要时重塑它是微不足道的。


推荐阅读