37

利用Pandas和SQLite提升超大数据的读取速度

 4 years ago
source link: http://bigdata.51cto.com/art/202003/613550.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

让我们想象,你有一个非常大的数据集,以至于读入内存之后会导致溢出,但是你想将它的一部分用Pandas进行处理,如果你在某个时间点只是想加载这个数据集的一部分,可以使用分块方法。

如果把数据集分为若干部分之后,分别加载进来,最终还是会很慢。

此时的解决方法,就是创建一个可供搜索的索引,使用SQLite就能轻松解决。

第一个方法:分块

来设想一个具体的示例:你要参加某个竞选,为此,你找到了一个CSV文件,里面包含你所在城市每个选民的信息。于是乎你派人去挨门挨户地拜访,邀请特定街道的所有登记选民参与投票。

现在,Pandas的DataFrame对象中有索引,但是必须要将数据读入内存,然而CSV文件太大了,内存无法容纳,于是,你想到,可以只载入你关注的记录。

这就是第一个方法,进行分块。

import pandas as pd 
 
def get_voters_on_street(name):  
    return pd.concat(  
       df[df["street"] == name] for df in  
       pd.read_csv("voters.csv", chunksize=1000)  
    )  

以分块的方式加载CSV文件,并且按照街道名称进行筛选,然后将得到记录并为一体。

虽然逐行加载,但是关注的是比较小的子集,所以需要一些开销。比如,对于只有70k的数据集,在我的计算机上执行上面的函数,需要574ms。2018年纽约市有460万登记选民,挨个街道查找,需要30s。

如果我们只做一次,没关系,如果需要反复做,这种方法并不是最佳。

创建索引对象

索引就是摘要,有这样一种说法:如果你关心它,就能在这里找到大量数据。在我们的示例中,我们想根据街道名称创建索引,于是就可以快速加载投票者所在的街道了。

如果你担心索引数据也会超出内存,那么数据库则能作为保存它们的容器,例如PostgreSQL、MySQL等数据库都能实现。哦,你不喜欢安装和维护那些讨厌的服务,好吧,SQLite应运而生了。

SQLite是一个功能齐全的关系型数据库,它能够像其它数据库一样运行,但是不需要服务器。Pyhton默认就支持这种数据库。SQLite将数据保存在独立的文件中,你必须管理一个SQLite数据文件,而不是CSV文件了。

用SQLite存储数据

下面演示一下如何用Pandas操作SQLite:

1. 将数据载入SQLite,并创建索引

SQLite数据库能够保存多张数据表,首先将voters.csv文件的数据载入SQLite,并保存为voters.sqlite文件,在这个文件中,我们创建一个名为voters的表。

接下来,在SQLite中创建街道的索引。

只需如下操作:

import sqlite3 
 
# Create a new database file: 
db = sqlite3.connect("voters.sqlite") 
 
# Load the CSV in chunks: 
for c in pd.read_csv("voters.csv", chunksize=1000): 
    # Append all rows to a new database table, which 
    # we name 'voters': 
    c.to_sql("voters", db, if_exists="append") 
# Add an index on the 'street' column: 
db.execute("CREATE INDEX street ON voters(street)")  
db.close() 

虽然我们只创建单个索引,但我们还可以在其他列或多个列上创建其他索引,从而允许我们使用这些列快速搜索数据库。

8a4299fe207fee7e6158335503f84291.png-wh_651x-s_1278009243.png

2. 重写查询函数

现在,所有数据都已经载入SQLite,我们可以按照街道进行检索了。

def get_voters_for_street(street_name): 
    conn = sqlite3.connect("voters.sqlite") 
    q = "SELECT * FROM voters WHERE street = ?" 
    values = (street_name,) 
    return pd.read_sql_query(q, conn, values) 

执行上述函数,SQLite只加载与查询匹配的行,并其通过Pandas将它们保存为DataFrame对象。

50多倍的加速

那个CSV文件供给70,000行记录,原来花费了574ms,现在只用了10ms。

提速50多倍,这是因为,只需要加载我们关心的行,而不是CSV文件中的每一行。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK