انشاء خطوط تحليل البيانات باستعمال bpipe

برمجة nadhir 12038℃ 0تعليق

السلام عليكم,

عند تحليل البيانات في العادة نستعمل اكثر من أدات واحدة للحصول على النتيجة او النتائج النهائية . مثلا عند حساب التعبير الجيني يجب اولا ان نقوم بحساب جودة السلاسل باستعمال fastqc، بعدها نقوم بنزع المناطق ذات النوعية الرديئة (مثلا باستعمال Trimmomatic)  ، ثم مطابقتها للجينوم المرجعي ثم حساب التعبير الجيني و القيام بتحاليل اخرى.

بطيعة الحال سوف نقوم بهذه الخطوات مرات عديدة على عينات مختلفة, وفي بعض الأحيان نحتاج الى تحسين الخطوات او استبدال الأدوات (مثلا استبدال Tophat بـ STAR).اذا كنت تكتب سكربتات باستعمال Bash فسوف تحتاج في كل مرة الى تغير اسم الملفات والمسارات مما يزيد نسبة الخطأ كلما زاد السكريبت تعقيدا. كما انك ستواجه عدة اشكاليات عند حدوث مشكلة في وسط خط التحليل, فلا تدري اين وقعت وهل الملفات الموجوة كاملة ام كتب بعضها فقط. مما يجبرك في بعض الاحيان الى اعادة القيام بالتحليل للتخلص من كل الشكوك.

لتسهيل عملية تصميم خطوات تحليل البيانات Data processing pipeline تم تطوير مجموعة من الأدوات. بعض هذه الأدوات لديه واجهة سهلة التحكم لكن لا تعطيك الحرية الكاملة في التصميم (مثلا Galaxy و Taverna) او لا تسمح لك بالقيام بعمليات متوازية ( Parallel Processing) . في المقابل, هناك ادوات تعطيك الحرية الكافية لكن تستلزم منك إلماما بلغة البرمجة المستعملة.

في هذا المقال سوف نقوم بالتعريف بالأدات Bpipe التي تتمركز في الوسط بين السهولة والحرية. يمكن تعريف Bpipe على انها لغة برمجة لكتابة خطوط بيانات المعلوماتية الحيوية تتميز بسهولتها وليونتها نسبيا مقارنة بالأدوات المتوفورة. صممت Bpipe باستعمال لغة الـ Groovy وهي لغة كائنية التوجه يمكن استعمالها كلغة سكربت لمنصة جافا وهي مشابهة لـ Perl او Python من حيث المبدأ.

التعريف بـ Bpipe

شخصيا من بين الخصائص التي تعجبني فيBpipe هو سهولة تحويل سكريبتات Shell الى خطوات واضحة دون القيام بالكثير من التعديلات. يمكننا تلخيص خصائصBpipe في النقاط التالية:

سهولة تعريف الخطوات يمكن تحويل سكريبتات Shell بسهولة الى Bipipe
 الادارة المٌعاملاتية للخطوات  حيت يتم اعبار كل خطوة على انها مٌعاملة (Transaction). في حالة فشل الخطوة, يتم حذف الملفات التي تم انشاؤها و حفظ ملفات الـ log ويتم انهاء خط تحليل البيانات.
 الربط الاوتوماتيكي بين الخطوات يقوم  Bpipe بالربط الاوتوماتيكي بين مداخل ومخارج الخطوات المتتابعة, كما يسمح بالتسمية الاوتوماتيكية للمفات المنشأة في كل خطوة.
 سهولة اعادة تشغيل الخطوات في حالة حدوث مشكلة في خطوة من الخطوات يمكن اعادة تشغيل خط تحليل البيانات من نقطة حدوث المشكل وليس اعادة التحليل من البداية.
 سهولة القيام بعمليات الحوسبة المتوازية يسمح Bpipe بالتشغيل المتوازي للخطوات بحيث يمكن تحويل سكريبت Shellالى سكريبت متوازي دون القيام بأيت تغييرات جذرية.
 ارشفت الاوامر يسمح Bpipe بارشفت الاوامر التي تم استعمالها خلال تنفيذ خط التشغيل مما يسهل عملية اكتشاف الاخطاء.
دعمه للحوسبة الشبكية  يدعم Bpipe مجموعة من انظمة الحوسبة الشبكية (Grid computing) كـ Torque PBS,  Oracle Grid Engine و LSF

تنصيب Bpipe

لا يحتاج Bpipe الى خطوات معقدة لتنصيبه, فقط يجب تحميله من github بعدها فك الضغط و اضافة مساره الى الـ PATH. بالطبع ننوه هنا ان Bpipe يعمل فقط على انظمة Linux و MacOS, اما في حالة الـ Windows يجب تنصيب مكتبة الـ Cygwin لكن ربما لن تستفيد من كل الخصائص. اذا كنت تعمل في مجال المعلوماتية الحيوية او المجالات المشابهة فهناك احتمال كبير لاستعمالك للنيكس نظرا لتوفر العديد من الادوات في تلك البيئة.

لغة Bpipe

لا يهدف Bpipe الى تعريف لغة جديدة تحل محل سكربيتات Shell لكنها توفر مجموعة من الاوامر تسهل لك استعمال اوامر Shell استعمالا افضل. كما نوهنا سابقا, يعتمد Bpipe على لغة Groovy و التي هي لغة سكريبت مشتقة من جافا. يهدف Bpipe الى تعريف مجموعة من الاوامر البسيطة عن طريق تعريف الخطوات (stages) و الربط بينها من اجل تصميم خطوط تحليل البيانات (pipeline).

تُعَرف كل خطوة في Bpipe بالشكل المبين في المثال التالي:

stage_name = {
  exec "shell command to execute your pipeline stage"
}

حيث يمثل stage_name اسم الخطوة و يتم وضع امر الـ Shell في الجملة التي تلي امر  exec. اذا كان امر الـ Shell طويلا او يحتوي على مجموعة من الاوامر فيتحسن كتابة ثلاث علامات اقتباس  """ بعد امر exec.

بعد تعريف الخطوات, يمكن تحديد ترتيب تنفذيد الخطوات بالستعمال امر  run كما هو موضح في المثال التالي:

hello = {
  // An exec with single quotes
  exec "echo 'Hello'"
}

world = {
  // An exec with triple quotes
  exec """
    echo 'World'
  """
}

run {  hello + world }

من اجل تنفيذ هذا المثال, يمكنك حفظه في ملف مثلا mypipeline.groovy ثم تقوم بتشغيله كالتالي:

bpipe run mypipeline.groovy

سوف تتحصل على النتيجة التالية:

====================================================================================================
|                              Starting Pipeline at 2016-05-14 19:00                               |
====================================================================================================

=========================================== Stage hello ============================================
Hello

=========================================== Stage world ============================================
World

======================================= Pipeline Succeeded ========================================
19:00:27 MSG:  Finished at Sat May 14 19:00:27 CST 2016

التعامل مع مداخل ومخارج الخطوات

بطبيعة الحال عند كتابة انابيب تحليل البيانات الحقيقية نحتاج الى تحديد الملفات المستعملة كمَدخل ومَخرج لكل خطوة. يقوم Bpipe اتوماتيكيا بتعريف المتغيريين input و output عند بداية كل خطوة, بحيث تحتوي input على مسار الملفات المستعملة في الخطوة الحالية و output تحتوي على اسماء الملفات المنتجة في الخطوة التي هي في طور التنفيذ.

عند ربط الخطوات مع بعضها البعض يقوم Bpipe بتحويل output الخطوة السابقة الى input الخطوة الحالية. بطبيعة الحال يمكن تحديد الملفات التي نريد استقبالها و الملفات التي نريد ارسالها للخطوات التي تاتي من بعد, كما يمكن الاستغناء عن المتغيرين input و output و كتابة اسماء الملفات كاملة, لكن هذه الطريقة غير محبذة لانها تعيدنا الى نقطة الصفر.

لنأخذ مثلا المثال التالي:

hello = {
  exec "echo Hello | cat - $input > $output"
}
world = {
  exec "echo World | cat $input - > $output"
}
run { hello + world }

الذي يهدف الى اضافة كلمة Hello الى محتوى ملف وكتابته في ملف جديد, بعدها نضيف كلمة World و نحفظها في ملف جديد و الذي يمثل النتيجة النهائية. من اجل تنفيذ هذا الانبوب, نقوم بانشاء الملف input1.txt و نمرره لسكربنتا.

touch input1.txt
bpipe run mypipeline.goovy input1.txt

في هذا المثال بما اننا لم نحدد اسم الملفات في الـ output في كلا المرحلتين , سيقوم Bpipe  بتسميتهما اوتوماتيكيا حيث يسمي الملف الناتج عن المرحلة الاولى بـ : input1.txt.hello والثاني بـ: input1.txt.hello.world .

من اجل التسمية الاوتوماتيكية للملفات المنتجة في كل مرحلة يمكن استعمال عدة اوامر:

1. امر Filter

يسمح لك هذا الامر باظافة كلمة (او جمل) لاسم الملف لكن مع الحفاظ على نفس نوع الـ extension لملف الـ input . مثلا اذا كتبت:

remove_comments =
{
  filter("nocomments")
  {
    exec """
      grep -v '^#' $input > $output
    """
  }
}

run { remove_comments }

في هذا المثال مثلا اذا كانت الـ inputمثلا myinput.txtسوف يقوم Bpipe بحدف كل الاسطر التي تبدأ بـ # ثم انشاء ملف جديد اسمه myinput.nocomments.txt.

2.امر transform

يقوم هذا الامر بالمحافظة على اسم ملف الدخول لكن يغير الـ extension. مثلا اذا كانت الـ inputمثلا myinput.txt وكان لديك مرحلة (او stage) يقوم بإنتاج ملف من نوع csv سوف يكون اسم الـ output هو myinput.csv.
في هذا المثال سوف نستعمل امري filter و transform من اجل حذف التعليقات ثم تحويلها الى csv.

remove_comments =
{
  filter("nocomments")
  {
    exec """
      grep -v '^#' $input > $output
    """
  }
}

convert_to_csv =
{
  transform("csv")
  {
    // bash command is: tr "\\t" ","
    exec """
      cat $input | tr "\\\\t" "," > $output
    """
  }
}

run { remove_comments + convert_to_csv }

3.امر  produce

في بعض الاحيان, خاصة اذا كانت اسماء الملفات الناتجة ليس لها علاقة كبيرة باسم الملف في الـ input او كانت تسمية لها علاقة معقدة اكثر من فقط اظافة كلمة (كامر filter) او تغير extension الملف نقوم باستعمال الامر produce. مثلا في هذا المثال نقوم بانتاج ملفي و bar.txt.

basic_produce =
{
  produce ("foo.txt","bar.txt")
  {
    exec """
      echo Hello > $output1;
      echo World > $output2;
    """
  }
}

run { basic_produce }

4. امر forward

يستعمل امر forward لتحديد الملف الذي نريد تمريره الى الخطوات القادمة . مثلا اذا كنت لدينا خطوة نقوم فيها باستعمال برنامج FastQC لحساب بعض معايير الجودة في ملف fastq ثم بعدها نريد استعمال برنامج Trimmomatic لنزع الجزيئات القاعدية ذات النوعية الرديئة او التي تم سلسلتها من الـ Adapters و ليس من الجينوم.

في هذه الحالة الـ output التي نريد تمريها بعد نهاية برنامج FastQC ملف الـ fastqc الذي مرر كـ input و ليس الملفات الناتجة عن برنامج FastQC. يمكننا تلخيض ذلك في السكريبت.

fastqc = {
    exec "fastqc  $input"
    forward $input
}

trim = {
    
    filter("trim") {
        exec """ 
            trimmomatic SE -phred33 $input $output.gz \
            ILLUMINACLIP:TruSeq2-SE.fa:2:30:10 \
            TRAILING:20 LEADING:20 MINLEN:35;
        """
    }
}

run {
    fastqc + trim
}

4. تحديد extension الملفات

في بعض الاحيان نريد ان تستقبل خطوة من الخطوات نوعية محددة من الملفات (مثلا, فقط ملفات bam). يمكننا تحديد ذلك بكتابة الـ extension مع الـ input كما هو موضع في المثال التالي الذي نريد فيه صناعة index لملف من نوع bam.

index_bam = {
    transform("bam") to ("bam.bai") {
        exec "samtools index $input.bam"
    }
    forward input
}


run {
    index_bam
}

فهنا كتبنا $input.bam من اجل اخبار bpipe باننا نريد استقبال ملف من نوع bam.

هناك العديد من الخصائص الاخرى يمكن للقارىء المهتم ان يتعلمها من موقع bpipe لكن اظن ان هذه الاوامر تكفي للقيام بالعديد من الاشياء.

القيام بعمليات الحوسبة المتوازية

من بين الخصائص التي تستهوي مستعملي bpipe هو سهولة القيام بعمليات متوازية بدون الحاجة الى تغيير السكربت مما يسمح لنا بمعالجة العديد من الملفات بنفس السكربت. يمكن تحديد الخطوات المتوازية بوضعها بين علامتي []

يتم تقسم العمليات المتوازية الى نوعين:

  • عمليات منفصلة: وهي العمليات المستقلة عن بعضها البعض والترتيب غير مهم عند ادائها, يمكن الفصل بينها باستعمال الفاصلة للفصل بين العمليات المستقلة.

مثلا في مثال FastQC و Trimmomatic لا توجد علاقة بين نتائج البرنامجين لكنها يستعملان نفس الملفات يمكننا اذن كتابة

run {
    [ fastqc , trim ]
}

مثلا اذا كان لدينا الملفات:  expr1.fastq.gz و expr2.fastq.gz سوف يقوم Bpipe بانشاء اربعة عمليات متوازية, اثنان fastqc و اثنان trim.

  • عمليات متصلة: وهي العمليات التي المرتبطة ببعضها البعض و لايمكن الشروع في الخطوة التالية قبل نهاية الخطوة الحالية ويمكن استعمال رمز + لتحديدالترابط.

مثلا في هذا المثال نريد ان نرتب الـ reads في مجموعة ملفات bam وذلك لانشاء index لكل ملف و القيام ببعض الاخصائيات. لكن بما ان عملية الـ indexing تتطلب ملفا مرتبا على حسب الاحداثيات الجينومية, هناك ترابط بين هذين الخطوتين كما ان عملية حساب الاحصائيات تتطلب ملف bam مرتب ولديه index . لهذا, اذا كان لدينا مجموعة من ملفات bam, سوف نعالج كل ملفعلى حدى, لكن كل عملية متوازية  تقوم بثلاث خطوات.  المثال التالي يوضح ذلك.

sort_bam = {
    
    filter("sorted") {
        exec "samtools sort $input.bam $output.prefix"
    }
}

index_bam = {
    
    transform("bam") to ("bam.bai") {
        exec "samtools index $input.bam"
    }
    forward input
}

mapping_stats {
    transform("bam") to ("index_stats.txt"){
        exec "samtools idxstats $input.bam > $output"
    }
}

run {
    
    [ sort_bam + index_bam + mapping_stats ]
}

تحديد طريقة تقسيم الملفات بين المهام المتوازية

مثلا لو كان لدينا الملفات التالية:

.
├── expriment_1_R1.fastq.gz
├── expriment_1_R2.fastq.gz
├── expriment_2_R1.fastq.gz
└── expriment_2_R2.fastq.gz

نلاحظ انه لدينا ملفان R1 و R2 لكل تجربة يمثلان نتائج السلسلة باستعمال Pair-end Sequencing. بما اننا نريد ان نقوم بتحليل هذه البيانات بطريقة متوازية نريد ان يعالج الملفان expriment_1_R1.fastq.gz و expriment_1_R2.fastq.gz معا و الملفات expriment_2_R1.fastq.gz و expriment_2_R2.fastq.gz معا.
لتحديد طريقة التقسيم يمكننا استعمال التعابير القياسية التالية :

  • الرمز %: لاخبار Bpipe باننا نريد استعمال المنطقة التي تَحل مَحل الرمز % من اجل التقسيم الى مجموعات.
  • الرمز *: اخبار Bpipe بان هذه المنطقة متغيرة لكن لا تحدد طريقة التقسيم.

مثلا من اجل معالجة ملفات كل تجربة على حدى يمكننا كتابة expriment_%_R*.fastq.gz كما هو موضح في المثال التالي:

trim_PE = {

   filter("trim","trim") {
        exec """
                trimmomatic PE -phred33
                $input1.gz $input2.gz
                $output1.gz ${output1.prefix}.unpaired.gz
                $output2.gz ${output2.prefix}.unpaired.gz
                LEADING:15 TRAILING:15 SLIDINGWINDOW:5:15 MINLEN:30
        """
       }
}

run {
    "expriment_%_R*.fastq.gz" * [ trim_PE ]    
}

دعم الحوسبة الشبكية (Gird computing)

في العادة البيانات التي نتعامل معها تكون ملفات كبيرة و لمعالجتها نحتاج مساحة ذاكرة كبيرة و عدد كبير من المعالجات, لهذا تستعمل الكثير من مخابر المعلوماتية الحيوية نظم الحوسبة الشبكية. يدعم Bpipe الثلاثة انواع الاكثر تداولا من الشبكات: Torque PBS,  Oracle Grid Engine و LSF.

من اجل تفعيل الدعم, يجب انشاء ملف بإسم bpipe.config في نفس المجلد الذي نريد منه تشغيل Bpipe ثم تحديد نوع الشبكة وكمية المساحة المراد استعمالها وعدد المعالجات المطلوبة. مثلا في هذا المثال نستعمل Oracle Grid Engine.

executor="sge"
sge_request_options="-l h_vmem=30G -l p=32 -N myBigAnalysis" 

 تعريف المتغيرات

بما ان Bpipe مبني باستعمال لغة الـ Groovy يمكن كتابة سكربيتات معقدة تحتوي على المتغيرات و العبارات الشرطية و كل مايستطيع Groovy القيام به. هناك نوعين من المتغيرات:

  • المتغيرات المحلية (local variables): وهي متغيرات خاصة بكل خطوة يتم تعريفها باستعمال امر def او امر var اذا كنا نريد استعمالها كـ command line parameter variable.
  • المتغيرات العامة (global variables): وهي متغيرات مشترك بين كل الخطوات ويتم تعريفها بدون اسعمال كلمة def. مثلا مسار الجينوم المستعمل, ربما سوف نحتاجه في العديدمن الخطوات, لهذا يمكنا تعريفه كمتغير عام.

ليكن لدينا المثال التالي:

global="man"

hello = {
  var greetings : "Hello"
  exec "echo $greetings , $global"
}

world = {
  var name : "Earth"
  exec "echo welcom to $name , $global"
}

run{
    hello + world
}

اذا شغلنا هذا السكربت بدون تحديد قيم المتغيرات سوف نتحصل على :

$bpipe run mypipeline.groovy
Hello , man
welcom to Earth , man

اذا اردنا تغيير قيمة المتغيرين greetings و name يمكننا استعمال -p كما هو موضح في المثال التالي:

$bpipe run -p greetings="Good morning" -p name="Mars" mypipeline.groovy
Good morning , man
welcom to Mars , man

انشاء التقارير

يمكننا Bpipe من انشاء تقارير تلخص طريقة خط تحليل البيانات و الوقت المستغرق في كل خطوة. يمكن تفعيل خاصية التقارير باستدعاء Bpipe باستعمال -r. يمكن ايضا اضافة معلومات في كل خطوة تشرح دور الخطوة و المتغيرات المستعملة و اسم الشخص الذي كتبها ... الخ من العلومات. هنا مثال يوصح هذا الامر.

about title: "A simple pipeline to align paired reads"

REFERENCE_GENOME = "hg19.fa"
align_bwa =
{
  
  doc title: "Align DNA reads with bwa mem",
      desc: "Use bwa mem to align reads on the reference genome.",
      constraints: "Input must be compressed (fastq.gz)",
      author: "me@gmail.com"

  
  transform("bam")
  {
    exec """
      bwa mem $REFERENCE_GENOME $input1.fgz $input2.fgz |
      samtools view -bSu - |
      samtools sort - $output.bam.prefix;
    """
  }
}

يمكن تشغيل هذا الملف باستعمال:

bpipe run -r mypipeline.groovy

اعادة التشغيل من نقطة الخطأ

من بين اهم خصائص Bpipe هي القدرة على اتمام العمل من نقطة الخطأ. مثلا اذا كان لديك 5 خطوات وحدث مشكل في الخطوة الثالثة, يمكن اصلاع الخطأ في الخطوة الثالثة ثم استعمال امر retry بدلا من run من اجل الاستكمال من الخطوة الثالثة.

bpipe retry

خلاصة

في هذا المقال عرفنا ببعض خصائص Bpipe وبينا طرق استعماله و بعض المزايا التي تجعل من ادات مفيدة للتعريف خطوط البيانات. تعلمنا كيفية انشاء خطوط بيانات متوازية وكيفية استعمال الحوسبة الشبكية وغيره من المهارات التي تمكننا من استعمال Bipe. لكن هذا المقال يعتبر فقط مقدمة في التعريف بـ Bpipe, يمكن التعرف اكثر على خصائصه Bpipe عند طريق استعماله والاطلاع الى المصادر اسفله.

في الأخير اتمنى ان يكون هناك من استفاد من هذا المقال.

مصادر

  • Sadedin SP, Pope B, Oshlack A: Bpipe : a tool for running and managing Bioinformatics pipelines. Bioinformatics. 2012.
  • Bpipe documentation (http://bpipe-test-documentation.readthedocs.io/en/latest/)
  • Bpipe workshop (https://github.com/tucano/bpipe_workshop)

رابط المقالة : المعلوماتية الحيوية بالعربية » انشاء خطوط تحليل البيانات باستعمال bpipe

معجب (11)