本系列共有三篇文章,依次按照pandas数据类型及其结构、内置模块对数据处理功能、可视化工具以及形如房价预测的案例分析内容展开介绍。参考自书籍《Python for Data Analysis(Second Edition)》,本篇文章的代码均已测试通过,数据集下载详见【资源】。
注意:代码文件应和解压后的数据及文件夹在同一目录下才能相对路径引用到,当然也可使用绝对路径。
将表格型数据读取为DataFrame对象是pandas的重要特性。
部分功能函数如下所示:
函数 | 描述 |
---|---|
read_csv | 从文件、URL或文件型对象读取分隔好的数据,逗号是默认分隔符 |
read_table | 从文件、URL或文件型对象读取分隔好的数据,制表符('\t' )是默认分隔符 |
read_fwf | 从特定宽度格式的文件中读取数据(无分隔符) |
read_clippboard | read_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,从文件或用户名处获得列名,或没有列名 |
类型推断和数据转换 | 包括用户自定义的值转换和自定义的缺失值符号列表 |
日期时间解析 | 包括组合功能,也包括将分散在多个列上的日期和时间信息组合成结果中的单个列 |
迭代 | 支持对大型文件的分块迭代 |
未清洗的数据问题 | 跳过行、页脚、注释以及其他次要数据,比如使用逗号分隔千位的数字 |
表1 read_csv/read_table函数参数
参数 | 描述 |
---|---|
path | 表明文件系统位置的字符串、URl或文件型对象 |
sep或delimiter | 用于分隔每行字段的字符序列或正则表达式 |
header | 用作列名的行号,默认是0(第一行),如果没有列名的话,应该为None |
index_col | 用作结果中行索引的列号或列名,可以是一个单一的名称/数字,也可以是一个分层索引 |
names | 结果的列名列表,和names=None一起用 |
skiprows | 从文件开头起,需要跳过的行数或行号列表 |
na_values | 需要用NA替换的值序列 |
comment | 在行结尾处分隔注释的字符 |
parse_dates | 尝试将数据解析为datetime,默认为False。如果为True,将尝试解析所有的列;也可以指定列号或列名列表。如果列表的元素是元组或列表,将会把多个列组合在一起进行解析 |
keep_date_col | 如果连接列到解析日期上,保留被连接的列,默认是False |
converters | 包含列名映射到函数的字典(例如{‘foo’: f}会把函数f应用到’foo’列) |
dayfirst | 解析非明确日期时,按照国际格式处理 |
date_parser | 用于解析日期的函数 |
nrows | 从文件开头处读入的行数 |
iterator | 返回一个TextParser对象,用于零散地读入文件 |
chunksize | 用于迭代的块大小 |
skip_footer | 忽略文件尾部的行数 |
verbose | 打印各种解析器输出的信息,比如位于非数值列的缺失值数量 |
encoding | Unicode文本编码(例如’utf-8’) |
squeeze | 如果解析数据只包含一列,返回一个Series |
thousands | 千位分隔符(例如’,‘或’.') |
打印文件内容的终端命令:
!type examples/ex1.csv
!cat examples/ex1.csv
读取CSV文件:
df = pd.read_csv('examples/ex1.csv')
print(df)
pd.read_table('examples/ex1.csv', sep=',') # 指定分隔符——逗号
"""输出为:
a b c d message
0 1 2 3 4 hello
1 5 6 7 8 world
2 9 10 11 12 foo
"""
# pandas自动分配默认列名
pd.read_csv('examples/ex2.csv', header=None)
pd.read_csv('examples/ex2.csv', names=['a', 'b', 'c', 'd', 'message']) # 指定列名
# 将message列成为返回DataFrame的索引,可以指定位置'4'的列为索引,或传递参数给index_col
names = ['a', 'b', 'c', 'd', 'message']
pd.read_csv('examples/ex2.csv', names=names, index_col='message')
"""输出为:
a b c d
message
hello 1 2 3 4
world 5 6 7 8
foo 9 10 11 12
"""
# 当你想要从多个列中形成一个分层索引,需要传入一个包含列序号或列名的列表
!cat examples/csv_mindex.csv
parsed = pd.read_csv('examples/csv_mindex.csv',
index_col=['key1', 'key2'])
print('\n', parsed)
"""输出为:
value1 value2
key1 key2
one a 1 2
b 3 4
c 5 6
d 7 8
two a 9 10
b 11 12
c 13 14
d 15 16
"""
解决文本不对齐问题:
# 使用正则表达式'\s+'或手动校准因不同数量的空格造成的不对齐
result = pd.read_table('examples/ex3.txt', sep='\s+')
result
处理缺失值:
!cat examples/ex4.csv
# skiprows接收参数以跳过出现异常的行——第一行、第三行、第四行
skiprows = pd.read_csv('examples/ex4.csv', skiprows=[0, 2, 3])
print('\n', skiprows, '\n')
!cat examples/ex5.csv
# na_values可以传入一个列表或一组字符串处理缺失值
result = pd.read_csv('examples/ex5.csv', na_values=['NULL'])
# 每列都可指定不同的缺失值标识
sentinels = {'message': ['foo', 'NA'], 'something': ['two']}
pd.read_csv('examples/ex5.csv', na_values=sentinels)
"""输出为:
something a b c d message
0 one 1 2 3.0 4 NaN
1 NaN 5 6 NaN 8 world
2 three 9 10 11.0 12 NaN
"""
pd.options.display.max_rows = 10 # pandas读取数据只显示前十行
print(pd.read_csv('examples/ex6.csv', nrows=5), '\n') # pandas读取前五行数据
# 对读入文件的截取(只读其中的小片段或小块遍历文件)
chunker = pd.read_csv('examples/ex6.csv', chunksize=100)
print(chunker, '\n')
# 对截取出的文件遍历一遍,并对'key'列聚合获得计数值
tot = pd.Series([], dtype='float64') # 官方文档提示:对于任何空序列的Series类的dtype将会默认是'float64'或'int64',需要指定其dtype否则会报警告
for piece in chunker:
tot = tot.add(piece['key'].value_counts(), fill_value=0)
tot = tot.sort_values(ascending=False)
print(tot[:10])
import sys
data = pd.read_csv('examples/ex5.csv')
data.to_csv('examples/out.csv') # DataFrame的to_csv方法将数据导出为逗号分隔的文件
data.to_csv(sys.stdout, sep='|') # 使用其它分隔符
print('\n')
data.to_csv(sys.stdout, na_rep='NULL') # 用空字符串的形式表示缺失值
print('\n')
# 不读出列标签,并按顺序读出行标签
data.to_csv(sys.stdout, index=False, columns=['a', 'b', 'c'])
dates = pd.date_range('1/1/2000', periods=7)
ts = pd.Series(np.arange(7), index=dates)
ts.to_csv('examples/tseries.csv') # Series的to_csv方法将数据导出为逗号分隔的文件
!cat examples/tseries.csv
# 对于任何带有单字符分隔符的文件都可使用内建的csv模块
import csv
# 首先,将任一打开的文件或文件型对象作为参数传入csv.reader()函数
f = open('examples/ex7.csv')
reader = csv.reader(f)
for line in reader:
print(line)
# 然后,对文件进行处理
with open('examples/ex7.csv') as f:
lines = list(csv.reader(f))
# 将数据拆分成列名行和数据行
header, values = lines[0], lines[1:]
# 用字典推导式和zip(*values)生成一个包含数据列的字典,字典中行转置成列
data_dict = {h: v for h, v in zip(header, zip(*values))}
print('\n', data_dict)
【对文件的处理模块还可参考import zipfile
】
import sys
f = open('examples/ex7.csv')
# csv文件有多种不同风格,如需根据不同的分隔符、字符串引用约定或行终止符定义一种新格式时,可以使用csv.Dialect定义一个简单的子类
class my_dialect(csv.Dialect): # dialect——方言参数
lineterminator = '\n'
delimiter = ';'
quotechar = '"'
quoting = csv.QUOTE_MINIMAL
reader = csv.reader(f, dialect=my_dialect)
# 也可以不定义子类,直接将方言参数(dialect)传入csv.reader的关键字参数
reader = csv.reader(f, delimiter='|')
CSV方言选项
参数 | 描述 |
---|---|
delimiter | 接收一个用于分割字段的字符作为参数的关键字,默认是',' |
lineterminator | 行终止符,默认是'\r\n' ,读取器会忽略行终止符并识别跨平台行终止符 |
quotecher | 用在含有特殊字符字段中的引号,默认是" |
quoting | 引用惯例。选项包括csv.QUOTE_ALL (引用所有字段),csv.QUOTE_MINMAL (只使用特殊字符),csv.QUOTE_NONNUMERIC 和csv.QUOTE_NONE (不引用) |
skipinitialspace | 忽略每个分隔符后的空白,默认为False |
doublequote | 如何处理字段内的引号。如果为True,则是双引号 |
escapechar | 当引用设置为csv.QUOTE_NONE 时用于转义分隔符的字符串,默认是禁用的 |
JSON(JavaScript Object Notation,JavaScript 对象表示法)出了空值(NULL)和一些其他的细微差别外(如不允许列表末尾的逗号),基本类型是obj(字典)、数组(列表)、字符串、数字、布尔值和空值。对象中的所有键都必须是字符串。
import json
obj = """
{"name": "Larissa",
"places_lived": ["China", "Spain", "Germany"],
"pet": null,
"siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
{"name": "Katie", "age": 38,
"pets": ["Sixes", "Stache", "Cisco"]}]
}
"""
result = json.loads(obj)
# 将Python对象转换回JSON
asjson = json.dumps(result)
print(result, '\n')
siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])
siblings
# read_json()默认选项是假设JSON数组中的每个对象是表里的一行
data = pd.read_json('examples/example.json')
print(data.to_json())
print(data.to_json(orient='records'))
Python中有很多可以对HTML和XML(eXtensible Markup Language)格式进行读取、写入的库,例如:lxml、Beatiful Soup和html5lib
# 安装read_html所使用的附加库——ltml
tables = pd.read_html('examples/fdic_failed_bank_list.html')
print('The len of tables: ', len(tables), '\n')
failures = tables[0]
print(failures.head(), '\n')
# 可以进行的数据处理——计算已倒闭的银行数量
close_timestamps = pd.to_datetime(failures['Closing Date'])
close_timestamps.dt.year.value_counts()
# 使用ltml解析较为简单的XML格式的数据
from lxml import objectify
path = 'datasets/mta_perf/Performance_MNR.xml'
parsed = objectify.parse(open(path)) # ltml.objectify()
root = parsed.getroot() \
# 填充数据值
data = []
# 标签名序列
skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ',
'DESIRED_CHANGE', 'DECIMAL_PLACES']
# root.INDICATOR 返回一个生成器
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
perf = pd.DataFrame(data)
perf.head()
# 使用ltml解析更复杂的XML格式的元数据
from io import StringIO
tag = 'Google'
root = objectify.parse(StringIO(tag)).getroot()
# 可以访问标签或链接文本中的任何字段
print(root, '\n')
root # 文本中的元素在内存中的位置
root.get('href') # 文本元素的来源——即链接
root.text
pickle仅被推荐作为短期的存储格式,问题在于pickle很难确保格式的长期有效性。pandas内建支持其他两个二进制格式:HDF5和MessagePack。
pandas或NumPy其他的存储格式包括:
# 将数据以pickle形式写入硬盘
frame = pd.read_csv('examples/ex1.csv')
print(frame)
frame.to_pickle('examples/frame_pickle') # 或:pd.read_pickle('examples/frame_pickle)
HDF5用于存储大量的科学数组数据,支持多种压缩模式的即时压缩,使得重复模式的数据可以更高效地存储。它以C库的形式提供,并且具有许多其他语言接口,包括Java、Julia、MATLAB和Python。I/O密集型困难的数据分析类问题使用HDF5会大大加速其应用
如果是在本地处理数据,建议使用 PyTables 和 h5py
# pd.HDFStore类
frame = pd.DataFrame({'a': np.random.randn(100)})
store = pd.HDFStore('mydata.h5') # 有个可选的独立参数,需要conda install,否则会报错
store['obj1'] = frame # 或: store.put('obj1', frame, format='table')
store['obj1_col'] = frame['a']
print(store)
# 可以进行字典式索引
print(store['obj1'])
# 显示指定行数范围内的内容:
print('\nstore中第10行至第15行中的内容是:\n', store.select('obj2', where=['index >= 10 and index <= 15']))
store.close()
# HDFStore支持两种存储方式——'fixed'和'table',后者更慢
frame.to_hdf('mydata.h5', 'obj3', format='table')
# 注意:以下语句不加< mode='r+' > 会报错The file 'mydata.h5' is already opened, but not in read-only mode (as requested).
pd.read_hdf('mydata.h5', 'obj3', where=['index < 5'], mode='r+')
pandas支持通过ExcelFile
类或pandas.read_excel
函数来读取Excel 2003或更高版本文件中的表格型数据,这些工具内部是使用附加包xlrd
和openyxl
来分别读取XLS和XLS文件的。
import pandas as pd
xlsx = pd.ExcelFile('examples/ex1.xlsx')
pd.read_excel(xlsx, 'Sheet1') # 读取为DataFrame的表格型数据
frame = pd.read_excel('examples/ex1.xlsx', 'Sheet1')
# 将pandas数据写入到Excel格式中
writer = pd.ExcelWriter('examples/ex2.xlsx')
frame.to_excel(writer, 'Sheet1') # 或:frame.to_excel('examples/ex2.xlsx')
writer.save()
frame
使用requests包
import requests
# 向网站发送一个HTTP GET请求
url = 'https://api.github.com/repos/pandas-dev/pandas/issues'
resp = requests.get(url)
print(resp)
# Response对象的json方法将返回一个包含解析为本地Python对象的JSON的字典
data = resp.json()
print(data[0]['title'])
# data中的每个元素都是一个包含Github问题页面上的所有数据的字典(注释除外)
issues = pd.DataFrame(data, columns=['number', 'title',
'labels', 'state'])
issues
# 生成一个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) # 文件定位,注意:第一次运行生成文件并保存后再运行,会报错该表格型文件已存在
# con.commit() # 保存
# 插入数据
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、psymssql等)返回的是元组的列表
cursor = con.execute('select * from test')
rows = cursor.fetchall()
print(rows)
# 将元组的列表传给DataFrame构造函数
print(cursor.description)
pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
# SQLAlchemy项目是一个流行的Python SQL工具包,抽象去除了SQL数据库之间的许多常见差异
import sqlalchemy as sqla
db = sqla.create_engine('sqlite:///mydata.sqlite')
pd.read_sql('select * from test', db)
其他补充详见系列文章——Pandas数据清洗
如果我们要删除包含空字段的行,可以使用 dropna() 方法,并通过 isnull() 判断各个单元格是否为空。
DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
参数说明:
axis:默认为 0,表示逢空值剔除整行,如果设置参数 axis=1 表示逢空值去掉整列
how:默认为 ‘any’ 如果一行(或一列)里任何一个数据有出现 NA 就去掉整行,如果设置 how=‘all’ 一行(或列)都是 NA 才去掉这整行。
thresh:设置需要多少非空值的数据才可以保留下来的
subset:设置想要检查的列。如果是多个列,可以使用列名的 list 作为参数
inplace:如果设置 True,将计算得到的值直接覆盖之前的值并返回 None,修改的是源数据。
示例见代码如下:
import pandas as pd
# Pandas 把 n/a 和 NA 当作空数据,na 不是空数据,不符合我们要求,我们可以指定空数据类型:
missing_values = ["n/a", "na", "--"]
df = pd.read_csv('property-data.csv', na_values = missing_values)
print (df['NUM_BEDROOMS'])
print (df['NUM_BEDROOMS'].isnull())
import pandas as pd
df = pd.read_csv('property-data.csv')
df.dropna(subset=['ST_NUM'], inplace = True)
print(df.to_string())import pandas as pd
df = pd.read_csv('property-data.csv')
df.dropna(subset=['ST_NUM'], inplace = True)
print(df.to_string())
fillna()
方法来替换一些空字段。Pandas使用 mean()、median() 和 mode() 方法计算列的均值(所有值加起来的平均值)、中位数值(排序后排在中间的数)和众数(出现频率最高的数)import pandas as pd
df = pd.read_csv('property-data.csv')
x = df["ST_NUM"].mean()
df['PID'].fillna(12345, inplace = True)
df.fillna(12345, inplace = True)
print(df.to_string())
print("{:-^80}".format('分割线'))
df["ST_NUM"].fillna(x, inplace = True)
print(df.to_string())
import pandas as pd
# 第三个日期格式错误
data = {
"Date": ['2020/12/01', '2020/12/02' , '20201226'],
"duration": [50, 40, 45]
}
df = pd.DataFrame(data, index = ["day1", "day2", "day3"])
df['Date'] = pd.to_datetime(df['Date'])
print(df.to_string())
import pandas as pd
person = {
"name": ['Google', 'Baidu' , 'Wiki'],
"age": [50, 40, 12345] # 12345 年龄数据是错误的
}
df = pd.DataFrame(person)
df.loc[2, 'age'] = 30 # 修改数据
print(df.to_string())
import pandas as pd
person = {
"name": ['Google', 'Baidu' , 'Wiki' , 'Wiki'],
"age": [50, 40, 40, 23]
}
df = pd.DataFrame(person)
print(df.duplicated())
df.drop_duplicates(inplace = True)
print(df)