>>> from env_helper import info; info()
页面更新时间: 2023-12-27 09:30:02
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

4.6. 使用Pandas处理大型CSV文件

CSV (Comma Separated Values)作为一种逗号分隔型值的纯文本格式文件,在实际应 用中经常用到, 如数据库数据的导人导出、数据分析中记录的存储等因此很多语言都提供 了对CSV文件处理的模块, Python也不例外,其模块csv提供了一系列与CSV处理相关的 API。

我们先来看一下其中几个常见的API:

  1. reader(csvfile[,dia]ect='excel'][, fmtparam]) ,主要用于 CSV 文件的读取,返回一个 reader对象用于在CSV文件内容上进行行迭代。

参数csvfile,需要是支持迭代(Iterator)的对象,通常对文件(file)对象或者列表(list) 对象都是适用的,并且每次调用next()方法的返回值是字符串(string);参数dialect的默认 值为excel,与excel兼容;frntparam是一系列参数列表,主要用于需要覆盖默认的Dialect 设晋.的情形当dialect设置为excel的时候,默认Dialect的值如下:

class excle():

delimiter=',' #单个字符,用于分隔字段 quotechar='"' #用于特殊符号加引号,常见的引号为" doublequote=True #用于控制quotechar符号出现的时候的表现形式 skipinitalspace=False #设置为true的时候delimiter后面的空格将会忽略 lineterminator="rn" #行结束符 quoting=QUOTE_MINIMAL #是否在字符段前加引号,QUOTE_MINIMAL表示仅当一个字段包 #含引号或者定义符号的时候才加引号

  1. csv.writer(csvfile, dialect='excel', **fmtparams),用于写人 CSV 文件。参数同上。来看一个使用例子。

import csv with open ('data.csv','wb' ) as csvfile:

csvwriter = csv.writer(csvfile, dialect='excel', delimiter="|", quotechar='"' , quoting=csv.QUOTE_MINIMAL) csvwriter.writerow(["l/3/09 14:44","'Productl'","1200''", "Visa","BGouya"])

#写入行 #python2 可执行

  1. csv.DictReader(csvfile,fieldnames=None,restkey=None,restval==None, dialect='excel', *args,**kwds),同reader()方法类似,不同的是将读人的信息映射到一个字典中去,其中字 典的key由fieldnames指定,该值省略的话将使用CSV文件第一行的数据作为key值。如果 读入行的字段的个数大于filednames中指定的个数,多余的字段名将会存放在restkey中,而 restval主要用于当读取行的域的个数小于fieldnames的时候,它的值将会被用作剩下的key 对应的值。

  2. csv.DictWriter(csvfile, fieldnames , restval='', extrasaction='raise',dialeet='excel',*args, **kwds),用T支持字典的写入。

import csv #DictWriter with open('test.csv',"wb") as csv_file: #设置列名称

FIELDS = ['Transaction_date','Product1','Price','Payment_Type'] writer = csv.DictWriter(csv_file, fieldnames=FIELDS)

#写入列名称

writer.writerow(dict(zip(FIELDS, FIELDS))) d = ("'Transaction_date's'1/2/09 6:17* f'Product':'Productl','Price*:'1200ayment_Type V: 'Mastercard'")

with open ('test.csv','rb') as csv_file:
for d in csv. DictReader (csv_file):

print (d)

#python2 内执行

CSV模块使非常简单,基本可以满足大部分需求。但你有没有恩考过这个问题:有些 应用中需要解析和处理的CSV文件可能有上百MB甚至几个GB,这种情况下CSV模块是否 能够应付呢?先来做个实验,临时创建一个1GB的CSV文件并将其加载到内存中.看看会 有什么问题发生。

import csv f=open("large.csv","wb") f.seek(1073741824-1) f.write(b"0") f.close() import os os.stat("large.csv").st_size #python2 内执行 with open("large.csv","rb") as csvfile:

mycsv=csv.reader(csvfile,delimiter=";") for row in mycsv:

print( row)

上面的例子中当企图读人这个CSV文件的时候抛出了 MemoryError异常a这是为什 么?因为csv模块对于大型CSV文件的处理无能为力。这种情况下就需要考虑其他解决方案 了,pandas模块便是较好的选择。

Pandas即Python Data Analysis Library,是为了解决数据分析而创建的第三方工具,它 不仅提供了丰富的数据模型,而且支持多种文件格式处理,包括CSV、HDF5、HTML等, 能够提供高效的大型数据处理。其支持的两种数据结构——Series和DateFrame——是数据处 理的基础。下面先来介绍这两种数据结构。

  • Series:它是一种类似数组的带索引的一维数据结构,支持的类型与NumPy兼容。如 果不指定索引,默认为0到N-1通过obj.values()obj.index()可以分别获取值和索引。当给Series传递一个字典的时候,Series的索引将根据字典中的键棑序。如果传 人字典的时候同时重新指定了 index参数,当index与字典中的键不匹配的时候,会 出现时数据丢失的情况,标记为NaN。

在pandas中用函数isnull()notnull()来检测数据是否丢失。

>>> import pandas
>>> obj1=pandas.Series([1,'a',(1,2),3],index=['a','b','c','d'])
>>> obj1
a         1
b         a
c    (1, 2)
d         3
dtype: object
>>> obj2=pandas.Series({"Book":"Python","Author":"Dan","ISBN":"011334","Price":25},index
>>>                   =["book","Author","ISBM","Price"])
>>> obj2
book      NaN
Author    Dan
ISBM      NaN
Price      25
dtype: object
  • DataFrame:类似于电子表格,其数据为排好序的数据列的集合,每一列都可以是 不同的数据类型,它类似于一个二维数据结构.支持行和列的索引。和Series — 样,索引会自动分配并且能根据指定的列进行排序9使用最多的方式是通过一个长 度相等的列表的字典来构建。构建一个DataFrame最常用的方式是用一个相等长度 列表的字典或NumPy数组。DataFrame也叶以通过columns指定序列的顺序进行 排序。

>>> data={'orderDate':['1-6-10','1-23-10','2-9-10','3-23-20','3-19-10'],
>>>       'region':['east','cesdf','asdcve','egedsa','east'],
>>>       'Rep':['jones','kivell','aasdfj','gill','sorvion']}
>>>
>>> pandas.DataFrame(data,columns=['orderdata','region','rep'])
orderdata region rep
0 NaN east NaN
1 NaN cesdf NaN
2 NaN asdcve NaN
3 NaN egedsa NaN
4 NaN east NaN

Pandas中处理CSV文件的函数主要为read_csv()to_csv()这两个,其中read_csv()读取CSV文件的内容并返回DataFmme, to_csv()则是其逆过程。两个函数都支持多个参数, 由于其参数众多且过于复杂,本节不对各个参数一一介绍,仅选取几个常见的情形结合具体 例子介绍。下面举例说明

1)指定读取部分列和文件的行数。具体的实现代码如下:

>>> df=pandas.read_csv("sampledata.csv",nrows=9,usecols=['orderdata','item','total'])
>>> df
orderdata item total
0 1-6-10 pencil NaN
1 2-6-10 pencil NaN
2 2-9-10 pencil NaN
3 3-16-10 pencil NaN
4 3-26-10 pencil NaN
5 5-36-10 pencil NaN
6 5-56-10 pencil NaN
7 6-66-10 pencil NaN

方法read_csv()的参数nrows指定读取文件的行数,usecols指定所要读取的列的列名, 如果没有列名,可直接使用索引0、1、…、n-1。上述两个参数对大文件处理非常有用,可 以避免读人整个文件而只选取所需要部分进行读取。

2)设置CSV文件与excel兼容。dialect参数可以是string也可以是csv.Dialect的实例。 如果将图4-2所示的文件格式改为使用“丨”分隔符,则需要设置dialect相关的参数。error_badlines设置为False,当记录不符合要求的时候,如记录所包含的列数与文件列设置不相 等时可以直接忽略这些列。下面的代码用于设置CSV文件与eXCd兼容,其中分隔符为”|“, 而error_bad_lines=False会直接忽略不符合要求的记录。

>>> import csv
>>> dia=csv.excel()
>>> dia.delimiter="|"
>>> pandas.read_csv("sampledata.csv")
orderdata region rep item units unit cost total
0 1-6-10 east jones pencil 95 1.99 189.05 NaN
1 2-6-10 easdfast jones pencil 95 1.99 189.05 NaN
2 2-9-10 edfast jones pencil 95 1.99 189.05 NaN
3 3-16-10 east jones pencil 95 1.99 189.05 NaN
4 3-26-10 central jones pencil 95 1.99 189.05 NaN
5 5-36-10 central jones pencil 95 1.99 189.05 NaN
6 5-56-10 central jones pencil 95 1.99 189.05 NaN
7 6-66-10 east jones pencil 95 1.99 189.05 NaN
>>> pandas.read_csv("sampledata.csv",dialect=dia,error_bad_lines=False)
/tmp/ipykernel_20649/2299398768.py:1: FutureWarning: The error_bad_lines argument has been deprecated and will be removed in a future version. Use on_bad_lines in the future.


  pandas.read_csv("sampledata.csv",dialect=dia,error_bad_lines=False)
orderdata,region,rep,item,units,unit,cost,total
0 1-6-10,east,jones,pencil,95,1.99,189.05
1 2-6-10,easdfast,jones,pencil,95,1.99,189.05
2 2-9-10,edfast,jones,pencil,95,1.99,189.05
3 3-16-10,east,jones,pencil,95,1.99,189.05
4 3-26-10,central,jones,pencil,95,1.99,189.05
5 5-36-10,central,jones,pencil,95,1.99,189.05
6 5-56-10,central,jones,pencil,95,1.99,189.05
7 6-66-10,east,jones,pencil,95,1.99,189.05

3)对文件进行分块处理并返回一个可迭代的对象。分块处理可以避免将所有的文件载人 内存,仅在使用的时候读人所需内容。参数chimksize设置分块的文件行数,10表示每一块 包含10个记录。将参数iterator设置为True时,返回值为TextFileReader,它是一个可迭代对象,来看下面的例子,当chunk5ize=10、iterator=True时,每次输出为包含10个记录的块。

>>> reader=pandas.read_table("sampledata.csv",chunksize=10,iterator=True)
>>> reader
<pandas.io.parsers.readers.TextFileReader at 0x7f0200a7b650>
iter(reader).next()# 将 TextFileReader 转换为送代器并调用 next 方法
0   l-6-10rEast# Jones,Pencilr 95r 1.99 • 189.05
1   l-23-10#CentralrKivell^Binder,50# 19.99 , 999.50
2   2-9-10#CentralrJardine,Pencilr36r 4.99 , 179.64
3   2-2-10rCentral,Gill#Pen,27# 19.99 , 539.73
4   3-15-10,WestrSorvinofPencilf56r 2.99 , 167.44
5   4-l-10rEast,JonesfBinderr60, 4,99 i 299.40
6   4-18-10,Central^ Andrews, Pencilf 75, K99 , 149.2S
7   5-5-10,CentraliJardine,Pencilr90, 4^99 , 449.10
8   5-22-10,West,Thompson,Pencil, 32, 1.99 , 63.68
9   6-8-10,East,Jones,Binderf 60, 8*99 j 539.40

4)当文件格式相似的时候,支持多个文件合并处理。以下例子用于将3个格式相同的 文件进行合并处理。

>>> import os
>>> filelst=os.listdir('test')
>>> print(filelst)
['test1.csv', 'test2.csv']
os.chdir("test")
dfs=[pandas.read_csv(f) for f in filelst]
total_df=pandas.concat(dfs)
total_df

了解完pandas后,读者可以自行实验一下使用pandas处理前面生成的1GB的文件,看 着还会不会抛出MemoryError异常

在处理CSV文件上,特别是大型CSV文件,pandas不仅能够做到与csv模块兼容,更 重要的是其CSV文件以DataFirame的格式返回,pandas对这种数据结构提供了非常丰富的 处理方法,同时pandas支持文件的分块和合并处理,非常灵活,由于其底层很多算法采用 Cython实现运行速度较快。实际上pandas在专业的数据处理与分析领域,如金融等行业已经得到广泛的应用。