数据处理之python list

from Suslo

背景:要处理900个npy文件,每个文件包含250*2048即512000个数据,为了进行数据预处理,利用numpy读取这900个numpy数据存入列表train,先对train进行归一化,归一化完后已经使用了好几G内存。由于想使用KNN算法,而train里的每一个元素都是ndarray,因此将每个ndarray转换为list再装入train里。

一开始的代码:

import os
import numpy as np
​
train = []
train_path = './data/train/train_feature/'
train_listdir = os.listdir(train_path)
for file in train_listdir:
    fn = train_path + file
    t = np.load(fn)
    t = t.flatten()
    train.append(t)
  
  # 归一化
for i in range(len(train)):
    for j in range(len(train[i])):
        train[i][j] = train[i][j] / 7.4186134
       
train = [list(i) for i in train]

这样的结果就是内存爆满,电脑死机,当程序处理到550份数据左右时就已经使用了约14G内存。显然,这样是不行的,那就开始优化。

(一)

Numpy数组转换为python list并减少内存消耗,那么可以使用Numpy的tolist()方法而不是list()。这是因为Numpy的tolist()方法不仅可以实现将Numpy数组转换为python list,同时不会生成新的python对象,显然这就可以减少内存的消耗,所以修改代码如下:

import os
import numpy as np

train = []
train_path = './data/train/train_feature/'
train_listdir = os.listdir(train_path)
for file in train_listdir:
    fn = train_path + file
    t = np.load(fn)
    t = t.flatten().tolist()
    # 归一化
    t = [i/7.4186134 for i in t]
    train.append(t)

此时,上段代码可以使用相同的约14G内存处理完约725份数据,内存的消耗获得了一定的减少。但是仍需进一步优化。

(二)

归一化可以用python的除法操作,还可以用Numpy提供的除法操作,并且Numpy的除法操作使用更简便,同时由于不用再转为list,就使得内存损耗大大下降。Numpy的ndarray与list比较如下。

NumPy 中的 ndarray 对象比 Python 内置的 list 对象更有效率,占用更少的内存,这是由于以下几个原因:

  1. 连续的内存块:NumPy 的 ndarray 对象在内存中是连续的,这意味着数组中的所有元素都存储在一个连续的内存块中,这使得访问和操作元素的速度更快。相比之下,Python 的 list 对象存储在不同的内存块中,每个元素都是一个指向另一个对象的指针,因此访问和操作元素的速度较慢。

  2. 固定的数据类型:NumPy 的 ndarray 对象具有固定的数据类型,这意味着它们可以使用更紧凑的内存布局存储数据。例如,一个由 1 百万个 32 位浮点数组成的 NumPy 数组只需要占用 4MB 的内存空间,而 Python 的 list 对象在存储相同数量的浮点数时需要占用更多的内存空间。

  3. 优化的算法:NumPy 实现了许多针对数组的优化算法,这些算法可以在不消耗额外内存的情况下对数组进行操作。例如,对一个 NumPy 数组进行简单的数学运算可以使用优化的 C 代码实现,这比使用 Python 的 for 循环实现更快,并且不需要额外的内存开销。

因此,由于内存块连续、固定的数据类型和优化的算法,NumPy 的 ndarray 对象比 Python 的 list 对象更有效率,占用更少的内存。

而且,可以发现的是同一个数字,Numpy的ndarray存储的是0.00180735,而list存储的小数位数可能是Numpy的ndarray存储的小数位数的将近两倍。

因此,替换为如下代码:

import os
import numpy as np

train = []
train_path = '/kaggle/input/visual-feature-coding-data/'
train_listdir = os.listdir(train_path)
for file in train_listdir:
    fn = train_path + file
    # 归一化
    t = np.load(fn).flatten()/7.4186134
    train.append(t)
print(len(train))

train = np.array(train)

以上的代码不仅可以处理所有数据文件(900份),同时只占用了约3G。而采用(一)内的代码则需要约18G的内存空间,显然这种优化是必要的。

为什么不在一开始就新建ndarray对象呢?

首先,python列表中的单个数据可以是不同数据类型的;但Numpy中所有的元素必须是同类型的。而且在创建 NumPy 的 ndarray 对象时,通常需要指定数组的维度。考虑到以下原因:

1. Numpy存储数据所占的内存更小

2. 在 NumPy 中,np.array() 函数创建的 ndarray 对象是固定大小的,无法直接追加元素。一旦创建了 ndarray 对象,其大小(即维度)是固定的,无法动态地增加或减少。这是因为使用 np.array() 函数创建数组时,内存会分配一个连续的块来存储数组的元素。这个块的大小取决于数组的形状和数据类型。每个元素在内存中占据一定的空间,且相邻元素的内存地址是连续的。所以通过追加元素来扩展数组时,NumPy 会创建一个新的数组,并将原始数组的数据复制到新数组中。这会导致额外的内存开销,因为在复制数据时需要分配新的内存空间。

所以np.append() 函数会创建新的数组并复制数据,每次追加操作都会导致内存重新分配和数据复制,因此在循环中频繁使用 np.append() 可能会导致性能问题。为了避免这种情况,如果需要动态地追加元素,可以考虑使用 Python 的列表对象(list)进行操作,然后在必要时将列表转换为 NumPy 数组。

至此,优化告一段落。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值