数据载入、存储及文件格式——《利用python数据分析》第六章学习

数据载入、存储及文件格式——《利用python数据分析》第六章学习

前言

这次带来第六章的学习笔记,希望这次效率高点,快点写完。

代码环境这次小升级,Pycharm 2021.3 + Anaconda3, 另外用到了Windows的 cmd

访问数据是使用该书中各类工具的必需的第一步,重点关注利用pandas进行数据输如和输出。

输入和输出通常有以下集中类型:读取文本文件及硬盘上其他更高效的格式文件、从数据库载入数据、与网络资源进行交互,如WebAPI。

一 文本格式数据的读写

将表格型数据读取为DataFrame 对象是 pandas 的重要特性。 表 6-1 总结了部分实现该功能的函数,有兴趣的可以参考。read_csv 和 read_table 是书中使用的最多函数, 而 read_excel 则是我推荐的, 用来处理excel 文件。

先打开cmd, 切到指定文件目录,然后先创建一个csv文件,命令为

type nul>ex1.csv

这是创建一个新的空文件的意思,然后可以找到该文件,使用任意编辑软件,这里我使用win10自带的记事本编写下该文件的内容,便于后续的读取操作。(其实创建文件也可以直接使用记事本,不需要cmd)

a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

接下来,开始我们熟悉的python操作了

由于该文件是逗号分隔的,可以使用read_csv将它读入一个DataFrame

也可以使用read_table ,需要改下分隔符

在创建一个新的文件, 内容如下

1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

要读取该文件,需要选择一些选项,可以允许pandas自动分配默认列名,也可以自己指定

假设想要message 列成为 返回DataFrame的索引,可以指定位置4的列为缩影,或将 ‘message’ 传给参数index_col

当想要从多个列中形成一个分层索引,需要传入一个包含列序号或列名的列表,先写一个csv文件,内容如下:

key1,key2,value1,value2
one,a,1,2
one,b,3,4
one,c,5,6
one,d,7,8
two,a,9,10
two,b,11,12
two,c,13,14
two,d,15,16

并命名为csv_mindex.csv

key1,key2则是实现了分层索引

当字段是以多种不同数量的空格分开时,可以使用read_table,传入一个正则表达式作为分隔符

可以使用skiprows来跳过一些不想读取的行数

默认情况下,pandas 使用一些插件的表示,例如NA和NULL显示缺失值

na_values 选项可以传入一个列表或一组字符串来处理缺失值,在字典中,每列可以指定不同的缺失值标识

import pandas as pd
import numpy as np

# 使用read_csv, 默认',' 作为分隔符
df = pd.read_csv('ex1.csv')
print(df)
print('-------------------------')
# 默认'\t', 可以指定分隔符
print(pd.read_table('ex1.csv', sep=','))
print('-------------------------')
print(pd.read_csv('ex2.csv', header=None))
print('-------------------------')
print(pd.read_csv('ex2.csv', names=['a', 'b', 'c', 'd', 'message']))
print('-------------------------')
names = ['a', 'b', 'c', 'd', 'message']
print(pd.read_csv('ex2.csv', names=names, index_col='message'))
print('-------------------------')
parsed = pd.read_csv('csv_mindex.csv',
                     index_col=['key1', 'key2'])
print(parsed)
print('-------------------------')
print(list(open('ex3.txt')))
print('-------------------------')
result = pd.read_table('ex3.txt', sep='\s+')
print(result)
print('-------------------------')
# 忽略行
print(pd.read_csv('ex4.csv', skiprows=[0, 2, 3]))
print('-------------------------')
# 缺失值显示
result = pd.read_csv('ex5.csv')
print(result)
print('-------------------------')
# 是否是缺失值
print(pd.isnull(result))
print('-------------------------')
result = pd.read_csv('ex5.csv', na_values=['NULL'])
print(result)
print('-------------------------')
sentinels = {'message': ['foo', 'NA'], 'something': ['two']}
print(pd.read_csv('ex5.csv', na_values=sentinels))
print('-------------------------')

表6-2 列举了这两个解析函数常用的选项

1. 分块读入文本文件

当处理大型文件或找出正确的参数集来正确处理大文件时,可能需要读入文件的一个小片段或按小块遍历文件。这里为了方便,可以从本书提供的github地址仓库,获取数据,这里提供ex6.csv文件的地址pydata-book/ex6.csv at 2nd-edition · wesm/pydata-book (github.com)

在尝试大文件之前按,可以先对pandas的显示设置进行调整,使之更为紧凑

如果指向读取一小部分行,可以指明nrows

为了分块读入文件,可以指定chunksize作为每一块的行数,read_csv 返回的 TextParser 对象允许你根据 chunksize 遍历文件。

TextParser 还具有get_chunk方法,允许你按任意大小读取数据块

import pandas as pd
import numpy as np

pd.options.display.max_rows = 10
result = pd.read_csv('ex6.csv')
print(result)
print('-----------------------------')
print(pd.read_csv('ex6.csv', nrows=5))
print('-----------------------------')
chunker = pd.read_csv('ex6.csv', chunksize=1000)
print(chunker)
print('-----------------------------')
tot = pd.Series([])
for piece in chunker:
    tot = tot.add(piece['key'].value_counts(), fill_value=0)

tot = tot.sort_values(ascending=False)
print(tot[:10])

2. 将数据写入文本格式

数据可以导出为分隔的形式, 使用DataFrame 的 to_csv 方法,可以将数据导出为逗号分隔的文件

其他的分隔符也是可以的,写入到sys.stdout 时,控制台打印的文本结果

输出值在输出时,以空字符串出现,可以时na_rep 参数对缺失值进行标注

也可以仅写入列的子集,并且按选择的顺序写入

Series 也有 to_csv 方法

import pandas as pd
import numpy as np
import sys

data = pd.read_csv('ex5.csv')
print(data)
print('-----------------------')
data.to_csv('out.csv')
print(pd.read_csv('out.csv'))
print('-----------------------')
data.to_csv(sys.stdout, sep='|')
print('-----------------------')
data.to_csv(sys.stdout, na_rep='NULL')
print('-----------------------')
data.to_csv(sys.stdout, index=False, header=False)
print('-----------------------')
data.to_csv(sys.stdout, index=False, columns=['a', 'b', 'c'])
print('-----------------------')
dates = pd.date_range('1/1/2000', periods=7)
ts = pd.Series(np.arange(7), index=dates)
ts.to_csv('tseries.csv')
print(pd.read_csv('tseries.csv'))

3. 使用分隔格式

绝大多数的表格型数据都可以使用read_table从硬盘中读取,然而,在某些情况下,一些手动操作可能是必不可少的。接收一个带有一行或多行错误的文件并不少见,在该节有一些基础工具可以用于应对这些情况。

先从github仓库中获取ex7.csv文件

对于任何带有单字符分隔符的文件,可以使用Python的内建csv模块。要使用它,需要将任一打开的文件或文件型对象传给csv.reader。像遍历文件那样遍历reader会产生元组,元组的值为了删除引号的字符。

之后可以自行做一些必要处理,以将数据整理为需要的形式。

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

需要手动写入被分隔的文件时,可以使用csv.writer,接收一个已经打开的可写入文件对象以及和csv.reader相同的csv方言,格式选项。

import pandas as pd
import numpy as np
import sys
import csv

f = open('ex7.csv')
reader = csv.reader(f)
for line in reader:
    print(line)
print('-------------------------------')
# 先将文件读取为行的列表
with open('ex7.csv') as f:
    lines = list (csv.reader(f))
# 将数据拆分为列名行和数据行
header, values = lines[0], lines[1:]
# 生成一个包含数据列的字典,字典中行转置为列
data_dict = {h: v for h, v in zip(header, zip(*values))}
print(data_dict)
print('---------------------------------')
class my_dialect(csv.Dialect):
    lineterminator = '\n'
    delimiter = ';'
    quotechar = '"'
    quoting = csv.QUOTE_MINIMAL

表6-3 提供了CSV发言选项 作为参考

4. JSON 数据

JSON(JavaScript Object Notation),已经成为Web浏览器和其他应用间通过HTTP请求发送数据的标准格式。它是一种比CSV等表格文本形式更为自由的数据形式。

JSON 非常接近有效的Python代码,除了它的空值null和一些其他细微差别(例如,不允许列表末尾的逗号)之外。基本类型就是对象(字典)、数组(列表)、字符串、数字、布尔值和空值。对象中的所有键都必须是字符串。有几个Python库用于读写JSON数据。json 内置在 Python 标准库中,将JSON字符串转换为Python形式时,使用json.loads方法

另一方面json.dumps 可以将 Python对象转换回JSON

我们可以决定如何将JSON对象 或对象列表转换为DataFrame或其他数据结构,比较方便的方式是将字典构成的列表(之前是JSON对象)传入DataFrame构造函数,选出数据字段子集

pandas.read_json 可以自动将jason数据集 按照规定次序转换为 Series 或 DataFrame

从pandas 中数据导出为JSON,一种办法是对 Series 和 DataFrame 使用to_json方法

import pandas as pd
import numpy as np
import json

obj = """
    {"name": "Wes",
     "places_lived": ["United States", "Spain", "Germany"],
     "pet": null,
     "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
                  {"name": "Katie", "age": 38,
                   "pets": ["Sixes", "Stache", "Cisco"]}]
    }
    """

result = json.loads(obj)
print(result)
print('-------------------------------')
asjson = json.dumps(result)
print(asjson)
print('-------------------------------')
siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])
print(siblings)
print('-------------------------------')
data = pd.read_json('example.json')
print(data)
print('-------------------------------')
print(data.to_json())
print('-------------------------------')
print(data.to_json(orient='records'))

5. XML 和 HTML:网络抓取

python 拥有很多对 HTML 和 XML 格式进行读取、写入数据的库。

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

先安装read_html所使用的附加库,在Anaconda Prompt 下键入如下命令:

conda install lxml
pip install beautifulsoup4 html5lib

read_html 函数有很多选项,默认情况下,它会搜索并尝试解析所有包含在

标签中的表格型数据,返回的是DataFrame 对象的列表

import pandas as pd
import numpy as np
import json

tables = pd.read_html('fdic_failed_bank_list.html')
print(len(tables))
failures = tables[0]
print(failures.head())
print('----------------------------------------')
close_timestamps = pd.to_datetime(failures['Closing Date'])
print(close_timestamps.dt.year.value_counts())
5.1 使用 lxml.objectify 解析 XML

XML(eXtensible Markup Language)是另一种常用的结构化数据格式,使用元数据支持分层、嵌套数据。

先从github仓库中下载xml文件Performance_MNR.xml

使用lxml.objectify, 我们可以解析这个文件,并用getroot来获得对XML文件的根节点的引用

root.INDICATOR 返回一个生成器,可以产生每一个XML 元素。对于每条记录,我们可以将标签名称的字典填充为数据值。

XML 数据可以更复杂,每个标签也可以包含元数据。

import pandas as pd
import numpy as np
import json
from lxml import objectify

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

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)

perf = pd.DataFrame(data)
print(perf.head())

from io import StringIO
tag = 'Google'
root = objectify.parse(StringIO(tag)).getroot()
print(root)
print(root.get('href'))
print(root.text)

二 二进制格式

使用Python 内建的 pickle 序列化模块进行二进制格式操作是存储数据(也称为序列化)最高效、最方便的方式之一。pandas对象的to_pickle方法就是如此。

可以使用内建的pickle读取文件中“pickle化” 对象,或更方便地使用pandas.read_pickle 做上述操作

pickle 仅被推荐为短期的存储格式。问题在于pickle很难确保格式地长期有效性;一个今天被pickle化的对象可能明天会因为库的新版本而无法序列化。

pandas 内建支持其他两个二进制格式 HDF5 和 MessagePack。pandas或NumPy 其他的存储格式包括:bcolz,Feather

import sys

import pandas as pd
import numpy as np

frame = pd.read_csv('ex1.csv')
print(frame)
print('------------------------------')
frame.to_pickle('frame_pickle')
print(pd.read_pickle('frame_pickle'))
print('------------------------------')

1. 使用HDF5格式

HDF5是一个备受好评的文件格式,用于存储大量的科学数组数据。以C库的形式提供,并且具有许多语言的接口,HDF5中的 “HDF” 表示分层数据格式。每个HDF5文件可以存储多个数据集并且支持元数据,支持多种压缩模式的即时压缩,可以高效地存储重复模式的数据,适用于处理不适合在内存中存储的超大型数据,方便我们高效地读写大型数组的一小块。

pandas提供了一个HDF5的高阶接口,可以简化Series和DataFrame的存储。HDFStore类像字典一样工作并处理低级别的细节

包含在HDF5文件中的对象可以使用相同的字典型API进行检索

HDFStore 支持两种存储格式, ‘fixed’ 和 ‘table’。 后者速度更慢,但支持一种特殊语法的查询操作

put 是 store[‘obj2’] = frame 方法的显示版本,但允许我们设置其他选项,比如存储格式

pandas.read_hdf 函数是这些工具的快捷方法

如果你处理存储在远程服务器上的数据时,比如Amazon S3 或 HDFS, 使用其他专门为分布式存储而设计的二进制格式更为合适,比如Apache Parquet。 Paequet 和其他类似的存储格式仍然在发展中。

如果是在本地处理大量数据,书中推荐尝试 PyTables 和 h5py,看看它们是否符合需求。由于很多数据分析的困难在于I/O密集,使用像HDF5 这样的工具可以大大加速应用。

HDF5 并不是数据库,而是一种适合一次写入多次读取的数据集。尽管数据可以在任何时间添加到文件中,但如果是多个写入者持续写入,文件可能会损坏。

import pandas as pd
import numpy as np

frame = pd.DataFrame({'a': np.random.randn(100)})
store = pd.HDFStore('mydata.h5')
store['obj1'] = frame
store['obj_col'] = frame['a']
print(store)
print('-------------------')
print(store['obj1'])
print('-------------------')
store.put('obj2', frame, format='table')
print(store.select('obj2', where=['index >= 10 and index <= 15']))
store.close()
print('-------------------')
frame.to_hdf('mydata.h5', 'obj3', format='table')
print(pd.read_hdf('mydata.h5', 'obj3', where=['index < 5']))

2. 读取Microsoft Excel 文件

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

这些附加包需要安装,可以参考下列命令

conda install xlrd
conda install openpyxl

之后运行下述代码会出现一个问题

import numpy as np
import pandas as pd

xlsx = pd.ExcelFile('examples/ex1.xlsx')

数据载入、存储及文件格式——《利用python数据分析》第六章学习_第1张图片

或者是让你指定一个egine, 或者是上面的问题,可能的原因有多种,我看了相关的blog最后才发现和那些blog说的指定egine或者没安装相关包无关,而是文件损坏,重新写下文件就好了。

如果读取含多个表的文件,生成ExcelFile 更快,但也可以更简洁地将文件名传入read_excel

如果需要将数据写入到Excel格式中,必须先生成一个ExcelWriter,然后使用pandas对象地to_excel 方法

也可以将文件路径传给to_excel,避免直接使用ExcelWriter

import numpy as np
import pandas as pd

xlsx = pd.ExcelFile('examples/ex2.xlsx')
print(pd.read_excel(xlsx, 'Sheet1'))
print('----------------------------------------')
frame = pd.read_excel('examples/ex2.xlsx', 'Sheet1')
print(frame)
print('----------------------------------------')

writer = pd.ExcelWriter('examples/ex3.xlsx')
frame.to_excel(writer, 'Sheet1')
writer.save()
frame.to_excel('examples/ex3.xlsx')
print(pd.read_excel('examples/ex3.xlsx'))

三 与Web API交互

很多网站都有公开API, 通过JSON 或其他格式提供数据服务,很多方式可以利用Python来访问API,书的作者推荐使用requests包。

Response 对象地json方法 将返回 一个包含解析为 本地 Python 对象地 JSON 地字典

import numpy as np
import pandas as pd
import requests

url = 'http://api.github.com/repos/pandas-dev/pandas/issues'
rep = requests.get(url)
print(rep)
print('-------------------------')
data = rep.json()
print(data[0]['title'])
print('-------------------------')
issues = pd.DataFrame(data, columns=['number', 'title', 'labels', 'states'])
print(issues)

四 与数据库交互

在业务场景中,大部分数据并不是在文件或Excel文件中的。基于SQL的关系型数据库(例如SQL Server、 PostgreSQL和 MySQL)使用广泛,很多小众数据库也变得越发流行。数据库的选择通常取决于性能、数据完整性以及应用的可伸缩性需求。

从SQL中将数据读取为DataFrame是简单直接的,pandas 有很多方法可以简化这个过程。作为例子,我将使用Python内建的squlite3驱动来生成一个SQLite数据库

import numpy as np
import pandas as pd
import sqlite3

query = """
CREATE TABLE test
(a VARCHAR(20), b VARCHAR(20),
 c REAL,        d INTEGER
);"""
con = sqlite3.connect('mydata.sqlite')
print('----------------------------')
con.commit()
data = [('Atlanta', 'Georgia', 1.25, 6),
        ('Tallahassee', 'Florida', 2.6, 3),
        ('Sacramento', 'California', 1.7, 5)]
stmt = "INSERT INTO test VALUES(?, ?, ?, ?)"
print(con.executemany(stmt, data))
con.commit()

cursor = con.execute('select * from test')
rows = cursor.fetchall()
print('----------------------------')
print(rows)
print('----------------------------')
print(cursor.description)
print('----------------------------')
print(pd.DataFrame(rows, columns=[x[0] for x in cursor.description]))
print('----------------------------')

import sqlalchemy as sqla

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

小结 后话

访问数据是第一步,接下来会继续学习数据处理、可视化、时间序列分析和其他主题。

今天又寄了,但是程璐赢了,我可以开摆了。

it()

cursor = con.execute(‘select * from test’)
rows = cursor.fetchall()
print(’----------------------------’)
print(rows)
print(’----------------------------’)
print(cursor.description)
print(’----------------------------’)
print(pd.DataFrame(rows, columns=[x[0] for x in cursor.description]))
print(’----------------------------’)

import sqlalchemy as sqla

db = sqla.create_engine(‘sqlite:///mydata.sqlite’)
print(pd.read_sql(‘select * from test’, db))




## 小结 后话

访问数据是第一步,接下来会继续学习数据处理、可视化、时间序列分析和其他主题。

今天又寄了,但是程璐赢了,我可以开摆了。

你可能感兴趣的:(python,数据分析,数据挖掘)