Python运行速度慢?试试看这些

论坛 期权论坛 期权     
拍黑米   2019-7-20 09:57   2719   0
在机器学习领域,Python因为其兼具对用户友好和功能强大两个特点,是最热门的语言之一。但在Python初步入门时,可能会遇到运行很慢的问题。尤其当数据量很大时,某些操作会降低整个项目的效率。今天我来聊聊我在Python入门时的一些体会。


Pandas读取数据时,谁吃掉了我的内存?

1)Numpy Array vs Python List
Pandas是基于Numpy的数组array构建的,大家最常使用的库之一。在导入数据时,大家有时会遇到内存占用过大的问题,但是如果注意一些小技巧,可以将内存占用大大降低。在具体聊怎么操作之前,我们先看一下为什么要使用Pandas处理数据。



Figure 1: Numpy Array vs Python List 存储形式图。
图片来源:https://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/

Fig. 1图右是一个Numpy Array的存储形式图, 图左是一个Python List的存储形式图。相比于Python将数据存放在不连续的区域,Numpy在创建array的时候,是寻找内存上的一连串区域来存放。因此在批量处理数据时,Numpy只需要在这一片连续的区域内走动,就可以实现快速索引,从而比用Python List处理数据高效得多。

2)优化Pandas的object类型
回到Pandas读取数据,这里我随便选取了一个样本数据,数据内容不必在意,操作都是在jupyter notebook中进行的。首先导入数据,并用info查看内存占用情况。
  1. 1import pandas as pd
  2. 2data = pd.read_csv('data.csv', delimiter=',',encoding='utf-8')
  3. 3data.info( verbose = False, memory_usage='deep')
复制代码
  1. 1
  2. 2RangeIndex: 1602282 entries, 0 to 1602281
  3. 3Columns: 96 entries, col1 to col96
  4. 4dtypes: float64(58), int64(30), object(8)
  5. 5memory usage: 1.9 GB
复制代码
这里我们看到data中有58列float64类型,30列int64类型以及8列object类型。在Pandas中,每种数据类型都是分开存储的,如果分别看这几类数据占了多少内存,就会发现仅8列object类型,就占用了接近50%(894.MB/1.9G)的内存消耗。
  1. 1data.select_dtypes('object').info(verbose=False, memory_usage='deep')
复制代码
  1. 1
  2. 2RangeIndex: 1602282 entries, 0 to 1602281
  3. 3Columns: 8 entries, obj1 to obj8
  4. 4dtypes: object(8)
  5. 5memory usage: 895.4 MB
复制代码
object类型是个什么意思呢,它就是表示使用Python内置类型存储字符串数据。于是我们又看到了这张熟悉的结构图。Fig. 2左边是以NumPy数据类型存储数值数据结构图,右边是使用Python内置类型存储字符串数据结构图。



Figure 2: Pandas中数值型和字符串型数据存储形式图
图片来源:https://jakevdp.github.io/blog/2014/05/09/why-python-is-slow/

当使用object类型时(Fig. 2左),尽管每个指针只占用1字节的内容,但如果每个字符串在 Python 中都是单独存储的,那就会占用实际字符串那么大的空间。其实我们接触的字符型数据,往往只有有限的unique values,比如性别变量只有男,女,缺失三种情况。针对这种数据,如果将object类型转化成category类型,Pandas就使用最节省空间的int子类型来表示该列中的所有不同值,从而可以大大减少内存占用。
  1. 1for col in ['obj1', 'obj2', 'obj3', 'obj4']:
  2. 2   data[col] = data[col].astype('category')
  3. 3   data.info(verbose=False, memory_usage='deep')
复制代码
  1. 1
  2. 2RangeIndex: 1602282 entries, 0 to 1602281
  3. 3Columns: 96 entries, col1 to col96
  4. 4dtypes: category(4), float64(58), int64(30), object(4)
  5. 5memory usage: 1.5 GB
复制代码
我们看到仅仅是将样本数据中的4列object类型转化为category类型后,内存就从1.9GB降到了1.5GB。如果你查看这几列数据,除了这几列的数据类型变了之外,数据看起来是完全一样的。

观察一下我们的样本数据,剩下的object列中存储的都是日期。对于日期数据,可以转化成datetime类型。Pandas中datetime类型是64位的,所以如果你的日期变量是以数值型存储,那转换成datetime也许并没有内存优势。但还是建议转换成datetime,因为这样日期方面的操作会变得很简单。一个小技巧,在进行datetime转换时,不指定日期格式也可以,但是指定日期格式会极大缩短转换操作时间,其中具体原因这里就不解释了。
  1. 1data['date'] = pd.to_datetime(date, format='%Y%m%d')
  2. 2data.info(verbose=False, memory_usage='deep')
复制代码
  1. 1
  2. 2RangeIndex: 1602282 entries, 0 to 1602281
  3. 3Columns: 96 entries, col1 to col96
  4. 4dtypes: category(4), datetime64[ns](4), float64(58), int64(30)
  5. 5memory usage: 1.1 GB
复制代码
再次查看一下内存使用,我们看到内存占用又降低了0.4GB。所以在对object类型优化后,内存占用从1.9GB降低到1.1GB,一共降低了0.8GB。

除了对object类型优化,还可以对数值型数据优化。比如当正整数变量的类型是有符号整数时,可以将其设为无符号整数类,数值范围很小的变量从int64转换为int32等等,这里就不讨论了。


矢量化操作

当我们对Pandas进行一些操作时,如果你抱怨某个操作花费了很多时间,有可能是你使用了不必要的循环。比如对我们刚刚的样本数据,想根据列login1和createtime的以下规则,增加新的一列new_col:

createtime
new_col
0
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:5
帖子:1
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP