【问题标题】:Slow python code to parse VCF file and insert in database解析VCF文件并插入数据库的慢python代码
【发布时间】:2019-12-05 17:32:41
【问题描述】:

我有以下代码用于解析 VCF(变体调用格式)文件:

Python 代码:

import vcf
import psycopg2
datalist  = []
def create_snp_tables() :
    drop_table_query = 'DROP TABLE IF EXISTS sampletable;'
    cursor.execute(drop_table_query)
    create_value_table_query = '''CREATE TABLE IF NOT EXISTS sampletable (as_ID INT, as_NM TEXT, as_DT_ID INT, as_DT_NM TEXT, VCF_ID TEXT, SAMPLE_ID TEXT, VARIANT_ID TEXT, as_DT_LINE_SEQ INT, DATE_START DATE, DATE_END DATE, as_DT_VAL_SEQ INT, as_DT_VA_NM TEXT, as_DT_VALUE TEXT); '''
    cursor.execute(create_value_table_query)
    conn.commit()
def createtupplelist(as_id, vcf_id,as_dt_nm, sample_id, as_dt_va_nm, as_dt_value, as_va_seq, as_dt_va_line_seq):
    variant_id= 'variant_id'
    as_nm = 'as_name'
    datalist.append("({0},'{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}')".format(as_id,str(as_nm),'1',as_dt_nm,vcf_id,sample_id,variant_id,as_dt_va_line_seq,as_va_seq,as_dt_va_nm,variable_value))
    if len(datalist)==20:
        insertdata()
def insertdata():
    global datalist 
    iter_datalist=iter(datalist) 
    args_str = ','.join(iter_datalist)
    cursor.execute("INSERT INTO sampletable(as_ID,as_NM,as_DT_ID,as_DT_NM,VCF_ID,SAMPLE_ID,Variant_ID,as_DT_LINE_SEQ,as_DT_VAL_SEQ,as_DT_VA_NM,as_DT_VALUE) VALUES "+args_str)
    print("inserted")
    conn.commit()
    datalist=[]
#read vcf file using pyvcf library
file_name  = 'sample.vcf'
vcf_reader = vcf.Reader(open(file_name, 'r'))
conn = psycopg2.connect(host="localhost",database="mydb", user="postgres", password="pgAdmin")
cursor = conn.cursor()
create_snp_tables()
line_index = 0
as_dt_variant = 'Variant'
index = 0
for record in vcf_reader :
    index=index+1
    line_index += 1
    sample_name = ''
    variable_value = record.CHROM
    variable_name = "CHROM"
    createtupplelist('1', file_name,  as_dt_variant, sample_name, variable_name, variable_value,  str(index), str(line_index))

这是我通过脚本传递的示例文件:

示例 VCF 文件:

    #CHROM  POS ID  REF ALT QUAL    FILTER  INFO    FORMAT  BA12878.40x.S7508
    chr1    10069   .   A   AC  136.17  RF  AC=1;AN=2;CRF=0.817;DP=7;GC=0.505;MQ=70.3709;MQ0=770;NS=7;QD=1.945;STR_LENGTH=90;STR_PERIOD=6   GT:GQ:DP:MQ:PS:PQ:AD:ADP:AF:ARF:BQ:FRF:MC:MF:SB:RFQUAL:FT   1|0:136:70:36:10069:99:309:944:0.327:0.046:.:0.97:164:0.165:0.000:2.45:RF
    chr1    10069   .   A   AC  136.17  RF  AC=1;AN=2;CRF=0.817;DP=7;GC=0.505;MQ=70.3709;MQ0=770;NS=7;QD=1.945;STR_LENGTH=90;STR_PERIOD=6   GT:GQ:DP:MQ:PS:PQ:AD:ADP:AF:ARF:BQ:FRF:MC:MF:SB:RFQUAL:FT   1|0:136:70:36:10069:99:309:944:0.327:0.046:.:0.97:164:0.165:0.000:2.45:RF
    chr1    10069   .   A   AC  136.17  RF  AC=1;AN=2;CRF=0.817;DP=7;GC=0.505;MQ=70.3709;MQ0=770;NS=7;QD=1.945;STR_LENGTH=90;STR_PERIOD=6   GT:GQ:DP:MQ:PS:PQ:AD:ADP:AF:ARF:BQ:FRF:MC:MF:SB:RFQUAL:FT   1|0:136:70:36:10069:99:309:944:0.327:0.046:.:0.97:164:0.165:0.000:2.45:RF
    chr1    10069   .   A   AC  136.17  RF  AC=1;AN=2;CRF=0.817;DP=7;GC=0.505;MQ=70.3709;MQ0=770;NS=7;QD=1.945;STR_LENGTH=90;STR_PERIOD=6   GT:GQ:DP:MQ:PS:PQ:AD:ADP:AF:ARF:BQ:FRF:MC:MF:SB:RFQUAL:FT   1|0:136:70:36:10069:99:309:944:0.327:0.046:.:0.97:164:0.165:0.000:2.45:RF
    chr1    10069   .   A   AC  136.17  RF  AC=1;AN=2;CRF=0.817;DP=7;GC=0.505;MQ=70.3709;MQ0=770;NS=7;QD=1.945;STR_LENGTH=90;STR_PERIOD=6   GT:GQ:DP:MQ:PS:PQ:AD:ADP:AF:ARF:BQ:FRF:MC:MF:SB:RFQUAL:FT   1|0:136:70:36:10069:99:309:944:0.327:0.046:.:0.97:164:0.165:0.000:2.45:RF

我的 Postgres 表中的输出 - sampletable

as_id   as_nm       as_dt_id    as_dt_nm    vcf_id      sample_id   variant_id  as_dt_line_seq  date_start  date_end    as_dt_val_seq   as_dt_va_nm     as_dt_value

1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               CHROM           chr1
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               POS             10069
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               ID              None
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               REF             A
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               ALT             AC
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               QUAL            136.17
1       as_name     2           Variant     sample.vcf  ""          variant_id  1               null        null        1               FILTER          RF

我的 Python 代码运行缓慢。它在 5 分钟内插入大约 1000 条记录。我有超过 500 万条记录。

我正在寻求一些帮助来优化 Python 代码以更快地插入它。请提出建议。

【问题讨论】:

  • 请正确缩进您的代码。
  • 这在您的代码中不清晰可见,因为它不包含 import 语句,而您共享的内容已将 PyVCF 类直接导入其命名空间。您是否希望我们每个人都知道Header 意味着pyvcf.header?如果您的代码格式不正确并包含代码的所有相关部分,我们将无法为您提供帮助。
  • 报废代码并告诉我们您需要做什么 - 从该 VCF 文件中获取哪些数据以及需要将哪些数据保存在数据库中。您已经展示了示例输入,现在向我们展示了需要从该输入中获取什么以及输出是什么,即它是如何保存到数据库的。此代码不可读且损坏。
  • MCVE 暗示您提供程序的预期输出。
  • 我可以从这里看到解析操作运行很快(我的计算机上每秒大约 1 000 000 行,使用 5000 行示例文件进行测试)。您说您的程序以每秒大约 3.333 次插入的速度运行。由于解析只占程序执行时间的 0.00033 %,因此您应该考虑优化 数据库插入 而不是解析算法。

标签: python vcf-variant-call-format vcftools


【解决方案1】:

INSERT INTO XXX(列列表)VALUES(值列表)不是最优的 我用语法“INSERT INTO XXX VALUES”替换它,格式化列表

它在 10 分钟内大幅提升性能 ==> 不到 15 秒

(我是在 python 3.7.5 中完成的)

# list have to be a string
datalist.append("({0},'{1}',{2},'{3}','{4}','{5}','{6}',...)".format(...))

def insertdata():
    global datalist
    iter_datalist=iter(datalist)  

    args_str = ','.join(iter_datalist)
    cursor.execute("INSERT INTO sampletable VALUES "+args_str)
    conn.commit()
    datalist=[]    

【讨论】:

  • 我不确定我是否理解这一点。您能否专门针对我的代码分享答案?
  • 性能瓶颈很可能出在你的一一“insert into XXX values (a,b,c)”语句中,尝试更改for和“INSERT INTO comment VALUES”+args_str的效率要高得多。它需要以这种方式转换您的 insertdata() 函数 def insertdata(): global datalist iter_datalist=iter(datalist) args_str = ','.join(iter_datalist) cursor.execute("INSERT INTO sampletable VALUES "+args_str) conn.commit () 数据列表=[]
  • 如果您检查我的代码,我已经这样做了。 cursor.executemany("INSERT INTO sampletable(as_ID,as_NM,as_at_ID,as_at_NM,VCF_ID,SAMPLE_ID,Variant_ID,as_at_LINE_SEQ,as_at_VAL_SEQ,as_at_VA_NM,as_at_VALUE) 值 (%s,%s,%s,%s, %s,%s ,%s,%s,%s,%s,%s);", 数据列表)
  • 要求按正确的顺序填写所有表格字段
  • 它给了我错误。 args_str = ','.join(iter_datalist) TypeError: sequence item 0: expected string, tuple found
【解决方案2】:

一些想法:

尝试使用事务来加速数据库操作。 每个 UPDATE 语句必须扫描整个表以查找与名称匹配的任何行。 name 列上的索引可以防止这种情况发生,并使搜索速度更快。

避免不必要的循环: https://www.monitis.com/blog/7-ways-to-improve-your-python-performance/

尝试使用生成器。

在 Python 中优化 I/O 操作:https://towardsdatascience.com/optimized-i-o-operations-in-python-194f856210e0

你能用 numpy 数组工作吗? numpy + numba。

也许您可以使用用 C/c++/rust 编写的一小段代码并使用 cffi 导入它们。 看看这里: https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-insertions-971aff98eef2

我不知道 sqlalchemy 是否会减慢操作,但我发现使用数据库很聪明。看看你是否也可以使用 pypy 或 nuitka。

还有:http://alimanfoo.github.io/2017/06/14/read-vcf.html 我希望你会在那里找到一些有趣的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 2013-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多