数据分析学习笔记之数据载入-存储及文件格式

479 阅读11分钟

「这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战」。

《数据载入、存储及文件格式》

1.文本格式数据的读写

Pandas的解析函数

函数描述
read_csv从文件、URL或文件型对象读取分隔好的数据,逗号是默认分隔符
read_table从文件、URL或文件型对象读取分隔好的数据,制表符('\t')是默认分隔符
read_fwf从特定宽度格式的文件中读取数据(无分隔符)
read_clipboardread_table的剪贴板版本,在将表格从Web页面上转换数据时有用
read_excel从Excel的XLS或XLSX文件中读取表格数据
read_hdf读取用pandas存储的HDF5文件
read_html从HTML文件中读取所有表格数据
read_json从JSON字符串中读取数据
read_msgpack读取MessagePack二进制格式的pandas数据
read_pickle读取以Python pickle格式存储的任意对象
read_sas读取存储在SAS系统中定制存储格式的SAS数据集
read_sql将SQL查询的结果(使用SQLAlchemy)读取为pandas的DataFrame
read_stata读取Stata格式的数据集
read_feather读取Feather二进制格式

上述函数的可选参数主要有以下几种类型:

  • 索引:可以将一列或多个列作为返回的DataFrame,从文件或用户处获得列明,或者没有列名;
  • 类型推断和数据转换:包括用户自定义的值转换和自定义的缺失值符号列表;
  • 日期时间解析:包括组合功能,也包括将分散在多个列上的日期和时间信息组合成结果中的单个列;
  • 迭代:支持对大型文件的分块迭代;
  • 未清洗数据问题:跳过行、页脚、注释以及其他次要数据,比如使用逗号分隔千位的数字。

读取文件内容相关说明:

  • 对不包含表头行的文件,可以允许pandas自动分配默认列名,也可以自己指定列名:
    • 设置header = None就是允许pandas自动分配默认列名;
    • 设置names = [列名序列]就是指定列名。
  • 如果要指定某列为返回DataFrame的索引,可以将列名传给参数index_col;
  • 如果想要从多个列中形成一个分层索引,需要将一个包含序列号或列名的列表传给index_col;
  • 某些情况下,一张表的分隔符并不是固定的,比如空白或者其他字符。当字段是以多种不同数量的空格分开时,可以向read_table传入一个正则表达式作为分隔符(正则表达式知识点回顾见正则笔记);
  • 解析函数有很多附加参数帮助处理各种发生异常的文件格式,可以看下方函数参数表;
  • 缺失值处理:通常情况下,缺失值要么不显示(空字符串),要么用一些标识值。默认情况下,pandas使用一些常见的标识,例如NA和NULL。

一些read_csv/read_table函数参数

参数描述
path表明文件系统位置的字符串、URL或文件型对象
sep或delimiter用于分隔每行字段的字符序列或正则表达式
header用作列名的行号,默认是0(第一行),如果没有列名的话,应该为None
index_col用作结果中行索引的列号或列名,可以是一个单一的名称/数字,也可以是一个分层索引
names结果的列名列表,和header = None一起用
skiprows从文件开头处起,需要跳过的行数或行号列表
na_values需要用NA替换的值序列
comment在行结尾处分隔注释的字符
parse_dates尝试将数据解析为datetime,默认是False。如果为True,将尝试解析所有的列。也可以指定列号或列名列表来进行解析。如果列表的元素是元组或列表,将会把多个列组合在一起进行解析(例如日期/时间将拆分为两列)
keep_date_col如果链接列到解析日期上,保留被连接的列,默认是False
converters包含列名称映射到函数的字典(例如{'foo' : f}会把函数f应用到'foo'列
dayfirst解析非明确日期时,按照国际格式处理(例如 10/29/2021 -> Oct,29,2021),默认为False
date_parser用于解析日期的函数
nrows从文件开头处读入的行数
iterator返回一个TextParser对象,用于零散地读入文件
chunksize用于迭代的块大小
skip_footer忽略文件尾部的行数
verbose打印各种解析器输出的信息,比如位于非数值列中的缺失值的数量
encodingUnicode文本编码(例如'utf-8'用于表示UTF-8编码的文本)
squeeze如果解析数据只包含一列,返回一个Series
thousands千位分隔符(例如',',或'.')

1.1 分块读入文本文件

  • 在读取大文件之前,可以先对pandas的显示设置进行调整:pd.options.display.max_rows = 10;
  • 如果只想读取一小部分行,可以指明nrows:nrows = 要读入的行数;
  • 为了分块读入文件,可以指定chunksize作为每一块的行数;
    • read_csv返回的TextParser对象允许根据chunksize遍历文件;
    • TextParser对象还有get_chunk方法,可以按照任意大小读取数据块。

1.2 将数据写入文本文件

  • 使用DataFrame的to_csv方法,可以将数据导出为逗号分隔的文件;
  • 也可以自定义其他分隔符:sep = 新的分隔符;
  • 使用其他标识值对缺失值进行标注:na_rep = 新的缺失标识值;
  • 没有其他被指定的选项的话,行和列的标签都会被写入,也可以禁止写入二者:index/header = False;
  • 也可以只写入列的自己,并且按照自定义顺序:index = False, columns = [指定的列顺序表];
  • Series也有to_csv方法。

1.3 使用分隔格式

  • 对于任何带有单字符分隔符的文件,可以使用Python的内建csv模块。要使用它,需要将任一打开的文件或文件型对象传递给csv.reader;

    import csv
    f = open('example.csv')
    reader = csv.reader(f)
    
  • 遍历reader会产生元组,元组的值为删除了引号的字符;

  • CSV文件有多种不同的风格。如需根据不同的分隔符、字符串引用约定或行终止符定义一种新的格式时,可以使用csv.Dialect定义一个简单的子类;

    class my_dialect(csv.Dialect):
        lineterminator = '\n'
        delimiter = ';'
        quotechar = '"'
        quoting = csv.QUOTE_MINIMAL
    
    reader = csv.reader(f, dialect = my_dialect)
    
  • 也可以不必定义子类,直接将CSV方言参数传入csv.reader的关键字参数;

    reader = csv.reader(f, delimiter = '|')
    
  • 需要手动写入被分隔的文件时,可以使用csv.writer。这个函数接收一个已经打开的可写入文件对象以及和csv.reader相同的CSV方言。

    with open('mydata.csv', 'w') as f:
        writer = csv.writer(f, dialect = my_dialect)
        writer.writerow(('one', 'two', 'three'))
        writer.writerow(('1', '2', '3'))
        writer.writerow(('4', '5', '6'))
        writer.writerow(('7', '8', '9'))
    

CSV方言选项

参数描述
delimiter一个用于分隔字段的字符,默认是‘,’
lineterminator行终止符,默认是'\r\n',读取器会忽略行终止符并识别跨平台行终止符
quotecher用在含有特殊字符字段中的引号,默认是' “ '
quoting引用惯例。选项包括csv.QUOTE_ALL(应用所有的字段),csv.QUOTE_MINIMAL(只使用特殊字符,如分隔符),csv.QUOTE_NONNUMERIC和csv.QUOTE_NONE(不引用)。默认是QUOTE_MINIMAL
skipinitialspace忽略每个分隔符后的空白,默认是False
doublequote如何处理字段内部的引号。如果为True,则是双引号
escapechar当引用设置为csv.QUOTE_NONE时用于转义分隔符的字符串,默认是禁用的

对于具有更复杂或固定的多字符分隔符的文件,将无法使用csv模块,在此类情况下,需要使用字符串的split方法或正则表达式方法re.split进行行拆分和其他清理工作

1.4 JSON数据

使用Python内置标准库:json。

  • 将JSON字符串转换为Python形式时,使用json.loads方法;
  • json.dumps方法可以将Python对象转回为JSON字符串;
  • pandas.read_json方法可以自动将JSON数据集按照指定次序转换为Series或DataFrame;
  • 如果需要从pandas中将数据导出为JSON,可以对Series和DataFrame使用to_json方法。

1.5 XML和HTML:网络抓取

pandas的内建函数read_html可以使用lxml和Beautiful Soup等库将HTML中的表自动解析为DataFrame对象。

conda/pip install lxml
pip install beautifulsoup4 html5lib

使用lxml.objectify解析XML:

from lxml import objectify

path = 'datasets/mta_perf/Performance_MNR.xml'
parsed = objectify.parse(open(path))
root = parsed.getroot()

root.INDICATOR返回一个生成器,可以产生每一个XML元素。对于每条记录,可以将标签名称的字典(如YTD_ACTUAL)填充为数据值(不包括几个标签):

data = []
skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ', 'DESIRED_CHANGE', 'DECIMAL_PLACES']

for elt in root.INDICATOR:
    el_data = {}
    for child in elt.getchildren():
        if child.tag in skip_fields:
            continue
        el_data[child.tag] = child.pyval
    data.append(el_data)

最后,将包含字典的列表转换为DataFrame:

pref = pd.DataFrame(data)

2.二进制格式

使用Python内建的pickle序列化模块进行二进制格式操作是存储数据(也称为序列化)最高效、最方便的方式之一。pandas对象拥有一个to_pickle方法可以将数据以pickle格式写入硬盘。

  • 可以使用pandas.read_pickle读取文件中“pickle化“的对象:pd.read_pickle(pickle化的文件);
  • pickle仅被推荐作为短期的存储格式,因为pickle很难确保格式的长期有效性,在使用pickle时尽可能保持向后兼容性;
  • pandas内建支持其他两个二进制格式:HDF5和MessagePack;
  • pandas或NumPy支持的其他存储格式包括:
    • bcolz:基于Blosc压缩库的可压缩列式二进制格式;
    • Feather:R编程社区的Hadly Wickham设计的跨语言列式文件格式,Feather使用Apache箭头列式存储器格式。

2.1 使用HDF5格式

  • HDF5用于存储大量的科学数组数据;
  • HDF5中的HDF代表分层数据格式;
  • 每个HDF5文件可以存储多个数据集并且支持元数据;
  • HDF5支持多种压缩模式的即时压缩,使得重复模式的数据可以更高效地存储;
  • HDF5适用于处理不适合在内存中存储的超大型数据,可以高效读写大型数组的一小块。

pandas提供了一个高阶的接口,HDFStore类像字典一样工作并处理低级别细节。

frame = pd.DataFrame({'a': np.random.randn(100)})
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj1_col'] = frame['a']
[in]
store
[out]
<class 'pandas.io.pytables.HDFStore'>
File path: mydata.h5
  • HDFStore支持两种存储模式:'fixed'和'table',后者速度更慢,但支持一种特殊语法的查询操作:

    store.put('obj2', frame, format='table')
    store.select('obj2', where=['index >= 10 and index <= 15'])
    
    • put是store['obj2'] = frame方法的显式版本,但允许设置其他选项,如存储格式。
  • pandas.read_hdf函数是这些工具的快捷方法:

    frame.to_hdf('mydata.h5', 'obj3', format='table')
    pd.read_hdf('mydata.h5', 'obj3', where=['index < 5'])
    
  • HDF5并不是数据库,它是一种适合一次写入多次读取的数据集。

2.2 读取Microsoft Excel文件

pandas也支持通过ExcelFile类或pandas.read_excel函数来读取存储在Excel文件中的表格型数据。这些工具内部是使用附加包xlrdopenpyxl来分别读取XLS和XLSX文件的。

  • 使用ExcelFile时,通过将xls和xlsx的路径传入,生成一个实例:

    xlsx = pd.ExcelFile('examples/ex1.xlsx')
    
  • 存储在表中的数据可以通过pandas.read_excel读取到DataFrame中;

    pd.read_excel(xlsx, 'Sheet1')
    
  • 如果读取的是含有多个表的文件,生成ExcelFile更快,但也可以更简洁地将文件名传入pandas.read_excel:

    frame = pd.read_excel('examples/ex1.xlsx', 'Sheet1')
    
  • 如需将pandas数据写入到excel中,必须先生成一个ExcelWriter,然后使用pandas对象的to_excel方法将数据写入:

    writer = pd.ExcelWriter('examples/ex2.xlsx')
    frame.to_excel(writer, 'sheet2')
    writer.save()
    
  • 也可以直接将文件路径传给to_excel,避免直接调用ExcelWriter:

    frame.to_excel('examples/ex2.xlsx')
    

2.3 与Web API交互

使用requests包来与网站公开的API交互,获取数据。

例如,通过requests发送一个HTTP GET请求:

import requests
url = 'https://api.github.com/repos/pandas-dev/pandas/issues'
resp = requests.get(url)

2.4 与数据库交互

从SQL中将数据读取为DataFrame,pandas有多个函数可以简化此过程:

举例:使用Python内建的sqlite3驱动来生成一个SQLite数据库来模拟此过程:

import sqlite3

query = """
    CREATE TABLE test
    (
        a VARCHAR(20),
        b VARCHAR(20),
        c REAL,
        d INTEGER
    );
"""

con = sqlite3.connect('mydata.sqlite')
con.execute(query)

再插入几行数据:

data = [('Atlanta', 'Georgia', 1.25, 6),('Tallahassee', 'Florida', 2.6, 3), ('Sacramento', 'California', 1.7, 5)]
stmt = "INSERT INTO test VALUES(?,?,?,?)"
con.executemany(stmt, data)
con.commit()

当从数据库的表中选择数据时,大部分Python的SQL驱动(PyODBC,psycopg2,MySQLdb,pymssql等)返回的是元组列表:

cursor = con.execute('select * from test')
rows = cursor.fetchall()
rows
[out]
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

可以将元组的列表传给DataFrame构造函数,但还需要包含在游标的description属性中的列名:

cursor.description
[out]
(('a', None, None, None, None, None, None),
 ('b', None, None, None, None, None, None),
 ('c', None, None, None, None, None, None),
 ('d', None, None, None, None, None, None))

pd.DataFrame(rows, columns = [x[0] for x in cursor.description])
[out]
	a	b	c	d
0	Atlanta	Georgia	1.25	6
1	Tallahassee	Florida	2.60	3
2	Sacramento	California	1.70	5

pandas有一个read_sql函数允许从通用的SQLAlchemy连接中轻松地读取数据。这里,将使用SQLAlchemy连接到相同的SQLite数据库,并从之前创建的表中读取数据(SQLAlchemy项目是一个流行的Python SQL工具包,抽象去除了SQL数据库之间的许多常见差异):

import sqlalchemy as sqla
db = sqla.create_engine('sqlite:///mydata.sqlite')
pd.read_sql('select * from test', db)