$ C: ]" J5 l& b7 P1 Tart_454的使用 3 \. f- ?3 G1 J! J( v) E S首先至art系列软件的官网,下载软件,在ubuntu系统安装,然后阅读相关参数设置的帮助文档,运行程序。8 @7 ?0 _' E% o( C" d5 M
GenomeABC 6 N" W2 ]4 ^2 x1 L* P" m( c8 O
进入GenomeABC(http://crdd.osdd.net/raghava/genomeabc/),输入参数,获得模拟测序结果。1 C/ c- s( J1 g& d! k7 m6 f
编程模拟测序 7 l, X% O. r5 H2 c- f
下载安装python,并且安装biopython扩展模块,编写程序,模拟单端/双端测序。* L* _. Y) D9 G+ w3 v
三、结果 8 I+ x+ w9 I$ }( [( c1 P" y 7 T. U& Q1 V/ f; K p U: l9 o, B# T1、art_454的运行结果 ; r: D) ?, B) C% X 8 v5 w# g5 T! g; [. k: a0 G6 p' {4 X无参数art_454运行,阅读帮助文档 # F: Y3 P4 R7 [( i+ b
& e1 v+ w: s2 T2 j* n x
图表 1无参数art_454运行 : ?- U8 D% [; m' c/ `对酵母基因组进行基因组单端测序模拟,FOLD_COVERAGE设为20,即覆盖度为20. 7 G/ n& n* i7 S; {% V$ M下图为模拟单端测序,程序运行过程及结果 6 {. O' l' O; [* Y7 U) S- L; ~% o2 U+ t8 Q6 ?
图表 2 art454单端测序 * j6 l+ N1 d3 ~/ F& Q2 k: d5 [ ; d5 e6 { G, c3 {& O! V# r图表 3 art454单端模拟结果 - P' A" B2 n6 [. G0 g' e双端测序模拟,FOLD_COVERAGE设为20,即覆盖度为20;MEAN_FRAG_LEN设为1500,即平均片段长度为1500;STD_DEV设为20,即长度的标准差为20 3 D, k% J# g+ ~$ t% y下图为模拟双端测序,程序运行过程及结果 ! b0 \4 h, i6 Y
0 @( m+ \0 w5 M p图表 4 art454双端测序 * e+ c) o# e& ?! x6 f" A$ ^
8 L# W9 i E0 A- a0 J$ I% M' A图表 5 art454双端模拟结果 8 i k9 ~6 V8 A4 @! @, A& s
2、GenomeABC , d! Z4 }. u8 U" _8 U下图为设置参数页面 5 p S7 M0 _. M/ E& S3 P " s' H$ X$ ?# n( W( R下图为结果下载页面 ; } s6 w/ ^! X, j! X ' C" O' T' b& [图表 6 结果下载页面 # S6 j5 e9 C4 u: m' Q Y' v
3、编程模拟测序结果 1 _5 \2 k; {2 g2 C5 F1 g拷贝数是这里的N值;覆盖度是m,测序深度是宏观的量,在这里与覆盖度意思相同,就是测序仪10X,20X。 ' {# Y0 A, z$ _) K7 f' u
单端测序 0 P/ _0 z# _& V 2 C3 a `0 z( g! t7 f( d. t图表 7 程序模拟单端测序 2 P" [% L5 H, b3 |- Q双端测序 % S0 `5 H7 m$ g- y7 r$ {! D* k' b
3 {# o" k" m) {
图表 8 程序模拟双端测序 p) p% Y- J% q# W, ^6 h; o
测序结果 4 v) w! [5 @* ^5 Y9 f( K( |! Z3 g 5 r* c A! Y6 m4 K图表 9 结果文件 . X/ g( B7 G5 ?2 H+ Z6 m! b$ z! ~2 G+ w
因为期望片段长度是600bp,在片段长度区间200-1000bp内,所以大部分的片段都没有删除。 ( [$ l* ]8 L R; D8 x0 {+ S
测序结果统计表% h+ e0 v8 U m$ z& g0 y# Y8 ]9 S
1 ~9 T5 j$ w, D2 s- t) }2 K
测序方式 基因组大小(bp) 片段长度区间 (bp) N值 期望片段长度 克隆保留率 片段数量 Reads长度范围(bp) Reads总数量 Reads总长度 覆盖度(m值) 理论丢失率(e-m) 覆盖率(1-e-m) + Z- e l' z& e* k7 O2 p单端 12157kb 200-1000 10 600 0.95 107378 50-100 101968 7645.541kb 0.62889 0.53318 0.46682 8 r( L3 ~" a+ }8 P& }2 c6 ?! V单端 12157kb 200-1000 20 600 0.95 213722 50-100 202996 15227.882kb 1.25259 0.28576 0.71424 6 k& ?6 ^$ s$ N8 G双端 12157kb 200-1000 10 600 0.95 106704 50-100 202770 15212.662kb 1.25134 0.28612 0.71388! Q0 Q, [, r5 B
双端 12157kb 200-1000 20 600 0.95 214212 50-100 407186 30534.265kb 2.51164 0.08114 0.91886 " A9 r" H+ f8 B$ l四、讨论和结论5 I) D- H3 A/ s! U: o' o7 I) X
( c& u, a/ P( Z
程序运行方法 ( ]! u+ y; e. v4 I6 \& I6 V2 z; I& B ( D; d6 m/ K T# e, ~- L在类的构造方法init()中,调整参数。 + H! d$ i# X" O9 V3 CAveragefragmentlength为片段平均的长度; $ d' r8 b: z; z% i5 m
minfragmentlength和maxfragmentlength是保留片段的范围; 5 T6 b* w+ i( u6 _cloneRetainprobability是克隆的保留率; 1 |7 `: ]3 T& J/ U* x; P
minreadslength和maxreadslength是测序reads的长度范围 : i; Q. P2 s8 i! ^, R) V2 U' U8 d$ G( V) w
模拟测序的诸多方法都封装成了Sequencing类,只需要创建类,并调用singlereadsequencing()和pairreadsequencing()方法,传入文件名的参数即可。 9 q7 r6 I4 X4 a- ?& d. |* [& f+ ] r1 x p
附录 q; s$ c2 O! V" K
- x& E# I- p5 ~) Ufrom Bio import SeqIO( K5 `6 E: O! W/ W) m2 J7 ~
from math import exp ; U+ X& c/ {. M- I; Z; f9 \9 }import random 8 O" C% T: y5 c+ W& v2 d: l- m, \ [/ N+ l1 J6 U6 v
class Sequencing:6 {) a( M( p- t; q; C9 ~
# N代表拷贝份数 3 B% s' L0 ^) h. e# G% a def __init__(self)+ r- B% M5 Y# M3 R( y8 h4 z
self.fragmentList = [] 4 V R, H& W* A: w6 O* R& u self.readsID = 1; M0 @) r) ^8 Q7 v; a* a
self.readsList = [] / z0 Y& P. W: H, ^, D) `6 P: q9 J self.averagefragmentlength = 6502 p% u. p+ ]5 |& u1 `
self.minfragmentlength = 500: ~7 Z: F: s, ~1 o* r7 H
self.maxfragmentlength = 800- E0 H! v2 ^" b1 T% ?% ~8 R! h+ w
self.cloneRetainprobability = 1 ! L% O: V( G4 G; s9 J. }4 q; o# i self.minreadslength = 50; q6 ^: R' |5 l3 c( d* C5 s, q7 v
self.maxreadslength = 150 " P6 z4 ]5 k, l& Y. c self.N = 10" j b3 F% Q" _! T3 s8 C0 A
self.genomeLength = 0, k4 i7 {* [3 I( h* d( @5 e
self.allreadslength = 0 8 `+ E* e" w, @, H: [' c, Q6 m# f$ t3 s
# 生成断裂点. [/ u* X3 t' {
def generatebreakpoint(self, seqlen, averageLength): J7 x. R( M, b, p # 假设平均每500bp 产生一个断裂点(averageLength = 500),通过随机函数生成seqlen/500个随机断裂点(1到seqlen之间的随机整数); D1 N8 `; y1 }6 N$ e3 k6 F8 m$ }! M
breakpoint = [random.randint(0, seqlen) for _ in range(int(seqlen / averageLength))] ! g- l4 q8 m& @' q! X: A breakpoint.append(seqlen)# a' ~3 ?7 {( v9 |
breakpoint.append(0) 4 @ ?* e& D8 A% T @ # 把随机断裂点从小到大排序) a7 M; E S' W$ P% l- Y
breakpoint.sort() 1 g& D2 `% d1 t2 c) v- N return breakpoint 9 S- H7 \/ k. @: V. O 4 o6 @2 ~, O( g- O0 K # 沿断裂点打断基因组,并删除不符合长度要求的序列片段,定义片段范围:200-1000bp ) J# k9 P" c8 G3 u* N$ O def breakgenome(self, seq, breakpoint, minfragmentlength, maxfragmentlength): 3 v- H8 `. u- w1 |' o. N for i in range(len(breakpoint) - 1):/ k0 h& U) @5 U9 }$ a) n
fragment = seq[breakpoint:breakpoint[i + 1]] / c9 h+ _4 N, F if maxfragmentlength > len(fragment) > minfragmentlength: 7 w3 v1 g" O9 y- E. E+ P c self.fragmentList.append(fragment)( Y' ~% I# h+ A7 ^0 n) J
return self.fragmentList) g$ ^- c7 }- v8 a' n. u
# B5 E3 L# }! y8 V
# 模拟克隆时的随机丢失情况,random.random()生成0-1的保留概率" i$ Z7 V2 v/ C) [
def clonefragment(self, fragmentList, cloneRetainprobability):* H$ f# l4 f @
clonedfragmentList = []6 m2 w3 [" J" A% K
Lossprobability = [random.random() for _ in range(len(fragmentList))]! m+ ^: n. l2 |
for i in range(len(fragmentList)): 2 h% I; p }0 C5 Q if Lossprobability <= cloneRetainprobability: . w: \6 D6 c8 F* E2 ], O F# Y5 R clonedfragmentList.append(fragmentList)& K. {/ n# p. s7 ^* A/ c% L; y1 P
return clonedfragmentList {& S0 j) I- |* S
# v1 c+ J. g* {( |7 k. o; E7 R
# 模拟单端测序,并修改reads的ID号 ! T; Q- W1 o& V# I, G def singleread(self, clonedfragmentList): , d+ x, I/ e8 Z4 G- H% C; h- r for fragment in clonedfragmentList:. w9 x& r; O& G2 C
fragment.id = "" ( e; r: H6 Z& w6 ?9 ] fragment.name = "" 9 \$ R& K2 [. |7 _ fragment.description = fragment.description[12:].split(",")[0]0 ?- n( q# ?; U( [
fragment.description = str(self.readsID) + "." + fragment.description 6 g; s( P5 V) c" \' z& I6 C self.readsID += 15 n1 |1 @4 t9 i5 ] s" @
readslength = random.randint(self.minreadslength, self.maxreadslength)$ k$ g8 x3 t; w6 O1 Y4 B
self.allreadslength += readslength 8 i1 V y7 Z9 U7 v' q ? self.readsList.append(fragment[:readslength]) 8 v, r4 j* l( | : U) N6 r/ w& ^/ T5 c* g4 I$ O9 I; T def singlereadsequencing(self, genomedata, sequencingResult):. d6 L: R: d1 p/ i4 s
for seq_record in SeqIO.parse(genomedata, "fasta"): 7 z0 ^" w8 {( l seqlen = len(seq_record)4 ]: V) V& y1 @. f5 l
self.genomeLength += seqlen' i ~6 J' k# g+ `' `
for i in range(self.N):+ s4 u7 s8 O7 b, Q8 G$ U2 }6 u
# 生成断裂点) \) P3 V q# f. v) ]4 u" H2 M
breakpoint = self.generatebreakpoint(seqlen, self.averagefragmentlength) $ U: Z7 i" [" r) \$ L # 沿断裂点打断基因组 , p' L+ i2 `+ a; I* V self.breakgenome(seq_record, breakpoint, self.minfragmentlength, self.maxfragmentlength) * @8 e, z: T" b2 D6 W+ e # 模拟克隆时的随机丢失情况8 J+ q- u/ T* t. s" j ]
clonedfragmentList = self.clonefragment(self.fragmentList, self.cloneRetainprobability) C' f( U2 |, E2 Q6 ~
# 模拟单端测序: k% K+ \* ]& P& @
self.singleread(clonedfragmentList) 7 J; g2 v" g8 `' G SeqIO.write(self.readsList, sequencingResult, "fasta"): w O4 c' I0 K- s" X6 [& w# @3 u
/ n6 u( D- v8 P def pairread(self, clonedfragmentList): 3 }, v# p# s: ^3 W for fragment in clonedfragmentList:# }) R4 R7 C4 {; O. V
fragment.id = ""+ }. i! ~5 ~& x5 Z9 k
fragment.name = ""9 j2 p# z' \( T, \9 D
description = fragment.description[12:].split(",")[0] ; J6 z' z, |. X fragment.description = str(self.readsID) + "." + description8 e; c: f% \1 G K
readslength = random.randint(self.minreadslength, self.maxreadslength) 3 Q4 e8 @( N, C& \( h4 H H1 \ self.allreadslength += readslength; L# R# |+ M/ k( F
self.readsList.append(fragment[:readslength]) 3 O% L" p, p! ]8 U% k 9 {. ~- r! w U; W# k6 g1 s5 r3 V readslength = random.randint(self.minreadslength, self.maxreadslength) / T- G9 Q5 \9 T5 p3 ? self.allreadslength += readslength ( }( u: S" D. w# t* y0 h / v) @+ m! X6 U8 N fragmentcomplement = fragment.reverse_complement()/ V7 L. z( f+ m! t) i
fragmentcomplement.id = ""6 E; n9 \8 C! d: Z6 Q" [
fragmentcomplement.name = "" 8 C: F7 \; P! I S( o; l) m1 x fragmentcomplement.description = str(self.readsID) + "." + description 1 I. F9 ?8 R2 S) ?3 r, l7 m self.readsList.append(fragmentcomplement[:readslength])1 E" C! m' [8 C* I0 Q3 g- E( o- O
9 o3 d* j2 }, _* o% T self.readsID += 1) ^$ a1 @1 m' x5 Q* h
3 R' N7 ^! W. G3 ? def pairreadsequencing(self,genomedata, sequencingResult_1, sequencingResult_2): # s" l t! J$ X( U* c& l$ D, W for seq_record in SeqIO.parse(genomedata, "fasta"): " S+ k9 ?0 M @6 b o+ y seqlen = len(seq_record) 1 ^; l, H( j4 j) Z; G& C" q! {5 y+ N self.genomeLength += seqlen $ g2 p0 Y3 M+ M for i in range(self.N):- Z0 `. M ]) E" f# O4 L4 b
# 生成断裂点 ( [, }' Y& ~ `# S+ h4 q) c2 ?# v breakpoint = self.generatebreakpoint(seqlen, self.averagefragmentlength) 0 w$ g, q2 ~' ^4 ~6 P6 e # 沿断裂点打断基因组; e9 y# ?4 k5 C5 `
self.breakgenome(seq_record, breakpoint, self.minfragmentlength, self.maxfragmentlength) 6 Y7 f) ]! e) q8 H) u: A2 H1 y # 模拟克隆时的随机丢失情况; v1 f' M$ n6 J9 [
clonedfragmentList = self.clonefragment(self.fragmentList, self.cloneRetainprobability)2 h+ o" \' ^% h6 @6 D
# 模拟双端测序 . {- u: i: O/ {8 B' J; V$ R! b self.pairread(clonedfragmentList) & ^5 P( | D( u- [ readsList_1 = [self.readsList for i in range(len(self.readsList)) if i % 2 == 0], S8 k, @* Q: b- O7 _5 K
readsList_2 = [self.readsList for i in range(len(self.readsList)) if i % 2 == 1] 0 R0 Y- b: o$ U0 b SeqIO.write(readsList_1, sequencingResult_1, "fasta") k I. `, a" K' K4 f
SeqIO.write(readsList_2, sequencingResult_2, "fasta") , L7 I, D9 p1 @* i: x9 n. L$ j3 W! W+ P, K- e5 I& Z+ c
def resultsummary(self): ! @9 l+ |4 }* N, f+ e' B$ a+ j print("基因组长度:" + str(self.genomeLength / 1000) + "kb")0 y! \7 B; N* ?/ z$ D& T
print("片段长度区间:" + str(self.minfragmentlength) + "-" + str(self.maxfragmentlength)) 3 k$ {- n/ v/ S* P0 L8 ^ print("N值:" + str(self.N)) # N% `' ^- p* o0 x print("期望片段长度:" + str(self.averagefragmentlength)): d4 C0 K; Y0 {- b, W( O3 S6 G
print("克隆保留率:" + str(self.cloneRetainprobability))3 ?3 F. U' r# |6 {
print("片段数量:" + str(len(self.fragmentList)))9 F1 K# w# H# L$ |, q# u
print("reads长度:" + str(self.minreadslength) + "-" + str(self.maxreadslength))4 v4 F+ o; y3 E
print("reads总数量:" + str(len(self.readsList))) 8 W ^# Q. P# ~1 m3 i# s print("reads总长度:" + str(self.allreadslength / 1000) + "kb") % k# s4 N4 y' A m = self.allreadslength / self.genomeLength & u" p4 k# o3 u print("覆盖度(m值):" + str(round(m, 5)))7 A* K0 T' x3 S5 y
print("理论丢失率(e^-m):" + str(round(exp(-m), 5))) ) M! ]# c& F- @( \1 u print("覆盖率(1-e^-m):" + str(round(1 - exp(-m), 5))) 9 y9 X1 R8 A4 Z. p/ K5 I* s# -------------------------------------------主程序-------------------------------------------0 D9 A ]! }) n _# e
# 模拟单端测序 0 y8 r& r4 C5 n1 A# c. j% m+ nsequencingObj = Sequencing() 0 j z7 W' R! A) w% B! gsequencingObj.singlereadsequencing("data/NC_025452.fasta", "result/virusSingleRead.fa") 7 Z5 ]7 u$ J1 D* @, WsequencingObj.resultsummary()! F- O9 o: P9 B' Q! f$ A; B
4 D7 f: W% X. P
# 模拟双端测序$ ^8 D7 A% z9 X& f, e8 p' h0 [
sequencingObj = Sequencing()8 }* s+ T4 g, ~% T3 O8 F1 G4 r
sequencingObj.pairreadsequencing("data/GCF_000146045.2_R64_genomic.fna", "result/yeastPairRead_1.fa", "result/yeastPairRead_2.fa")* z6 O% r# w" d* u2 \3 n& K
sequencingObj.resultsummary()( U7 a! [% m* O; {9 b' T
from Bio import SeqIO$ M2 q$ S. G l; t" a1 f& b! a! J6 K
from math import exp . Z d/ O4 J+ E2 u$ n Z) zimport random 0 r4 ?) c+ a' q8 B% q# E# r, W& B, U( X$ R0 o
class Sequencing:- q6 K% U+ z8 Z* s3 Y
# N代表拷贝份数# v1 u- p( i: _" s" b8 [! q
def __init__(self):' T% g. Y+ y [( P6 E) r
self.fragmentList = [] 8 [8 E3 K! b8 K self.readsID = 1 9 Y8 v; c8 `" Z. p& ` self.readsList = [] # }9 o5 H6 ]1 ?9 @% \: b" x self.averagefragmentlength = 650 ) Q, H f4 @- W+ g3 c Q5 f6 j! n self.minfragmentlength = 500 / }1 n1 {! m) l+ t self.maxfragmentlength = 800. i0 d: P, @& V4 o4 ~0 t. J
self.cloneRetainprobability = 1 ( ?5 K0 R) |! Z2 a: K self.minreadslength = 500 J8 [/ h, X. h1 J4 `
self.maxreadslength = 150. }4 o: o T; k% `% B( J
self.N = 10 7 m" N% i' A3 R. n2 W' _ self.genomeLength = 0 9 i; Y: d9 b2 u9 x7 ~ self.allreadslength = 0. n+ { N1 S( N
6 g" W' V; l, g, G/ B
# 生成断裂点+ \' ], `0 s8 f& _3 R0 W7 m9 C0 t) H
def generatebreakpoint(self, seqlen, averageLength):% A5 M, Z% M- A( J: L4 h6 J
# 假设平均每500bp 产生一个断裂点(averageLength = 500),通过随机函数生成seqlen/500个随机断裂点(1到seqlen之间的随机整数) 2 q! l: C! e, ]) x }1 b& V, O breakpoint = [random.randint(0, seqlen) for _ in range(int(seqlen / averageLength))]4 I# ]4 B. {1 j7 m+ Z) ^0 Y8 X
breakpoint.append(seqlen) 8 P* s) s; X. |% D3 P C breakpoint.append(0) . z# T3 G9 N! @ # 把随机断裂点从小到大排序6 v d' R; c1 j0 n' L
breakpoint.sort() - p W/ p# B, N8 a return breakpoint: x% b% \7 t% b. H, ]! j( _
' i [" T+ Z/ H8 Q1 o' C. h # 沿断裂点打断基因组,并删除不符合长度要求的序列片段,定义片段范围:200-1000bp 8 t, H+ H( n Y. k7 B2 A def breakgenome(self, seq, breakpoint, minfragmentlength, maxfragmentlength): / R) e% {; W* L for i in range(len(breakpoint) - 1):4 l) d# _0 ]: y4 T6 a7 b
fragment = seq[breakpoint:breakpoint[i + 1]], o1 a( t) P3 d
if maxfragmentlength > len(fragment) > minfragmentlength: * p7 P8 Y4 A/ k self.fragmentList.append(fragment)4 J* d) o3 @0 ~6 \' G7 _
return self.fragmentList) V9 A' V8 H4 F5 @) t+ V! b3 [. x8 l' d
" H7 l" x0 @$ e3 Y3 K # 模拟克隆时的随机丢失情况,random.random()生成0-1的保留概率 ; X0 U: c7 ]8 a! n' x def clonefragment(self, fragmentList, cloneRetainprobability):$ y2 y& I" d- P' G6 v2 F5 e
clonedfragmentList = []. `# R( F' Y2 y& U) }
Lossprobability = [random.random() for _ in range(len(fragmentList))]- z: ]; f3 T* t* o7 v
for i in range(len(fragmentList)): : f; A. w8 ?: o# r) G5 Z if Lossprobability <= cloneRetainprobability: 4 r9 c% f3 x/ w clonedfragmentList.append(fragmentList) ' m" B) T7 J0 H0 y7 h return clonedfragmentList4 B% E O3 O7 X- y4 G% R3 K
$ @. b L$ u1 }* U$ s( ]# k # 模拟单端测序,并修改reads的ID号 9 P* i4 x4 e; k5 \* s/ d: n, L: i3 X def singleread(self, clonedfragmentList): 1 t, b$ p, t5 I A# }. j3 S for fragment in clonedfragmentList:9 d% X$ u8 ^1 U2 \) G
fragment.id = ""5 G* C2 L' I) g- s- A
fragment.name = ""4 I7 J; ^* x1 G1 t
fragment.description = fragment.description[12:].split(",")[0] ' t/ c- y. A N/ L% X0 y fragment.description = str(self.readsID) + "." + fragment.description3 F, E) s+ ^- |. z& C$ ?
self.readsID += 19 _- n! h9 D3 o2 V; A$ `
readslength = random.randint(self.minreadslength, self.maxreadslength)5 y2 C. P5 W% x8 v8 [2 f+ J
self.allreadslength += readslength , ]* W: Y+ M: H9 A self.readsList.append(fragment[:readslength]) ; \; k( H. n/ }1 z4 l+ v9 M, f( M& s# Y+ [$ N5 X9 `# B
def singlereadsequencing(self, genomedata, sequencingResult): . d x* V$ f' e for seq_record in SeqIO.parse(genomedata, "fasta"): 0 ~/ _" \, @' w% ~! E2 l1 B- ]3 a seqlen = len(seq_record)' w# \3 z4 M: c N9 v, _
self.genomeLength += seqlen6 k+ m% s/ R1 D3 }
for i in range(self.N): / K+ \9 R1 Y# c1 ~; Z3 n # 生成断裂点 * U$ Z" }& u& `! @ breakpoint = self.generatebreakpoint(seqlen, self.averagefragmentlength)6 l8 T# o$ w, p/ _* t% x U, T
# 沿断裂点打断基因组4 o0 k4 [7 j; \8 Z+ O% Y
self.breakgenome(seq_record, breakpoint, self.minfragmentlength, self.maxfragmentlength). p/ v! K; B$ G. B1 v
# 模拟克隆时的随机丢失情况3 Q* P. t9 R& i& ~: M
clonedfragmentList = self.clonefragment(self.fragmentList, self.cloneRetainprobability) ' \1 U$ s# P4 ?$ D. k # 模拟单端测序 + Q, ?5 x: g# R& k2 T: R' m+ D self.singleread(clonedfragmentList) R8 G0 V( N/ R2 f# n& i6 y0 W/ O SeqIO.write(self.readsList, sequencingResult, "fasta")! K+ h/ ?6 I7 a5 p5 T