处理大规模数据集往往很棘手,尤其是当内存无法完全加载数据时。在资源受限的情况下,可以使用PythonPandas提供的一些函数来减少加载数据集的内存占用。可用的技术包括压缩、索引和数据分块。上述过程中需要解决一些问题,其中之一就是数据量大。如果数据量超过了本地内存的容量,项目执行就会出现问题。有什么解决方案?有几种方法可以解决数据量过大的问题。它们要么耗时,要么需要额外投资。可能的解决方案投资解决方案:新购买的计算机具有更强大的CPU和更大的内存,能够处理整个数据集。或者租用云服务或虚拟内存并创建集群来处理工作负载。耗时的解决方案:如果内存不足以处理整个数据集,而硬盘的容量远大于内存,可以考虑使用硬盘来存储数据。但是使用硬盘来管理数据会大大降低处理性能,甚至SSD的速度也比RAM慢很多。在资源允许的情况下,这两种解决方法都是可行的。如果项目资金充足,或者时间成本受到威胁,那么以上两种方法是最简单直接的解决方案。但如果情况并非如此呢?也许你的资金有限,或者数据集太大,从磁盘加载会增加处理时间5-6倍或更多。是否有不需要额外资本投资或时间开销的大数据解决方案?这个问题触手可及。有多种技术可用于大数据处理,不需要额外投资,也不会消耗大量加载时间。本文介绍了其中三种使用Pandas处理大规模数据集的技术。压缩第一种技术是数据压缩。压缩并不是将数据打包成ZIP文件,而是将数据以压缩格式存储在内存中。换句话说,数据压缩是一种使用更少内存来表示数据的方法。数据压缩有两种类型,无损压缩和有损压缩。这两种类型只影响数据的加载,不影响处理代码。无损压缩无损压缩不会造成任何数据丢失,即原始数据和压缩后的数据在语义上保持不变。可以通过三种方式执行无损压缩。下面我们将以美国各州的新冠病毒病例数据集依次进行介绍。加载特定数据列示例中使用的数据集具有以下结构:)data.sample(10)加载整个数据集占用111MB内存!如果我们只需要数据集中的两列,州名和病例数,为什么要加载整个数据集?仅需36MB的数据即可加载所需的两列,从而减少了32%的内存使用量。使用Pandas加载所需数据列的代码如下:本节使用的代码片段如下:#加载所需软件库Importneededlibraryimportpandasaspd#Datasetcsv="https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv"#加载整个数据集data=pd.read_csv(csv)data.info(verbose=False,memory_usage="deep")#创建数据子集df=data[["county","cases"]]df.info(verbose=False,memory_usage="deep")#加速需要的两列数据df_2col=pd.read_csv(csv,usecols=["county","cases"])df_2col.info(verbose=False,memory_usage="deep")代码地址:https://gist.github.com/SaraM92/3ba6cac1801b20f6de1ef3cc4a18c843#file-column_selecting-py操作数据类型另一种减少数据内存占用的方法是拦截有价值的物品。比如加载CSV到DataFrame,如果文件中包含值,那么一个值需要64字节来存储。但是,可以通过使用int格式截断值来节省内存。int8存储从-128到127的值;int16存储从-32768到32767的值;int64存储从-9223372036854775808到9223372036854775807的值。如果可以预先确定该值不大于32767,那么可以使用int16或int32类型,列的内存占用可以减少75%。假设每个州的案例数不超过32767(尽管实际情况并非如此),那么可以将该列截断为int16类型,而不是int64。稀疏列如果你的数据集在一个或多个列中有大量NaN空值,你可以使用稀疏列表示来减少内存使用,使空值不消耗内存。假设状态列中有一些空值,我们需要跳过所有包含空值的行。这个需求可以使用pandas.sparse轻松实现(译者注:原文使用的是SparseSeries,但是SparseSeries在Pandas1.0.0中已经被移除)。有损压缩如果无损压缩不能满足需要,需要进一步压缩,应该怎么办?这时,可以使用有损压缩来以100%的数据准确性为代价来权衡内存使用。有损压缩有两种方式,分别是修改值和采样。修改值:有时值不需要保留所有精度。在这种情况下,int64可以截断为int32甚至int16。抽样:如果您需要确认某些州的COVID-19病例比其他州多,您可以从某些州抽样数据以查看哪些州的病例更多。这是一种有损压缩,因为并未考虑所有数据行。第二种技术:数据分块(chunking)另一种处理大规模数据集的方法是数据分块。将大规模数据分成多个小块,然后对每个块分别进行处理。所有块都处理完后,可以比较结果并得出最终结论。本文使用的数据集包含1923行数据。假设我们需要找到案例最多的状态,那么我们可以将数据集拆分成100行数据的块,分别处理每个数据块,并从这些小结果中获取最大值。本节代码片段如下:#导入所需软件库importpandasaspd#Datasetcsv="https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv"#循环处理每个数据块,获取每个数据块中的最大值result={}forchunkinpd.read_csv(csv,chunksize=100):max_case=chunk["cases"].max()max_case_county=chunk.loc[chunk[cases]==max_case,county].iloc[0]result[max_case_county]=max_case#给出结果print(max(result,key=result.get),result[max(result,key=result.get)])代码地址:https://gist.github.com/SaraM92/808ed30694601e5eada5e283b2275ed7#file-chunking-py第三种方法:indexdatachunking非常适合数据集只加载一次的情况。但是如果你需要多次加载数据集,你可以使用索引技术。索引可以理解为一本书的目录。无需阅读整本书即可获取所需信息。例如,分块技术非常适合获取给定状态的案例数。这可以通过编写以下简单函数来实现。IndexvsChunkingChunking需要读取所有数据,而index只需要读取部分数据。上面的函数加载了每个块中的所有行,但我们只关心其中一个状态,这导致了很多开销。可以使用Pandas数据库操作,例如,简单的方法是使用SQLite数据库。首先需要将DataFrame加载到SQLite数据库中,代码如下:importsqlite3csv="https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-counties.csv"#Create一个新的数据库文件db=sqlite3.connect("cases.sqlite")#LoadCSVfilesinchunksforcinpd.read_csv(csv,chunksize=100):#Loadalldatarowsintoanewdatabasetablec.to_sql("cases",db,if_exists="append")#为“state”列添加索引db.execute("CREATEINDEXstateONcases(state)")db.close()代码地址:https://gist.github.com/SaraM92/5b445d5b56be2d349cdfa988204ff5f3#file-load_into_db-py使用数据库,下面需要重写get_state_info函数。这减少了50%的内存使用量。总结使用大型数据集通常很棘手,尤其是当数据无法完全加载到内存中时。有些解决方案要么耗时,要么成本高昂。毕竟,增加资源是最简单直接的解决办法。但是在资源受限的情况下,可以使用Pandas提供的一些函数来减少加载数据集的内存占用。可用的技术包括压缩、索引和数据分块。
