pandas
虽然是个很是流行的数据分析利器,但不少朋友在使用pandas
处理较大规模的数据集的时候常常会反映pandas
运算“慢”,且内存开销“大”。app
特别是不少学生党在使用本身性能通常的笔记本尝试处理大型数据集时,每每会被捉襟见肘的算力所劝退。但其实只要掌握必定的pandas
使用技巧,配置通常的机器也有能力hold住大型数据集的分析。性能
本文就将以真实数据集和运存16G的普通笔记本电脑为例,演示如何运用一系列策略实现多快好省地用pandas
分析大型数据集。学习
咱们使用到的数据集来自kaggle
上的TalkingData AdTracking Fraud Detection Challenge竞赛( https://www.kaggle.com/c/talkingdata-adtracking-fraud-detection ),使用到其对应的训练集,这是一个大小有7.01G的csv
文件。优化
下面咱们将按部就班地探索在内存开销和计算时间成本之间寻求平衡,首先咱们不作任何优化,直接使用pandas
的read_csv()
来读取train.csv
文件:3d
import pandas as pd raw = pd.read_csv('train.csv') # 查看数据框内存使用状况 raw.memory_usage(deep=True)
能够看到首先咱们读入整个数据集所花费的时间达到了将近三分钟,且整个过程当中由于中间各类临时变量的建立,一度快要撑爆咱们16G的运行内存空间。code
这样一来咱们后续想要开展进一步的分析但是说是不可能的,由于随便一个小操做就有可能会由于中间过程大量的临时变量而撑爆内存,致使死机蓝屏,因此咱们第一步要作的是下降数据框所占的内存:对象
由于pandas
默认状况下读取数据集时各个字段肯定数据类型时不会替你优化内存开销,好比咱们下面利用参数nrows
先读入数据集的前1000行试探着看看每一个字段都是什么类型:blog
raw = pd.read_csv('train.csv', nrows=1000) raw.info()
怪不得咱们的数据集读进来会那么的大,原来全部的整数列都转换为了int64
来存储,事实上咱们原数据集中各个整数字段的取值范围根本不须要这么高的精度来存储,所以咱们利用dtype
参数来下降一些字段的数值精度:ip
raw = pd.read_csv('train.csv', nrows=1000, dtype={ 'ip': 'int32', 'app': 'int16', 'device': 'int16', 'os': 'int16', 'channel': 'int16', 'is_attributed': 'int8' }) raw.info()
能够看到,在修改数据精度以后,前1000行数据集的内存大小被压缩了将近54.6%,这是个很大的进步,按照这个方法咱们尝试着读入全量数据并查看其info()
信息:内存
能够看到随着咱们对数据精度的优化,数据集所占内存有了很是可观的下降,使得咱们开展进一步的数据分析更加顺畅,好比分组计数:
( raw # 按照app和os分组计数 .groupby(['app', 'os']) .agg({'ip': 'count'}) )
那若是数据集的数据类型没办法优化,那还有什么办法在不撑爆内存的状况下完成计算分析任务呢?
若是咱们的分析过程并不须要用到原数据集中的全部列,那么就不必全读进来,利用usecols
参数来指定须要读入的字段名称:
raw = pd.read_csv('train.csv', usecols=['ip', 'app', 'os']) raw.info()
能够看到,即便咱们没有对数据精度进行优化,读进来的数据框大小也只有4.1个G,若是配合上数据精度优化效果会更好:
若是有的状况下咱们即便优化了数据精度又筛选了要读入的列,数据量依然很大的话,咱们还能够以分块读入的方式来处理数据:
利用chunksize
参数,咱们能够为指定的数据集建立分块读取IO流,每次最多读取设定的chunksize
行数据,这样咱们就能够把针对整个数据集的任务拆分为一个一个小任务最后再汇总结果:
from tqdm.notebook import tqdm # 在下降数据精度及筛选指定列的状况下,以1千万行为块大小 raw = pd.read_csv('train.csv', dtype={ 'ip': 'int32', 'app': 'int16', 'os': 'int16' }, usecols=['ip', 'app', 'os'], chunksize=10000000) # 从raw中循环提取每一个块并进行分组聚合,最后再汇总结果 result = \ ( pd .concat([chunk .groupby(['app', 'os'], as_index=False) .agg({'ip': 'count'}) for chunk in tqdm(raw)]) .groupby(['app', 'os']) .agg({'ip': 'sum'}) ) result
能够看到,利用分块读取处理的策略,从始至终咱们均可以保持较低的内存负载压力,而且同样完成了所需的分析任务,一样的思想,若是你以为上面分块处理的方式有些费事,那下面咱们就来上大招:
dask
相信不少朋友都有据说过,它的思想与上述的分块处理其实很接近,只不过更加简洁,且对系统资源的调度更加智能,从单机到集群,均可以轻松扩展伸缩。
推荐使用conda install dask
来安装dask
相关组件,安装完成后,咱们仅仅须要须要将import pandas as pd
替换为import dask.dataframe as dd
,其余的pandas
主流API使用方式则彻底兼容,帮助咱们无缝地转换代码:
能够看到整个读取过程只花费了313毫秒,这固然不是真的读进了内存,而是dask
的延时加载技术,这样才有能力处理超过内存范围的数据集。
接下来咱们只须要像操纵pandas
的数据对象同样正常书写代码,最后加上.compute()
,dask
便会基于前面搭建好的计算图进行正式的结果运算:
( raw # 按照app和os分组计数 .groupby(['app', 'os']) .agg({'ip': 'count'}) .compute() # 激活计算图 )
而且dask
会很是智能地调度系统资源,使得咱们能够轻松跑满全部CPU:
关于dask
的更多知识能够移步官网自行学习( https://docs.dask.org/en/latest/ )。
以上就是本文的所有内容,欢迎在评论区与我进行讨论~