Sunday, October 31, 2010

ฉบัับที่ 14 (ตุลาคม 2010)

มจำได้ว่าเดือนตุลาคมของปีก่อน ผมได้นำเสนอชีวประวัติและเรื่องราวน่าสนใจของชาร์ล ดาร์วิน (Charles Darwin) ลงใน Highlight ของ BNNews และในเดือนตุลาคมปีนี้ ผมเลยถือโอกาสนำเรื่องราวเกี่ยวกับวิวัฒนาการมานำเสนออีกรอบหนึ่งในคอลัมน์ Highlight ด้วยเรื่องราวของ Molecular Phylogenetics แบบง่ายๆ หรือจะเรียกว่าเป็นขั้นพื้นฐานก็ว่าได้ ทั้งนี้ก็เพื่อเรียกน้ำย่อยของคนที่สนใจงานด้าน phylogeny และผมยังมีโอกาสเชิญน้องฝน facebook admin ของเรามา review หนังสือเล่มหนึ่งเกี่ยวกับชีวประวัติในอีกแง่มุมของครอบครัวและความรักของดาร์วินอีกด้วย

และทีมงาน THAI Bioinformatics ขอแสดงความยินดีกับความสำเร็จอีกก้าวหนึ่งของน้องก้อย ที่เรียนจบปริญญาเอกเป็นที่เรียบร้อยแล้ว ตอนนี้คงต้องเรียกว่า ดร. จิตสุพางค์ รอดบำเรอ ได้เต็มปากแล้วครับ หวังว่าพวกเราคงมีโอกาสได้เชิญน้องก้อยมาแบ่งปันความรู้กับเราในโอกาสหน้า นอกจากนี้ผมยังมีเรื่องน่ายินดีอีกเรื่องหนึ่งครับ คือเรามีนักเขียนหน้าใหม่เพิ่มมา 2 คนครับ คือ อภิชาต ศุรธณี และ กิติพร พลายมาศ ครับ ทั้งสองคนนี้เป็น bioinformaticians ที่มีพื้นฐานทางด้านคณิตศาสตร์ครับ และมาเปิดตัวกับเราด้วยคอลัมน์ใหม่เอี่ยมอ่อง เกี่ยวกับ R Programing ครับ ต้องติดตามกันเอาเองครับ

Talk to... ฉบับนี้ น้องปุ้มฉีกแนวครับ แอบไปชวนน้องๆ นิสิตจุฬาฯ มาคุยกันเรื่อง bioinformatics ถ้าอยากรู้ว่าน้องๆ หน้าใสๆ เขามีความเห็นเกี่ยวกับ bioinformatics อย่างไร ห้ามพลาดครับ นอกจากนี้เรายังมีเรื่องราวน่าสนใจอีกมากมายในเล่มครับ

สำหรับเพื่อนๆ ที่ยังไม่ได้เป็นสมาชิกกับเราก็อย่าช้านะครับ ตอนนี้ THAI Bioinformatics Network ของเราเปิดรับสมาชิกอย่างเป็นทางการแล้วครับ ใครยังนิ่งๆ อยู่คงต้องรับเขียน e-mail มาหาเราพร้อมแจ้งรายละเอียดเกี่ยวกับ ชื่อ-นามสกุล เป็นภาษาไทย มาที่ thaibioinfo@gmail.com ตั้งแต่วันนี้จนถึง 31 ธันวาคม พ.ศ. 2553 นี้เท่านั้นนะครับ

Saturday, October 30, 2010

Highlight

แกะรอยวิวัฒนาการสิ่งมีชีวิตด้วย Molecular Phylogenetics


ประเวช อรรจวัฒนวงศ์


Jacques Monod นักวิทยาศาสตร์รางวัลโนเบลชาวฝรั่งเศส ผู้ที่มีชื่อเสียงจาก lac operon ได้กล่าวไว้ตอนหนึ่งว่า

“A curious aspect of the theory of evolution is that everybody thinks he understands it.”

ผมมีความเชื่อส่วนตัวว่า คำพูดในประโยคข้างบนนี้ไม่เกินเลยไปจากความจริงเลย ถ้าคุณเดินไปบนท้องถนน แล้วสุ่มถามคนเดินไปมาสักคนหนึ่ง คงไม่มีใครไม่รู้จักคำว่าวิวัฒนาการหรอกครับ แต่บนโลกใบนี้จะมีสักกี่คนที่เข้าใจคำว่าวิวัฒนาการอย่างแท้จริง

สิ่งมีชีวิตทุกชีวิตบนโลกใบนี้ ล้วนอยู่ภายใต้กระบวนการอันซับซ้อนของธรรมชาติที่เรียกว่า “วิวัฒนาการ” ด้วยกันทั้งสิ้น วิวัฒนาการเป็นกระบวนการที่เกิดขึ้นตลอดเวลา เกิดขึ้นมานานแสนนานก่อนที่มนุษยชาติจะถือกำเนิดขึ้นบนโลก และจะดำเนินการต่อไปไม่มีวันสิ้นสุดถึงแม้ว่าไม่มีสิ่งมีชีวิตหลงเหลือแล้วก็ตาม วิวัฒนาการเป็นสิ่งที่ดำรงอยู่เคียงคู่กับธรรมชาติ และมนุษย์เป็นสิ่งมีชีวิตเพียงสปีชีส์เดียวที่รู้ว่าวิวัฒนาการนั้นมีอยู่จริง

นักวิทยาศาสตร์ทุกยุคทุกสมัย เฝ้าเพียรพยายามหาคำตอบของคำถามที่ว่า “Who are we?” ผมอยากให้ลองนึกถึงเหตุการณ์สมมติว่า ถ้าคุณตื่นขึ้นมาในวันหนึ่ง แล้วพบว่าคุณจำอะไรในอดีตไม่ได้เลย สิ่งแรกที่คุณต้องหาคำตอบคือ “เราเป็นใคร” คำตอบที่คุณอยากทราบ คงจะไม่ใช่ เราเป็นพลเมืองไทยคนหนึ่ง หรือเราเป็นนักวิทยาศาสตร์คนหนึ่ง แต่คำตอบที่คุณอยากรู้คือพ่อแม่ของเราเป็นใคร ครอบครัวของเราเป็นใคร บ้านของเราอยู่ที่ไหน เราดำเนินชีวิตอย่างไร คำถามของนักวิทยาศาสตร์ก็เช่นเดียวกัน เขาอยากทราบว่าบรรพบุรุษของมนุษย์คือใคร มนุษย์เป็นญาติพี่น้องใกล้เคียงเป็นใคร มนุษย์ดำเนินชีวิตผ่านช่วงเวลาตั้งแต่อดีตมาได้อย่างไร

ข้อตกลงเบื้องต้นทางวิวัฒนาการ

ก่อนที่เราจะเข้าสู่เรื่องราวของ molecular phylogenetics นั้น ผมอยากจะขอทำความเข้าใจเบื้องต้นเล็กน้อยเสียก่อน เพื่อไม่ให้เกิดคำถามที่จะทำให้คุณไขว้เขวได้ในภายหลัง การศึกษา phylogenetics นั้นจะต้องมีเงื่อนไขเล็กน้อย ที่เราเรียกกันในภาษาอังกฤษว่า basic assumption นั่นแหละครับ สิ่งแรกก็คือ วิวัฒนาการของสิ่งมีชีวิตนั้นมีอยู่จริง นั่นเพราะเรามีหลักฐานทางวิวัฒนาการมากมายที่ยืนยันได้ตั้งแต่ระดับ fossil ไปจนถึงระดับโมเลกุลครับ (ผมจะไม่ขอกล่าวในรายละเอียด) และปัจจัยสำคัญที่ทำให้เกิดความหลากหลายของสิ่งมีชีวิตคือ ความแปรผันทางพันธุกรรม (genetic variation) สิ่งนี้ทำให้แต่ละชีวิตในประชากรมีความแตกต่างกันไป ความแปรผันทางพันธุกรรมเหล่านั้นเกิดขึ้นจากความผิดพลาดจากการเพิ่มจำนวน DNA ในขั้นตอนของการแบ่งเซลล์ ยิ่งเซลล์แบ่งตัวเร็วมากเท่าใด โอกาสที่เซลล์จะสร้าง DNA ที่ผิดเพี้ยนไปจาก DNA แม่แบบก็มีมากตามไปด้วย ความผิดพลาดเหล่านี้ถูกนักวิทยาศาสตร์เรียกรวมๆ ว่า mutation

เมื่อสิ่งมีชีวิตกระจายตัวไปตามที่ต่างๆ ก็จะต้องพบกับสภาพแวดล้อมที่แตกต่างกัน หรือแม้แต่สิ่งมีชีวิตที่ไม่ได้เคลื่อนย้ายไปไหน แต่เมื่อเวลาผ่านไปสภาพแวดล้อมก็เปลี่ยนแปลงไป ทำให้สิ่งมีชีวิตบางตัวในประชากรต้องล้มตายไปเพราะความอ่อนแอ และไม่สามารถปรับตัวให้เข้ากับสภาพแวดล้อมที่เปลี่ยนแปลงไป (อันที่จริงทฤษฎีวิวัฒนาการมีนัยที่ลึกซึ้งกว่านี้มาก แต่ผมขออธิบายแค่ผิวเผินเพื่อให้เข้าใจกันง่ายๆ ครับ) สิ่งมีชีวิตที่บังเอิญมีพันธุกรรมที่เหมาะสมกับการมีชีวิตอยู่ในสิ่งแวดล้อมใหม่ก็จะแพร่พันธุ์ในสิ่งแวดล้อมใหม่ได้มาก ทำให้เราไม่ค่อยได้เห็นสิ่งมีชีวิตที่มีพันธุกรรมแบบเดิม (ซึ่งไม่เหมาะสมกับสิ่งแวดล้อมใหม่แล้ว) กระบวนการนี้เรียกว่าการคัดเลือกโดยธรรมชาติ (natural selection)

สิ่งสำคัญที่สุดอีกเรื่องหนึ่งที่พึงต้องทำความเข้าใจก่อนจะไปทำความรู้จักกับ molecular phylogenetics ก็คือสิ่งมีชีวิตทั้งหลายบนโลกนี้ล้วนแล้วแต่มีกำเนิดมาจากบรรพบุรุษเดียวกัน โดยที่บรรพบุรุษของสิ่งมีชีวิตจะมีขบวนทางวิวัฒนาการที่เรียกว่า การเกิดสปีชีส์ใหม่ (speciation) โดยจะมีการแยกสปีชีส์จากเดิมเพียงชนิดเดียวออกเป็นสปีชีส์ใม่ 2 ชนิด ถ้าวันหนึ่งในภยภาคหน้า นักวิทยาศาสตร์พบว่าทฤษฎีที่ผมเกริ่นมาเหล่านี้ไม่เป็นความจริง แนวคิดทาง phylogenetics ก็เป็นอันต้องล้มเลิกครับ เพราะ phylogenetics เกิดขึ้นมาจากทฏษฎีเหล่านี้ทั้งนั้น

ถ้าจะพูดให้ถูกกันจริงๆ เรามีข้อตกลงพื้นฐานทางวิวัฒนาการที่เยอะกว่านี้มากนัก แต่ผมเกรงว่าถ้าเอามาเล่าให้ฟังทั้งหมดอาจจะเป็นการบั่นทอนเวลาของผู้ที่สนใจเรื่อง phylogenetics เลยขอยกเอาเฉพาะประเด็นสำคัญๆ มาเกริ่นให้คุณผู้อ่านทราบเป็นเมนูเรียกน้ำย่อยกันไปก็แล้วกันครับ ถ้าท่านผู้อ่านสนใจจะเป็นนักวิจัยด้าน phylogenetics อย่างจริงจัง ก็คงต้องศึกษาข้อตกลงเบื้องต้นให้มากกว่านี้ครับ เพื่อให้การนำความรู้ไปใช้งานจริง ไม่เกิดความผิดพลาดโดยไม่เจตนาครับ

ความสัมพันธ์เชิงวิวัฒนาการ

หลายคนอาจคิดว่า phylogenetics เป็นเรื่องใหม่ ที่กำลังทันสมัยในวงการอณูชีววิทยา แต่ความจริง phylogenetics ไม่ใช่เรื่องใหม่แต่อย่างใด เพราะมีคนศึกษา phylogenetics มาก่อนหน้าพวกเรานานมากแล้ว เพียงแค่ไม่ได้ใช้คำว่า phylogenetics เท่านั้นเองครับ

พูดกันให้เข้าใจง่ายๆ phylogenetics ก็คือ การศึกษาความสัมพันธ์เชิงวิวัฒนาการของสิ่งมีชีวิต โดยใช้องค์ความรู้พื้นฐานทางด้านวิวัฒนาการมาเป็นตัวบ่งชี้ความสัมพันธ์เหล่านั้น แล้วนำเสนอความสัมพันธ์ออกมาเป็นรูปแบบแผนภูมิที่เรียกว่า phylogenetic tree หรือ phylogeny ครับ บางคนที่คิดว่า phylogenetics เป็นแค่การแสดงภาพ tree เพื่อบอกความสัมพันธ์ของสิ่งมีชีวิตนั้นงต้องพิจารณาใหม่นะครับ เพราะคำคำนี้มีความหมายตั้งแต่การวิเคราะห์ความสัมพันธ์ไปจนกระทั่งการแสดง (visualization) ความสัมพันธ์ออกมาเป็น phylogeny เลยครับ

ในยุกแรกๆ ของการศึกษา phylogenetics เราอาศักข้อมูลจากสัณฐานวิทยา (morphology) ของสัตว์และพืช ทำให้การศึกษาด้าน phylogenetics ถูกจำกัดอยู่ในกลุ่มนักวิจัยทางสัตววิทยา (zoologist) นักพฤษศาสตร์ (botanist) และนักโบราณชีววิทยา (paleontologist) นั่นเพราะสิ่งมีชีวิตชนิดอื่นซึ่งถือว่าเป็นสมาชิกส่วนมาก ล้วนแล้วแต่เป็นจุลินทรีย์ซึ่งมีโครงสร้างทางสัณฐานวิทยาไม่มากและยังคล้ายคลึงกันอีกด้วย ต่อมาเมื่อแซงเกอร์ ได้นำเสนอเทคนิค protein sequencing และตามมาด้วย DNA sequencing ทำให้นักวิจัยเริ่มหันมาใช้ sequence ในการสร้าง tree จึงเป็นที่มาของคำว่า molecular phylogenetics เพราะเขาใช้ molecular sequence เป็นข้อมูลในการวิเคราะห์นั่นเอง

Phylogenetic tree ที่เก่าแก่และมีคนกล่าวถึงมากที่สุดเห็นจะเป็น tree ของ ชาร์ล ดาร์วิน ครับ เขาวาดภาพ tree นี้ลงในสมุดบันทึกหลังจากที่เกิดแนวคิดว่าสิ่งมีชีวิตทั้งหลายล้วนแต่ต้องมีความเกี่ยวข้องกัน และถ้านับกันจริงๆ phylogenetics เป็นศาสตร์ที่นักวิทยาศาสตร์ให้ความสนใจมากตั้งแต่ช่วง 2-3 ศตวรรษที่ผ่านมา จนกระทั่งถึงปัจจุบัน โดยเฉพาะ molecular phylogenetics กลายมาเป็นเรื่องเด่นในการศึกษาวิจัยในช่วง 50 ปีหลังจากการค้นพบโครงสร้าง DNA นั่นเองครับ

Phylogenetic tree กับความเข้าใจที่ผิดมาเป็นเวลานาน

ในบทความครั้งนี้ ผมจะไม่ขอเล่ารายละเอียดเกี่ยวกับวิธีการสร้าง tree เนื่องจากต้องใช้เวลาเยอะ และหลายคนก็ไม่อยากจะทรายในรายละเอียดขนาดนั้น แต่ผมจะให้ความสำคัญกับการอ่าน tree เพราะถ้าคุณเป็นคนหนึ่งที่กำลังทำงานด้าน phylogenetics หรือกำลังจะต้องทำงานด้านนี้ในอนาคต คุณจะต้องอ่านบทความวิชาการที่มี phylogenetic tree ปรากฏอยู่มากมาย ดังนั้นการอ่าน tree จากภาพที่ปรากฎในบทความจึงเป็นสิ่งสำคัญสำหรับมือใหม่ และถึงแม้ว่าคุณจะเป็นมืออาชีพแล้วก็มีโอกาสที่จะหลงลืมประเด็นบางอย่าง ทำให้เกิดความเข้าใจผิดได้

ผมอยากให้คุณพิจารณา phylogeny ด้านบนขวามือนะครับ นี่เป็น tree ของสัตว์มีกระดูกสันหลัง เราจะเห็นว่ามีสิ่งมีชีวิตอยู่ 5 ชนิดบน tree (คน หนู ไก่ กบ และปลา) สิ่งมีชีวิตทั้ง 5 ชนิดนั้นมีบรรพบุรุษร่วมกัน (เป็นไปตามข้อตกลงเบื้องต้นที่ผมกล่าวไว้แล้วนะครับ) ปกติแกนนอนของ tree (หรือที่เราเรียกกันว่า branch) จะหมายถึงเวลา หรือความแตกต่าง แล้วแต่วิธีการคำนวณ แต่ว่าผมให้หลักังเกตง่ายๆ ครับ ถ้า tree นั้นมีความยาวของ branch เท่าๆ กัน มักจะหมายถึงเวลา แต่ถ้า tree มี branch ที่ยาวต่างกันมักจะหมายถึงความแตกต่าง แม้ว่าข้อสังเกตนี้จะไม่ถูกต้อง 100% แต่ก็พอที่จะนำไปใช้เดาได้ประมาณ 80-90% ครับ

เอาล่ะครับ จากรูป tree ในหน้าก่อน คุณจะพบว่า branch ของคนและ และ branch ของหนูมีจุดเชื่อมต่อกัน ในทางวิชาการเราเรียกว่า “คนและหนูมีบรรพบุรุษร่วมกัน” แต่เราไม่รู้ว่าบรรพบุรุษของคนและหนูคือตัวอะไรกันแน่ ทาง phylogenetics เราเรียกสิ่งมีชีวิตตัวนี้ว่า hypothetical taxonomic unit (HTU) แต่ทางวิวัฒนาการเราจะเรียกว่า missing link ครับ คราวนี้ บรรพบุรุษของคนและหนู (ผมจะเรียกย่อๆ ว่า HTU1 ก็แล้วกันครับ) กับไก่ ก็มีบรรพบุรุษร่วมกันอีก และเป็นตัวอะไรก็ไม่ทราบ ผมขอเรียกว่า HTU2 ก็แล้วกันครับ ถึงตรงนี้ ถ้าผมถามว่า หนูมีความสัมพันธ์เชิงวิวัฒนาการใกล้ชิดกับคนหรือไก่มากกว่ากัน เราก็ต้องมาพิจารณาที่บรรพบุรุษของสิ่งมีชีวิตครับ จะเห็นหนูมีบรรพบุรุษร่วมกับคน ในขณะที่ไก่มีบรรพบุรุษร่วมกันกับ HTU2 (ไม่ใช่หนู และไม่ใช่คนด้วยนะครับ) แสดงว่าหนูและคนมีความใกล้ชิดกันทางวิวัฒนาการ (ศัพท์วิชาการเรียกว่า evolutionary relationship) มากกว่า หนูกับไก่ นั่นเอง

ทีนี้ถ้าคำถามเกิดขึ้นว่า กบมีความสัมพันธ์เชิงวิวัฒนาการใกล้ชิดกับคนหรือปลามากกว่า อันนี้มักจะทำให้มือใหม่หลายคนสับสน เพราะมือใหม่ชอบ จำมาผิดๆ ว่าให้ลากเส้นไปตาม branch จากกบไปหาคน และจากกบไปหาปลา ถ้าทำแบบนี้จะเห็นว่าการลากเส้นไปหาปลาอาศัยวิวัฒนาการเพียงครั้งเดียว แต่การลากเส้นจากกบไปหาคน ต้องผ่าน HTU มากมาย ประกอบกับนักวิจัยมือใหม่ชอบคิดว่า กบกับปลา มีความคล้ายกันทางสัณฐานวิทยามากกว่ากบกับคน ทำให้ตอบคำถามดังกล่าวผิดไปว่า กบกับปลา ต้องมีความใกล้ชิดกันทางวิวัฒนาการมากกว่า เอาเป็นว่า การตอบคำถามนี้ก็ยังคงอาศัยแนวคิดเดิมเรื่องบรรพบุรุษร่วมนะครับ จาก phylogeny เราพบว่า กบมีบรรพบุรุษร่วมกับ HTU3 ซึ่งนั่นเป้นต้นตอบรรพบุรุษของคน แสดงว่าคนกับกบมีบรรพบุรุษร่วมกัน (เมื่อเวลานานมาแล้ว) และในกลุ่มนี้ ไม่มีปลาเข้ามาเกี่ยวข้องเลย ดังนั้นกบจึงมีความสัมพันธ์ใกล้ชิดกันกับคนมากกว่าปลาครับ

ความผิดพลาดที่ผมยกมาเล่าให้ฟังนี้ เกิดจากการอ่น tree ที่ผิดวิธีครับ คนส่วนมาก (ที่อ่านผิด) จะอ่น tree จากปลาย branch ด้านที่มีชื่อสิ่งมีชีวิต ในศัพท์วิชาการเราเรียกว่า อ่านจาก tip ไปยัง root ทำให้เขาชอบไล่นับความยาวของ branch ซึ่งจะก่อให้เกิดความสับสนในการอ่าน tree เยอะมาก ผมจึงอยากจะแนะนำให้อ่าน tree จาก root ไปยัง tip เพื่อที่เราจะได้เห็นว่า HTU ใดที่อยู่ใกล้ root กว่ากัน วิธีการนีจะช่วยให้คุณที่เพิ่งจะจับงานด้าน phylogenetics ไม่หลงไปอ่าน tree ผิดวิธี

นอกจากนี้ผมก็มีประเด็นเล็กๆ อีกอย่างหนึ่งจะขอเตือนให้ระวังเวลาอ่าน tree นะครับ นั่นคือที่ node ทุก node บน tree สามารถหมุนรอบตัวเองได้ โดยไม่ทำให้ความหมายของ tree เปลี่ยนแปลงเลย ในงานตีพิมพ์บางฉบับเขาก็อาจจะใช้วิธีการหมุน node เหล่านี้มาหลอกตาผู้อ่านให้สับสนไปตามๆ กัน ผมเองก็งงหลายครั้งเหมือนกันครับเวลาเจอ tree ขนาดใหญ่ แต่เราจะไม่โดนหลอกจากการหมุน node ครับถ้าเราอ่าน tree จาก root ไปยัง tip

Phylogenetics ช่วยอะไรเราได้บ้าง

สำหรับผู้อ่านที่ไม่ค่อยมีประสบการณ์วิจัยทางด้าน molecular evolution หรือ sequence analysis ก็อาจจะสงสัยว่า molecular phylogenetics มีประโยชน์อะไรกับเราบ้าง ผมจะขอยกตัวอย่างแต่งานเด่นๆ มาเล่าให้ฟังสักงาน สองงานก็แล้วกันครับ เพราะการประยุกต์ใช้ molecular phylogenetics นั้นมีมากมายเหลือเกิน

Comparative Genomics

Comparative genomics คือการเปรียบเทียบ genome ของสิ่งมีชีวิตตั้งแต่ 2 genomes ขึ้นไป เพื่อตอบคำถามหลักๆ ว่า สิ่งมีชีวิตในกลุ่มที่ศึกษา มี orthologous gene อะไรบ้าง ผมจะอธิบายง่ายๆ เพื่อให้เข้าใจคำว่า orthologous gene แบบนี้ครับ สมมติว่าบรรพบุรุษของคนและลิง (เป็นตัวอะไรก็ไม่ทราบ) มี globin gene อยู่ใน genome เมื่อสิ่งมีชีวิตตัวนั้นเกิดการแยกสปีชีส์ (speciation) จนกลายมาเป็นลิงและคน ก็ทำให้ทั้งลิงและคนมี globin gene อยู่ใน genome แต่ globin gene ของทั้งคนและลิงได้ผ่านกระบวนการวิวัฒนาการจนมี sequence ที่แตกต่างกันไป แบบนี้ เราถือว่า globin gene ของคนและลิง เป็น orthologous gene ซึ่งกันและกัน

ในงานวิจัยด้าน comparative genomics นั้น ถ้าเราไม่สามารถระบุได้ว่ายีนใดเป็น orthologous ก็จะทำให้ผลการเปรียบเทียบมีความผิดพลาดและเชื่อถือไม่ได้ ตั้งแต่อดีตจนถึงปัจจุบัน นักวิจัยคงใช้ phylogenetics มาเป็นเครื่องมือในการพิสูจน์ว่ายีนที่สงสัยนั้นเป็น orthologous หรือไม่ แต่เนื่องจากการวิเคราะห์ในลักษณะนี้ทำได้ยาก และใช้เวลานาน นักวิจัยส่วนมากจึงเลือกใช้วิธีอื่นที่รวดเร็วกว่า แต่ก็มีความถูกต้องลดลงกว่า phylogenetic analysis

Forensic Medicine

ในการพิสูจน์หลักฐานทางนิติเวชนั้น หลายกรณีที่ใช้ phylogenetic เข้ามาเป็นเครื่องมือ ผมชอบตัวอย่างการพิสูจน์หลักฐานกรณีหนึ่ง จะเรียกว่าเป็น classic case ก็ได้ นั่นเป็นการพิสูจน์หาผู้ติดเชื้อ HIV ซึ่งเป็นคนไข้ของทันตแพทย์ผู้หนึ่งในสหรัฐอเมริกา งานนี้ทันตแพทย์เป็นเอดส์และคนไข้บางคนก็ได้รับเชื้อเอดส์มาจากทันตแพทย์ในขณะที่คนไข้บางคนของทันตแพทย์ผู้นั้นได้รับเชื้อ HIV มาจากผู้อื่น จากการใช้ความรู้ทาง phylogenetics เข้ามาวิเคราะห์ DNA sequence ในส่วน V3 ของเชื้อไวรัส HIV ก็สามารถบอกได้ว่าคนไข้รายใดได้รับเชื้อมาจากทันตแพทย์

เป็นอย่างไรบ้างครับ กับการใช้ความรู้ทาง molecular phylogenetics เข้ามาทำความเข้าใจกับความสัมพันธ์เชิงวิวัฒนาการของสิ่งมีชีวิต ความจริงผมมีเรื่องราวเกี่ยวกับ phylogenetics เยอะมากครับ ถ้ามีโอกาสคงจะได้ทะยอยนำมาเขียนให้เพื่อนๆ ได้อ่านกันในโอกาสถัดๆ ไปครับ แต่หลังจากที่ได้ทำความรู้จักกับ phylogenetics ครั้งนี้แล้วคุณผู้อ่านคงจะไม่หลงกล phylogenetic tree ที่ยากๆ อีกแล้วนะครับ และผมเชื่อว่าถ้าคุณอ่าน tree ได้อย่างถูกต้องแล้ว บทความวิชาการทาง phylogenetics ก็กลายเป็นเรื่องสนุกไปอย่างแน่นอนครับ

Python Programing

Reverse complement สาย DNA ด้วย Python (ภาคจบ)


ประพัฒน์ สุริยผล


ภาพยนตร์ส่วนใหญ่มักจะจบกันที่ไตรภาค แต่ reverse complement ของเราผ่านไปแล้ว 3 ภาคยังจบไม่ลงสักที เราก็จะมาจบกันที่ครั้งนี้ครับ หวังว่าภาคจบนี้ เราคงได้เรียนรู้อะไรกันอีกพอสมควร


Reverse sequence revisited


ผมมีจังหวะแวะเข้าไปดู blog ของ THAI Bioinformatics e-magazine แล้วพบว่ามี comment มาจากคุณ Tanawut แนะนำให้ผมใช้ trick หนึ่งของ Python ซึ่งผมก็ลืมนึกถึงไปเลย จึงขออนุญาตเอามาลงในครั้งนี้ด้วยครับ


code สุดท้ายที่เราเขียนไว้สำหรับ reverse_sequence เป็นดังด้านล่าง

def reverse_sequence(dna):

____return ''.join(reversed(list(dna)))

แต่ code ที่คุณ Tanawut แนะนำมาจะเป็น


def reverse_sequence(dna):

____return dna[::-1]

สั้นลงไปอีก แต่อาจจะสับสนเล็กน้อย สำหรับคนที่ไม่คุ้นเคยกับ Python ตัวผมเองคิดว่า code นี้สั้นและเป็นสไตล์ Python แท้ๆ เลยครับ โดยใช้คุณสมบัติของ string ใน Python ที่สามารถมองเป็น list ได้ ต้องขอบคุณคุณ Tanawut มากครับ


Slicing


เรามาทำความเข้าใจเรื่อง slicing กันเล็กน้อย จากตัวอย่างครับ


>>> a = ['a', 'b', 'c', 'd']

>>> a

['a', 'b', 'c', 'd']

>>> a[0:2]

['a', 'b']

>>> a[:2]

['a', 'b']

>>> a[2:4]

['c', 'd']

>>>a[2:]

['c', 'd']


การ slicing คือการดึงข้อมูลของ list ในส่วนที่เราต้องการ จากตัวอย่าง เราสร้างตัวแปร a ให้เป็น list ของตัวอักษร เมื่อเราต้องการใช้ slicing ให้เรากำหนดตำแหน่งที่เราต้องการภายในวงเล็บแบบสี่เหลี่ยม [] โดยกำหนดตำแหน่งเริ่มต้น และตำแหน่งสุดท้าย


อย่างเช่น a[0:2] แปลว่า เราต้องการ slicing ที่ตำแหน่ง 0 (ตำแหน่งแรกสุด) ไปจนถึงก่อนตำแหน่งที่ 2 (ก็คือตัว 'c' เพราะเรานับจาก 0) สิ่งเราได้มาก็จะเป็น list ของ ['a', 'b']


ถ้าหากเราละไว้ ไม่ใส่ตัวเลขที่ตำแหน่งเริ่มต้น ค่าปกติก็จะเป็น 0 เพราะฉะนั้น คำสั่ง a[:2] จึงให้คำตอบเหมือนกัน a[0:2] อ่านว่า เราต้องการ slicing list ตั้งแต่ตำแหน่งเริ่มต้นถึงก่อนตำแหน่งที่ 2


ในทำนองเดียวกัน เราสามารถละตำแหน่งสุดท้ายได้ ซึ่งจะเป็นการบอกว่า เราต้องการ slicing ไปจนถึงตัวสุดท้ายของ list เพราะฉะนั้น ค่าของ a[2:4] และ a[2:] จึงเหมือนกัน


ผู้อ่านบางท่านอาจจะนึกสงสัยว่า แล้วถ้าเราละตัวเลขทั้งสองตำแหน่งล่ะ จะเป็นอย่างไร คำตอบก็คือเราจะได้ copy ของ list ตัวนั้น ซึ่งจะไม่ใช่ list ตัวเดียวกันแต่มีค่าเหมือนกัน งงใช่ไหมครับ วิธีนี้เป็น trick ที่มีประโยชน์และช่วยให้เข้าใจเรื่องของ list ได้ดีทีเดียว ลองดูตัวอย่างกันต่อนะครับ


>>> a = ['a', 'b', 'c', 'd']

>>> b = a

>>> c = a[:]

>>> a

['a', 'b', 'c', 'd']

>>> b

['a', 'b', 'c', 'd']

>>> c

['a', 'b', 'c', 'd']

จากตัวอย่าง จะเห็นว่าตัวแปร a, b, c มีค่าเหมือนกันหมดถูกไหมครับ แต่เราสร้างตัวแปร b กับ c คนละแบบกัน


ตัวแปร b เรากำหนดให้เท่ากับตัวแปร a เลย แต่ตัวแปร c เราให้เป็น copy ของตัวแปร a


ให้ลองนึกว่า list ก็คือกล่องใบนึงนะครับ เรามีกล่องที่แปะชื่อเอาไว้ว่าชื่อ a ในกล่อง a มีลูกบอลอยู่ 4 ลูก เขียนไว้ว่า a, b, c และ d


พอเรากำหนดให้ b = a ก็เหมือนกับว่า เราเอาชื่อ b ไปแปะไว้ที่กล่องเดิมนั่นแหละครับ กล่องนั้นจึงมีชื่อทั้ง a และ b โดยที่ลูกบอลในกล่องก็เป็นชุดเดียวกัน


แต่ถ้าเรากำหนดให้ c = a[:] อันนี้ เหมือนกับว่า เราหากล่องมาอีกกล่องครับ ข้างในมีลูกบอล 4 ลูก เขียนว่า a, b, c และ d เช่นกัน และกล่องนี้แปะชื่อว่า c ถ้าดูที่ลูกบอลในกล่องจะพบว่าทั้งกล่อง a, b และ c เหมือนกันหมด


ความแตกต่างจะอยู่ตรงนี้ครับ ถ้าเราเปลี่ยนแปลงของในกล่อง a ของในกล่อง b ก็จะเปลี่ยนไปด้วย เพราะมันคือกล่องใบเดียวกัน นึกออกไหมครับ ในขณะที่ของในกล่อง c ไม่ถูกกระทบไปด้วย ดูตัวอย่างนะครับ


>>> a[0] = 'e'

>>> a

['e', 'b', 'c', 'd']

>>> b

['e', 'b', 'c', 'd']

>>> c

['a', 'b', 'c', 'd']

เรื่องนี้สำคัญนะครับ โปรแกรมทำงานผิดพลาดมีสาเหตุสำคัญจากการไม่เข้าใจเรื่องของ list ในลักษณะกล่องนี่ล่ะครับ ทำให้เราเปลี่ยนค่าไปโดยที่เราไม่ตั้งใจ


ทุกครั้งที่เราเปลี่ยนค่าในกล่อง ไม่ว่าจะเป็นตัวแปร a หรือ b จะส่งผลกระกบทั้งคู่ เพราะทั้งสองชื่อหมายถึงกล่องเดียวกัน เราสามารถแยกไม่ให้มีผลกระทบได้ ก็คือใช้วิธี copy กล่องแบบที่เราทำกับตัวแปร c ครับ เราจะมาลองกับตัวแปร b กันอีกทีครับ


>>> b = a[:]

>>> a[-1] = 'f'

>>> a

['e', 'b', 'c', 'f']

>>> b

['e', 'b', 'c', 'd']

>>> c

['a', 'b', 'c', 'd']

เราเริ่มต้นด้วยคำสั่ง b = a[:] เท่ากับว่าเราเอากล่องมาใหม่อีกหนึ่งใบ ข้างในมีลูกบอลเหมือนที่ a มีอยู่ในขณะนี้ และแกะชื่อ b ที่เดิมแปะอยู่กับกล่องเดียวกันกับ a มาแปะเอาไว้ที่กล่องใหม่นี้ เท่ากับว่าตอนนี้เรามี 3 กล่องที่ไม่เกี่ยวข้องกันแล้ว จากนั้น เราลองเปลี่ยนค่า item ตัวสุดท้ายของตัวแปร a โดยเรากำหนดค่า index เป็น -1 หมายถึง นับจากข้างหลังครับ -1 หมายถึงตัวสุดท้าย -2 หมายถึงตัวก่อนสุดท้าย ไล่ไปเรื่อย นึกออกไหมครับ หลายครั้งการใช้ index ในลักษณะนี้ก็จะทำให้เราสะดวกมากขึ้น เพราะเราไม่ต้องรู้ว่า list นี้ยาวเท่าไหร่ ตัวสุดท้ายอยู่ตำแหน่งอะไร


เรามาลองดูค่าสุดท้ายของตัวแปรแต่ละตัวกันครับ ตัวแปร a ก็จะเป็นตัวแปรที่มีการเปลี่ยนแปลงทั้งตัวแรกและตัวสุดท้าย ตัวแปร b มีแค่ตัวแรกที่เป็น 'e' เพราะว่าเรา copy จากตัวแปร a หลังจากที่เราเปลี่ยนตัวแรกไปแล้ว ส่วนตัวแปร c ไม่มีการเปลี่ยนแปลงใดๆ


เรามาถึง trick สุดท้ายของการทำ slicing ครับ คือนอกจากเรากำหนดว่าเราจะ slicing จากไหนถึงไหนได้แล้ว เรายังกำหนด step ในการอ่านค่า slicing ได้ด้วย งง อีกแล้วครับ ดูตัวอย่างดีกว่า


>>> c[0:2:1]

['a', 'b']

>>> c[2:0:-1]

['c', 'b']

>>> c[::-1]

['d', 'c', 'b', 'a']

>>> c[::2]

['a', 'c']

>>> c[::-2]

['d', 'b']

ผมเลือกใช้ตัวแปร c นะครับ เพราะค่าเรียงจาก a ไป d ดูตามง่ายกว่า


คำสั่งแรก c[0:2:1] จะให้ผลไม่ต่างจาก c[0:2] ที่เราเคยเรียกใช้กัน เพราะว่าค่าปกติของ step ก็คือ 1 อยู่แล้ว Python ก็จะ slicing จากตำแหน่งที่ 0 แล้วก็เพิ่มทีละ 1 ไปจนถึงก่อนตำแหน่งสุดท้ายที่กำหนด


มาดูที่คำสั่งถัดไป c[2:0:-1] ครั้งนี้ เรากำหนด step เป็น -1 (ตัวเลขสุดท้ายหลังโคลอน) โดยเริ่มต้น slicing ที่ตำแหน่งที่ 2 คือตัว 'c' ด้วย step ติดลบ ตำแหน่งถัดไปจึงเป็น 1 ก็คือตัว 'b' ตำแหน่งถัดไปคือ 0 แต่ว่า slicing จะดึงค่ามาจนถึงก่อนตำแหน่งสุดท้าย จึงไม่รวมตำแหน่งที่ 0 มา คำตอบที่ได้จึงเป็น ['c', 'b']


ถ้าหากเราละค่าตำแหน่ง เช่นเรียกใช้ c[::1] ก็จะไม่ต่างอะไรจาก c[:] เพราะค่า step 1 เป็นค่าปรกติอยู่แล้ว แต่ว่าถ้าเราใช้ c[::-1] เราก็จะได้ copy ของ list ที่ reverse เพราะจะวิ่งจากตัวสุดท้ายย้อนมาตัวหน้าสุด คุณสมบัติอันนี้ น่าสนใจมากครับ


เราสามารถกำหนด step ที่ไม่ใช้ 1 และ -1 ได้ ดังตัวอย่างครับ ผมให้เป็นหน้าที่ของท่านผู้อ่านที่จะลองไล่ดูนะครับว่า ถ้า step เป็น 2 และ -2 แล้วได้คำตอบดังตัวอย่างได้อย่างไร


เกริ่นมายาวมากเลย เพื่อมาปิดท้ายที่การใช้ string ในลักษณะของ list ผู้อ่านจะเห็นว่าเราสามารถใช้ slicing กับ string เช่นกัน และผลลัพธ์ที่ได้ ก็จะถูกแปลงกลับมาให้เป็น string เหมือนเดิม สะดวกมากครับ ดังตัวอย่าง


>>> b = 'abcd'

>>> b[0:2]

'ab'

>>> b[:2]

'ab'

>>> b[2:4]

'cd'

>>> b[2:]

'cd'

เพราะฉะนั้น ถ้าเราต้องการ reverse ตัวแปร string เราทำอย่างไรครับ ก็เพียงแค่ใช้คำสั่งแบบที่คุณ Tanawut แนะนำครับ


reverse_string = b[::-1]

เรามาต่อกันที่ function complement ที่ครั้งที่แล้วเราปรับปรุงด้วยการใช้ dictionary ช่วย ซึ่งโปรแกรมครั้งที่แล้วมีจุดบอดอยู่สองสามแห่ง หนึ่งคือ key ของ dictionary ที่เราใช้เป็นเบสตัวเล็กทั้งหมด ถ้าหากเราได้ข้อมูลสาย DNA เป็นเบสตัวใหญ่ เช่น ATGC แทนที่จะเป็น atgc โปรแกรมของเราก็จะทำงานผิดพลาด อันที่สองคืออาจจะมีเบสแปลกๆ เข้ามาได้เช่น N ซึ่งโปรแกรมของเราไม่รองรับ สามคือถ้าหากเราต้องการคงสภาพตัวเล็กตัวใหญ่ไว้ เช่นถ้าเบสเข้ามาเป็น A เราก็ต้องได้ค่า complement เป็น T แต่ถ้าเข้ามาเป็น a ให้คืนค่าเป็น t เราก็ต้องมาปรับโปรแกรมกันพอสมควร จากของเดิม


def complement(dna):

____complementary_dict = {'a':'t', 't':'a', 'g':'c', 'c':'g'}

____result = ''

____for base in dna:

________result = result + complementary_dict[base]

____return result


เราต้องเพิ่ม complementary_dict เป็นดังนี้ครับ

complementary_dict = {'a':'t', 't':'a', 'g':'c',\

______________________'c':'g', 'A':'T', 'T':'A',\

______________________'G':'C', 'C':'G', 'n':'n',\

______________________'N':'N'}


ถ้าเป็นเบสตัวอื่นนอกเหนือจากนี้ โปรแกรมของเราก็จะหยุดทำงาน พร้อมกับรายงานว่าไม่มี key ตัวนั้นอยู่ ผมขอปล่อยให้เป็นแบบนั้นก็แล้วกันนะครับ เพราะปกติเราก็พบเบสเพียงแค่เท่านี้ ถ้าหากมีอะไรแปลกปลอมเข้ามา โปรแกรมก็จะโวยวายมาให้เราทราบ


ตอนนี้เรามาลองดูว่า นอกจาก dictionary สำหรับใช้ในงานนี้แล้ว Python มีอะไรให้เราใช้อีกบ้าง เนื่องจากเราต้องการเปลี่ยนค่าใน string เมื่อไปดูคู่มือ Python พบว่า Python เตรียมคำสั่ง translate เราไว้ให้เราใช้แล้ว


>>> a = 'aabbccdd'

>>> import string

>>> table = string.maketrans('ab', 'ef')

>>> a.translate(table)

'eeffccdd'

การใช้งาน เริ่มต้นจากการเตรียมตารางสำหรับแปลงค่า วิธีง่ายที่สุดคือเรียกใช้คำสั่ง maketrans ใน module string โดยเรา import module string เข้ามา แล้วเรียกใช้ดังตัวอย่าง พารามิเตอร์ที่เราส่งไปให้คำสั่ง maketrans จะมีสองค่า ค่าแรกจะเป็นค่าเริ่มต้น ค่าที่สองจะเป็นค่าที่ต้องการเปลี่ยน ความยาวของทั้งสองค่าต้องเท่ากัน เพราะ maketrans จะจับคู่ไล่ไป ดังเช่นในตัวอย่าง เราต้องการจะเปลี่ยนจาก a เป็น e และ b เป็น f จึงส่งค่า 'ab' และ 'ef' ไป


จากนั้น เราสามารถเรียกใช้คำสั่ง translate ได้เลย ส่วนตัวอักษรตัวอื่นที่ไม่ได้อยู่ในส่วนที่เราต้องการให้เปลี่ยนแปลงก็จะคงอยู่เหมือนเดิม


ดังนั้น ถ้าเราต้องการนำคำสั่ง translate มาใช้กับ function complement ของเราบ้าง เราก็จะได้ดังนี้


import string

complement_table = string.maketrans('atgcATGC', 'tacgTACG')

def complement(dna):

____return dna.translate(complement_table)


ผมแยกบรรทัดที่ import กับบรรทัดที่สร้างตัวแปร complement_table ออกมาข้างนอก เพราะถ้าเราใส่ไว้ใน function จะถูกเรียกใช้ทุกครั้งที่เราเรียกใช้ function complement โดยอันที่จริงแล้ว เรา import ครั้งเดียวก็พอ และตัวแปร complement_table ก็ตายตัว สร้างครั้งเดียวแล้วก็ไม่เปลี่ยนแปลง สามารถนำไปใช้ได้เรื่อยๆ ผมจึงแยกออกมา


function complement อันนี้ ต่างจากอันที่ใช้ dictionary คือมันจะไม่โวยวายถ้าได้ค่าเบสที่มันไม่รู้จัก เพราะคำสั่ง translate จะคืนค่าเดิมกลับมา เช่นในตารางเราไม่ได้ใส่เบส N เพราะว่าค่าที่คืนกลับมาเป็นค่าเดิม


สุดท้ายแล้ว โปรแกรม reverse complement ของเราจะหน้าตาเป็นแบบนี้ครับ


import string

COMPLEMENT_TABLE = string.maketrans('atgcATGC', 'tacgTACG')

def complement(dna):

____return dna.translate(COMPLEMENT_TABLE)


def reverse_sequence(dna):

____return dna[::-1]

dna = raw_input('Please enter DNA sequence -> ')

reversed_dna = reverse_sequence(dna)

reverse_complement_dna = complement(reversed_dna)

print reverse_complement_dna

ถ้าไปเปรียบเทียบกับครั้งแรกที่เราเขียน จะเห็นว่าโปรแกรมสั้นลงไปมาก และอ่านเข้าใจได้ง่ายขึ้นด้วย แต่ถ้าอยากให้สั้นกว่านี้อีก เราก็ตัดนิยาม function ทิ้ง แล้วเรียกใช้แบบ chain of function ได้ตามสะดวกครับ


import string

COMPLEMENT_TABLE = string.maketrans('atgcATGC', 'tacgTACG')

dna = raw_input('Please enter DNA sequence -> ')

print dna.translate(COMPLEMENT_TABLE)[::-1]

เหลือ 4 บรรทัดครับ และยังคงอ่านได้ไม่ยากนัก Python ก็ดีอย่างนี้นี่เอง แล้วพบกันใหม่ฉบับหน้าครับ