《python数据分析(第2版)-阿曼多.凡丹戈》读书笔记第8章-应用数据库
第8章应用数据库
本章中,我们将为读者介绍各种数据库及其应用编程接口。这里所说的数据库包括关系型数据库以及非关系型(NoSQL)数据库。关系型数据库是由数据表汇集而成的,更重要的是,这些数据表中的数据是按照数据项之间的关系进行组织的。当然,这里所说的关系,也可以是某个数据表中的行数据与其他数据表中的行数据之间的关系。关系型数据库不仅涉及数据表之间的关系,首先,它要处理同一个数据表中不同列之间的关系(很明显,一个数据表内的各列毫无疑问是相关的);其次,它还要处理数据表之间的关系。
伴随着大数据和Web应用的流行,非关系型(Not Only SQL,NoSQL)数据库也开始野蛮生长。NoSQL系统将成为类SQL事实上的标准。NoSQL数据库的主旨在于,使用比关系模型更为灵活的方式来存储数据。这就可能意味着,无需数据库模式或者灵活的数据库模式。当然,灵活性和速度也是有代价的,例如无法始终保证事务的一致性。NoSQL数据库可以利用面向列的方法以字典的形式来储存数据,这些数据对象包括文档、对象、图、元组,甚至这些对象的组合体。本章将要介绍的主题如下。
- 基于sqlite3的轻量级访问
- 通过Pandas访问数据库
- SQLAlchemy
- Pony ORM
- Dataset:懒人数据库
- PyMongo与MongoDB
- 利用Redis存储数据
- 利用memcache存储数据
- Apache Cassandra。
8.1基于sqlite3的轻量级访问
SQLite是一款非常流行的关系型数据库,它非常轻盈,因此被大量应用程序广泛采纳,如Mozilla Firefox等浏览器。安卓系统上的大部分应用的数据存储也是通过SQLite完成的。
Sqlite3是Python标准发行版自带的一个模块,可以用于处理SQLite数据库。使用sqlite3模块时,数据库既可以存放到文件中,也可以保留在内存中。本例采用后一种方式。下面我们导入sqlite3,代码如下。
import sqlite3
首先,连接数据库。如果希望把数据库存到文件中,那么必须提供一个文件名,否则,可以通过下列方式将数据库留在内存中。
with sqlite3.connect(":memory:") as con:
上面使用了Python的with语句。需要注意的是,这个语句依赖于特定上下文管理器类的exit()方法的存在。如果使用了这个语句,我们就无需显式关闭数据库连接了。这是因为上下文管理器会自动替我们关闭连接。连接到数据库后,我们还需要一个游标。游标在数据库中的作用,至少从概念上来讲,类似于文本编辑器中的光标。注意,这个游标也需要由我们来关闭。
下面开始创建游标,代码如下。
c = con.cursor()
现在,可以直接创建数据表了。通常,必须首先创建一个数据库,或数据库专家已经替我们建立了一个数据库。在本章中,我们不仅需要了解Python,而且还得懂SQL。SQL是一种专门用来查询和操作数据库的语言。限于篇幅,这里不可能对SQL进行详尽地介绍。不过,只要稍微努力,读者就能够掌握基本的SQL。为了创建数据表,我们需要向游标传递一个SQL字符串,具体如下。
c.execute('''CREATE TABLE sensors (date text, city text, code text, sensor_id real, temperature real)''')
上面的代码会创建一个包含很多列的数据表,具体名称为sensors。在上面的字符串中,text和real用来表明字符串和数值的类型。通过上面的代码,我们就能创建一个可用的数据表了。如果创建过程发生错误,我们就会收到相应的出错提示。列出数据库中数据表的方法与数据库本身紧密相关。通常,有一个或一组专门的数据表来存放用户数据表的元数据。下面我们列出SQLite数据表,具体如下。
for table in c.execute("SELECT name FROM sqlite_master WHERE type = 'table'"): print("Table", table[0])
正如所料,我们将得到如下的输出。
Table sensors
现在,我们要插入并查询一些随机数据,具体如下。
1 c.execute("INSERT INTO sensors VALUES ('2016-11-05','Utrecht','Red',42,15.14)") 2 c.execute("SELECT * FROM sensors") 3 print(c.fetchone())
下面我们输出插入的记录。
(u’2016-11-05’, u’Utrecht’, u’Red’, 42.0, 15.14)
当不再需要某个数据表时,我们就可以将其删除了。需要注意的是,删除是一项非常危险的操作,因此,我们必须绝对肯定再也用不到这个数据表了。因为数据表一旦被删,就无法恢复了,除非之前已经做好了备份。下面的代码将删除数据表并显示删除操作执行后所剩数据表的数量,具体如下。
1 con.execute("DROP TABLE sensors") 2 print("# of tables", c.execute("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").fetchone()[0])
输出内容如下。
# of tables 0
下列代码摘自本书代码包中的ch-08.ipynb文件。
1 import sqlite3 2 3 with sqlite3.connect(":memory:") as con: 4 c = con.cursor() 5 c.execute('''CREATE TABLE sensors (date text, city text, code text, sensor_id real, temperature real)''') 6 7 for table in c.execute("SELECT name FROM sqlite_master WHERE type = 'table'"): 8 print("Table", table[0]) 9 10 c.execute("INSERT INTO sensors VALUES ('2016-11-05','Utrecht','Red',42,15.14)") 11 c.execute("SELECT * FROM sensors") 12 print(c.fetchone()) 13 con.execute("DROP TABLE sensors") 14 15 print("# of tables", c.execute("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").fetchone()[0]) 16 17 c.close()
8.2通过Pandas访问数据库
我们可以向Pandas提供一个数据库连接,如前面例子中的连接或SQLAlchemy连接。关于SQLAlchemy连接,我们将在本章后面部分进行讲解。正如第7章所述,这里也要用到statsmodels模块的太阳黑子活动数据。为了加载数据,我们可以使用下列代码。
(1)创建元组列表,以构建Pandas DataFrame。
rows = [tuple(x) for x in df.values]
与之前的示例不同,这里要创建的是一个未规定数据类型的数据表,代码如下。
con.execute("CREATE TABLE sunspots(year, sunactivity)")
(2)我们知道,executemany()方法可以执行多条语句。就本例而言,我们要插入一些记录,这些记录来自元组列表。下面将这些数据行插入数据表并显示行数。
1 con.executemany("INSERT INTO sunspots(year, sunactivity) VALUES (?, ?)", rows) 2 c.execute("SELECT COUNT(*) FROM sunspots") 3 print(c.fetchone())
数据表的行数如下。
(309,)
(3)execute()函数返回结果中rowcount属性存放的是受影响的数据行的数量。这个属性有点古怪,而且与SQLite版本密切相关。另外,就像上面代码中看到的那样,SQL查询是无歧义的。下面我们删除事件数大于20的记录,代码如下。
print("Deleted", con.execute("DELETE FROM sunspots where sunactivity > 20").rowcount, "rows")
结果如下。
Deleted 217 rows
(4)如果把数据库连接至Pandas,就可以利用read_sql()函数来执行查询并返回Pandas DataFrame了。下面选择前1732条记录,代码如下。
print(read_sql("SELECT * FROM sunspots where year < 1732", con))
最终得到如下Pandas DataFrame。
year sunactivity
0 1700.0 5.0
1 1701.0 11.0
2 1702.0 16.0
3 1707.0 20.0
4 1708.0 10.0
5 1709.0 8.0
6 1710.0 3.0
7 1711.0 0.0
8 1712.0 0.0
9 1713.0 2.0
10 1714.0 11.0
11 1723.0 11.0
以下代码摘自本书代码包中的ch-08.ipynb文件。
1 import statsmodels.api as sm 2 from pandas.io.sql import read_sql 3 import sqlite3 4 5 with sqlite3.connect(":memory:") as con: 6 c = con.cursor() 7 8 data_loader = sm.datasets.sunspots.load_pandas() 9 df = data_loader.data 10 rows = [tuple(x) for x in df.values] 11 12 con.execute("CREATE TABLE sunspots(year, sunactivity)") 13 con.executemany("INSERT INTO sunspots(year, sunactivity) VALUES (?, ?)", rows) 14 c.execute("SELECT COUNT(*) FROM sunspots") 15 print(c.fetchone()) 16 print("Deleted", con.execute("DELETE FROM sunspots where sunactivity > 20").rowcount, "rows") 17 18 print(read_sql("SELECT * FROM sunspots where year < 1732", con)) 19 con.execute("DROP TABLE sunspots") 20 21 c.close()
8.3SQLAlchemy
SQLAlchemy以基于设计模式(design pattern)的对象关系映射(ORM)而闻名。也就是说,它可以把Python的类映射为数据库的数据表。实际上,这意味着添加了一个额外的抽象层,因此,我们需要使用SQLAlchemy应用程序接口来跟数据库打交道,而非使用SQL命令。使用SQLAlchemy的好处是,它能够在幕后替我们处理各种细节。不过,凡事有利皆有弊,使用SQLAlchemy的缺点是我们不得不学习其应用程序接口,同时,性能也会有所下降。本节将学习如何安装SQLAlchemy以及如何通过SQLAlchemy填充和查询数据库。
8.3.1 SQLAlchemy的安装和配置
下面是安装SQLAlchemy所需的命令。
$ pip3 install sqlalchemy
在编写本书时,SQLAlchemy的最新版本是1.3.15。从其官网可以找到SQLAlchemy的安装程序和代码仓库。
此外,SQLAlchemy还有一个支持页面,在使用SQLAlchemy的时候,需要定义一个超类,代码如下。
1 from sqlalchemy.ext.declarative import declarative_base 2 Base = declarative_base()
本节及后面几节中会使用一个带有两个数据表的小型数据库,其中第1个数据表是关于观测站的,第2个数据表是描述观测站内的传感器的。每个观测站可以有0个、1个或者多个传感器,其中,每个观测站都有一个整数ID,这些数字是由数据库自动生成的。此外,每个观测站都有一个名称,而且这个名称是唯一的,也是强制性的。
同时,每个传感器也有自己的整数ID。我们将记录传感器的最后一次观察值。这个值可以是观测值的倍数。具体情况读者可以参考本书代码包中的ch-08.ipynb代码。注意,我们不必直接运行这个脚本,因为它是供其他脚本使用的。
1 from sqlalchemy import Column, ForeignKey, Integer, String, Float 2 from sqlalchemy.ext.declarative import declarative_base 3 from sqlalchemy.orm import relationship 4 from sqlalchemy import create_engine 5 from sqlalchemy import UniqueConstraint 6 7 Base = declarative_base() 8 class Station(Base): 9 __tablename__ = 'station' 10 id = Column(Integer, primary_key=True) 11 name = Column(String(14), nullable=False, unique=True) 12 13 def __repr__(self): 14 return "Id=%d name=%s" %(self.id, self.name) 15 16 class Sensor(Base): 17 __tablename__ = 'sensor' 18 id = Column(Integer, primary_key=True) 19 last = Column(Integer) 20 multiplier = Column(Float) 21 station_id = Column(Integer, ForeignKey('station.id')) 22 station = relationship(Station) 23 24 def __repr__(self): 25 return "Id=%d last=%d multiplier=%.1f station_id=%d" 26 # %(self.id, self.last, self.multiplier, self.station_id) 27 28 if __name__ == "__main__": 29 print("This script is used by code further down in this notebook.")
8.3.2 通过SQLAlchemy填充数据库
数据表的创建将在后面介绍。本节先来准备一个脚本,以便用来填充数据库(注意,不用直接运行这个脚本,因为它是供8.3.3节中的脚本使用的)。通过DBSession对象,可以向数据表中插入数据。当然,我们还需要一个引擎,不过,引擎的创建方法也留到8.3.3节介绍。
1. 创建DBSession对象,代码如下。
1 Base.metadata.bind = engine 2 3 DBSession = sessionmaker(bind=engine) 4 session = DBSession()
2. 创建两个观测站。
1 de_bilt = Station(name='De Bilt') 2 session.add(de_bilt) 3 session.add(Station(name='Utrecht')) 4 session.commit() 5 print("Station", de_bilt)
在提交这个会话前,这些数据行是不会被插入的。下面是与第一个观测站有关的输出。
Station Id=1 name=De Bilt
3.同样,还要插入传感器记录,代码如下。
1 temp_sensor = Sensor(last=20, multiplier=.1, station=de_bilt) 2 session.add(temp_sensor) 3 session.commit() 4 print("Sensor", temp_sensor)
这个传感器位于第一个观察站中,所以,将得到如下输出内容。
Sensor Id=1 last=20 multiplier=0.1 station_id=1
数据库填充代码可以在本书代码包中的ch-08.ipynb文件中找到。同样,这个脚本也无需直接运行,它也是供其他脚本使用的。填充代码如下。
1 from sqlalchemy import create_engine 2 from sqlalchemy.orm import sessionmaker 3 4 #from alchemy_entities import Base, Sensor, Station 5 6 def populate(engine): 7 Base.metadata.bind = engine 8 9 DBSession = sessionmaker(bind=engine) 10 session = DBSession() 11 12 de_bilt = Station(name='De Bilt') 13 session.add(de_bilt) 14 session.add(Station(name='Utrecht')) 15 session.commit() 16 print("Station", de_bilt) 17 18 temp_sensor = Sensor(last=20, multiplier=.1, station=de_bilt) 19 session.add(temp_sensor) 20 session.commit() 21 print("Sensor", temp_sensor) 22 23 if __name__ == "__main__": 24 print("This script is used by code further down in this notebook")
8.3.3 通过SQLAlchemy查询数据库
下面通过URI来创建引擎,代码如下。
1 engine = create_engine('sqlite:///demo.db')
通过这个URI,我们规定要使用的引擎为SQLite,同时指出要把数据存放到文件demo.db中。然后,利用刚才创建的引擎来创建两个数据表,即station和sensor。相应代码如下。
1 Base.metadata.create_all(engine)
对于SQLAlchemy查询,我们也需要一个DBSession对象,这个对象在8.3.2节已经介绍过了。
下面选择数据表station中的第一行数据。
1 station = session.query(Station).first()
下面代码用于选择所有观测站,具体如下。
1 print("Query 1", session.query(Station).all())
下面是输出结果。
Query 1 [Id=1 name=De Bilt, Id=2 name=Utrecht]
选择所有传感器,代码如下。
1 print("Query 2", session.query(Sensor).all())
下面是输出结果。
Query 2 [Id=1 last=20 multiplier=0.1 station_id=1]
下面,选择第一个观测站的第一个传感器,代码如下。
1 print("Query 3", session.query(Sensor).filter(Sensor.station == station).one())
下面是输出结果。
Query 3 Id=%d last=%d multiplier=%.1f station_id=%d
还可以使用pandas的read_sql()方法进行查询,具体如下。
1 print(read_sql("SELECT * FROM station", engine.raw_connection()))
输出内容如下。
id name 0 1 De Bilt 1 2 Utrecht
以下代码摘自本书代码包中的ch-08.ipynb文件。
1 #from alchemy_entities import Base, Sensor, Station 2 #from populate_db import populate 3 from sqlalchemy import create_engine 4 from sqlalchemy.orm import sessionmaker 5 import os 6 from pandas.io.sql import read_sql 7 8 9 engine = create_engine('sqlite:///demo.db') 10 Base.metadata.create_all(engine) 11 populate(engine) 12 Base.metadata.bind = engine 13 DBSession = sessionmaker() 14 DBSession.bind = engine 15 session = DBSession() 16 17 station = session.query(Station).first() 18 19 print("Query 1", session.query(Station).all()) 20 print("Query 2", session.query(Sensor).all()) 21 print("Query 3", session.query(Sensor).filter(Sensor.station == station).one()) 22 print(read_sql("SELECT * FROM station", engine.raw_connection())) 23 24 try: 25 os.remove('demo.db') 26 print("Deleted demo.db") 27 except OSError: 28 pass
8.4Pony ORM
Pony ORM是Python编程语言下的另一款ORM程序包。Pony ORM是用纯Python编写的,它能自动进行查询优化,同时提供了一个图形用户界面的数据库模式编辑器。此外,它还支持自动事务处理、自动缓存和组合关键字(Composite Keys)。有了Pony ORM,我们就可以通过Python的生成器表达式来查询数据库了。当然,这些生成器最终都会转换为SQL。
安装这个程序库的命令如下。
1 $ pip3 install pony
本例中我们将用到这个程序包,因此需要将其导入。下面的导入代码摘自本书代码包中的pony_ride.py文件。
1 from pony.orm import * 2 # database,db_session 3 import statsmodels.api as sm
下面我们来创建一个in-memory型的SQLite数据库。
1 db = Database('sqlite', ':memory:', create_db = True)
下面通过Pandas的write_frame()函数来加载太阳黑子数据并将其写入数据库。具体代码如下。
1 with db_session: 2 data_loader = sm.datasets.sunspots.load_pandas() 3 df = data_loader.data 4 df.to_sql("sunspots", db.get_connection()) 5 6 print(db.select(" count(*) from sunspots"))
这个太阳黑子数据表的行数如下。
[309]
8.5Dataset:懒人数据库
Dataset是一个Python库,实际上就是SQLAlchemy的一个包装器。这个库的开发主旨是尽量做到易于使用,也就是尽量让懒人满意。
安装dataset的命令如下。
$ pip3 install dataset
创建并连接一个in-memory型的SQLite数据库,代码如下。
1 import dataset 2 3 db = dataset.connect('sqlite:///:memory:')
创建一个名为books的数据表,代码如下。
1 table = db["books"]
事实上,在数据库中这个表尚未创建,因为我们还没有为其指定任何列。我们只是创建了一个相关的对象。我们调用insert()方法时,会自动创建数据表模式,同时,向insert()方法传递含有书名的字典,具体代码如下。
1 table.insert(dict(title="NumPy Beginner's Guide", author='Ivan Idris')) 2 table.insert(dict(title="NumPy Cookbook", author='Ivan Idris')) 3 table.insert(dict(title="Learning NumPy", author='Ivan Idris'))
通过下列代码,可以显示数据表各行的内容。
1 for row in table.find(_limit=5): 2 print(row)
结果如下。
id title author 0 1 NumPy Beginner's Guide Ivan Idris 1 2 NumPy Cookbook Ivan Idris 2 3 Learning NumPy Ivan Idris OrderedDict([('index', 0), ('YEAR', 1700.0), ('SUNACTIVITY', 5.0)]) OrderedDict([('index', 1), ('YEAR', 1701.0), ('SUNACTIVITY', 11.0)]) OrderedDict([('index', 2), ('YEAR', 1702.0), ('SUNACTIVITY', 16.0)]) OrderedDict([('index', 3), ('YEAR', 1703.0), ('SUNACTIVITY', 23.0)]) OrderedDict([('index', 4), ('YEAR', 1704.0), ('SUNACTIVITY', 36.0)])
现在,我们可以轻松显示该数据库中各个数据表了,代码如下。
1 print("Tables", db.tables)
上述代码的输出结果如下。
Tables ['books', 'sunspots']
下面的代码摘自本书代码包中的ch-08.ipynb文件。(邀月修正“AttributeError: 'Connection' object has no attribute 'raw_connection'”错误)
1 import dataset 2 from pandas.io.sql import read_sql 3 from pandas.io.sql import to_sql 4 import statsmodels.api as sm 5 6 db = dataset.connect('sqlite:///:memory:') 7 table = db["books"] 8 table.insert(dict(title="NumPy Beginner's Guide", author='Ivan Idris')) 9 table.insert(dict(title="NumPy Cookbook", author='Ivan Idris')) 10 table.insert(dict(title="Learning NumPy", author='Ivan Idris')) 11 12 # print(help(db.executable)) 13 14 # print(read_sql('SELECT * FROM books', db.executable.raw_connection()) ) 15 # print(read_sql('SELECT * FROM books', db.executable.contextual_connect()) ) 16 print(read_sql('SELECT * FROM books', db.engine) ) 17 18 data_loader = sm.datasets.sunspots.load_pandas() 19 df = data_loader.data 20 #write_frame(df, "sunspots", db.executable.raw_connection()) 21 # df.to_sql("sunspots", db.executable.contextual_connect()) 22 df.to_sql("sunspots", db.engine) 23 24 25 table = db['sunspots'] 26 27 for row in table.find(_limit=5): 28 print(row) 29 30 print("Tables", db.tables)
tips:
1、原书中print(read_sql('SELECT * FROM books', db.executable.raw_connection()) )引发AttributeError: 'Connection' object has no attribute 'raw_connection'
2、改用print(read_sql('SELECT * FROM books', db.executable.contextual_connect()) )不报错,但会引发一个警告,大意是:SADeprecationWarning: The Engine.contextual_connect() and Connection.contextual_connect() methods are deprecated. This method is an artifact of the threadlocal engine strategy which is also to be deprecated. For explicit connections from an Engine, use the Engine.connect() method
3、最后使用print(read_sql('SELECT * FROM books', db.engine) )正常运行。
8.6PyMongo与MongoDB
MongoDB是一个面向文档的NoSQL数据库,其名称取自humongous一词,即硕大无比之意,其中,文档将以类似JSON的BSON格式进行存储。我们可以下载MongoDB,安装过程非常简单,我们只需将压缩文件解压即可。创作本书时,这个程序库的版本为3.4.0。这个版本的bin目录下面有一个名为mongod的文件,用来启动服务器。MongoDB位于/data/db目录下面,该目录就是存储数据的地方。当然,可以通过下列命令来指定其他目录。
$ mkdir /tmp/db
进入存放数据库二进制可执行代码的目录并启动数据库,命令如下。
./mongod –dbpath /tmp/db
这个进程需要一直运行,这样我们才能查询数据库。
PyMongo是MongoDB的Python驱动程序,这个程序的安装方法如下。
1 $ pip3 install pymongo
与MongoDB的测试数据库建立连接,代码如下。
1 from pymongo import MongoClient 2 3 client = MongoClient() 4 db = client.test_database
别忘了,我们是可以利用pandas DataFrame来创建JSON的。下面创建JSON并将其存于MongoDB中,代码如下。
1 data_loader = sm.datasets.sunspots.load_pandas() 2 df = data_loader.data 3 rows = json.loads(df.T.to_json()).values() 4 db.sunspots.insert_many(rows)
现在,我们来查询刚才创建的文档。
1 cursor = db['sunspots'].find({}) 2 df = pd.DataFrame(list(cursor)) 3 print(df)
这将打印输出整个pandas DataFrame。下列代码摘自本书代码包中的ch-08.ipynb文件。
1 from pymongo import MongoClient 2 import statsmodels.api as sm 3 import json 4 import pandas as pd 5 6 client = MongoClient() 7 db = client.test_database 8 9 data_loader = sm.datasets.sunspots.load_pandas() 10 df = data_loader.data 11 rows = json.loads(df.T.to_json()).values() 12 db.sunspots.insert_many(rows) 13 14 cursor = db['sunspots'].find({}) 15 df = pd.DataFrame(list(cursor)) 16 print(df) 17 18 db.drop_collection('sunspots')
注意:因邀月本机无MongoDB,所以未运行本实例,如有报错,请告知。谢谢!
8.7利用Redis存储数据
Redis是一个in-memory型的键-值数据库,由C语言编写而成。Redis这个名称源自Remote Dictionary Server,即远程字典服务器。处于内存存储模式时,Redis的速度快得惊人,而且读写操作的速度几乎一样快。Redis遵循发布订阅模式并且通过Lua脚本来处理存储过程。发布订阅模式通过客户端可订阅的信道来接收消息。创作本书时的Redis最新版本为3.2.6,我们可以下载Redis。完成安装之后,我们就可以通过以下命令来运行服务器了。
1 $ src/redis-server
下面开始安装一个Python驱动程序,命令如下。
1 $ pip3 install redis
Redis的使用非常简便,只要把它当作一个庞大的字典即可。不过,Redis也有自身的局限性。有时,它的易用性仅仅体现在将复杂的对象存储为JSON字符串或其他格式。这正是我们联合使用Pandas DataFrame的原因。下面,我们与Redis建立连接,代码如下。
1 r = redis.StrictRedis()
通过JSON字符串创建一个键-值对,代码如下。
1 r.set('sunspots', data)
通过下列代码检索数据。
1 blob = r.get('sunspots')
下面的代码非常简单,它们摘自本书代码包中的ch-08.ipynb文件。
1 import redis 2 import statsmodels.api as sm 3 import pandas as pd 4 5 r = redis.StrictRedis() 6 data_loader = sm.datasets.sunspots.load_pandas() 7 df = data_loader.data 8 data = df.T.to_json() 9 r.set('sunspots', data) 10 blob = r.get('sunspots') 11 print(pd.read_json(blob))
注意:因邀月本机无Redis,所以未运行本实例,如有报错,请告知。谢谢!
8.8利用memcache存储数据
与Redis类似,memcache也是一个in-memory型的键-值数据库。在安装运行memcache服务器之后,就可以通过下列命令来安装memcache的Python客户端了。
1 $ pip3 install python3-memcache
下面的代码摘自ch-08.ipynb文件,它将创建一个memcache客户端,然后将DataFrame存储到Memcache,其中自动过期值为600秒。这段代码与前面介绍Redis时看到的代码非常相似。
1 import memcache 2 import statsmodels.api as sm 3 import pandas as pd 4 5 client = memcache.Client([('127.0.0.1', 11211)]) 6 data_loader = sm.datasets.sunspots.load_pandas() 7 df = data_loader.data 8 data = df.T.to_json() 9 client.set('sunspots', data, time=600) 10 print("Stored data to memcached, auto-expire after 600 seconds") 11 blob = client.get('sunspots') 12 print(pd.read_json(blob))
注意:因邀月本机无memcache,所以未运行本实例,如有报错,请告知。谢谢!
8.9Apache Cassandra
Apache Cassandra是结合了键-值和传统关系型数据库特性的混合型数据库。对于传统的关系型数据库而言,数据表中的列是固定的。可是,对于Cassandra来说,同一个数据表中的各行可以具有不同的列。从这个角度看,Cassandra是一种面向列的数据库,因为它允许各行灵活使用不同的模式。这个数据库中的各列,都是按照所谓的列族(Column Family)进行组织的,这里的列族相当于关系型数据库中的数据表。Cassandra数据库已经摒弃了各种连接和子查询操作。读者可以自行下载Cassandra。在本书写作时,这个数据库的最新版本是3.9。
(1) 从命令行运行服务器,命令如下。
1 $ bin/cassandra-f
(2)可以创建conf/cassandra.yaml中列出的目录,或者进行如下的调整。
1 data_file_directories:/tmp/lib/cassandra/data 2 commitlog_directory: /tmp/lib/cassandra/commitlog 3 saved_caches_directory: /tmp/lib/cassandra/saved_caches
(3)如果不想保存某些数据,可以考虑下列命令。
1 $ mkdir -p /tmp/lib/cassandra/data 2 $ mkdir -p /tmp/lib/cassandra/commitlog 3 $ mkdir -p /tmp/lib/cassandra/saved_caches
(4)下面安装Python驱动程序,命令如下。
1 $ pip3 install cassandra-driver
(5)下面开始编写代码。首先,与集群建立连接并创建一个会话,代码如下。
1 cluster = Cluster() 2 session = cluster.connect()
(6)在Cassandra中,有一个所谓的keyspace的概念,它实际上就是用来存放数据表的一个容器。Cassandra建立了自己的查询语言,名为Cassandra查询语言(Cassandra Query Language,CQL)。CQL的用法与SQL类似。下面创建keyspace并设置使用该keyspace的会话,代码如下。
1 session.execute("CREATE KEYSPACE IF NOT EXISTS mykeyspace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };") 2 session.set_keyspace('mykeyspace')
(7)现在,创建一个数据表来存放太阳黑子数据,代码如下。
1 session.execute("CREATE TABLE IF NOT EXISTS sunspots (year decimal PRIMARY KEY, sunactivity decimal);")
(8)创建一条语句,该语句将在循环语句中把元组作为数据行来插入,代码如下。
1 query = SimpleStatement( 2 "INSERT INTO sunspots (year, sunactivity) VALUES (%s, %s)", 3 consistency_level=ConsistencyLevel.QUORUM)
(9)下列代码用于插入数据。
1 for row in rows: 2 session.execute(query, row)
(10)取得数据表中数据的行数。
1 rows=session.execute("SELECT COUNT(*) FROM sunspots") 2 for row in rows: 3 print(row)
输出的行数如下。
[Row(count=309)]
(11)删除keyspace,关闭集群。
1 session.execute('DROP KEYSPACE mykeyspace') 2 cluster.shutdown()
下列代码摘自本书代码包中的ch-08.ipynb文件。
1 from cassandra import ConsistencyLevel 2 from cassandra.cluster import Cluster 3 from cassandra.query import SimpleStatement 4 import statsmodels.api as sm 5 6 cluster = Cluster() 7 session = cluster.connect() 8 session.execute("CREATE KEYSPACE IF NOT EXISTS mykeyspace WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };") 9 session.set_keyspace('mykeyspace') 10 session.execute("CREATE TABLE IF NOT EXISTS sunspots (year decimal PRIMARY KEY, sunactivity decimal);") 11 12 query = SimpleStatement( 13 "INSERT INTO sunspots (year, sunactivity) VALUES (%s, %s)", 14 consistency_level=ConsistencyLevel.QUORUM) 15 16 data_loader = sm.datasets.sunspots.load_pandas() 17 df = data_loader.data 18 rows = [tuple(x) for x in df.values] 19 for row in rows: 20 session.execute(query, row) 21 22 rows=session.execute("SELECT COUNT(*) FROM sunspots") 23 for row in rows: 24 print(row) 25 26 session.execute('DROP KEYSPACE mykeyspace') 27 cluster.shutdown()
注意:因邀月本机无Cassandra,所以未运行本实例,如有报错,请告知。谢谢!
8.10小结
我们可以把年度太阳黑子周期数据存储在不同的数据库中,包括关系型数据库和NoSQL数据库。
这里所谓的关系,不仅限于数据表之间的关系,首先,它与一个数据表内部各列之间的关系有关;其次,它还涉及数据表之间的关系。
实际上,我们可以通过Python的标准模块sqlite3来跟SQLite数据库打交道。我们可以通过pandas与SQLite数据库或SQLAlchemy数据库来建立连接。
SQLAlchemy以其基于设计模式的ORM而闻名天下,通过这个库,可以把Python的类映射为数据库的数据表。实际上,ORM模式是一种通用的架构模式,因此同样适用于其他各种面向对象程序设计语言。SQLAlchemy将使用数据库的各种技术细节剥离出去,有了它,我们甚至连SQL都不用写了。
MongoDB是一个面向文档的数据仓库,可以存放巨量的数据。
进入in-memory模式后,Redis不仅运行速度极快,而且写操作也几乎与读操作一样快。Redis是一个键-值型数据仓库,功能上与Python的字典相仿。
Apache Cassandra不仅具有键-值数据库的特性,同时还具备传统的关系型数据库特性。这是一种面向列的数据库,它的各个列都以列族的形式组织在一起,这里的列族相当于关系型数据库中的数据表。在Apache Cassandra数据库中,数据行已经摆脱了特定列组合的束缚。
第9章将介绍纯文本数据的分析技术,因为纯文本数据在各个组织和互联网上随处可见。一般来说,纯文本数据的非结构化程度都很高,与处理已经清洗并制表的数据相比,分析纯文本数据需要使用一些截然不同的方法。为了分析这类数据,我们需要借助另一个开源Python程序包,即NLTK。NLTK发展得已经非常完备,而且自身备有相应的数据集。
第8章完。
随书源码官方下载:
https://www.ptpress.com.cn/shopping/buy?bookId=bae24ecb-a1a1-41c7-be7c-d913b163c111
需要登录后免费下载。