#Pyhton# 使用Peewee玩转SQL数据库--导入数据

Peewee轻量级ORM

支持 MySQL、PostgreSQL、Sqlite、BerkeleyDB等常见数据库,相信能够满足大多数需求了
官方文档十分详尽,虽然都是英文的,这个模块有太多可以挖掘的东西,目前发现基本涵盖了所有需要的数据库的功能
官方文档

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第1张图片
官方文档主页

关于ORM,摘抄自维基百科

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在>编程语言里使用的“虚拟对象数据库”。如今已有很多免费和收费的ORM产品,而有些程序员更倾向于创建自己的ORM工具。
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
简单的说:ORM相当于中继数据。具体到产品上,例如下边的ADO.NET Entity Framework。DLINQ中实体类的属性[Table]就算是一种中继数据。
对象关系映射成功运用在不同的面向对象持久层产品中,如:Torque,OJB,Hibernate,TopLink,Castor JDO,TJDO,Active Record,NHibernate,ADO.NET Entity Framework 等。

说白了,就是当你需要用SQL数据库的时候怎么办?尤其是需要快速大量的操纵数据的时候怎么办,很明显直接学习和使用SQL不是特别的方便。ORM就是来解决这个问题的,将复杂的SQL语言转化为开发者用的顺手的语种,以Python的Peewee为例,它将每个SQL中的表都转化为一个类,对类进行操作,对于Python写手来说,一点也不复杂了吧。

安装模块

  • 最基本的安装方法,不推荐,Linux还好,依赖问题好解决。Windows的话,八成会要求安装一个什么C++的解释器,总之,我没折腾懂怎么解决
pip install peewee
  • 从源码装 github(https://github.com/coleifer/peewee),我觉着吧,应该也会有依赖问题难以解决
git clone https://github.com/coleifer/peewee.git
cd peewee
python setup.py install
  • 官方没有提到,但是我最推荐,最方便的安装方法,anaconda,没听说过的请移步
conda install -c conda-forge peewee
  • 官方文档中还有其他的安装方法,比如cpython等,由于不常用,就不说了

基础知识

peewee将数据库的不同部分映射为了类(官方文档称之为Model)的不同成分

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第2张图片
classes

peewee将数据库中对应的各种类别,转化为了模块中的各种Filed,比如

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第3张图片
Fields

还有一个需要提一下的是ForeignKeyFiled, 方便将两个table union到一起的

from peewee import Model, CharField, ForeignKeyField
class Category(Model):
    name = CharField()
    parent = ForeignKeyField('self', related_name='children', null=True)

每个Filed都有一些参数可以设置:

  • null = False,当前这个Field是否可以为null值
  • unique = False, 当前这个Field中的值是否唯一,比如id
  • default = None,指定一个默认值

更多

关于Sqlite多说几句(其他数据库可不可以这样用,不清楚,没试过)

一下是关于commit的问题,默认Sqlite是自动commit的,问题就是巨慢,因此连接数据库时可以指定autocommit为False,但是就需要用一下三种方式手动进行commit,后边还有一个atomic()功能与transaction()一致

# Define a database with autocommit turned off.
db = SqliteDatabase('my_app.db', autocommit=False)

# You must call begin()
db.begin()
User.create(username='charlie')
db.commit()

# If using a transaction, then no changes are necessary.
with db.transaction():
    User.create(username='huey')

# If using a function decorated by transaction, no changes are necessary.
@db.transaction()
def create_user(username):
    User.create(username=username)

言归正传,开始举例

基本使用方法

我使用Sqlite数据库来做展示,Sqlite可以直接导入导出CSV文件,官方也给出了解决方法 【详情】
然而,我还是决定自己处理,毕竟有很多数据需要修饰什么的
第一次接触这个模块是从《Python高效开发实战Django Tornado Flask Twisted》(刘长龙著),对它的第一印象就是貌似很简单(?),因此,就直接沿着这个模块用下来了。
以Human protein atlas下载到的细胞系表达量数据为例,简单说明一些用法

下载到一个CSV文件,总大小51.3M

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第4张图片
csv文件信息

一共包含五列数据,分别是ensembl_id,gene_name, tissue, value,value_type

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第5张图片
内部数据格式

1. 连接数据库

第一步,先指定一个数据库,新建各种所需的model

#!/usr/bin/python3
# -*- coding: utf-8 -*-
u"""peewee数据库."""
import os
from peewee import Model
from peewee import SqliteDatabase
from peewee import CharField, FloatField

# autocommint顾名思义,就是没进行一个操作数据库是否自己commit一次,默认True
# 如果要进行大量的insert或者update操作的话,特别耗时,不过不要紧,peewee提供了解决方法
db = SqliteDatabase(os.path.join(
    os.path.dirname(__file__), 'expression_value.db'), autocommit=True)

# 一个基础Model,没别的作用
# 如果一个数据库中有多个不同的表,其中很多内容有一样,就可以通过基础类全部指定好
# 然后不同的Model简单的一继承就好了
class BaseModel(Model):
    u"""基础类,指定数据库和基本数据类型.s"""
    tissue = CharField()
    ensembl_id = CharField()
    gene_name = CharField()
    value = FloatField()

    class Meta:
        u"""指定数据库."""
        database = db

# 用来储存HPA数据的那张表,建立一些新的独有的列
# 表是按照tissue和ensembl_id来排序
class HPA(BaseModel):
    u"""存储HPA表达量数据的."""
    value_type = CharField(default='TPM')

    class Meta:
        u"""指定表."""
        db_table = 'HPA'       # table name
        order_by = ('tissue', 'ensembl_id', )

以上一个数据库和一张表就搞定了

2. 插入数据的基础方法

首先自然是读取文件,根据自己的文件格式,自己调整代码
我读取csv的方式如open_csv()所示

class Bulk(object):
    u"""测试bulk."""

    def __init__(self, csv):
        u"""csv."""
        self.csv = csv

    def open_csv(self):
        u"""打开csv文件,然后返回信息."""
        with open(self.csv) as reader:
            for line in reader:
                # 去除换行符
                line = line.rstrip()
                # csv每列按照,分隔,每一列还用“包围,因此,分割再分割就好了
                yield [x.replace('"', '') for x in line.split(',')]

    @staticmethod
    def creat_new_table(table):
        u"""用来创建table的."""
        # 如果,该Model代表的table没有被创建,就新建一个
        if not table.table_exists():
            table.create_table()

    def normal_insert(self):
        u"""最普通的insert方式."""
        # 创建HPA这个表
        self.creat_new_table(HPA)
        # 最基本的插入方法就是create(),而且由于autocommit是True,因此我们无需手动进行db.commit()
        for line in self.open_csv():
            # try 用来处理表头无法转化成float的问题,遇到表头就略过
            try:
                HPA.create(ensembl_id=line[0], gene_name=line[1],
                           tissue=line[2], value=float(line[4]))
            except ValueError:
                continue

create()是peewee中最基础的数据插入方式,但同时,也是最慢的,尤其是autocommit=True的情况下

有多慢呢?

这个50+M的csv,我用了两天多才搞定,这两天反正电脑一直在用,就这么浪吧。

3. 插入数据的进阶方法

peewee是支持直接将字典导入数据库的,因此,有dict_to_model和model_to_dict两个类方法,能够实现SQL与Python内置类型以及json的交互,然而,这次我需要用字典,但是用不上这两个方法,感兴趣可以自己查

首先需要对所有的数据进行修饰,将其转化成字典

u"""接上回,想要加快导入的速度,必须使用字典导入的方式,用法如下"""
    def construct_dict(self):
        u"""按照field: value构成字典"""
        # 这些键值,都是数据库中定义的列名
        keys = ['ensembl_id', 'gene_name', 'tissue', 'value', 'value_type']
        # 将每一行的数据都转换成一个字典
        for line in self.open_csv():
            try:
                line[3] = float(line[3])
            except ValueError:
                continue
            yield {key: value for key, value in zip(keys, line)}

constract_dict()返回的结果如下,每一行的数据都转化成一个对应的字典

{
    ‘ensembl_id’: 'EN*******',
    'gene_name': 'TP53',
    'tissue': 'liver',
    'value': 5,
    'value_type': 'TPM'
}

然后,将其导入数据库,根据官方文档的示例修改而来,确实有用,速度快多了

    # ticktock是自己写的一个装饰器,用来展示function运行时间的
    @ticktock()
    def insert_(self):
        u"""测试atomic()."""
        # 新建表
        self.creat_new_table(HPA)
        # 上下文管理一下,使用了atomic()之后,就会在上下文退出的时候才最终commit
        # 减少了中间最耗时的反复commit过程
        with db.atomic():
            # 然后将处理好的字典一次导入数据库
            for data_dict in self.construct_dict():
                HPA.create(**data_dict)

运行时间,一共两次,第一次autocommit=False,第二次True
表明,在atomic()的光辉下,autocommit不autocommit的对性能不缠身根本性的影响

运行时间

蓝色的输出就是ticktock()的能力了

4. 插入数据,再快点?

然而,根据官方文档来看,上边那个还不是最快的,最快的是insert_many()这个方法

需要手动execute的来了

    @ticktock()
    def insert_many(self):
        u"""测试insert many."""
        self.creat_new_table(HPA)
        # 基本上就是,将每一行的数据转化成一个字典,然后这些个字典构成一个list
        # 直接导入,即可
        data = [x for x in self.construct_dict()]
        with db.atomic():
            HPA.insert_many(data).execute()

然而,我一次导入的量太大,崩溃了

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第6张图片
崩溃

因此,如果量太大,还是老老实实的按照官网的说明,将所有的数据分成chunks来处理

    @ticktock()
    def insert_many(self):
        u"""测试insert many."""
        self.creat_new_table(HPA)
        # 最懒的写法,内存耗费最大,完全浪费了生成器节省内存的有点,我图什么???
        data = [x for x in self.construct_dict()]
        with db.atomic():
            # 100个100个的导入总没问题了吧
            for idx in range(0, len(data), 100):
                HPA.insert_many(data[idx: idx + 100]).execute()

事实胜于雄辩,速度确实有提升了非常多

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第7张图片
insert_many的速度

所以,最开始,我用create,慢慢create了两天,现在一分半就解决了,我图什么呢???

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第8张图片
无fuck说

更多原创精彩视频敬请关注生信杂谈:

#Pyhton# 使用Peewee玩转SQL数据库--导入数据_第9张图片

你可能感兴趣的:(#Pyhton# 使用Peewee玩转SQL数据库--导入数据)