Python数据分析之Pandas入门(中)

前言

本系列共有三篇文章,依次按照pandas数据类型及其结构、内置模块对数据处理功能、可视化工具以及形如房价预测的案例分析内容展开介绍。参考自书籍《Python for Data Analysis(Second Edition)》,本篇文章的代码均已测试通过,数据集下载详见【资源】。
📢注意:代码文件应和解压后的数据及文件夹在同一目录下才能相对路径引用到,当然也可使用绝对路径。


四、数据载入及存储


1.文本格式数据的读写

将表格型数据读取为DataFrame对象是pandas的重要特性。

部分功能函数如下所示:

函数描述
read_csv从文件、URL或文件型对象读取分隔好的数据,逗号是默认分隔符
read_table从文件、URL或文件型对象读取分隔好的数据,制表符('\t')是默认分隔符
read_fwf从特定宽度格式的文件中读取数据(无分隔符)
read_clippboardread_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打印各种解析器输出的信息,比如位于非数值列的缺失值数量
encodingUnicode文本编码(例如’utf-8’)
squeeze如果解析数据只包含一列,返回一个Series
thousands千位分隔符(例如’,‘或’.')

打印文件内容的终端命令:

  • Windows:!type examples/ex1.csv
  • Unix shell:!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

③使用分隔格式

  • 考虑使用read_table从硬盘中读取的数据仍存在一行或多行错误的情况
# 对于任何带有单字符分隔符的文件都可使用内建的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_NONNUMERICcsv.QUOTE_NONE(不引用)
skipinitialspace忽略每个分隔符后的空白,默认为False
doublequote如何处理字段内的引号。如果为True,则是双引号
escapechar当引用设置为csv.QUOTE_NONE时用于转义分隔符的字符串,默认是禁用的

④JSON数据

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'))

⑤XML和HTML

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 = '<a href="http://www.google.com">Google</a>'
root = objectify.parse(StringIO(tag)).getroot()

# 可以访问标签或链接文本中的任何字段
print(root, '\n')
root        # 文本中的元素在内存中的位置
root.get('href')    # 文本元素的来源——即链接
root.text

2.二进制格式

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格式

  • 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')        # 有个可选的独立参数<tables>,需要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+')

②读取Microsoft Excel文件

pandas支持通过ExcelFile类或pandas.read_excel函数来读取Excel 2003或更高版本文件中的表格型数据,这些工具内部是使用附加包xlrdopenyxl来分别读取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

③与Web API交互

使用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数据清洗


1.清洗空值

如果我们要删除包含空字段的行,可以使用 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())


2.删除指定列有空值的行

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())

3.替换空字段

  • 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())

4.清洗格式错误的数据


①格式化日期

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())

③重复数据

  • 要清洗重复数据,可以使用 duplicated() 和 drop_duplicates() 方法
  • 对应的数据是重复的,duplicated() 会返回 True,否则返回 False
  • 删除重复数据,可以直接使用drop_duplicates() 方法
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)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Larissa857

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值