- สามารถกด shift ค้างไว้แล้วลากขอบ Range
- สามารถลากที่หัวตารางของ Table ได้เลย
- สามารถใช้ Advanced Filter มาช่วยได้
รายละเอียดดูได้ในคลิปครับ

รายละเอียดดูได้ในคลิปครับ

บทความนี้จะสอนคุณใช้ FREQUENCY มาสร้างตารางแจกแจงความถี่ ซึ่งเป็นวิธีที่ยืดหยุ่นและสะดวกที่สุดวิธีนึงเลยล่ะ
เวลาที่เรามีข้อมูลแล้วอยากจะทำตารางแจกแจงความถี่ จริงๆ แล้วก็มีวิธีที่ทำได้ก็มีหลายแบบ เช่น ใช้ Pivot Table Group เอา แต่ก็มีข้อเสียคือแต่ละช่วง ( bin )ต้องมีขนาดเท่ากัน หากจะทำแบบ Bin ไม่เท่ากัน ก็อาจจะใช้ VLOOKUP Approximate Match มาช่วยจัดกลุ่มก่อนที่จะนับ ซึ่งก็ยุ่งยากอีก ดังนั้น Excel จึงมีฟังก์ชันที่เอาไว้จัดการเรื่องนี้โดยเฉพาะ นั่นก็คือ FREQUENCY นั่นเอง
สมมติว่าเรามีข้อมูลความสูงตัวละครใน Dragon Ball ตามไฟล์นี้
เราสามารถใช้ฟังก์ชัน FREQUENCY มาช่วยได้ ซึ่งมีวิธีใช้ดังนี้
=FREQUENCY(data_array,bins_array)
หากใช้ Excel 365 ที่รองรับ Dynamic Array เราสามารถใช้ฟังก์ชัน FREQUENCY นี้ได้ง่ายๆ ตามการเขียนสูตรปกติเลย มันจะงอกผลลัพธ์ออกมาให้มีขนาดสูงกว่า bin_array 1 ช่องโดยอัตโนมัติ (ผลลัพธ์มันล้ำไป 1 ช่อง นี่แหละถึงบอกว่าเป็นฟังก์ชันล้ำที่สุด)

แต่ถ้าใช้ Excel versionเก่ากว่านั้น เราจะต้องลากคลุมพื้นที่ผลลัพธ์ให้สูงกว่า bin_array 1 ช่อง แล้วคอ่นเขียนสูตร FREQUENCY แล้วพอเขียนสูตรเสร็จต้องกดปุ่ม Ctrl+Shift+Enter ด้วย เพื่อบอก Excel ว่าจะเขียนสูตรแบบ Array Formula
ซึ่งพอมีตารางแจกแจงความถี่แล้ว เราก็สามารถสร้าง Histogram ได้ง่ายๆ ได้ด้วย Column Chart ธรรมดาๆ (แต่ปรับ Gap Width เป็น 0% ) แล้วล่ะครับ (แม้ว่าใน Excel version ใหม่ๆ จะสร้าง Histogram ได้จาก Data ดิบทันทีเลยก็เถอะ… )

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

หาก bin_array ซ้ำกัน มันจะไม่นับแล้ว ซึ่งจะได้ 0 ทันที (เพราะมันจะไม่นับซ้ำเด็ดขาด)
สังเกตว่า การที่เลข 150 หายไป ส่งผลให้ bin ช่วงถัดไปก็อาจจะมีขนาดเปลี่ยนไปได้นะครับ (เดิม >150 – 160 กลายเป็น >140 – 160) แต่เป็นคนละประเด็นกับเลขซ้ำนะ

ด้วยเหตุนี้เอง จึงมีการนำ FREQUENCY ไปประยุกต์ใช้อะไรพิสดารหลายอย่างเลย เช่น
หากเราใส่ bin_array ให้เป็นตัวเดียวกับ data_array ไปเลย ผลที่ได้จะเป็นการนับจำนวนความถี่ของแต่ละตัว ซึ่งถ้าข้อมูลไม่ซ้ำกันเลยก็จะได้เลข 1 เหมือนกันหมด แต่ถ้ามีซ้ำ ตัวนั้นที่โผล่มาอันแรกก็จะนับได้มากกว่า 1 และตัวที่โผล่มาทีหลังจะนับเป็น 0 ไปโดยปริยาย
=FREQUENCY(B2:B51,B2:B51)

ดังนั้นถ้าเราใส่เงื่อนไขให้สนใจเฉพาะตัวที่มากกว่า 0 ให้เป็น TRUE แล้วก็แปลงเป็นเลข 1 ซะด้วยการคูณ1 แล้วทำการ SUM ด้วย SUMPRODUCT หรือ SUM เราก็จะได้จำนวนข้อมูลที่ไม่ซ้ำกันนั่นเอง
=SUMPRODUCT((FREQUENCY(B2:B51,B2:B51)>0)*1)
ซึ่งใน Data จะนับได้ 41 ตัวนั่นเองครับ
บทความนี้ขอแนะนำเท่านี้ก่อน ไว้เดี๋ยวจะแสดงตัวอย่างที่ซับซ้อนขึ้นอีกทีนะครับ เช่น นับจำนวนวันที่เครื่องจักรทำงานได้ต่อเนื่องสูงสุดโดยที่ไม่เสียเลย เป็นต้น

หากเราจะกรอกข้อมูลเวลา 7 โมง 45 นาที ในตอนเช้า เราจะต้องกรอกว่า 7:45 โดยใช้เครื่องหมาย : (Colon) ห้ามกรอกว่า 7.45 ที่ใช้เครื่องหมาย . (dot) เด็ดขาด เพราะมันเป็นคนละเรื่องกันโดยสิ้นเชิง
การเขียน 7:45 หากปรับ Number Format ให้เป็น general จะเห็นว่าค่าที่แท้จริงของเลขนั้นคือ 0.3229 กว่าๆ ไม่ใช่ 7.45 แน่นอน (ใครงงว่าทำไม ไปอ่านได้ที่นี่)

ซึ่งเลข 0.3229 นั้นก็คือเป็นการบอกว่าเวลา 7:45 คือ 0.3229 วันนั่นเอง (เวลาคือการบอกรายละเอียดว่าเป็นสัดส่วนเท่าไหร่ของ 1 วัน)
ตอนนี้เรารู้แล้วล่ะว่าควรจะกรอกด้วยเครื่องหมาย : ไม่ใช่จุด แต่ถ้าดันกรอกผิดไปแล้วตั้งเยอะ หรือมีคนกรอกมาให้อีกทีจะทำไงดีล่ะ?
วิธีแก้โดยเบื้องต้น คือ เราจะต้องเปลี่ยนเครื่องหมาย . ให้เป็น : ให้ได้ ซึ่งเราจะใช้ฟังก์ชัน SUBSTITUTE ในการทำ เช่น

อย่างไรก็ตามผลลัพธ์ของ SUBSTITUTE จะออกมาเป็น Text ดังนั้นเราต้องบังคับให้เป้นตัวเลขซะ ด้วยการ *1 เช่น

หลังจากนั้นก็เปลี่ยน Format ให้เป็นเวลาซะ

ผลลัพธ์ก็ออกมาเป็นเวลาที่ถูกต้องแล้ว
อย่างไรก็ตามสูตรแบบนี้มันยังใช้ไม่ได้กับทุกกรณี
สมมติว่าเป็นเวลา 7:30 แล้วดันกรอกมาเป็น 7.30 จะเกิดอะไรขึ้น?

เลข 7.30 จะกลายเป็น 7.3 (เพราะ 0 หลังทศนิยมไม่มีควาหมาย) ทำให้เวลาที่ออกมากลายเป็น 7 โมง 3 นาทีไปซะอย่างงั้น!
ทางแก้ไขคือ ต้องใช้ฟังก์ชัน TEXT บังคับเปลี่ยนเลขให้เป็น Text ที่มีทศนิยม 2 ตำแหน่งซะก่อนที่จะ SUBSTITUTE จุดเป็น :
นั่นคือ จาก A1 ธรรมดา จะกลายเป็น TEXT(A1,”0.00″)
ทำให้สูตรสุดท้ายเป็นดังนี้
=SUBSTITUTE(TEXT(A1,"0.00"),".",":")*1
สูตรนี้จึงจะสมบูรณ์ ใช้ได้กับเลขทุกตัวครับ


สมมติเล่นเกม จาก 50 ครั้ง ชนะ 30 ครั้ง จะคำนวณว่าชนะกี่ % เราควรทำแบบใด ระหว่าง
30/50 * 100 = 60
กับ
30/50 = 0.6 แล้วกดเป็น % ได้ 60%

เว็บไซต์เทพเอ็กเซลแห่งนี้ก็เป็นเว็บเชิง Content ที่มีคนใช้งานค่อนข้างเยอะ (ล่ะมั้๊ง) ถ้าดูจาก Google Analytics ก็จะเห็นว่าปีนึงมีคนเข้าเกินล้านคน และกำลังเพิ่มขึ้นเรื่อยๆ ขณะที่เขียนบทความนี้คือกลางเดือน May 2020 อยู่ระดับที่ Week ละประมาณเกือบ 5 หมื่นคน (ถ้าครบปีก็จะประมาณ 2 ล้านกว่าคนต่อปีอย่างแน่นอน) ซึ่งผมจะลองเอามาวิเคราะห์ให้ดูว่ามีอะไรที่น่าสนใจบ้าง


ถ้านับในรอบปีที่ผ่านมา ก็มีล้านกว่า User ส่วนเพจวิวก็จะเยอะเป็น 2 เท่า ก็คือประมาณ 2 ล้านกว่าวิวเลย ซึ่งค่อนข้างเยอะเลยล่ะ

ช่องทางที่คนเข้ามา มาจากการ Search Google เป็นหลักเลย (เกิน 80%) รองลงมาคือ Facebook นี่แหละ

คนที่เข้ามาดูส่วนใหญ่ Google บอกว่าอายุ 25-34 ปี และเป็นผู้หญิงมากกว่าผู้ชายเล็กน้อย

เว็บของผมค่อนข้างแปลกกว่าเว็บทั่วไปตรงที่คนมาดูส่วนใหญ่ยังใช้ Desktop มากกว่า Mobile อยู่พอสมควรเลย ในขณะที่เว็บอื่นๆ ใช้ Mobile กันมากกว่าแล้วมั๊ง อาจเป็นเพราะ Excel เป็นอะไรที่คนทำงานใน Desktop แล้วติดปัญหาก็เลย Search ณ ตอนนั้น

ช่วงเวลาที่คนใช้งานส่วนใหญ่จะเป็นเวลาทำงานนั่นแหละ นี่ก็ยืนยันว่าส่วนใหญ่มีปัญหาปุ๊ปก็คง Search ใน Google มาเจอเว็บผมนั่นเอง

แน่นอนว่าประเทศที่เข้ามาดูเว็บเทพเอ็กเซลมากที่สุดก็ไม่พ้นประเทศไทยแน่นอน(ตั้ง 95%) และที่มาจากต่างประเทศไกลๆ ก็คงเป็นคนไทยที่อยู่ต่างแดนด้วย (เพราะดูจาก Language มีหลายส่วนตั้งเป็น Thai) ยกเว้นแบบประเทศลาวที่อ่านไทยออกได้เลยอันนี้ไม่แปลก

ถ้าเจาะมาที่ประเทศไทย นี่คือจังหวัด Top 10 ครับ มีจังหวัดของเพื่อนๆ อยู่มั้ย? (กทม. เพียบเป็นเรื่องปกติ คงไม่น่าสนใจเท่าจังหวัดอื่น)
ส่วนบทความที่คนเข้ามาดูเยอะสุดคืออันนี้ครับ เป็นเรื่องเทคนิคที่ทำให้ทำงานเร็วขึ้น ใครสนใจบทความยอดนิยมก็ลองเข้าไปดูได้ที่นี่

นื่องจากการที่คนที่ใช้งานส่วนใหญ่อยู่ในประเทศไทย ดังนั้นการเลือก Hosting ไทย ก็น่าจะได้ผลลัพธ์เรื่องความเร็วดีกว่า Hosting เมืองนอก
การหา Hosting ที่รองรับเว็บที่มีคนเข้าเยอะ และยังคงต้องได้ความรวดเร็ว แถมราคาคุ้มค่า ไม่แพง จึงเป็นเรื่องสำคัญ
แต่เดิมผมใช้ Hosting เจ้านึงอยู่มาเป็นเวลานาน ข้อดีคือเค้า Support ปัญหาต่างๆ ได้รวดเร็ว แต่มีข้อเสียสำคัญคือ มีการจำกัด Bandwidth หรือปริมาณรับส่งข้อมูลของเว็บอยู่พอสมควร ทำให้ผมต้องคอย Upgrade Plan อยู่เรื่อยๆ ซึ่งมาถึงจุดที่ผมกำลังจะต้อง Upgrade Plan เป็นอันสุดท้ายของ Host เจ้านั้น ซึ่งราคาค่อนข้างแพงมาก คือปีละหมื่นบาท แต่ก็ยังจำกัด Bandwidth อยู่ที่เดือนละ 600 GB อยู่ดี ซึ่ง ณ ปัจจุบันผ่านไปครึ่งเดือนผมก็ 300 GB ไปแล้ว ดังนั้นถึงผม Upgrade ไปก็คงไม่ช่วยอะไรมาก ดังนั้นก็เลยลองหา Hosting อื่นดูบ้าง
คำถามว่า “Hosting ไทยที่ไหนดี?” คงจะเป็นคำถามที่คนอยากหา Hosting อยากรู้ ผมก็ลองหาดูหลายๆ ที่ สุดท้ายก็มาจบที่ Hosting อันนึงชื่อว่า Host Atom ซึ่งมี Plan ที่น่าสนใจหลายรูปแบบให้เลือก
(*ในบทความนี้มี Link Affiliate ซึ่งหากคุณสนใจคลิ๊ก Link แล้วทำการซื้อ Hosting จาก Host Atom ผมก็จะได้ส่วนแบ่งรายได้ด้วย แต่คุณไม่ได้เสียอะไรนะครับ ซึ่งผมแนะนำตามความจริงเพราะตอนนี้ผมก็ใช้อยู่เหมือนกัน)
สุดท้ายผมก็ได้เลือก Plan ที่คิดว่าเหมาะสำหรับเว็บผมที่สุด ก็คือ WordPress Plan เนื่องจากเว็บผมเองใช้ WordPress อยู่ด้วย และราคาถูกกว่า Hosting แบบอื่นมากเลย แค่จำกัดเรื่องจำนวน Visitor ต่อเดือนแทนเรื่อง Bandwidth ซึ่งตัวเลข Visitor ที่ให้ค่อนข้างสูงเลยล่ะ

สุดท้ายผมใช้ Plan WP-2 ราคาอยู่ที่ 2490/ ปี ในขณะที่ Host เดิมผมจ่ายเกือบ 6000 บาทต่อปีและถูกจำกัด Bandwidth ที่ 300GB/เดือน แปลว่าผมจ่ายถูกลง 2 เท่าเลย แถมได้เว็บที่เร็วเหมือนเดิม และรองรับ Bandwidth ไม่จำกัด แถมที่ดูเหมือนจะจำกัดจำนวนคนต่อเดือน เค้าบอกว่าหากมีการปรับจูนที่ดีก็ยังใช้เกินได้เลยด้วยซ้ำ (แต่ถึงจะจำกัด ผมก็ยังไม่ถึงอยู่ดี กว่าจะถึงก็อีกนานพอควร)
นอกจากนี้เค้ายังมี Plan แบบอื่นอีกสำหรับกรณีที่อาจไม่ได้ใช้พวก WordPress เช่น

เท่าที่ลองใช้ดู ความเร็วก็ดี ราคาก็ถูกลง ตอบคำถามก็เร็ว ยังไม่มีปัญหาอะไร สรุปได้คำเดียวว่า “คุ้มสุดๆ” ก็เลยอยากจะมาแนะนำให้เพื่อนๆ เผื่อใครสนใจก็ไปลองดูรายละเอียดได้ใน Link นี้ครับ

คลิปสั้นๆ ง่ายๆ อันนี้จะมาสอนการ Random แบบต่างๆ ใน Excel และการประยุกต์ใช้นะครับ
คุณจะได้เรียนรู้ทั้ง RANDBETWEEN, RAND และ RANDARRAY เลยล่ะ

ในคลิปนี้เราจะเอาความสูงของตัวละครต่างๆ ในการ์ตูนสุดฮิตอย่างดราก้อนบอล (Dragon Ball) มาช่วยให้เราเรียนรู้เกี่ยวกับฟังก์ชัน IF ใน Excel กันครับ คลิปสั้นๆ แต่ดูจบได้ความรู้เต็มเปี่ยมแน่นอน ดูจบใช้เป็นเลย
คุณสามารถโหลดไฟล์ประกอบได้ที่ https://github.com/ThepExcel/download/blob/master/DBZ.xlsx
ถ้าชอบคลิปนี้ ช่วยกด Like / Subscribe / ช่วยแชร์ ให้ด้วยนะครับ

ตัวอย่างการวิเคราะห์ข้อมูลแบบง่ายๆ จากเกมสุดฮิตในเครื่อง Nintendo Switch อย่าง Animal Crossing : New Horizons ครับ คุณจะได้เรียนรู้ทั้งเรื่องของ Pivot Table, SUMIFS, COUNTIFS, การกำจัดตัวซ้ำ, การใช้สูตรแบบ Dynamic Array เลยล่ะ
สามารถโหลดไฟล์ประกอบได้ที่นี่ https://www.kaggle.com/jessemostipak/animal-crossing?select=items.csv

ผมเคยเขียนบทความเกี่ยวกับ Array Formula เมื่อหลายปีมาแล้ว และนี่ก็คงถึงเวลาที่ต้อง Refresh เนื้อหาใหม่ซักหน่อย เพราะใน Excel ยุคใหม่อย่าง Excel 365 ที่รองรับ Dynamic Array นั้นทำให้การเขียนสูตรแบบ Array ง่ายขึ้นและเจ๋งขึ้นมากๆ เลย
สำหรับคนที่ยังไม่รู้จักว่า Array Formula คืออะไร ผมก็คงจะต้องอธิบายก่อนแหละ ว่ามันคือวิธีการเขียนสูตรที่มีลักษณะประมาณนี้
ซึ่งข้อดีของมันคือการที่เราเขียนสูตรคำนวณที่ซับซ้อนได้ในช่องเดียว แบบไม่ต้องทดออกมาใน Cell อื่น แบบตอนที่เขียนสูตรตามปกติ
ฟังแล้วก็ยังงงๆ เนอะ ไปดูตัวอย่างดีกว่า…
หากผมเขียนสูตรแบบนี้ ในช่อง E7 แล้วกด Enter
=B2:B5-C2:C5
จะสามารถแสดงผลลัพธ์แบบพุ่งพรวดออกมาทีเดียว 4 ช่องได้เลย โดยที่ ผลลัพธ์แต่ละตัวเกิดจาก สมาชิกแต่ละตัวของ Range แรก ลบด้วย สมาชิกแต่ละตัวของ Range ที่สอง ทำให้มีผลลัพธ์ออกมา 4 ตัว นั่นเอง และใน Excel 365 มันสามารถเอาผลลัพธ์ 4 ตัวโปรยลงไปแสดงในหลายๆ ช่องได้ (เรียกว่า Spill โดยที่สูตรจริงๆ อยู่ในช่อง E7 ช่องเดียวนะ ช่องอื่นไม่ได้มีสูตรอยู่จริง)

จะต้องลากคลุมพื้นที่ E7:E10 ก่อน แล้วเขียนสูตร
=B2:B5-C2:C5
แล้วค่อยกด Ctrl+Shift+Enter ถึงจะได้ผลเหมือนกัน ซึ่งจะเห็นใน Formula Bar มีเครื่องหมายปีกกา { } มาครอบสูตรโดยอัตโนมัติ
ถ้าเผลอกด Enter ธรรมดาก็จะขึ้น Error แบบ #VALUE! ทันที เพราะว่า Excel version เก่าไม่สามารถแสดงผลลัพธ์หลายค่าได้จากการใส่สูตรในช่องเดียว
ซึ่งจะเห็นว่าใน Excel version 365 นั้นง่ายกว่าเยอะมากๆ เลย
Array ที่ให้ผลลัพธ์หลายค่า เราสามารถซ้อนสูตรเพื่อรวบคำตอบให้เหลือค่าเดียวก็ได้ เช่น เขียนฟังก์ชันสรุปข้อมูลเช่น SUM, AVERAGE, MAX.. พวกนี้ครอบลงไป
=MAX(B2:B5-C2:C5)
ซึ่งถ้าเป็น Excel Version เก่า เราก็ยังต้องกด Ctrl+Shift+Enter อยู่ดีนะ ถึงจะไม่ Error

เวลาเขียนสูตรใน Excel ยิ่งเขียนซับซ้อนมากขึ้น เราก็ยิ่งจะต้องตรวจสอบสูตรให้เป็น ไม่งีั้นจะยิ่งงงหนักเลย ซึ่งวิธีการตรวจสอบก็คือ Hightlight สูตรส่วนที่ต้องการแล้วกด F9
เช่น Highlight แบบนี้

กด F9 แล้วจะเห็นข้อมูลออกมา 4 อันอยู่ในเครื่องหมายปีกกา (สัญลักษณ์ของ Array) ข้อมูลแต่ละอันคั่นด้วย semicolon แปลว่าอยู่คนละบรรทัด (ถ้าคั่นด้วย comma แสดงว่าอยู่คนละคอลัมน์)

ตรวจสอบเสร็จอย่าลืมกด Esc เพื่อ Cancel ไม่งั้นมันจะบันทึกค่าคงที่นี้ลงไปในสูตรเลย
หากเราเอา Array 2 ตัวที่มีมิติหลายๆ แบบ มาทำอะไรกันซักอย่างจะให้ผลยังไง มาดูกัน
ผลลัพธ์ก็เป็นแนวตั้ง

ผลลัพธ์ก็เป็นแนวนอน

เดี๋ยวลองกด F9 ให้ดู จะเห้นว่าคราวนี้คั่นด้วยเครื่องหมาย Comma แล้ว เพราะอยู่คนละคอลัมน์

ผลลัพธ์กลายเป็นตาราง ขนาดเท่ากับแนวตั้ง x แนวนอน

ถ้าอันนี้กด F9 จะเห็นว่าคั่นด้วย Comma ก่อนเพื่อขึ้นคอลัมน์ใหม่ พอขึ้นบรรทัดใหม่ก็จะคั่นด้วย Semicolon

ผลลัพธ์ก็เป็นตารางขนาดเท่าเดิม

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

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

อย่างไรก็ตาม ข้อมูลแนวนอนต้องมีจำนวนคอลัมน์เท่ากับข้อมูลในตารางนะ ไม่งั้นมันจะ Error ในส่วนที่จับคู่กันไม่ได้
เราสามารถใส่ Input แบบ Array ลงไปในฟังก์ชันที่ปกติรับค่าค่าเดียวได้ครับ เช่นอันนี้เราจะหาว่าชื่อที่ยาวที่สุดมีกี่ตัวอักษร
ปกติ LEN จะรับ input แค่ตัวเดียว แต่นี่เราใส่ไปทีเดียว 5 ตัวในแนวตั้ง มันก็เลยให้ผลลัพธ์ออกมา 5 ตัวในแนวตั้งด้วยเช่นกัน
=LEN(A2:A6)

สุดท้ายค่อยรวบด้วย MAX ครอบเข้าไป ทำให้ผลลัพธ์กลับมาเหลือตัวเดียวอีกครั้ง
=MAX(LEN(A2:A6))

และถ้าเราอยากได้ว่าชื่อที่ยาวที่สุดชื่ออะไรด้วย ก็ใช้พวก index match มาช่วยก็ได้ เช่น
=INDEX(A2:A6,MATCH(C2,LEN(A2:A6),0))

สังเกตุว่าใน MATCH เราก็ใส่ LEN ซ้อนลงไปได้เลยเหมือนกัน เพื่อให้มันให้ค่ามาเป็นจำนวนตัวอักษรแต่ละชื่อก่อน จะได้ MATCH หาเลข 17 ได้เจอ

เดี๋ยวตอนต่อไปเราจะมาดูตัวอย่างการเขียนที่เจ๋งยิ่งขึ้นกว่านี้ นั่นก็คือการเขียน array formula แบบมีเงื่อนไขครับ

ในที่สุดเราก็มาถึงบทที่จะใช้ความสามารถของการเขียนโปรแกรมกันอย่างเต็มที่กันซักที ซึ่งก็คือความสามารถในการวน Loop นั่นเองครับ เรามาดูกันมามีเรื่องอะไรที่ควรจะต้องรู้บ้าง
การวน Loop คือการสั่งให้ Run Code อะไรบางอย่างหลายๆ รอบ ซึ่งแต่ละรอบอาจจะมีอะไรบางอย่างเปลี่ยนไปก็ได้นะ
เช่น ถ้าเราอยากให้ Excel สร้าง Sheet ใหม่ขึ้นมา เราสามารถใช้คำสั่ง Worksheets.Add ได้เลย
และถ้าเราอยากให้ทำแบบนี้ 5 รอบล่ะ?
เราก็ทำแบบนี้ได้
Worksheets.Add
Worksheets.Add
Worksheets.Add
Worksheets.Add
Worksheets.Add
แต่มันไม่เท่เลย เพราะ Maintain แก้ Code ยาก แถมเหนื่อยด้วยถ้าต้องทำเยอะๆ หรือ code เรามีมากกว่า 1 คำสั่ง เป็นต้น
ดังนั้นเราจะใช้การวน Loop มาช่วยก็ได้ครับ จะได้ไม่ต้องเขียนอะไรซ้ำๆ หลายๆ รอบไงล่ะ
การจะใช้ Loop ได้ดี เราควรจะรู้จักวิธีการอ้างอิง Cell อีกแบบนึงก่อน ที่ใช้ตัวเลขมาช่วยแทนที่จะเป็นชื่อ นั่นเป็นเพราะเลขทำให้วน Loop ได้ง่ายยังไงล่ะ
สามารถใช้ การอ้างอิงด้วย Cells เพื่ออ้างอิง Range ด้วยตัวเลขได้ ในรูปแบบของ Cells(เลขแถว, เลขคอลัมน์)
Tips : เวลา Excel อ้างอิงเลขแถว/เลขคอลัมน์ มักจะอ้างอิงแถวก่อนคอลัมน์เสมอ (Row ก่อน Column) ลองสังเกตดูได้ ไม่ว่าจะใส่สูตร Index, Offset หรือพวก Matrix ก็เป็นแบบนั้น
Cells(3,2).Select
แบบนี้มีค่าเท่ากับ Range(“B3”).Select
เราสามารถสามารถใช้ Cells ผสมกับ Range ได้แบบนี้
Range( Cell เริ่มต้น, Cell สิ้นสุด)
Range( Cells(3,2), Cells(5,4)).Select
แบบนี้มีค่าเท่ากับ Range(“B3:D5”).Select
เราสามารถใส่ Property ที่ชื่อว่า Offset เพื่อเลื่อน Range ไปยังทิศต่างๆ ได้ ในรูปแบบของ
Range.Offset(จำนวนแถวที่จะเลื่อน,จำนวนคอลัมน์ที่จะเลื่อน)
เช่น
Range("A1:B3").Offset(4, 2).Select

ใน VBA นั้นมี For Loop อยู่ 2 แบบ นั่นก็คือ For… Next กับ For Each เรามาดูทีละตัวกัน
มีวิธีการใช้ คือ
For Counter = เลขเริ่ม To เลขจบ
Code คำสั่งที่จะให้ Run ซ้ำๆ
Next Counter
'Counter จะเป็นตัวแปรชื่ออะไรก็ได้ ปกติชอบใช้ i กัน
เช่น
For i = 1 To 5
Worksheets.Add
Next i
แบบนี้ก็จะมีการรัน Worksheets.Add จำนวน 5 ครั้ง นั่นเอง
ซึ่งใน Code ที่รัน เรามักจะเอา Counter มาทำอะไรซักอย่างด้วย เช่น ใช้ประกอบกับ Cells(เลขแถว,เลขคอลัมน์) แบบนี้
For i = 1 To 5
Cells(i, 1) = i * 10
Next i
เมื่อ Run ก็จะได้ผลลัพธ์ดังรูป

สำหรับ For Loop อีกแบบ จะเป็นการวน Loop เท่ากับจำนวนสมาชิกตัวลูกที่อยู่ใน Collection นั้นๆ ในรูปแบบดังนี้
For Each element In collection
Code คำสั่งที่จะให้ Run ซ้ำๆ
Next element
เช่น
หากเราเลือก Range ไว้หลายช่อง จริงๆ แล้ว Range ที่เลือกก็เป็น Collection ที่ประกอบไปด้วย Range ย่อยๆ หลายๆ อันเหมือนกันนะ

แบบนี้มันจะวน Loop จนครบสมาชิกทุกอันของ Selection ได้เลย ว่าในแต่ละ cell อยู่แถวเลขอะไร โดยที่เราไม่ต้องมานับเองว่ามันมีกี่อัน ซึ่งมีประโยชน์มากๆ ในชีวิตจริง โดยส่วนตัวเป็นรูปแบบ Loop ที่ผมชอบมากที่สุดเลยล่ะ
ถ้าเขียน Code แบบตัวอย่างก่อนหน้า Excel จะยังไม่รู้ว่า c คือ element อะไร ทำให้ไม่มี ToolTips ขึ้นมาช่วย ถ้าให้ดีเราต้องบอกมันนิดนึง ด้วยการประกาศตัวแปร ในรูปแบบว่า
Dim ตัวแปร as ประเภทข้อมูล
เช่น
Dim c As Range
For Each c In Selection
c.Value = c.Row
Next c
พอประกาศตัวแปรชัดเจนแล้ว จะทำให้ตัว Tool Tips ขึ้นมาได้อย่างถูกต้องเลย

เราสามารถออกจาก For ได้โดยการใช้คำสั่ง Exit For ซึ่งมักใช้ผสมกับคำสั่ง If เช่น
Sub Macro3()
Dim c As Range
For Each c In Selection
If c.Value > 30 Then
Exit For
Else
c.Font.Color = RGB(255, 0, 0)
End If
Next c
End Sub
มันก็จะ Loop ไปเรื่อยๆ ที่ละ Range แต่พอไปเจอว่าค่ามากกว่า 30 ก็ออกจาก Loop For ทันที (30 ยังไม่ออก)

ต่อไปก็เป็น Loop อีกประเภทนึงที่ควรรู้จัก นั่นก็คือ การทำอะไรบางอย่างในขณะที่เงื่อนไขยังคงเป็นจริงอยู่ ในรูปแบบว่า
Do While เงื่อนไข
Code คำสั่งที่จะให้ Run ซ้ำๆ
Loop
ซึ่งมีการเช็คเงื่อนไขบางอย่างก่อน กรณีที่เงื่อนไขเป็นจริงจะทำ Action ตามที่เรากำหนด แล้วก็กลับไปเช็คเงื่อนไขใหม่อีกครั้ง เช่น
Sub Macro3()
Range("A1").Select
Do While ActiveCell.Value < 35
ActiveCell.Font.Color = RGB(0, 0, 255)
ActiveCell.Offset(1, 0).Activate
Loop
End Sub

แบบนี้พอมันเช็คว่าค่าใน A4 (ที่เป็น ActiveCell ใน Loop นั้น) ไม่ได้น้อยกว่า 35 มันก็ออกจาก Loop เลย ไม่ได้เปลี่ยนให้เป็นสีฟ้า
Tips : ส่วนการออกจาก Do While ก็สามารถใช้ Exit Do ได้ครับ
จริงๆ ยังมี Loop แบบอื่นอีก แต่ผมคิดว่าไม่จำเป็นหรอก เราใช้ Loop แค่ 3 แบบนี้ก็เหลือแหล่แล้วครับ
ใน Python ก็มีแค่ For กับ While ยังทำงานได้เลย ^^
ลอง Fill สี R G ไล่ 1 ถึง 255 ส่วน B เป็น 0 ไป ลงไปใน Cell แบบสวยงาม
ใน Code จะเห้นว่าเราสามารถใส่ For Loop 2 ชั้นซ้อนกันก็ได้นะ แต่ใช้ Counter คนละตัว ซึ่งผมใช้ r กับ g อย่างละ 255
Sub Macro3()
Range("A1").Select
Application.ScreenUpdating = False
For r = 1 To 255
For g = 1 To 255
Cells(r, g).Interior.Color = RGB(r, g, 0)
Next g
Next r
Application.ScreenUpdating = True
End Sub
Tips : เราใส่ Application.ScreenUpdating = False เพื่อปิดการแสดงผลจนกว่าจะทำเสร็จ ค่อยทำให้เป็น True ไม่งั้นมันจะ Run นานมาก เพราะต้องรันไปแสดงผลไป (การทำแบบนี้จะเร็วขึ้นมากๆ ผมลองแล้วเร็วกว่ากัน 10 เท่า++ ได้)
จะได้ผลลัพธ์แบบนี้ (ผม Zoom Out ออกมาให้ เหลือแค่ 10% นะ แต่ละช่องจะเล็กมากๆ เลย)

ถ้าเปลี่ยนไปรันตัว b แทนก็จะได้อีกสีนึง สวยดีเนอะ 555


ในตอนที่แล้วเราได้พูดถึง Excel VBA พื้นฐาน กันแบบสุดๆ กันไปแล้ว คราวนี้เรามาดูเรื่อง VBA Object ซึ่งเป็นหัวใจสำคัญของ Excel VBA กันเพื่อเป็นพื้นฐานก่อนที่จะเขียน Loop ในตอนถัดไปครับ ซึ่งในตอนนี้จะเป็นทฤษฎีเยอะหน่อย ขอให้อดทนนิดนึงนะ
ในความเป็นจริงแล้ว VBA นั้นมอง Excel เป็น Object หรือวัตถุแบบนึง ซึ่งประกอบไปด้วยวัตถุย่อยๆ อีกหลายๆ ตัว ซึ่งตัวที่เป็นตัวหลักที่สุดก็คือตัวโปรแกรม Excel เอง เรียกว่า Application Object และภายใต้มันก็ประกอบไปด้วย Object อีกหลายตัวมากๆ แต่ตัวที่เรารู้จักกันเป็นอย่างดีก็คือ Workbook Object (แต่ละไฟล์) ซึ่งย่อยลงไปอีกก็จะเป็น Worksheet Object (แต่ละชีท) และ Range Object (แต่ละ range/cell) ตามลำดับ ซึ่งเรื่องนี้คือหัวใจสำคัญของ VBA เลยล่ะ
โชคดีที่มีคนทำ Diagram Object ที่ใช้บ่อยออกมาเป็นภาพที่ดูง่ายให้แล้วดังนี้

ซึ่งถ้าแบบเต็มๆ จริงๆ แล้ว Object จะมีเยอะมากๆ ดังนี้ (อาจมีอีกด้วยซ้ำ เพราะภาพนี้ก็เก่าแล้ว ช๊อคแปป…)
Tips : ถ้าอยากดูทั้งหมดจริงๆ สามารถกดปุ่ม F2 ใน VBE เพื่อเปิดดู Object Browser ได้ด้วยนะ
ถ้าสังเกตดีๆ แล้วล่ะก็ตัว Object เองก็จะมีบางอันมีทั้งแบบพหูพจน์และแบบเอกพจน์ด้วย เช่น
ตัวที่เป็นพหูพจน์เรียกว่า Collection ซึ่งก็คือ Object ที่เก็บ Object ย่อยที่มีลักษณะแบบเดียวกันไว้ด้วยกัน
เช่น Workbooks คือ Collection ที่รวบรวม Workbook แต่ละไฟล์เอาไว้ ซึ่งถ้าดูใน Model แล้วมันก็จะสามารถมีตัวย่อยคือ Worksheets ซึ่งก็คือ Collection ที่รวบรวม Worksheet แต่ละแผ่นเอาไว้ ซึ่งก็จะเก็บตัวย่อยคือ Range เอาไว้ได้ ดังนั้นถ้าจะไล่จากต้นสุดไปถึง Cell B3 ที่อยู่ใน Sheetที่ชื่อว่า แผนก ซึ่งเป็นชีทที่ 2 ของไฟล์ A จะต้องไล่ประมาณนี้
Application -> Workbooks ซึ่งมี Workbook ชื่อว่า A -> Worksheets ซึ่งมี Worksheet ชื่อว่า แผนก -> Range B3
สรุปแล้วเราจะอ้างอิงได้หลายแบบเลย ดังนี้
Application.Workbooks("A.xlsx").Worksheets("แผนก").Range("B3")
Application.Workbooks("A.xlsx").Worksheets(2).Range("B3")
'assume ว่า sheet ที่ต้องการวางอยู่ลำดับที่ 2
Application.Workbooks(1).Worksheets(2).Range("B3")
'assume ว่าเป็น Workbook ที่เปิดอยู่ไฟล์แรก
แต่ละ Object มีสิ่งสำคัญอยู่ 2 อย่าง คือ Property กับ Method ซึ่งต่างกันดังนี้
ซึ่งทั้งสองตัวใช้ในรูปแบบที่เหมือนกันเลย ก็คือ ใช้อยู่หลังจุด เช่น
เราสามารถพิมพ์ชื่อ Object แล้วตามด้วย . แล้วมันจะมี Tool Tips ขึ้นมาช่วยเอง เช่น

คิดว่าแบบนี้น่าจะดีขึ้นแล้วเนอะ (แต่ก็ยังเยอะจนเลือกไม่ค่อยถูกอยู่ดี 555)
สังเกตมั้ยว่า ตอนที่เราเขียน Code ในตอนที่แล้ว เราเขียนแค่ Range(“A1”)=10 แค่นี้มันก็ทำงานได้แล้ว ไม่ได้เขียน Object ตั้งแต่ Application ด้วยซ้ำ ไม่เห็นจะได้เขียน Property/Method อะไรซักอย่างเลย
ทั้งนี้เป็นเพราะการเขียน Code VBA เราสามารถเขียนแบบย่อได้ ซึ่งมันจะ Assume ตามค่า Default ให้เราหลายๆ อย่างดังนี้
จากรูปจะเห็นว่าเราเขียน Code ได้หลายที่เลย ทั้งที่ Sheet/ThisWorkbook/Module ซึ่งในตอนที่แล้วเราเขียนใน Module ไป มันก็จะเรียกไปที่ Active Workbook/ Active Sheet ให้ และจะอ้างอิงไปถึง Property Value ให้โดยอัตโนมัติ

ดังนั้นการเขียนว่า
inputName=Range("B1")
สามารถเขียนเต็มๆ ได้ว่า
inputName = Application.Workbooks("vba-basic.xlsm").Worksheets("Sheet1").Range("B1").Value
การจะเข้าใจเรื่องนี้ได้ดี จะต้องเข้าใจความแตกต่างของคำที่คล้ายๆ กันด้วย นั่นคือ Active/Activate vs Selection/Select
สมมติว่าเราเลือกข้อมูลใน Excel ด้วยการลากคลุมพื้นที่ B2:D4 แบบนี้เราเรียกว่ามีการเลือก (Select) Range B2 ถึง D4 แต่ว่า Cell Range ที่มีการเลือกอันแรกสุดจะกลายเป็น Active Cell แค่อันเดียว

หรือกรณีเลือกพื้นที่หลายอัน ด้วยการกด Ctrl ค้างไว้แล้วเลือก ก็จะมี Active Cell อันเดียว

เวลาเราเขียนสูตรหรือพิมพ์ค่าคงที่ลงไป แล้วกด Enter ปกติมันก็จะใส่สูตรลงไปใน Active Cell แค่ตัวเดียวเท่านั้น แล้ว Active Cell ก็จะเลื่อนไป Selection ช่องถัดไปให้โดยอัตโนมัติ (เพราะกด Enter)
Tips : ถ้าเราจะใส่ทุกช่องที่เลือก (Selection ทั้งหมด) พร้อมกันก็ต้องกด Ctrl+Enter แทน
มีประเด็นสำคัญ 2 อัน คือ
ซึ่งใน VBA ก็จะมี Method ที่เกี่ยวข้องกับ 2 ตัวนี้ นั่นก็คือ Range.Select กับ Range.Activate นั่นเอง
ถ้าเราลองสั่ง Activate Object มันจะทำการ Select Object นั้นไปด้วยในตัวเลย แต่ก็จะ Select แค่ตัวเดียวเท่านั้นนะ
Worksheets("Sheet2").Activate
Tips : เราสามารถ Run Code VBA ได้ด้กวยการกดปุ่ม Run หรือปุ่ม F5 ก็ได้ ไม่จำเป็นต้อง Assign Button เสมอไป
แบบนี้ก็จะทำการ Activate Sheet ที่ชื่อว่า Sheet 2 ซึ่งสามารถทำได้โดยไม่ต้อง Select ไว้ก่อนเลย
แต่ถ้าใช้กับ Range ได้เช่นกัน แบบนี้ก็จะ Activate Range B5 ของ Active Sheet (ซึ่งของผมอยู่ที่ Sheet2)
Range("B5").Activate
ถ้า Select Cell เดียว Cell นั้นก็จะ Active ไปด้วยเช่นกัน (เหมือนกับ Activate ก็จะ Select ไปด้วย)
แต่ถ้า Select เป็น Range ปกติจะ Active ที่ Cell ซ้ายบน ยกเว้นว่าจะระบุชัดเจนไปเลยว่าจะให้ตัวไหน Active
Range("B3:D7").Select
Range("C6").Activate

จะเห็นว่าเราสามารถ Select Range เป็นช่วงได้ และสามารถ Activate Cell ภายในนั้นได้ (ถ้า Activate นอก Range เดิม Selection ก็จะหายไป)
หวังว่าเพื่อนๆ จะเข้าใจความหมายของ Select กับ Activate มากขึ้นแล้วนะครับ
อย่างไรก็ตาม การจะทำอะไรกับ Cell เราก็ไม่จำเป็นต้อง Select หรือ Activate มันก่อนเสมอไปนะ เช่นที่เราทำในบทความก่อนหน้า เราก็ยัดค่าลง Value ลง Range ได้เลย เช่น Range(“B5”)=10 แบบนี้ ก็ไม่ได้ Select หรือ Activate ก่อนเลย
แต่ข้อดีของ Select กับ Activate นั้นทำให้สามารถอ้างอิงได้ด้วยคำว่า Selection หรือ ActiveCell, ActiveSheet, ActiveWorkbook เป็นต้น เช่น
Range("B6").Select
Selection = 10
'หรือแบบข้างล่างก็ได้
Selection.Value = 10
ActiveCell = 10
ActiveCell.Value = 10
อย่างไรก็ตามเราจะไม่สามารถไป Select/Activate Range ใน Sheet อื่น หากว่าไม่ได้ Select/Activate Sheet นั้นๆ ก่อน เช่น

ถ้า Select Sheet ก่อนแบบนี้จะไม่มีปัญหา
Sub Macro3()
Worksheets("Sheet1").Select
Range("E5:G8").Select
End Sub
สำหรับบทความนี้ก็น่าจะเนื้อหาเริ่มเยอะแล้ว เดี๋ยวจะปวดหัวกันเกินไป ตอนนี้เราก็ได้เรียนรู้เรื่อง Object ต่างๆ ไปพอสมควรแล้ว เดี๋ยวตอนหน้าก็ถึงเวลาที่จะเรียนรู้เรื่อง Loop กันซักทีครับ

หลายคนเวลาจะเอาข้อมูลจาก Excel ไฟล์อื่น มักจะใช้การเขียนสูตร ทั้งๆ ที่จริงแล้ว วิธีที่ดีที่สุดคือการใช้ Power Query ต่างหากครับ

มาดูวิธี Link ข้อมูลข้าม Sheet / ข้ามไฟล์ใน Excel แบบง่ายๆ กันครับ จริงๆ ก็ไม่มีอะไรมากไปกว่า พิมพ์ = แล้วจิ้ม แล้ว Enter เนอะ 555

มีแฟนเพจหลายท่านอยากให้ผมสอนเรื่องเกี่ยวกับ VBA ให้ แม้ผมจะเคยเขียนเกี่ยวกับเรื่อง Concept สำคัญของ VBA ในแง่ของหลักการไปแล้ว แต่ผมยังไม่ได้สอนเขียน Code แบบจริงๆ จังๆ ซักที แต่ตอนนี้คงถึงเวลาแล้วล่ะ ^^
อ่อ! ในบทความนี้จะไม่ได้สอนเรื่อง record macro นะครับ อันนั้นลองไปหัดกันเองได้นะ หรือดูใน youtube ก็น่าจะมีเยอะเลย ในบทความนี้จะสอนหลักการการเขียนโปรแกรมให้ เพื่อให้เพื่อนๆ สามารถอ่าน code และแก้ code เองได้หลังจาก record macro แล้วนะครับ
เริ่มแรกสุดเลย การที่เราจะใช้งาน VBA ได้ก็ต้องเปิด Ribbon Developer ขึ้นมาซะก่อน ซึ่งอยู่ใน Excel Option ครับ

จะเข้าไปเขียนโปรแกรมใน VB Editor (หรือ VBE) ก็สามารถกดปุ่ม Visual Basic หรือ Alt+F11 ได้ครับ (ผมกด Alt+F11 ตลอด)

แต่เดี๋ยวเราปิดมันไปก่อนก็ได้ครับ เอาเป็นว่าเปิด VBE ขึ้นมาเป็นแล้วเนอะ กด Alt+F11 นะ
จะสั่งให้ Code ทำงานได้ มันต้องมี Trigger หรือ Event บางอย่างเกิดขึ้น ซึ่งตัวที่ใช้บ่อยที่สุดอันนึงก็คือการกดปุ่ม แล้วทำให้เกิด action บางอย่างขึ้นตาม Code ที่เราสั่ง
เรากำลังจะสร้างปุ่มที่ว่านี่แหละ ให้เรากด add button ดังนี้ Developer-> Insert -> Form Control -> Button

จากนั้นวาดรูปสี่เหลี่ยมลงไปในที่ว่างๆ ที่ต้องการให้เกิดปุ่มขึ้น

จากนั้นมันจะเข้าสู่ VBE ภายใต้ Sub ที่ชื่อว่า Test ตามชื่อ Macro ที่เราเขียนเลย

เมื่อเรากด Alt+F11 เพื่อเข้าสู่ VBE หรือกดสร้างปุ่มแล้ว เราก็จะเขียน code ได้
ก่อนจะเขียน Code ผมอยากให้เพื่อนๆ เข้าใจ Concept สำคัญในการเขียนโปรแกรมอันนึง ก็คือการใช้เครื่องหมาย = หรือ assignment operator ว่า เราสามารถเอาค่าไปใส่ให้กับตัวแปร หรือใส่ให้กับ Object ต่างๆ ที่เราต้องการได้
โดยรูปแบบคือให้ตัวด้านซ้าย มีค่าเท่ากับตัวด้านขวา
Object = ค่า
ตัวแปร = ค่า
เช่น
x=10
แปลว่าให้ตัวแปร x มีค่าเป็น 10
แล้วถ้าเราเขียนต่อไปอีกบรรทัดว่า
x=x+1
แม้จะดูผิดหลักคณิตศาสตร์ แต่เครื่องหมาย = ในที่นี้ไม่ใช่เครื่องหมายเปรียบเทียบในคณิตศาสตร์ แต่ในการเขียนโปรแกรม มันคือเครื่องหมายที่ใช้ในการกำหนดค่า (assignment operator) ที่ให้ตัวด้านซ้าย มีค่าเท่ากับตัวด้านขวา แล้ว Code ที่เราเขียนก็จะถูก Run จากบนลงล่างไปเรื่อยๆ
ดังนั้นความหมายของ x=x+1 คือให้เอาค่าเดิมใน x (ซึ่งคือ 10) ไปบวก1 จะได้ 11 แล้วใส่แทนค่าลงไปในตัวแปร x
ดังนั้น x จะมีค่าใหม่เป็น 11 นั่นเอง
ภายใน Sub Test() เราสามารถเขียน Code ในรูปแบบ Range(“Cell Reference”) เพื่ออ้างอิง Cell/Range ได้ เช่น
Range("A1")=10
แปลว่า ให้ช่อง A1 มีค่าเป็น 10
พอเขียนเสร็จก็กดปิด VBE ไปก่อนได้เลย แล้วลองไปกดปุ่มที่เราสร้างดูซะ

จากนั้น Code ที่เราเขียนก็จะถูก Run ทำให้ช่อง A1 มีเลข 10 อยู่

นอกจากจะอ้างอิง Cell เดียวแล้วยังใส่เป็นช่วงก็ได้ เช่น
Range("A1:C3") = "Cat"
สังเกตุว่า Cat ใส่อยู่ในเครื่องหมายคำพูด เพราะเป็น Text นะครับ

นอกจากนี้ถ้าเรามีการตั้งชื่อ Defined Name ไว้ เราก็สามารถเอามาใช้ใน Range นี้ได้ด้วย เช่น
Range("ชื่อที่ตั้งไว้") = 999
ซึ่งในชีวิตจริง เราจะใช้การอ้างอิงด้วยชื่อบ่อยมาก เพื่อป้องกันปัญหาการ แทรก/ลบ cell จนตำแหน่งช่องเปลี่ยนไปจนใน Code ไม่ตรงกับความเป็นจริง
เราสามารถให้ VBA สร้างผลลัพธ์เป็น Message Box เด้งขึ้นมาได้ ด้วยคำสั่งง่ายๆ ว่า MsgBox(“คำที่ต้องการ”) เช่น
MsgBox ("สวัสดี")

เราสามารถใช้เครื่องหมาย & มาเชื่อมข้อความ เพื่อผสมรวมคำจากค่าคงที่เช่น “สวัสดี” กับการอ่านค่าจาก Cell ช่อง B1 ที่ผมตั้งชื่อไว้ว่า FirstNameได้ เช่น
ที่สวัสดีอยู่ในเครื่องหมายคำพูด เพราะเป็น Text ส่วน inputName เป็นชื่อตัวแปร ไม่ต้องอยู่ในเครื่องหมายคำพูดนะครับ
Sub Test()
inputName = Range("FirstName")
MsgBox ("สวัสดี " & inputName)
End Sub

เราสามารถเขียน if ใน VBA ได้ในรูปแบบของ
If เงื่อนไข Then
ทำอะไรถ้าเงื่อนไขเป็นจริง
Else
ทำอะไรถ้าเงื่อนไขเป็นเท็จ
End If
เช่น
Sub Test()
inputName = Range("FirstName")
If inputName = "ศิระ" Then
MsgBox ("สวัสดีนายท่าน")
Else
MsgBox ("ยินดีที่ได้รู้จักครับ " & inputName)
End If
End Sub
ถ้า inputName ที่กรอกมาใน Range ที่ชื่อ FirstName เป็นคำว่า ศิระ ก็จะบอกว่า สวัสดีนายท่าน นอกนั้นก็จะบอกว่า ยินดีที่ได้รู้จักครับ แล้วตามด้วยชื่อ
สังเกตว่า ใช้ inputName = “ศิระ” ได้ เพราะมันรู้ว่าเครื่องหมาย = อันนี้คือเครื่องหมายเปรียบเทียบ ไม่ใช่ assignment แบบตอนที่เขียนส่งค่าให้ตัวแปรตามปกติ


ที่สำคัญ เวลาจะ Save ไฟล์ต้อง Save นามสกุล .xlsm, xlsb, xls เท่านั้นนะครับ เพราะถ้าดันไป save เป็น xlsx ปกติล่ะก็… code ที่เราเขียนจะหายไปหมดเลย ดังนั้นอย่าพลาดเด็ดขาดเลยนะ เดี๋ยวนั่งร้องไห้ไม่รู้ด้วย!

เอาล่ะ บทนี้ของ่ายๆ แค่นี้ก่อนนะครับ ยังไงก็ลองไปเล่นให้ชินมือเข้าไว้นะ ในบทต่อไปจะเป็นการอธิบาย Concept สำคัญใน VBA นั่นก็คือเรื่องของ Object นั่นเอง ซึ่งจะเป็นพื้นที่ไปสู่ บทที่ 3 ที่จะพูดถึงเรื่องการวน Loop ซึ่งคือหัวใจที่สำคัญที่สุดของการเขียนโปรแกรมล่ะ

มีแฟนเพจ inbox ถามผมมาว่าจะ Lookup ข้อมูลจากหลายคอลัมน์ได้ยังไง? ซึ่งโจทย์เป็นประมาณนี้ครับ
ด้านซ้ายมีตารางที่บันทึกการทำงานเอาไว้ ว่าใครจะต้องทำงานวันไหนบ้าง แต่มีการบันทึกวันละ 3 คอลัมน์ แทนที่จะมีคอลัมน์เดียว (อาจเป็นเพราะวันนึงมี slot การทำงาน 3 คนก็เลยบันทึกแบบนี้) ซึ่งการบันทึกแยกกัน 3 คอลัมน์ก็จะทำให้การ Lookup ยุ่งยากไปด้วย
ส่วนด้านขวาก็เป็นสิ่งที่อยากรู้แล้วล่ะ ว่าแต่ละวัน พนักงานแต่ละคนทำงานหรือไม่? (ถ้าเจอในตารางซ้ายแปลว่าทำงาน)

ในบทความนี้เราจะมาดูกันว่ามีวิธีการ Lookup ข้อมูลจากหลายคอลัมน์ด้วยวิธีไหนกันบ้างโดยไม่แก้ข้อมูลต้นฉบับครับ
ถ้าสังเกตดูแล้ว มันก็คือการ Lookup 2 Criteria จำนวน 3 รอบนั่นเองครับ ซึ่งเราแก้ไขได้หลายแบบ
วิธีแรกน่าจะเข้าใจง่ายสุด นั่นก็คือ ใช้ COUNTIFS ทีละคอลัมน์แล้วจับบวกกัน เช่น ใน H4
หาจากคอลัมน์พนักงาน1 : =COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน1],G4) หาจากคอลัมน์พนักงาน2 : =COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน2],G4) หาจากคอลัมน์พนักงาน3 : =COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน3],G4)
จับบวกกัน ก็ได้แบบนี้ ซึ่งถ้าทำงานก็จะออกมาเป็น 1 ถ้าไม่ทำงานก็ออกมาเป็น 0 (กรณีที่ทำซ้ำกันในวันเดียวไม่ได้นะ)
=COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน1],G4)+COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน2],G4)+COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน3],G4)
ถ้ามีสิทธิ์ที่จะทำงานวันนึงซ้ำกันได้มากกว่า 1 รอบ ก็ต้องเช็คว่า ถ้ามันออกมามากกว่า 0 แสดงว่าทำงาน ผลลัพธ์ก็จะออกมาเป็น TRUE/FALSE ทันที จบ
=(COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน1],G4)+COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน2],G4)+COUNTIFS(WorkTable[วันที่ทำงาน],F4,WorkTable[พนักงาน3],G4))>0

การใช้ Boolean ถ้าเงื่อนไขเป็น AND จะใช้การคูณ ถ้าเงื่อนไขเป็น OR จะใช้การบวกแล้วเทียบว่ามากกว่า 0 รึเปล่า ดังนั้นสูตรจะออกมาเป็นแบบนี้
=SUMPRODUCT((WorkTable[วันที่ทำงาน]=F4)*(((WorkTable[พนักงาน1]=G4)+(WorkTable[พนักงาน2]=G4)+(WorkTable[พนักงาน3]=G4))>0))
อธิบายทีละส่วนนะ
ส่วนสีฟ้า (((WorkTable[พนักงาน1]=G4)+(WorkTable[พนักงาน2]=G4)+(WorkTable[พนักงาน3]=G4))>0) เป็นการเช็คเงื่อนไขว่าพนักงานคอลัมน์ในคอลัมน์หนึ่งเป็น G4 หรือไม่ ถ้าใช่จะมีอย่างน้อย 1 ตัวได้ 1 พอบวกกันต้องมากกว่า 0 แน่นอน แสดงว่านี้คือเงื่อนไขแบบ OR
แล้วเอาไปคูณกับส่วนสีแดง คือ วันที่ต้องตรงกับ F4 ด้วย การคูณจะเป็นเงื่อนไขแบบ AND เพราะถ้าตัวใดตัวหนึ่งเป็นเท็จ ก็คือจะเป็นการคูณ 0 จะได้ผลลัพธ์ 0 ที่แสดงว่าเป็นเท็จนั่นเอง
ผลลัพธ์แต่ละบรรทัดจะได้ 0,1 แล้วแต่ว่าตรงตามเงื่อนไขหรือไม่ สุดท้ายเราจึงใช้ SUMPRODUCT หรือ SUM เพื่อรวม 0,1 ทุกบรรทัดให้เป็นค่าเดียว

ถ้ามีคอลัมน์พนักงาน1 อันเดียว เราก็จะ MATCH แบบนี้ได้
=MATCH(F4&G4,WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน1],0)
แต่ถ้ามีหลายพนักงาน เราก็อาจจะใช้วิธีนำคอลัมน์มารวมกันเป็นอันเดียวซะก่อน แล้วค่อยหา ซึ่งหาแบบ WildCard ก็ได้ เช่น
=MATCH("*"&F4&G4&"*",WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน1]&WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน2]&WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน3],0)

ถ้าผลลัพธ์ออกมาเป็นตัวเลขก็แสดงว่าหาเจอ เราก็ใช้ ISNUMBER มาช่วยเช็คได้
=ISNUMBER(MATCH("*"&F4&G4&"*",WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน1]&WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน2]&WorkTable[วันที่ทำงาน]&WorkTable[พนักงาน3],0))

วิธีนี้จะเข้าใจยากที่สุด จะต้องเข้าใจ Matrix ให้ดีก่อน ดังนั้นถ้าใครอยากดูวิธีเข้าใจง่ายก็ข้ามไป Power Query อันถัดไปได้เลย
เราสามารถหา Total Row ของแต่ละแถวว่ามีพนักงานตรงกับที่กำหนดรึเปล่าได้โดย
=MMULT(--(WorkTable[[พนักงาน1]:[พนักงาน3]]=G4),TRANSPOSE(COLUMN(WorkTable[[พนักงาน1]:[พนักงาน3]])^0))

จากนั้นก็คูณเอาเงื่อนไขวันทำงานเข้าไป เช่น
=MMULT(–(WorkTable[[พนักงาน1]:[พนักงาน3]]=G4),TRANSPOSE(COLUMN(WorkTable[[พนักงาน1]:[พนักงาน3]])^0))*(WorkTable[วันที่ทำงาน]=F4)
จากนั้นก็ SUMPRODUCT หรือ SUM เพื่อรวมผลลัพธ์ทุกแถวเข้าด้วยกัน
=SUMPRODUCT(MMULT(--(WorkTable[[พนักงาน1]:[พนักงาน3]]=G4),TRANSPOSE(COLUMN(WorkTable[[พนักงาน1]:[พนักงาน3]])^0))*(WorkTable[วันที่ทำงาน]=F4))
จะได้แบบนี้ครับ

ในเมื่อตารางต้นฉบับแยกหลายคอลัมน์ทำให้มันยาก งั้นใช้ Power Query แปลงให้ถูกต้องก่อนด้วย Unpivot จะได้เป็นการแก้ปัญหาที่แท้จริงไปเลย
เอา WorkTable เข้า Power Query แล้ว Unpivot Other Columns ซะ

จะได้แบบนี้ ก็เปลี่ยนชื่อคอลัมน์ให้เรียบร้อย

จากนั้นก็เอาตารางที่เราจะ Check เข้า Power Query แล้ว Merge กับ WorkTable ที่ Unpivot แล้ว
ซึ่งเราสามารถเลือกคอลัมน์ที่เป็นตัวเชื่อมได้หลายอันพร้อมกันนะ แค่กด Ctrl ค้างไว้เท่านั้นเอง

กด Expand เอา Slot ออกมาก็จะเห็นแล้วว่าเค้าทำงานใน Slot ไหน ถ้า null ก็คือไม่ได้ทำงาน

ถ้าอยากได้ 1,0 หรือ True, False ก็ใช้ Conditional Column มาช่วยก็ได้ครับ ไม่น่ายากเนอะ
หากต้องการแค่ทำงานหรือไม่ จริงๆ ใช้แบบวิธีนี้ทำได้ง่ายมากๆ เลย
=SUM((WorkTable[[พนักงาน1]:[พนักงาน3]]=G4)*(WorkTable[วันที่ทำงาน]=F4))
เพราะผลลัพธ์ของ Array ในแถวเดียวกันจะคูณด้วยกันหมดเลย จากนั้นเราก็แค่ sum เพื่อให้ได้ผลลัพธ์แบบ OR ทั้งตารางแค่นั้นเอง

Excel 365 เท่านั้น
จะเห็นว่าการที่เก็บข้อมูลในรูปแบบที่ไม่ถูกต้อง ก็จะทำให้เกิดความยากเรื่องประมวลผลข้อมูลโดยใช่เหตุนะ ดังนั้นก็เก็บข้อมูลกันดีๆ ล่ะ
นี่ก็คือวิธีการทั้งหมดที่ผมอยากนำเสนอ ใครมีวิธีอื่นอีกก็บอกมาได้เลยนะครับ ^^
ส่วนตัวผมก็ชอบวิธี Power Query ที่สุด เพราะว่ากดง่ายดี ไม่ต้องเขียนสูตรครับ แถมมีคอลัมน์เพิ่มก็ไม่กลัวด้วย Refresh ได้เลย

Excel นั้นเป็นเครื่องมือครอบจักรวาล ทำได้ทุกอย่าง แต่จะเหมาะสมกับงานนั้นที่สุดรึเปล่าก็เป็นอีกเรื่องนึงนะ ในบทความนี้เราจะมาดูตัวอย่างการใช้ Excel ทำเรื่องต่างๆ เทียบกับเครื่องมือเฉพาะทางในเรื่องนั้นๆ กัน ว่าต่างกันตรงไหน Excel ดีกว่าหรือแย่กว่าในมุมไหนบ้าง?
เพื่อไม่ให้เสียเวลามาดูกันเลย!
สมมติเราจะคิดเลขง่ายๆ เช่น ของราคา 250 บาท ลด 20% จะเหลือราคาเท่าไหร่?
ถ้าเป็นเครื่องคิดเลขแม่ค้า เราสามารถกด 250-20% (กด 250, -, 20, %) ได้เลย ก็จะได้คำตอบว่า 200 บาททันที (เพราะมัน assume ว่า 20% เป็นการคิด %ของเลขตั้งต้น)

แต่ถ้าเป็น Excel เราจะไปพิมพ์สูตรว่า =250-20% แบบนี้ไม่ได้เด็ดขาด เพราะมันแปลว่า =250-0.2 = 249.80 หรือ 24980% นั่นเอง ซึ่งเป็นคนละเรื่องกันเลย ดังนั้นถ้าจะพิมพ์ใน Excel ต้องพิมพ์ว่า =250-20%*250 เพื่อบอกว่า 20% น่ะเป็นของเลขอะไร ถึงจะได้คำตอบ 200 บาท ซึ่งจะเห็นว่ายุ่งยากกว่าการกดเครื่องคิดเลขนิดๆ
มันก็จะเหมาะสมตอนที่มีรายการหลายอัน หรืออะไรที่มันต้องคำนวณซับซ้อนน่ะสิ โดยเฉพาะหากเราต้องการตรวจสอบได้ แก้ตัวเลขเปลี่ยนเล่นได้ตามใจชอบ ยิ่งเหมาะ เช่น แยกช่องออกมาให้ชัดๆ เพื่อทำในสิ่งที่คล้ายๆ จะเป็น template ไว้เลย แบบนี้ แล้วผูกสูตรให้หมด จากนั้นเราก็สามารถเปลี่ยนเลขเล่นได้ เพิ่มรายการได้ง่ายๆแล้ว เรียกได้ว่ายากกว่าในขั้นต้น แต่ reuse ได้ง่ายกว่านั่นเอง

การทำแบบฟอร์มใน Excel นั้นเป็นอะไรที่ยุ่งยากมาก แถมข้อเสียที่ร้ายแรงที่สุดก็คือ หากจะเก็บข้อมูลไว้เป็นฐานข้อมูลกลางจะต้องเอาไฟล์ Excel ที่ทุกคนกรอกไว้มารวมกัน ซึ่งตอนรวมก็ยากอีก
แต่ถ้าเราใช้ Google Form เป็นตัวรวบรวมข้อมูลจะสะดวกกว่ามาก ทั้งสามารถสร้าง Form ได้ง่ายกว่า มีให้เลือกหลายรูปแบบทั้ง free text, choice, grid และทุกคนสามารถกรอกฟอร์มแบบ online ได้เลย พอ submit ฟอร์มปุ๊ป ผลลัพธ์ก็จะถูกรวมกันอยู่ในที่เดียวทันที

ถ้าจะเอาข้อมูลออกมาก็แค่กด Export ออกมา หรือจะ Link ผลลัพธ์ไว้ใน Google Sheet ก็ยังได้ ซึ่งถ้าเอาไว้ใน Google Sheet แล้วเราก็เอา Power Query ดึงออกมาได้เช่นกัน
ข้อเสียของ Google Form เท่าที่ผมเห็นมี 2 จุด คือ
ดังนั้นฟอร์มที่เหมาะกับการทำใน Excel มากกว่า ก็คือพวกฟอร์ม Template ช่วยคำนวณที่สามารถแสดงผลลัพธ์การคำนวณให้ User เห็นได้ทันที่ในระหว่างกรอกข้อมูลนั่นเอง เช่น ตัวอย่างการใช้ Excel คำนวณยอดผ่อนเงินกู้อันนี้

ข้อจำกัดสำคัญของ Excel คือข้อมูลมันไม่สามารถมารวมศูนย์กันได้ง่ายๆ นี่แหละเฮ้อ… ถึงจะมี Power Query ช่วยดึงข้อมูลหลายไฟล์มารวมกันได้ แต่มันก็คงสู้การที่กรอกข้อมูลปุ๊ปไปรวมอยู่ในตารางเดียวกันเลยแต่ต้นแบบ Google Form ไม่ได้หรอก
หนึ่งในงานที่หลายคนชอบใช้ Excel ทำก็คือเอามาทำงานเอกสาร โดยเฉพาะงานที่ต้องจัดหน้าพิมพ์ลงบนกระดาษจริงๆ ไม่ว่าจะเป็น Label, จดหมายเวียนต่างๆ
หากใช้ Excel ทำจะมีความยุ่งยากมากๆ เพราะต้องใช้พวกเทคนิคการ Lookup ค่าให้ได้ผลลัพธ์หลายตัวมาช่วย (ซึ่งยาก) ถ้าใครสนใจก็ลองอ่านได้ที่นี่
แต่ถ้าเราหันมาเรียนรู้เครื่องมือ Mail Merge ใน Microsoft Word ซึ่งออกแบบมาสำหรับเรื่องนี้โดยเฉพาะ ทุกอย่างจะง่ายขึ้นมากๆ เลย ถ้าใครสนใจลองอ่านบทความที่ผมเขียนสอนคร่าวๆ ได้ที่นี่

หากทำเป็น เราก็สามารถสร้างจดหมายเวียน หรือ เอกสารต่างๆ ที่ต้องเปลี่ยนรายละเอียดบางอย่าง เช่น ชื่อคน ชื่อสินค้า ราคา ไปเรื่อยๆ ได้อย่างง่ายได้
ปฏิเสธไม่ได้เลยว่า Google Sheets คือ Program Spreadsheet ที่เจ๋งมากๆ แถมใช้ฟรีอีกต่างหาก มีจุดแข็งอย่างยิ่งเรื่องการใช้แบบ Online และใช้งานพร้อมกันได้หลายคนพร้อมๆ กัน
สิ่งที่ Excel แข็งกว่า Google Sheets ก็คือพวกเครื่องมือสำเร็จรูปที่ใช้ง่ายกว่าเยอะ เช่น Pivot Table, Flash Fill, Power Query…
แต่สิ่งที่ Excel สู้ Google Sheets ไม่ได้เลยก็คือเรื่องของการเขียนสูตร ซึ่ง Google Sheets มีสูตรที่หลากหลายมากกว่า ซึ่ง Excel เพิ่งจะมาตีตื้นจากการมี Dynamic Array ใน Excel 365 นี่เอง ถึงจะสามารถใช้ฟังก์ชันพวก UNIQUE, SORT, FILTER ได้ ทั้งๆ ที่ Google Sheets ทำได้นานแล้ว
สิ่งที่ยังสู้ไม่ได้จะเป็นพวกสูตรที่เอาไว้ดึงข้อมูลแบบ Online รวมถึงสูตรที่เอาไว้จัดการข้อมูลประเภทข้อความ ที่ Google Sheets สามารถใช้การค้นหาแบบ Regular Expression (RegEx) ซึ่งรองรับการหาข้อความที่ซับซ้อนได้ และเดี๋ยวเราจะมาดูตัวอย่างเรื่องนี้กัน ว่าถ้าใช้ Excel จะสู้ยังไงดี?
เช่น ผมต้องการดึงตัวเลขจากข้อมูลมหาเศรษฐีไทย 20 อันดับจาก link นี้ https://www.prachachat.net/politics/news-451396
Regular Expression เป็นเครื่องมือที่สามารถตรวจจับ Pattern ที่ซับซ้อนได้ (ตามที่เราระบุ) และยังเลือกดึงข้อมูลเฉพาะส่วนที่เราสนใจได้ ซึ่ง Google Sheets ก็มีฟังก์ชันนี้ให้ใช้เลย
อย่างเช่นการจะดึงเฉพาะชื่อมาได้ เงื่อนไขค่อนข้างซับซ้อนเลยทีเดียว แต่ Regex ก็ทำได้ (แม้บางรายการจะ manual ชื่อบริษัทแบบโกงลงไปใน pattern นิดหน่อย) ถ้าเราใช้ Excel ทำน่าจะทำเรื่องนี้ได้ยากมากๆ

Tips : ถ้าทำ Regular Expression ในการเขียนโปรแกรมแบบเต็มตัว ก็จัดการข้อมูลที่ไม่ต้องแยกบรรทัดมาแต่แรกก็ยังได้ เช่น ใน Python ในรูปนี้ ผมก็ใช้ Regular Expression ดึงข้อมูลที่ต้องการมาได้เลยทีเดียวทั้งชุด
ปล. ผมยัง noob Python และ Regular Expressionอยู่มากเลย ถ้า code ไม่ค่อยดีก็ขออภัยด้วย กำลังหัดอยู่ครับ

เอาล่ะ เราได้เห็นความสามารถของ Regular Expression ใน Google Sheets ไปแล้ว ลองมาดูความสามารถของฝั่ง Excel กันบ้าง
Excel นั้นสามารถดึงข้อมูลบางส่วนจากข้อความได้ แต่มันก็ไม่ได้มีเครื่องมือที่ Support เรื่องนี้แบบครบเครื่องเท่าความสามารถของ Regular Expression ซึ่งเป็นเครื่องมือที่ออกแบบให้ทำเรื่องนี้ได้โดยเฉพาะ
สิ่งที่ Excel ทำได้นั้นเหมาะกับข้อความที่ไม่ได้ซับซ้อนมากนัก (ไม่งั้นจะต้องเขียนสูตรที่ยาวมากๆๆๆ เช่นใช้พวก FIND, SEARCH, LEFT, MID, RIGHT, LEN, SUBSTITUTE, REPLACE ผสมกันมั่วไปหมด) และเหมาะกับการดึงข้อมูลแค่ตัวแรกที่เจอตัวเดียวมากกว่า
ซึ่งใน Excel มีเครื่องมือให้เลือกใช้ได้หลายตัว เช่น เขียนสูตร (ซึ่งยาก) , Flash Fill หรือ Column From Example ใน Power Query ซึ่งข้อดีคือมันก็ใช้ง่ายมากๆ เลยล่ะ ดังนั้นผมจะไม่สู้ Google Sheets ด้วยสูตร แต่สู้ด้วย Flash Fill กับ Power Query แทน
วิธีนี้ ง่ายสุด เร็วสุด (โคตรเร็วสมชื่อ Flash Fill) เพราะแค่พิมพ์ตัวอย่างบางส่วนว่าคอลัมน์นั้น ๆต้องการอะไร แล้วกด Ctrl+E ก็จบเลย แต่มีข้อเสียคือถ้าข้อมูลต้นทางเปลี่ยนก็ต้องกดใหม่ตลอด (เพราะมันไม่ใช่สูตร)

ถ้าใช้ตัวนี้อาจต้องแยกทีละส่วนออมาก่อน ไม่งั้นถ้าซับซ้อนไป มันจะไม่เห็น Pattern ว่าควรใช้สูตรไหน เช่นในที่นี้มันจะเอาข้อมูลที่อยู่ระหว่าง “ยญ(” กับ “)” ออกมา

จากนั้นค่อยทำเพิ่มด้วย Column From Example เหมือนเดิมทีละส่วน เดี๋ยวมันคิดสูตรให้เอง (เช่น ในที่นี้มันจะเอาข้อมูลเก็บไว้แค่เลข 0-9 และ จุด)
ซึ่งวิธี Power Query แม้จะยุ่งยากกว่า Flash Fill แต่ดีกว่าตรงที่สามารถ Refresh ผลลัพธ์ได้นั่นเอง ซึ่งผมว่ามันเจ๋งมากๆ เลย ที่เราได้ผลลัพธ์มาได้อย่างง่ายๆ โดยไม่ต้องเขียนสูตรที่ซับซ้อนเลย แค่กดปุ่มไปเรื่อยๆ

ดังนั้นสรุปว่า Excel ก็สามารถดึงข้อความที่ไม่ซับซ้อนมากได้อย่างง่ายๆ และเร็วมาก ในขณะที่โปรแกรมอื่นก็จะรองรับการดึงรูปแบบที่ซับซ้อนได้ แต่ต้องใช้เวลาฝึกฝนเรียนรู้วิธีการเขียนคำสั่งให้มันทำงานได้เช่นกัน
สุดท้ายผมหวังอย่างยิ่งว่า Excel จะเอาความสามารถของ Regular Expression มาใช้เร็วๆ (จริงๆ Excel ก็ใช้ Regular Expression ได้ แต่ต้องใช้ผ่าน VBA ซึ่งทำให้ยุ่งยากเกินไป) เพราะมันเจ๋งกว่า wildcard ของ Excel หลายร้อยเท่าเลย ใครอยากได้ให้มันอยู่ในฟังก์ชันของ Excel ก็ไปช่วย Vote ได้ที่นี่
คิดว่าเกือบทุกท่านน่าจะรู้จักโปรแกรมวาดรูปเชิง Vector (หมายถึงรูปที่ขยายแล้วไม่แตกน่ะ) ที่โด่งดังที่สุดอันนึงในวงการ Graphic นั่นก็คือ Adobe Illustrator นั่นเอง ซึ่งมันก็ออกแบบมาให้ทำกราฟิกโดยเฉพาะนี่แหละ ดังนั้นมันจึงวาดรูปได้สวยงามแน่นอน แต่มันก็มีราคาแพงพอสมควร ซึ่งก็ทำให้หลายคนก็หันไปใช้โปรแกรมอื่นๆ ที่ราคาถูกลง หรือฟรีเลย แต่ก็อาจจะได้ความสามารถน้อยลงไปด้วยเล็กน้อย

Excel ก็สามารถวาดรูปเชิง graphic แบบ vector ได้นะ ซึ่งมันถูกออกแบบมาให้ทำรูปทรงง่ายๆเพื่อประกอบข้อมูลให้เข้าใจง่ายขึ้น แต่เอาจริงๆ วาด Shape ได้ทุกแบบ แถมไล่สีแบบ Gradient ก็ได้ ปรับ Opacity ก็ได้นั่นแหละ
แต่ก็มีตัวอย่างของการใช้ Excel มาวาดรูปสวยๆ แบบเว่อร์ๆ แบบที่ต้องใช้ความอดทนระดับเทพเจ้า (ซึ่งพวกเราคงทำแบบนี้ไม่ไหวหรอกมั้ง 55) เช่นกัน เค้าเรียกกันว่า “Excel Art” อย่างเช่น
ตัวอย่างที่เป็นรูปตัวการ์ตูนก็มีคนทำเยอะนะ เพราะสวยดี และไม่ซับซ้อนมากนัก เช่น อันนี้เป็นรูป Naruto


รวมถึงผลงานของคุณลุงชาวญี่ปุ่นผมเคยได้พูดถึงในบทความเก่า 6 ปีก่อน ถ้าใครไม่เชื่อว่าทำจาก Excel ก็โหลดไฟล์นี้ไปดูเล่นได้ นี่ผมก็เอามาเปิดดู (แค่เปิดก็จะ hang ละ ไม่รู้ว่าทนวาดจนจบได้ไง…) ใครสนใจรูปอื่นๆ ก็ไปดูใน Gallery ได้ที่นี่

หรือถ้าจะมาอีกแนวก็คือ ใช้การสร้างกราฟใน Excel มาช่วยทำกราฟฟิกแทน เช่น รูป Excel Skill Map ที่ผมเองทำขึ้นมาโดยใช้ กราฟแบบ Scatter Plot

แม้ว่า Excel จะทำ Dashboard ได้ผ่านการใช้ Pivot Table, Pivot Chart และ Slicer ผสมกัน แต่มันก็ยังสู้โปรแกรมที่ออกแบบมาเพื่อทำ Dashboard โดยเฉพาะอย่าง Power BI ไม่ได้
แม้จะคำนวณทุกอย่างได้เหมือนกัน มี Power Query, Data Model, DAX เหมือนกันทุกอย่าง แต่สิ่งที่ Excel สู้ Power BI ไม่ได้เลยก็คือตัว Visual หรือตัวกราฟต่างๆ ซึ่ง Power BI มีความ Interactive มากกว่าเยอะ
Power BI สามารถให้ User กดที่กราฟนึง แล้วส่งผลไปที่อีกกราฟนึงได้ แถมยังมีกราฟหลากหลายรูปแบบที่ยังโหลดเพิ่มมาจาก Internet ได้อีก และยังรองรับการเขียน Script เพิ่ม ด้วยภาษา Python และ R อีกต่างหาก
นอกจากนี้ Power BI ก็ออกแบบมาเพื่อการแชร์ข้อความให้คนอื่นใช้งาน Dashboard ได้ง่ายแบบ Online ได้ทันที ซึ่งประเด็นนี้ Excel ก็ทำไม่ได้ครับ
หากสนใจเรื่องของ Power BI ผมก็มีเขียนบทความเป็น Series Power BI ไว้เลยที่นี่
สรุปแล้ว ถ้าจะสร้าง Dashboard ให้ผู้บริหารกดเล่นได้ ผมคิดว่า Excel สู้ Power BI ไม่ได้โดยสิ้นเชิงเลยครับ นอกเสียจากผลลัพธ์ที่อยากได้เป็นตารางที่อยากให้ User แก้ข้อมูลเล่นได้ แบบนี้ Excel อาจจะดีกว่า
Excel นั้นมี add-in ที่ชื่อว่า Solver ซึ่งสามารถ Solve หาจุดที่ได้ค่า Max, Min ภายใต้ Constraint หรือข้อจำกัดที่เรากำหนด โดยสามารถเปลี่ยนค่าตัวแปรไปเรื่อยๆ ได้จำนวนสูงสุด 200 ตัวแปรด้วยกัน แถมยัง Solve ได้ทั้งแบบที่ Input กับ Output สัมพันธ์กันแบบ Linear (ต่อเนื่องเส้นตรง), Non-Linear (ต่อเนื่องแต่ไม่ตรง) และ Evolutionary (ไม่ต่อเนื่อง)
แค่นี้จริงๆ ก็มีประโยชน์มากๆ แล้วในสถานการณ์ปกติทั่วไป เช่น
หากว่าจำนวนตัวแปร เช่น ชนิดสินค้า จำนวนเส้นทาง มีไม่เกิน 200 ตัวแปร ก็จะทำได้ไม่มีปัญหา เช่น ในรูปนี้ เป็นการ Solve ว่าภายใต้ Shipping Cost ที่แตกต่างกันในแต่ละจุด เราจะส่งสินค้าไปให้ลูกค้าตาม demand ที่ต้องการได้ยังไงโดยที่ cost ต่ำที่สุด เช่น คนไหนควรส่งผ่าน Warehouse ก่อน กี่อัน มันก็จะตอบให้หมด และมีจำนวนตัวแปรคือช่องสีเหลืองไม่เกิน 200 ช่อง ก็สามารถใช้ได้สบายๆ

แต่ถ้าตัวแปรเกิน 200 เราก็จะใช้ Excel Solver ตรงๆ ไม่ได้ ก็ต้องเปลี่ยนมาใช้ add-in อื่นๆ หรือ ใช้โปรแกรมอื่นแทนครับ แต่ผมเองไม่เชี่ยวชาญด้านนี้ ที่แน่ๆ มันมีหลายโปรแกรมมากๆ ทั้งฟรีและไม่ฟรี อันที่ผมเคยเล่นสมัยมหาลัยก็มีโปรแกรม Matlab ซึ่งมีความสามารถที่สูงมากในด้านการคำนวณด้านคณิตศาสนตร์และวิทยาศาสตร์รวมถึงการทำ Model ต่างๆ และก็สามารถทำเรื่อง Optimization ได้ด้วยเช่นกัน ใครสนใจก็ลองดูคลิปได้
Excel มีภาษา VBA ซึ่งเป็นภาษา Visual Basic for Application สามารถใช้เขียนโปรแกรมเพื่อควบคุมสิ่งต่างๆ ใน Excel และโปรแกรม Microsoft Office อื่นๆ ได้สะดวก (รวมถึงโปรแกรม Microsoft อื่นๆ เช่น IE, ตัว Windows เอง)
เนื่องจากการที่ Excel VBA ออกแบบมาเพื่อทำงานกับ Excel โดยเฉพาะ มันจึงอาจจะทำงานกับ Excel ได้ดีและละเอียดกว่าการใช้โปรแกรมอื่นๆ เช่น Python ที่ต้องอาศัย Library พิเศษต่างๆ มาช่วยทำงาน
อย่างน้อยที่สุด VBA สามารถสร้างได้ง่ายๆ ด้วยการ Record Macro ซึ่งคือการบันทึกการกระทำของเราทั้งหมดออกมาให้เป็น VBA Code ซึ่งช่วยให้เราเขียนโปรแกรมได้เร็วขึ้น ใครสนใจลองอ่านได้ที่นี่
สิ่งที่เหมาะอีกอย่างคือโปรแกรมที่มีผลลัพธ์อยู่ในรูปแบบตาราง เช่น ทำเกมแนว board เกม เป็นต้น

ดังนั้น ในความเห็นส่วนตัวผม ถ้าเป็นมือใหม่แล้วจะเขียนโปรแกรมเพื่อจัดการสิ่งต่างๆ ใน Excel หรือทำอะไรที่เป็นตารางๆ เราใช้ VBA จะง่ายกว่าตรง Record Macro ได้ด้วยและ User สามารถAccess ตารางได้ง่าย
นอกจากนี้จริงๆ ใน Excel มันก็เขียนโปรแกรมทำเรื่องต่างๆ ได้เหมือนโปรแกรมอื่นแหละ แต่อาจจะยากกว่า เช่นที่ผมใช้ Excel ส่ง Notification ไปแจ้งเตือนใน Line เป็นต้น มันก็ทำได้ แต่ถ้าทำบนโปรแกรมอื่น น่าจะง่ายกว่านี้
ดังนั้นถ้าเขียนโปรแกรมคล่องแล้วหรืออยากจะควบคุมอย่างอื่นที่ไม่ใช่ Excel เราใช้โปรแกรมอื่น เช่น Python + Library เจ๋งๆ จะเขียนง่ายกว่าเยอะมากเลย โดยเฉพาะถ้าจะทำ Machine Learning ใน Excel คงไม่ไหวแน่เลย 55
นอกเหนือจากตัวอย่างที่ให้ไปจริงๆ แล้วก็ยังมีเรื่องอื่นอีกมากมายเช่น ใช้ Excel ทำงาน Project Management vs ใช้โปรแกรม Microsoft Project เป็นต้น แต่ผมขี้เกียจเขียนละ 555
สรุปแล้ว ทุกโปรแกรมก็มีข้อดีข้อเสียของมันนั่นแหละ โปรแกรมเฉพาะทางมักจะได้ผลลัพธ์ที่ดีกว่า แต่ก็จะทำงานได้แค่บางอย่าง ในขณะที่โปรแกรม Excel ที่มีความสามารถแบบ All-Around หรือที่เรียกว่าเป็ด ก็พลิกแพลงทำได้ทุกอย่าง แต่บางอย่างอาจจะไม่เหมาะ เลยยากกว่า
แต่ผมว่า “ยังไง Excel ก็ยังเก่งมากนะ เป็น Super เป็ดเลยล่ะ” เรียนรู้ไว้ไม่เสียหาย เพราะสามารถต่อยอดไปโปรแกรมอื่นได้ครับ เพราะสิ่งสำคัญที่สุดไม่ใช่เครื่องมือ แต่คือความคิดของเราเองต่างหาก
เพราะหากเรามี Logic ที่ดีเพื่อที่รู้ว่าควรแก้ด้วยแนวทางไหน (What to do)+ สามารถหาคำตอบใน Google ได้ว่าแนวทางนั้นๆ ทำด้วยโปรแกรมที่คุณเลือกยังไง (How to do?)
ถ้าทำได้ 2 ประเด็นนี้ โจทย์จะยากแค่ไหนก็สามารถแก้ได้อย่างแน่นอนครับ ^^

ในบทความนี้เราจะมาใช้ Matrix ช่วยค้นหาข้อมูลตามเงื่อนไขกันครับ ถ้าใครยังไม่ได้อ่าน 2 ตอนก่อนหน้าก็แนะนำให้อ่านก่อนจะได้เข้าใจนะ (โดยเฉพาะตอนที่ 2)
สมมติเรามี Data แบบนี้ แล้วเราจะหาว่ามีแผนกไหนบ้าง ที่ได้ยอดขายตามที่กำหนด
สมมติเราอยากจะ Detect เฉพาะยอดขายตั้งแต่ 70 ขึ้นไป ตามเงื่อนไขต่างๆ (ด้วยการฝึกใช้ Matrix) เช่น

การจะหาแบบนั้นได้ เราก็ต้องสร้าง Matrix เงื่อนไขที่ให้ผลลัพธ์เป็น 1,0 ก่อน ดังนี้ (ในวงเล็บจะได้ TRUE/FALSE แล้วพอจับคูณ 1 จะได้ 1,0)
=(C4:E7>=70)*1

ถ้าอยากรู้ว่า Dept แถวไหนบ้างที่ให้ค่าเป็น 1 กี่ตัวตามที่เราต้องการ เราก็ต้องทำการ Sum Total แต่ละแถว ให้ได้ก่อน ว่า Dept ๆ นั้นทำตามเงื่อนไขได้กี่เดือน
ซึ่งการหา Total แต่ละแถว เราได้เรียนรู้ไปในบทความก่อนหน้าแล้วไง
ดังนั้นเราลอง SUM แต่ละแถวก่อน ด้วยการเอาไปคูณ Matrix 3×1 ที่เป็นเลข 1 ทั้งหมด ซึ่งคราวนี้ผมจะ Manual Array {1;1;1} ลงไปในสูตรก่อนเพื่อความ Simple ในความเข้าใจ
=MMULT(G4#,{1;1;1})
G4# เป็นการอ้างอิง Array ที่ Spill ออกมาทั้งอัน (ถ้าเป็น Excel version เก่า ก็คือ G4:I7 นั่นแหละ)

ถ้าอยากรู้ว่าทำได้อย่างน้อย 1 เดือน ก็เช็คว่า Sum แล้วได้มากกว่าหรือเท่ากับ 1 หรือไม่ แล้วแปลงเป็น 0,1 ซะ ด้วยการคูณ 1 หรือจะใส่ — ข้างหน้าก็ได้ ซึ่งผมจะใส่ — แล้วกันจะได้ดูแล้วไม่งงกับเลข 1 ที่จะใช้เทียบ
=--(MMULT(G4#,{1;1;1})>=1)

ถ้าเขียนรวบทั้งหมดเข้าด้วยกันจะเป็นแบบนี้
=--(MMULT((C4:E7>=70)*1,{1;1;1})>=1)

และถ้าไม่อยากจะ Manual พิมพ์ Matrix 3×1 ที่เป็นเลข 1 แต่เขียนสูตรแบบ Dynamic ตามขนาดของ Data จริงได้ทั้งหมด ก็จะใส่ TRANSPOSE(COLUMN(C4:E7)^0) ลงไปแทน {1;1;1} ก็จะได้แบบนี้
=--(MMULT((C4:E7>=70)*1,TRANSPOSE(COLUMN(C4:E7)^0))>=1)
=SUMPRODUCT(--(MMULT((C4:E7>=70)*1,TRANSPOSE(COLUMN(C4:E7)^0))>=1))

เราก็แค่เปลี่ยนเงื่อนไขเดิมเล็กน้อยจากหาอย่างน้อย 1 เดือน
=--(MMULT((C4:E7>=70)*1,{1;1;1})>=1)
เป็นต้องได้อย่างน้อย 2 เดือน
=--(MMULT((C4:E7>=70)*1,{1;1;1})>=2)
แค่นั้นก็จบแล้ว

ถ้าผลลัพธ์มี Dept เดียวที่ทำได้ตามที่เราต้องการ เช่นในตัวอย่างอันที่สอง เราจะแสดงค่า Dept ออกมาได้ง่ายหน่อย โดยใช้ MATCH หาว่าเลข 1 อยู่ลำดับที่เท่าไหร่ (มันจะเจอ 1 ตัวแรก ซึ่งถ้ามี Dept เดียวจะไม่มีปัญหา) แล้วค่อยใช้ INDEX ถึงชื่อ Dept ตามลำดับที่ MATCH เจอ
=MATCH(1,--(MMULT((C4:E7>=70)*1,{1;1;1})>=2),0)
ซึ่งจะได้ว่าเจออยู่ในลำดับที่ 4
แล้วเราค่อยใช้ INDEX ดึง Dept ออกมา
=INDEX(B4:B7,MATCH(1,--(MMULT((C4:E7>=70)*1,{1;1;1})>=2),0))
เช่นในตัวอย่างแรก ที่มี 2 Dept ที่ทำได้ แบบนี้จะใช้ MATCH อย่างเดียวทื่อๆ ไม่ได้แล้ว เพราะมันจะเจอแค่ตัวแรก แต่ถ้าใครมี Office 365 ที่มีฟังก์ชัน Filter ล่ะก็ วิธีนี้จะง่ายกว่า MATCH อีก เช่น
จากเดิมที่เราทำไว้ว่า

เราก็แค่ใช้ฟังก์ชัน FILTER เอาเฉพาะ Dept ที่ได้ 1 ก็จบเลย
=FILTER(B4:B7,G4#)

และถ้าอยากให้มาแสดงในช่องเดียว คั่นด้วย Comma ก็ใช้ TEXTJOIN ได้อีก เช่น
=TEXTJOIN(", ",TRUE,FILTER(B4:B7,G4#))

และหากอยากจะรวบทุกอย่างเข้าด้วยกัน ไม่ทด 1,0 ออกมาข้างนอก ก็จะเป็นแบบนี้
=TEXTJOIN(", ",TRUE,FILTER(B4:B7,--(MMULT((C4:E7>=70)*1,TRANSPOSE(COLUMN(C4:E7)^0))>=1)))

หรือถ้าไม่ใช้ FILTER เราจะใช้ IF มาช่วยก็ได้ (คุณโบ Excel Wizard มาช่วย Comment สูตรให้ประมาณนี้)
=TEXTJOIN(", ",TRUE,IF(MMULT(N(C4:E7>=70),TRANSPOSE(COLUMN(C4:E7))^0),B4:B7,""))
ซึ่ง N ก็จะเหมือนกับการเอาไป *1 นั่นแหละ และค่าไหนที่ logical_test ของ IF ออกมาเป็น 0 ก็จะได้ FALSE ซึ่งจะกลายเป็น “” ซึ่งเป็นค่าว่าง ทีนี้ TEXTJOIN สามารถ Skip ค่าว่างได้ ก็เลยสามารถใช้ได้เลยโดยไม่ต้อง FILTER ก็ได้
สำหรับบทความนี้จบเท่านี้ดีกว่า เพราะเริ่มยากละ 555 เดี๋ยวไว้ดูตัวอย่างอื่นที่น่าสนใจกันอีกในบทความหน้า

ในตอนที่แล้วเราได้เรียนรู้การใช้ Matrix ใน Excel เพื่อแก้สมการกันไปแล้ว ในบทความนี้เราจะมาลองดูตัวอย่างการประยุกต์ที่น่าสนใจอีกอันก็คือการนำมาสรุปข้อมูลในแต่ละแถวหรือแต่ละคอลัมน์กันครับ
สมมติเรามีข้อมูลต้นฉบับเป็น Array หรือ Matrix 4×3 ดังนี้ (ก็พิมพ์ลงไปตรงๆ นั่นแหละ) มาดูซิว่าเราจะทำอะไรกับมันได้บ้าง?

หากอยากจะหาผลรวมแต่ละแถว ปกติแล้วเราก็จะสามารถ SUM แต่ละแถวง่ายๆ ได้เลยเนอะ แต่เพื่อวัตถุประสงค์ในการทำความเข้าใจกับ Matrix ให้มากขึ้น (เพื่อรองรับเคสที่ซับซ้อนขึ้นในอนาคต) เราจะลองมาดูกันว่า ต้องคูณ Matrix ยังไงถึงจะได้ผลรวมแต่ละแถว
เดิม Matrix นี้เป็น 4×3 ซึ่งถ้าจะได้ผลรวมแถว มันจะต้องออกมาเป็น Matrix ผลลัพธ์ 4×1
นั่นคือ มันต้องไปคูณกับ Matrix อะไรซักอย่างซึ่งมี 3 แถว 1 คอลัมน์แน่นอนด้วยเหตุผลตามรูปนี้

ซึ่งมันจะจับคู่แถวใน Matrix แรกมาคูณกับคอลัมน์แรกของ Matrix ที่สอง ซึ่งเราต้องการทั้งเลข 10, 85, 75 มาบวกกันหมดเลย แสดงว่ามันต้องคูณ 1 ทั้งหมดแน่นอน นั่นคือ Matrix ที่สองต้องเป็นเลข 1 หมดเลย เพื่อที่จะให้เป็น 10*1+ 85*1 + 75*1
ดังนั้นเขียนสูตรได้ว่า
=MMULT(B7:D10,F2:F4)

ซึ่งถ้าเราไม่อยากจะมาทด Matrix ที่มีเลข 1 แบบนั้น เราก็ต้องสร้างมันขึ้นมา เช่น ใช้เลขประจำ COLUMN ของ Matrix แรกมาทำให้เป็น 1 ด้วยการยกกำลัง 0 แล้ว Transpose เปลี่ยนจากแนวนอน เป็นแนวตั้ง ก็ได้ เช่น
=MMULT(B7:D10,TRANSPOSE(COLUMN(B7:D10)^0))

ถ้าคิดในทำนองเดียวกันกับอันแรก จะได้ผลลัพธ์เป็น 1 แถวx 3คอลัมน์ มันก็ต้องมี Matrix 1 x 4 มาคูณด้วยข้อมูลต้นฉบับของเรา และมันก็ต้องเป็นเลข 1 หมดเลยด้วย เพราะเราเอาทุกค่าคือ 1*10 + 1*30 + 1*70 +1*30
และสูตรก็คือ
=MMULT(A8:D8,F2:H5)

และถ้าไม่อยากมาพิมพ์ Matrix 1×4 ที่เป็นเลข 1 ทดไว้ ก็สามารถสร้าง Matrix จำลองในลักษณะคล้ายๆ เดิมได้ว่า
=MMULT(TRANSPOSE(ROW(F2:H5)^0),F2:H5)
เพียงแต่เปลี่ยนมาเอาจำนวนแถวของ Matrix Original มาทำให้เป็น 1 ทั้งหมด แล้ว Transpose มาเป็นแนวนอน

และนี่ก็เป็นการใช้ Matrix เริ่มมาประยุกต์ทำอะไรที่เกี่ยวกับ Excel โดยตรงมากขึ้น ตอนหน้าจะเริ่มน่าสนใจมากขึ้นแล้ว เช่น ใช้ Matrix ช่วยหาข้อมูลในหลายๆ คอลัมน์เป็นต้น ว่าข้อมูลที่ต้องการอยู่ในแถวไหนบ้าง ^^

เอาจริงๆ แล้วตั้งแต่เรียนวิชาคณิตศาสตร์มาในสมัย ม.ปลาย ส่วนตัวผมก็แทบไม่เคยได้ใช้เจ้า Matrix เลย และตอนเรียนสมัยเด็กๆ ก็ไม่รู้ด้วยว่าเรียนไปทำไม… (แต่ถ้าเป็นหนัง Matrix นี่ชอบมาก หนังในดวงใจผมเลย 55)
พอโตมาถึงเพิ่งรู้ว่ามันมีประโยชน์หลากหลายมากๆ และใช้ในหลายๆ สาขา เช่น การ Transform รูปภาพต่างๆ ในวงการ Graphic Design ก็ใช้ Matrix คำนวณทั้งนั้น อีกทั้งการทำพวก AI หรือ Machine Learning ก็ใช้ความรู้ Matrix ด้วยเช่นกัน
แล้วใน Excel ล่ะ? เราจะใช้ Matrix มาทำอะไรได้บ้าง ในบทความนี้จะใช้ Matrix แก้สมการเส้นตรงหลายตัวแปรซึ่งเป็นเรื่องการคำนวณที่ตรงไปตรงมามากที่สุดอันนึงของการใช้ Matrix เลยล่ะ
สมมติมีโจทย์เป็นสมการเส้นตรง 3 ตัวแปรว่า
ให้หาว่าค่า x, y, z คือเท่าไหร่?
การจะเข้าใจเรื่องนี้ได้ถ่องแท้ เดี๋ยวผมทบทวนความรู้เรื่องการคูณ Matrix ให้เล็กน้อย
มันคือเอาข้อมูลแนวนอนจาก Matrix ตัวแรก มาคูณกับข้อมูลแนวตั้งของ Matrix ตัวที่สองแล้วหาผลรวม ทำแบบนี้ทีละพิกัดจนครบทุกแถวของ Matrix ตัวแรก และครบทุกคอลัมน์ของ Matrix ตัวที่สอง ทำให้สรุปได้ 2 ประเด็นสำคัญว่า
แต่โชคดีที่ใน Excel เรามีฟังก์ชันที่ใช้ได้เลย คือ MMULT ซึ่งย่อมาจาก Matrix Multiplication (คูณ Matrix)
=MMULT(Matrix A, Matrix B) =MMULT(B11:D12,G3:H5)
** หากเป็น Excel Version เก่าที่ไม่รองรับ Dynamic Array จะต้องลากคลุม 4 ช่องก่อนใส่สูตร แล้วต้องกดเรียกใช้สูตรด้วยคำสั่ง Control+Shift+Enter แทนการกด Enter ธรรมดาด้วย

จากสมการข้างบน และความรู้จากวิธีการคูณ Matrix เราสามารถมองได้แบบนี้

นั่นคือ ถ้าหากว่า Matrix A คูณ Matrix X ได้ Matrix B…
หากเราต้องการหาว่า Matrix X มีค่าเท่าไหร่? มันก็คือ A Inverse คูณกับ B นั่นเอง (สลับกันไม่ได้นะ เพราะ Matrix ไม่มีคุณสมบัติการสลับที่ของการคูณ)
ถ้าเป็นคณิตศาสตร์ที่ต้องคำนวณเองก็จะยากหน่อย แต่ถ้าเป็น Excel ก็ง่ายเลย!! เราสามารถหาค่าของ Matrix X ได้แบบนี้
=MMULT(MINVERSE(A8:C10),G8:G10)
** หากเป็น Excel Version เก่าที่ไม่รองรับ Dynamic Array จะต้องลากคลุม 3 ช่องก่อนใส่สูตร แล้วต้องกดเรียกใช้สูตรด้วยคำสั่ง Control+Shift+Enter แทนการกด Enter ธรรมดาด้วย

ซึ่งผลลัพธ์จะออกมาเป็น 3 ค่า ซึ่งก็คือค่าของ x, y, z ตามลำดับ
Tips : หากไม่ชอบผลลัพธ์ในแนวตั้ง ก็สามารถเอา TRANSPOSE ครอบไปให้เป็นแนวนอนก็ได้ แต่ผมว่าแนวตั้งก็ ok อยู่นะ
และนี่คือตัวอย่างการใช้ Matrix แบบตรงไปตรงมามากๆ โดยใช้ในการแก้สมการเส้นตรงหลายตัวแปร แต่เดี๋ยวในบทความต่อไปจะให้ดูตัวอย่างการพลิกแพลงเอา MMULT มาทำอะไรแปลกๆ ให้ดูครับ

การรวมข้อมูลจาก Excel หลายๆ ไฟล์ใน Folder หากข้อมูลแต่ละอันเป็น Table ที่มีหัวตารางเรียบร้อย การรวมจะใช้เครื่องมืออัตโนมัติกด Combine ได้เลย หรือจะใช้สูตร Excel.Workbook รวมได้เลยแบบง่ายๆ ซึ่งรายละเอียดอยู่ใน https://www.thepexcel.com/get-data-from-excel/
แต่ถ้าข้อมูลในแต่ละชีท ไม่ได้อยู่ในรูปแบบที่เหมาะสม เช่น ต้องมีการ Transform ข้อมูลก่อน ถึงจะเอามา Combine รวมกันได้ แบบนี้จะใช้เครื่องมืออัตโนมัติไม่ได้แล้ว และในชีวิตจริงเราก็มักจะเจอกับข้อมูลแบบนี้ซะด้วย
พูดง่ายๆ สถานการณ์คือ
1 Folder มีหลายไฟล์
1 ไฟล์มีหลาย Sheet
แต่ละ Sheet มีความเน่า !!
ในคลิปจะแสดงอีกแนวทางนึง ที่ผมคิดว่าง่ายกว่าเดิมนิดหน่อย ลองดูได้ครับ
แนวทางจะเป็นแบบนี้ครับ
เราจะทำการรวมข้อมูลจาก Excel ทุกไฟล์ใน Folder ซึ่งในแต่ละไฟล์มีข้อมูลหลายชีทที่หน้าแบบนี้ ซึ่งแต่ละชีทเป็นรหัสพนักงานขาย และชื่อไฟล์เป็นปีที่ขาย

แต่ลองดูข้อมูลในชีทสิ มันอยู่ในรูปแบบที่ไม่ใช่ข้อมูลตาราง Database เลยซักนิด! หัวตารางดันอยู่บรรทัดที่ 5 แถมดันมีรหัสลูกค้าเป็นหัวตารางหลายๆ คอลัมน์อีก ซึ่งทั้งหมดนั้นควรจะมาอยู่ในคอลัมน์เดียวกัน (แปลว่าต้องมีการใช้ unpivot พลิกลงมา) และต้องกำจัดพวก subtotal/total ที่เกินๆ มา และต้องมีการ Fill Down ถมสินค้ามาให้เต็มด้วย ซึ่งจะเห็นว่าต้อง Transform หลายอย่างเลย (แต่ปัญหาหลักคือเรื่องหัวตารางนั่นแหละ)
ดังนั้นเราจะต้องใช้ฟังก์ชันรับ input ข้อมูลแต่ละชีทที่เน่าๆ มาจัดการดัดแปลงให้เป็น output ที่เรียบร้อยซะก่อนแล้วค่อยเอามารวมกัน ซึ่งเราจะเขียน Query ขึ้นมาแล้วแปลงเป็นฟังก์ชันได้ง่ายๆ ด้วยการใส่ (input)=> เข้าไปใน M Code แต่ว่าวิธีนี้จะทำให้แก้ M Code ด้วย UI ง่ายๆ ไม่ได้…
จากในบทความ https://www.thepexcel.com/4-steps-function-query/ ผมได้อธิบายวิธีการสร้างฟังก์ชันใน Power Query แบบที่สามารถปรับแก้ Step ด้วย User Interface ง่ายๆ ได้อยู่ ซึ่งในบทความก่อนผมใช้ Parameter ที่เป็นประเภท Text ซึ่งจะเป็นแบบง่าย แต่ในบทความนี้เราจะใช้ Parameter ที่เป็น Table ซึ่งจะยุ่งยากกว่า แต่มีประโยชน์สุดๆ ไปเลยครับ ดังนั้นใครอยากรู้ พลาดไม่ได้เด็ดขาด
เปิดไฟล์ Excel ใหม่ขึ้นมาแล้ว Get Data from Folder แล้วกด Transform แล้วตั้งชื่อ Query เป็น MyResult ซะ (ถ้าอยากให้ Dynamic ก็ทำ Path เป็น Parameter ซะจะได้เปลี่ยนได้ง่าย แต่ในบทความนี้ผมขี้เกียจทำ 55)

จากหลักการ 4 ขั้นตอนที่บอกไปในบทความก่อน ดังนี้
ในบทความนี้เราก็จะใช้หลักการเดียวกันนั่นแหละ แต่มันจะมีความยุ่งยากเกิดขั้นในขั้นตอนแรกสุด นั่นก็คือการสร้าง Parameter ขึ้นมา เพราะว่าฟังก์ชันของเราคราวนี้เป็นการ Transform ตัวข้อมูลในแต่ละชีทซึ่งมี Data Type เป็น Table หรือตารางนั่นเอง (พูดถึงเรื่อง Data Type ของ PQ ซึ่งเป็นคนละเรื่องกับ Table ของ Excel นะ)
การที่ Parameter เป็นแบบ Table นั้น ในความเป็นจริงต้องสร้าง Parameter ที่มี Type แบบ Any แต่ถ้าเราเลือก Any ไปตรงๆ ตัวหน้าตา Parameter มันจะไม่ยอมให้ไปต่อ เราก็เลยต้องเลือกหลอกๆ ให้เป็น Binary ก่อน และการจะเลือกแบบ Binary ได้ เราก็ต้องสร้าง Query ที่ชี้ไปที่ตัวไฟล์ Binary ซึ่งก็คือไฟล์ Excel ของเราก่อนอีกทีนึง (เห็นมะว่ายุ่งยากแค่ไหน 555)
ดังนั้นผมจะขอเพิ่ม Step0 ขึ้นมา นั่นก็คือการเตรียม Sample สำหรับ Parameter ดังนี้
ใน MyResult ให้คลิ๊กขวาที่คำว่า Content แล้ว Remove Other Columns

แล้วคลิ๊กขวา Drill Down ลงไปใน Binary อันแรกสุด แล้วลบ Step สุดท้ายออกซะ ให้เหลือเท่านี้ (มันแปลว่าจะอ้างอิงไปที่ไฟล์แรกใน Folder เพราะเป็น index 0)

มันจะได้ Query ใหม่ที่อ้างไปถึง Binary ตามที่เราต้องการ ให้เราเปลี่ยนชื่อ Query เป็น MyValue ซะ
จากนั้นก็กลับไปสู่ Step ปกติได้ละ
กด Manage Parameter แล้ว New ขึ้นมาใหม่ สมมติชื่อว่า MyPara ซึ่งให้เลือก Type หลอกเป็น Binary ก่อน แล้วเลือก MyValue ไปเป็น Default Value และ Current Value ซะ

จากนั้นกด Advanced Editor แล้วให้แก้ Code เปลี่ยนประเภทจาก Binary เป็น Any (เพราะ input ที่เป็น Table ต้องเป็น Any)
MyPara meta [IsParameterQuery=true, BinaryIdentifier=MySample, Type="Binary", IsParameterQueryRequired=true]
MyPara meta [IsParameterQuery=true, BinaryIdentifier=MySample, Type="Any", IsParameterQueryRequired=true]
จากนั้นให้กลับไปใน Query MyValue เพื่อให้อ้างอิงไปที่ Table จริงๆ โดยการ Double Click ที่ตัวไฟล์เพื่อ Drill Down ลงไป (เดิมเราอ้างอิงไปที่ตัวไฟล์)
จากนั้นเราคลิ๊กขวาที่คอลัมน์ Data แล้ว Remove Other Columns –> แล้วคลิ๊กขวา Drill Down ลงไปใน Table อันแรกสุด
จากนั้น MyValue ของเราก็จะอ้างอิงไปที่ Table แล้วล่ะ

เนื่องจาก Parameter เราเป็นข้อมูลที่มี Data Type แบบ Table ดังนั้นเราจะสร้าง Query ต้นแบบขึ้นมาใน Blank Query ใหม่ โดยอ้างอิงไปที่ Parameter ตรงๆ เลย
แค่เขียนสูตรว่า =MyPara
จากนั้นตั้งชื่อ Query ว่า MySteps

แค่กดคลิ๊กขวาที่ MySteps แล้วกด Create Function

จากนั้นตั้งชื่อว่า MyFunction ก็ได้ (จะเห็นว่าฟังก์ชันนี้มี Parameter คือ MyPara)

จากนั้น PQ จะสร้าง Group ของ Query ให้เรา (เพื่อความเรียบร้อยเฉยๆ) และเพื่อให้เรียบร้อยขึ้น ให้เราลาก MyValue ขึ้นไปอยู่ Group MyFunction ด้วย

จากนั้นให้เราเข้า MySteps เพื่อทำการดัดแปลงข้อมูลตามใจชอบ ซึ่งฟังก์ชันเราจะทำการดัดแปลงตามสิ่งที่เราทำใน MySteps ทั้งหมดกับทุกๆ Sheet ก่อนจะเอามา Combine รวมกันนั่นเอง
ซึ่งสิ่งที่ผมทำมีดังนี้
สรุปใน MySteps ได้ออกมาแบบนี้

ซึ่งมี M Code ใน Advanced Editor ดังนี้
let
Source = MyPara,
#"Removed Top Rows" = Table.Skip(Source,4),
#"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true]),
#"Filled Down" = Table.FillDown(#"Promoted Headers",{"สินค้า"}),
#"Filtered Rows" = Table.SelectRows(#"Filled Down", each not Text.Contains([สินค้า], "Total")),
#"Removed Columns" = Table.RemoveColumns(#"Filtered Rows",{"Grand Total"}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Removed Columns", {"สินค้า", "วิธีการชำระเงิน"}, "Attribute", "Value"),
#"Renamed Columns" = Table.RenameColumns(#"Unpivoted Other Columns",{{"Attribute", "รหัสลูกค้า"}, {"Value", "ยอดขาย"}})
in
#"Renamed Columns"
เรากลับมาที่ MyResult เพื่อทำการเรียกใช้ฟังก์ชัน MyFunction ที่เพิ่งสร้างขึ้นมา แต่ฟังก์ชันเราต้องการ Input ที่เป็น Table ดังนั้นเราต้องทำการอ่านข้อมูลใน Binary ก่อนโดย Add Custom Column ขึ้นมาแล้วเขียนสูตรดังนี้
=Excel.Workbook([Content])

จากนั้นเราจะได้คอลัมน์ Custom ขึ้นมา ซึ่งคราวนี้เราต้องการข้อมูลที่เป็นชื่อไฟล์ด้วย ดังนั้นเราจะเก็บคอลัมน์ Custom กับ Name เอาไว้ ดังนั้น Remove Other Column ซะ แล้ว Expand เจ้า Custom ออกมา

จะพบว่าข้อมูลจริงที่ยังไม่ได้ Transform ซึ่งเราต้องใช้เป็นตัว Input ของฟังก์ชัน อยู่ใน Data นี่แหละ

ดังนั้นเราจะเรียกใช้ฟังก์ชันของเราโดย Invoke Custom Function

จะพบว่าจะได้คอลัมน์ MyFunction ใหม่ ที่เป็น Output แบบ Table ที่แปลงข้อมูลเรียบร้อยแล้ว

เราต้องการชื่อไฟล์ ชื่อชีท และ Data หลังแปลงแล้ว ดังนั้นเราจะเก็บไว้แค่คอลัมน์ Name, Item และ MyFunction จากนั้น Expand MyFunction ออกมา

Extract Name แบบ Before Delimiter จุด เปลี่ยนชื่อคอลัมน์ให้เรียบร้อย และเปลี่ยน Data Type ก็จะได้ดังภาพเป็นอันจบ

ถ้าอยากจะปรับการ Transform ก่อนจะรวมไฟล์ ก็ไปทำที่ MySteps ได้เลยแบบง่ายๆ นี่แหละคือข้อดีของการสร้าง Function แบบใช้ Parameter จริงจังครับ
ถ้าใครอ่านแล้วสงสัยตรงไหนก็ถามได้ หรือถ้าอยากดูแบบคลิป vdo ก็มีที่คุณโบ Excel Wizard เคยทำไว้ดังนี้ครับ (อย่างที่บอกในบทความที่แล้วว่าผมเอาเทคนิคนี้มาจากคุณโบนี่แหละ 55)

ในตอนที่แล้วผมได้พูดถึงเรื่องการทำ Data Model ที่รองรับการแสดงรายงานแบบ Target vs Actual ไปแล้ว แต่ว่าเราแสดงเป็น Target ระดับเดือนอยู่ ซึ่งหากว่าเราอยากจะแสดง ยอดขายที่ละเอียดแต่ละวันด้วย ดังนั้นเราก็จะต้องแจกเป้า หรือ Allocate Target ให้ลงมาถึงที่ระดับวันด้วยนั่นเอง
อีกประเด็นนึงก็คือ ถึงเราไม่ต้องการจะลงรายละเอียด Target ถึงระดับวัน แต่ถ้าใน Visual ดันดึงข้อมูลระดับวันมา มันก็จะมี Target ทั้งเดือนอยู่ที่วันแรกหมดเลย ซึ่งดูไม่ Make Sense ดังรูป ซึ่งก็อาจจะต้องจัดการอยู่ดี
ใช้ไฟล์จากบทที่แล้วต่อ หรือโหลดอันนี้ได้เลย

แนวคิดเบื้องต้นก็คือ เราจะหา Target เฉลี่ยของแต่ละวันให้ได้ก่อน แล้วค่อยเอามาแสดงค่า ขึ้นอยู่กับว่า Filter Context ของ Visual ที่แสดงอยู่มันมีกี่วัน
การจะหา Target เฉลี่ยของแต่ละวันเราจะใช้ DAX คำนวณก็ได้ หรือจะทำตั้งแต่ใน Power Query ก็ได้เช่นกัน แต่เพื่อให้ง่าย เราจะใช้ Power Query ทำ โดยกด Transform Data แล้วเข้าไป Edit Query ของ fTarget ดังนี้
หาจำนวนวันในเดือน โดยคลิ๊กที่วันที่ แล้วเลือกตามรูป

จากนั้นค่อยเอา Target รายเดือน มาหารด้วยจำนวนวันในเดือน

จากนั้นเราก็จะได้ Target รายวันมา ซึ่งผมขอตั้งชื่อว่า DailyTarget

กด Close & Apply เพื่อ Load ข้อมูลเข้า Data Model
จากนั้นลองสร้าง Measure ใหม่ โดย SUM ค่าของ Daily Target มาดังนี้
TotalTargetNew = SUM(fTarget[DailyTarget])
หากลองลากเข้า Visual ก็จะเป็นดังนี้

จะเห็นว่ามันเป็น Target ระดับวันแล้ว แต่มีแค่วันเดียว สิ่งที่เราจะต้องคิดคือเราจะต้องทำให้มันโผล่ขึ้นมาทุกวัน
โดยหลักการคือ เราต้องใช้ DAX ดึงค่า Target จากวันที่ 1 ของแต่ละเดือน มาโปรยลงวันอื่นๆ ด้วย ซึ่งเราจะใช้ CALCULATE ในการเปลี่ยน Filter Context ของวันอื่นๆ ให้กลายเป็นวันที่ 1 ให้หมดเลย ดังนี้
ThisMonthDailyTarget =
VAR currentDate =
FIRSTDATE ( dDate[Date] )
VAR currentYear =
YEAR ( currentDate )
VAR currentMonth =
MONTH ( currentDate )
RETURN
CALCULATE (
[TotalTargetNew],
dDate[Date] = DATE ( currentYear, currentMonth, 1 )
)

จะเห็นว่าเราสามารถแสดง Target ลงระดับวันได้แล้ว
แต่ว่า… หากเราเอาแกนเลขวัน DayNum ออกจาก Visual แล้ว ตัว Measure ของเราก็จะผิดทันที เพราะมันก็จะกลายเป็น Target ของวันเดียว แต่ตัว TotalTarget เรายังใช้ได้นี่!

ดังนั้นเราอาจจะสร้าง Measure ตัวใหม่ขึ้นมา เป็นตัวตัดสินว่าจะใช้ Measure ตัวไหนมาแสดงดี ขึ้นกับว่าในรายงานมันแสดง Dimension ละเอียดถึงระดับวันหรือไม่? ดังนี้
SelectTarget =
IF ( HASONEVALUE ( dDate[DayNum] ), [ThisMonthDailyTarget], [TotalTarget] )
ถ้า DayNum ไม่ได้มีค่าเดียว (แสดงว่าไม่ใช่ระดับวัน) เราก็แสดง TotalTarget

แต่ถ้า DayNum มีค่าเดียว (แสดงว่าเป็นระดับวัน) เราก็แสดง ThisMonthDailyTarget ได้แล้ว

วิธีข้างบนจะใช้ได้ ก็ต้อเมื่อเราเลือกว่าจะแสดงระดับเดือน หรือระดับวันเดียวไปเลย (ไม่ได้เลือกวันที่แค่บางช่วงของเดือน) ซึ่งจะมีปัญหาทันทีหากเลือกวันที่แค่บางช่วง เช่น ดูยอดแค่ 10 วันแรก มันก็ดันเอา Target ทั้งเดือนมาใช้อยู่ดี (เพราะว่ามี DayNum มากกว่า 1 Value เลยไปใช้ระดับ Month) ดังรูป

ดังนั้น ถ้าจะให้เนียนขึ้น เราอาจคิดว่า ThisMonthDailyTarget มาคูณด้วยจำนวนวันใน Filter Context นั้นๆ มากกว่า ซึ่งเราจะใช้ Measure ใหม่แทน SelectTarget ดังนี้
FinalTarget =
[ThisMonthDailyTarget] * COUNTROWS ( VALUES ( dDate[Date] ) )

ซึ่งลองไม่ Filter ระดับวัน ผลลัพธ์ก็ไม่ผิดด้วย เพราะมันจะคูณกับจำนวนวันในเดือน จนออกมาถูกต้องเอง

วิธีที่เราคิดข้างบนก็เหมือนจะถูกแล้ว แต่จริงๆ แล้วยังผิดที่ตัวรวมอยู่ เพราะมันดับเอา DailyTarget ของเดือนแรกที่เห็นใน Visual มาคูณจำนวนวันที่เห็นทั้งหมด
ถ้าจะให้เห็นภาพชัดขึ้น ต้องลองทำให้เห็นข้อมูลแค่ไม่กี่วัน แต่ให้มันข้ามเดือนซะ เช่น

จะเห็นว่ายอดรวม 2616129.03 ไม่เท่ากับ 2 เดือนรวมกันนะ ดังนั้นวิธีนี้จึงถือว่ายังไม่ดีเท่าไหร่
ดังนั้นแนวคิดที่ดีกว่าคือการใช้ DAX สร้างตารางจำลองวันที่แต่ละวันขึ้นมาในช่วง Filter Context นั้นๆ แล้วเรียกเอา ThisMonthDailyTarget มา Sum กันซะ ซึ่งฟังก์ชันที่ทำแบบนั้นได้ก็คือ SUMX นั่นเอง
ซึ่งพอ SUMX ใช้ผสมกับ Measure [ThisMonthDailyTarget] ก็จะเกิด Context Transition เกิดขึ้นในแต่ละแถวก่อนจะ Sum (ใครไม่เข้าใจลองย้อนไปอ่านเรื่อง Context Transition ได้ที่นี่)
แบบนี้จะออกมาถูกต้องที่สุด และ Dynamic มากที่สุดด้วยครับ
FinalTarget2 =
SUMX ( DISTINCT ( dDate[Date] ), [ThisMonthDailyTarget] )

จะเห็นว่าผลลัพธ์จะถูกเสมอ ไม่ว่าจะเลือก Filter แบบไหนก็ตาม นี่สิวิธีที่ดีที่สุด

สรุปแล้วเราก็ใช้แค่ TotalTargetNew (ที่เป็น DailyTarget) , ThisMonthDailyTarget (ที่เป็น DailyTarget แบบ Allocate ลงรายวัน ) และ FinalTarget2 (ที่ใช้ SUMX) เอง ตัวอื่นไม่ใช้ก็ลบไปซะ เช่น SelectTarget, FinalTarget ก็ไม่ใช้แล้ว
และตัว TotalTarget เราอาจจะอยากใช้กรณีที่อยากเห็น Target ของทั้งเดือนหรือทั้งปี แม้วันที่ยังไม่ครบตามนั้น
ดังนั้นก็ต้องเพิ่ม Measure %AchieveTarget ใหม่เป็นดังนี้ด้วย
%AchieveTargetAll = DIVIDE([TotalRevenue],[TotalTarget])
%AchieveTargetDaily = DIVIDE([TotalRevenue],[FinalTarget2])
เวลาเราเอามาใช้ใน visual ต่างๆ ค่าที่ได้ก็จะแม่นยำมากที่สุด

และนี่ก็คือการ Allocate Target ครับ ซึ่งอาจจะดูยุ่งยากอยู่บ้าง แต่ผมอยากจะลองไล่ Logic ให้ดูว่าทำไมการทำแบบบ้านๆ มันอาจจะผิดยังไงได้บ้าง และแบบที่ดีควรเป็นยังไง ถ้าอ่านแล้วสงสัยยังไงก็ Comment บอกได้นะครับ
ใครสนใจอยากเรียนเป็นคลิปวีดีโอ ผมมี

มีคน Inbox มาถามผมว่า จากฐานข้อมูลแบบซ้ายมือ ต้องการ LOOKUP สร้างรายงานผลลัพธ์หน้าตาแบบขวามือ จะทำยังไงดี? (อันนี้เป้นตัวอย่าง Mock up นะ แต่ concept ก็แบบนี้แหละ)

เอาจริงๆ ดูเหมือนมันจะเป็นรูปแบบรายงานที่เอาไว้ Print ลงหน้ากระดาษ ซึ่งผมว่าไม่เหมาะกับ Excel เท่าไหร่ ถ้าเป็นผมอาจจะใช้พวก Mail Merge ทำมากกว่า ถ้าใครสนใจลองไปอ่านได้ที่นี่
แต่ถ้าเราจะใช้ Excel ทำเรื่องแบบนี้จริงๆ มันก็พอจะมีแนวทางอยู่ และผมก็จะค่อยๆ ทำให้ดู ดังนี้
ก่อนอื่นก็ให้เราใช้ VLOOKUP หรือ INDEX MATCH สร้างผลลัพธ์ที่ link กับ lookup value ที่ต้องการ แต่ให้ผลลัพธ์เรียงเหมือนต้นฉบับให้ได้ก่อน เช่น

ช่องสีเหลืองคือเป็นการพิมพ์ลงไปเอง นอกนั้้นเป็นสูตรทั้งหมด
ช่อง B14 ผมใช้สูตรให้หาว่า TXID ที่ต้องการอยู่บรรทัดไหนของ Table1
=MATCH(C14,Table1[TXID],0)
ช่อง C14 ผมเขียนสูตรเพื่อดึงค่าจากคอลัมน์วันที่ออกมาจากแถวที่ Match เจอ แล้วลาก Fil Handle ไปเพื่อ Copy ให้มันเลื่อนไปเอาคอลัมน์อื่นๆ
=INDEX(Table1[วันที่],$B14)
จับ Cut -> Paste หรือ Move ลากเพื่อเปลี่ยนตำแหน่งผลลัพธ์ให้อยู่ตำแหน่งที่ต้องการได้เลย (การ cut paste สูตรจะไม่เพี้ยนอยู่แล้ว) และอย่าลืมเปลี่ยน Fomat ช่องวันที่จากเลข 4 หมื่นกว่าให้กลับเป็นวันที่ด้วยนะ (ใครจะตกแต่งสีอะไรก็แล้วแต่เลย)

ทีนี้เราต้องหาทาง copy paste สูตรให้ได้ง่ายๆ เช่น ถ้าสร้างแบบช่องสีเขียวด้านซ้ายได้จะง่ายแล้ว เราแค่ link สูตรจาก C14 ไป A14 แค่นั้นก็พร้อมจะ Copy ยาวลงมาข้างล่างแล้ว

แค่ Copy Block B13:J15 ข้างบนไว้ แล้วลากพื้นที่จะ Paste ในคอลัมน์ B ใน B16:B21

จากนั้นกด Paste

ทีนี้ก็เหลือแค่การสร้างตัวสรเขียวแล้ว ซึ่งทำไม่ยากเลย
ให้ไป Copy ข้อมูลต้นฉบับในคอลัมน์ TXID ออกมาไว้ซักที่นึง แล้วก็สร้างคอลัมน์เลข running เอาไว้ข้างๆ

จากนั้น Copy ตัว Running แล้ว Paste ลงไปข้างล่าง Running เดิมอีก 2 ชุด (เพราะเราจะเว้น 2 บรรทัด)

จากนั้นก็ Sort ตัว Running จากน้อยไปมาก มันจะดึงตัวบรรทัดว่างๆ ขึ้นมาติดกับข้างบน

แล้วเราค่อย Copy TXID ไปใช้แทนตัวเขียว

จากนั้นค่อย copy ผลลัพธ์เราลงมา ก็จะได้ผลลัพธ์ที่ต้องการ จบ!


วันนี้เราจะมาดูวิธี SUM เฉพาะที่มองเห็นแบบมีเงื่อนไขกันครับ (Sum Filter with Criteria) ซึ่งเป็นเรื่องที่แปลกดี แต่ก็มีคนถามผมมา เลยเอามาตอบให้ทุกคนพร้อมกันเลย
หลายคนคงพอจะรู้อยู่บ้างว่า ถ้าเราใช้ฟังก์ชัน SUM ปกติ มันก็จะบวกข้อมูลตัวเลขแค่พื้นที่ในสูตรตรงนั้น โดยไม่สนใจว่าเรา Filter อะไรอยู่หรือไม่
สมมติข้อมูลก่อน Filter เป็นแบบนี้ ซึ่งรวมจำนวนชิ้นสินค้าทั้งหมดได้ 31 ชิ้น

ถ้าเรา Filter ว่าเอาเฉพาะการชำระเงินเป็นเงินสด …จะพบว่า เจ้า SUM ก็จะได้เท่าเดิมอยู่ดี…
แต่ถ้าเราอยากจะ SUM แค่สิ่งที่ Filter อยู่เท่านั้น ต้องใช้ SUBTOTAL หรือไม่ก็ AGGREGATE มาช่วย เช่น
=SUBTOTAL(9,C5:C14)
เลข 9 ในสูตร เป็นการเลือกโหมดว่าจะให้ SUBTOTAL สรุปข้อมูลด้วยวิธีไหน? ซึ่ง 9 คือการ SUM นั่นเอง
=AGGREGATE(9,3,C5:C14)
ถ้าใช้ AGGREGATE เลข 9 ก็เหมือน SUBTOTAL แต่จะสามารถใส่ Option เพิ่มได้อีกว่าให้มันไม่สนใจอะไรบ้าง ซึ่งเลข 3 คือ ไม่สนแถวที่ซ่อนอยู่ ไม่สน error ไม่สน subtotal และ aggregate ที่ซ้อนอยู่ (พูดง่ายๆ คือ 3 ไม่สนมันทุกอย่างนี่แหละ)

ถ้าเราไม่ได้ Filter แต่จะ SUM แค่บางอย่าง แบบนี้เรียกว่า SUM แบบมีเงื่อนไข ซึ่งสามารถทำได้หลายแบบ เช่น
=SUMIFS(C5:C14,B5:B14,"อาหาร")
ซึ่งแปลว่า ให้ SUM พื้นที่ C5:C14 โดยเงื่อนไขคือ B5:B14 เป็นคำว่าอาหาร
=SUM((C5:C14)*(B5:B14="อาหาร"))
หลักการทำงานคือหากลองลากครอบ B5:B14=”อาหาร” แล้วกด F9 จะได้ TRUE/FALSE แบบนี้
=SUM((C5:C14)*({TRUE;FALSE;FALSE;TRUE;TRUE;FALSE;TRUE;FALSE;TRUE;FALSE}))
พอ TRUE/FALSE เชื่อมกับเครื่องหมายทางคณิตศาสตร์ มันก็จะกลายเป็น 1 กับ 0 ทำให้ ได้แบบนี้
=SUM({4;0;0;5;6;0;2;0;4;0})
ถ้าเป็น Excel ที่ไม่รองรับ Dynamic Array ต้องเขียนสูตรข้างบนแล้วกด Ctrl+Shift+Enter ด้วย จะมีปีกกางอกออกมา ถ้าไม่อยากต้องกด Ctrl+Shift+Enter ก็ให้ใช้ SUMPRODUCT แทน
=SUMPRODUCT((C5:C14)*(B5:B14="อาหาร"))

ฟังก์ชันพวก SUMIFS กับ SUMPRODUCT มันก็ดันไม่สนใจ Filter ด้วย เห็นมะว่าได้ 21 เท่าเดิมเลย

ถ้าเราเอา SUBTOTAL เขียนแบบ Array ล่ะ
=SUBTOTAL(9,(C5:C14)*(B5:B14="อาหาร"))
ปรากฏว่ามันไม่ยอมให้กด Enter ด้วยซ้ำ…
แล้วถ้าลองใช้ AGGREGATE ล่ะ เพราะมันก็สนใจเฉพาะตัวที่มองเห็นได้
=AGGREGATE(9,7,(C5:C14)*(B5:B14="อาหาร"))
ก็ดันขึ้น #VALUE! อีก เพราะฟังก์ชันหมายเลข 1-13 ดันไม่รองรับ Array…
แล้วถ้าเอา SUMPRODUCT ผสม SUBTOTAL ล่ะ!
=SUMPRODUCT(SUBTOTAL(9,C5:C14)*(B5:B14="อาหาร"))
คำตอบไม่ Error ด้วย แต่ออกมาได้ 135 ซึ่งผิดเห็นๆ
เพราะเกิดจาก =SUMPRODUCT(27*{TRUE;FALSE;FALSE;TRUE;TRUE;FALSE;TRUE;FALSE;TRUE;FALSE}) ซึ่งใช้ไม่ได้ เพราะมันดันคำนวณ SUBTOTAL ให้กลายเป็นค่าเดียวก่อนเลย แล้วค่อยมาคูณ กับ 1,0 ของ TRUE/FALSE
ถ้าเราทำให้ SUBTOTAL มันคิดเลขทีละตัวล่ะ แบบนี้ก็ยังพอมีความหวัง!
วิธีที่ง่ายคือ เพิ่มคอลัมน์พิเศษที่เอาไว้ดักการ Filter ซะ แล้วเขียนสูตรให้เป็น 1,0 โดยให้เป็น 1 เมื่อมองเห็น ดังนี้
=SUBTOTAL(3,A5)
ผมเลือก COUNTA ไปที่ช่อง A5 เพราะคิดว่าช่องนั้นยังไงก็ต้องมีค่าเสมอ ไม่ใช่ช่องว่าง

แบบนี้เราก็จะใช้ SUMIFS หรือ SUMPRODUCT ได้แล้ว
=SUMIFS(C5:C14,B5:B14,"อาหาร",E5:E14,1) แค่เพิ่มคอลัมน์พิเศษว่าต้องเป็น 1
=SUMPRODUCT(C5:C14*(B5:B14="อาหาร")*E5:E14) แค่คูณคอลัมน์พิเศษเข้าไป

ผมไปเจอในเว็บ ExcelJet ทำไว้ หลักการคือเค้าใช้ OFFSET ช่วยดึงค่ามาทีละช่องแล้วค่อยส่งให้ SUBTOTAL ทำงานต่อ ซึ่งใครสนใจก็ลองเข้าไปดูได้ครับ https://exceljet.net/formula/count-visible-rows-only-with-critera
ถ้าเป็นผมทำเองด้วยหลักการคล้ายๆ กันก็จะได้สูตรแบบนี้
=SUMPRODUCT((B5:B14="อาหาร")*SUBTOTAL(9,OFFSET(C4,ROW(INDIRECT("1:"&ROWS(C5:C14))),0)))
ซึ่งยากกว่าการเพิ่มคอลัมน์พิเศษเยอะเลยเนอะ
และนี่ก็คือเทคนิคที่อยากจะแนะนำครับ บางครั้งการเพิ่มคอลัมน์พิเศษเข้าไปก็ช่วยให้สูตรเขียนง่ายขึ้นเยอะเลยนะ และมันก็ไม่ได้แย่อะไรเลยด้วยเปลืองพื้นที่เพิ่มแค่คอลัมน์เดียว แต่เขียนสูตรง่าย และเร็วขึ้นเยอะ

เนื้อหาตอนนี้คิดว่าเป็นเรื่องที่หลายๆ คนอยากรู้ นั่นก็คือวิธีทำรายงานเทียบเป้าหมาย Target vs Actual โดยที่สามารถจะแสดงข้อมูลใน Visual เดียวกันได้ เทียบได้ว่าค่าจริงต่างจากเป้าหมายเท่าไหร่เป็นต้น
โหลดอันนี้ได้เลย มีไฟล์ Target ให้ด้วยครับ
หน้าตาของไฟล์ Target ของเราเป็นแบบนี้

ถ้าแปลงข้อมูลอันนี้ให้เป็นข้อมูลเชิง Database เราจะสามารถนำไปสร้าง Relationship กับตารางอื่นๆ ใน Data Model ได้
ไฟล์ Target ของเรามีการแบ่งมิติตาม Year Month และ ProductSubCategory ซึ่งเราจะสามารถทำ Target ตามมิติเหล่านี้ได้อย่างแน่นอน รวมถึงมิติที่ใหญ่กว่าซึ่งเกิดจากผลรวมของตัวเหล่านี้ด้วย เช่น ProductCategory ก็จะสามารถทำได้ไปด้วย
อย่างไรก็ตาม ถ้าหากอยากดูในมิติที่ย่อยลงกว่าเดิม มันขึ้นกับว่ามีวิธี Allocate ค่า Target จากตัวใหญ่ไปตัวย่อยหรือไม่? เช่น ถ้าจะ Allocate ค่าจากรายเดือน เป็นรายวัน ก็น่าจะทำได้ เพราะเอาไปหารจำนวนวันในเดือนก็น่าจะจบ แต่ถ้าจะดู Budget ของสินค้าแต่ละอันเลย จะทำไม่ได้ เพราะไม่มีวิธี Allocate ที่น่าจะดีพอ เป็นต้น
Data Model ของเราน่าจะต้องผูกกับ ตารางวันที่ และ ตารางสินค้าอย่างแน่นอน ซึ่งการจะผูกกับตารางวันที่อย่าง dDate ได้จะต้องใช้ Key เชื่อมที่เป็นระดับวันที่ และจะผูกตารางสินค้าซึ่งตอนนี้เรามี dProduct ได้ ก็จะต้องมี Key ระดับ ProductID นี่คือสิ่งที่ต้องรู้ในการผูก Data Model
สำหรับตารางวันที่ที่ต้องใช้วันเป็นตัวเชื่อม เรายังทำให้ตาราง Target ของเราเพิ่มคอลัมน์วันที่ไปได้ง่ายๆ เช่น เพิ่มให้เป็นวันที่ 1 หรือวันสุดท้ายของแต่ละเดือนไปซะ ก็พอจะเชื่อมได้
แต่ว่าสินค้า ใน Budget เรามีแต่ SubCategory ไม่มีข้อมูล ProductKey ให้ผูกเลย ทางแก้คือ เราต้องหาทางสร้างคอลัมน์ ProductKey ที่เป็นตัวแทนของ SubCategory นั้นๆ มาให้ได้ เพื่อจะเป็น Key ผูกให้ได้นั่นเอง
สรุปแล้ว เราจะต้องเพิ่มคอลัมน์ตัวแทน 2 อัน คือ DateKey และ ProductKey ขึ้นมาในตาราง Target ด้วย
เริ่มจาก Get Data จาก Excel มา แล้ว Promote Header และ Fill Down ProductSubCategory ซะ จากนั้นลบคอลัมน์ Grand Total ด้านขวาสุดออก
จากนั้นเลือกคอลัมน์ ProductSubcategory และ Year จากนั้นคลิ๊กขวา Unpivot Other Columns ลงมาซะ

เปลี่ยนชื่อคอลัมน์ให้เรียบร้อย

ทีนี้เราจะสร้างคอลัมน์ใหม่เพื่อให้เป็นวันที่วันแรกของเดือน วิธีที่ง่ายที่สุดคือสร้างคอลัมน์ใหม่ด้วย Column From Example จาก Year และ Month แบบนี้

จากนั้นกดเปลี่ยนประเภทข้อมูลให้เป็น Date ซะ (ถ้าไม่ได้ให้กด Using Locale)

การจะหา ProductKey มาเป็นตัวแทนของแต่ละ SubCategory วิธีที่ง่ายที่สุดคือสร้าง Query อีกตัวแล้ว Reference ค่าจาก dProduct ออกมาก่อน

ทำให้เหลือแค่ ProductKey และ ProductSubcategory โดยเลือก 2 ตัวนี้แล้วกด Remove Other Columns

จากนั้นสั่ง Remove Duplicates ที่คอลัมน์ ProductSubcategory ซะ

จากนั้นเปลี่ยนชื่อ Query เป็น MapProductKey ซะ แล้วเลือกเอา Enable Load ออกด้วย เพราะเราจะไม่เอาผลลัพธ์ตารางนี้ออกไปยัง Data Model
จากนั้นกลับไปยัง Target แล้วสั่ง Merge กับ MapProductKey เพื่อจะดึงค่า ProductKey ที่เป็นตัวแทนของแต่ละ ProductSubcategory มายังตาราง Target

จากนั้นแตกเอาคอลัมน์ ProductKey ออกมาอันเดียวก็จบ
เปลี่ยนชื่อ Query ให้เป็น fTarget ซะ แล้วกด Close & Apply
จากนั้นก็ผูก Date กับ DateKey เข้าด้วยกันซะ (เพราะชื่อคอลัมน์ไม่ตรงกัน เราเลยต้องทำเอง) สุดท้ายจะได้ Data Model แบบนี้ ซึ่งแปลว่า dDate และ dProduct สามารถ Filter ได้ทั้ง fSales และ fTarget พร้อมๆ กันแล้ว (ดูทิศทางการ Filter จากลูกศร)
ซึ่ง Model แบบนี้ก็คือ Model ที่มี Fact Table 2 ตาราง (ขึ้นไป) นั่นเอง แต่เป็นแบบที่ไม่ได้ผูกกันเองนะ แต่ผูกผ่าน Dimension Table อื่นๆ เช่น dDate กับ dProduct

ให้เราสร้าง Measure ที่ชื่อว่า TotalTarget ขึ้นมา โดยใช้สูตรที่ง่ายที่สุดในโลกอย่าง SUM ธรรมดาๆ
TotalTarget = SUM(fTarget[Target])
จากนั้นก็สามารลาก TotalTarget เข้า Visual ได้แล้ว และจะเห็นว่าเราสามารถลาก Dimension ที่มีความละเอียดเทียบเท่าหรือใหญ่กว่า Dimension ในตาราง fTarget ได้สบายๆ เช่น ProductCategory นั้นหยาบกว่า สามารถใช้ได้เลย รวมถึง MonthName ที่มีความละเอียดเทียบเท่ากันด้วย

และแน่นอนว่าถ้าจะทำคำนวณพวก % Achievement นั้นก็ง่ายเหลือเกิน แค่เอา Actual มาหารด้วย Target
%AchieveTarget = DIVIDE([TotalRevenue],[TotalTarget])

จากนั้นจะทำ Visual เป็นกราฟแบบไหนก็แล้วแต่คุณแล้ว

ซึ่งจะใช้พวก Custom Visual มาช่วยก็ได้กราฟเท่ๆ ไปอีกแบบนึง เช่น อันนี้เป็น Custom Visual ชื่อว่า Bullet Chart by OKViz ครับ ซึ่งทำให้ประหยัดเนื้อที่เรื่อง Target ไปได้เยอะเลย เพราะเหลือแค่ขีดเอง

เดี๋ยวบทความนี้เราจะขอจบเท่านี้ก่อนดีกว่า (เริ่มยาวละ) ในตอนต่อไปผมจะมาแนะนำวิธี Allocate Target กรณีที่จะดูในมิติที่ย่อยกว่าในตาราง fTarget ที่ทำไว้นะครับ
ใครสนใจอยากเรียนเป็นคลิปวีดีโอ ผมมี

ในเนื้อหาตอนที่ 9 ของ Series นี้ ผมได้บอกไปว่าเราสร้าง Date Table ขึ้นมาเพื่อที่จะได้ใช้ฟังก์ชันกลุ่ม Time Intelligence ได้ และในบทความนี้ก็ได้เวลาที่จะไปพูดถึงฟังก์ชันกลุ่ม Time Intelligence ซะทีครับ
มันคือฟังก์ชันกลุ่มที่จะช่วยให้เราคำนวณอะไรฉลาดๆ เกี่ยวกับวันที่และเวลาได้ เช่น คำนวณยอดขายสะสมตั้งแต่ต้นปี คำนวณยอดขายเทียบกับปีก่อน เป็นต้น
เรามาดูตัวอย่างการใช้ฟังก์ชันกลุ่ม Time Intelligence กันดีกว่าว่ามันทำอะไรได้บ้าง และใช้งานยังไง?
ใช้ไฟล์เดิมจากตอนก่อนหน้าได้เลยนะครับ หรือจะใช้อันนี้ก็ได้
ก่อนอื่นให้เราสร้าง Visual แบบ Table ใส่ ปี เดือน และ Total Revenue ลงไป (ที่เลือก ปี กับ เดือนมา เพื่อให้เห็นภาพชัดๆ)

ถ้าเราอยากได้ยอดขายสะสม วิธีที่ง่ายที่สุดคือใช้ Quick Measure โดยคลิ๊กขวาปุ่ม New Quick Measure ได้เลย


จากนั้นกด ok ด้านล่าง (ถ้ากดปุ่มไม่ได้เพราะอยู่ล่างเกินไป ให้ย้าย task bar ของ windows ไปด้านขวานะ…)
เราจะได้ measure ชื่อ TotalRevenue YTD ออกมาที่ซัก Table นึง (กด Search หา Field ชื่อ YTD ง่ายสุด)

จากนั้นก็ลากลงมาใน Visual ซะเราก็จะได้ยอด Total Revenue แบบ Year-To-Date

และก็ได้สูตรมาว่า
TotalRevenue YTD = TOTALYTD([TotalRevenue], 'dDate'[Date])
ซึ่งเจ้า TOTALYTD นี่คือฟังก์ชันสำเร็จรูปที่สามารถเปลี่ยน Filter Context ให้กับ Measure โดยให้มี Filter ช่วงวันที่นับตั้งแต่ต้นปีจนถึงวันล่าสุดที่ถูก Filter ในแต่ละบรรทัดนั้นๆ
และแน่นอนว่ามันก็มี TOTALQTD (Quarter to Date) และ TOTALMTD (Month to Date) ด้วย ลองไปใช้ดูได้
ซึ่งจริงๆ แล้ว TOTALYTD นั้น เหมือนกับเอา CALCULATE + DATESYTD ดังนี้
TotalRevenue YTD =
TOTALYTD([TotalRevenue], 'dDate'[Date])
TotalRevenue YTD2 =
CALCULATE([TotalRevenue],DATESYTD(dDate[Date]))
เนื่องจากฟังก์ชัน DATESYTD มีความสามารถในการ Filter เป็นช่วงเวลานับตั้งแต่ต้นปีนั่นเอง
ทีนี้หลายคนคงสงสัยว่าแล้วจะเรียนรู้การเขียนด้วย CALCULATE ไปทำไม ทั้งๆที่เขียน TOTALYTD ก็ง่ายกว่าตั้งเยอะ ?
นั่นเป็นเพราะหากใช้ CALCULATE เราสามารถใส่ Filter ได้เรื่อยๆ แต่ TOTALYTD ใส่ Filter ได้ 1 ตัวเท่านั้น ตามวิธีการใช้งานคือ
TOTALYTD ( <Expression>, <Dates> , [<Filter>] , [<YearEndDate>] )
แต่ถ้าไม่ได้จะ Filter อะไรเพิ่มเติม ใช้ TOTALYTD ก็ง่ายกว่าจริงๆ นั่นแหละ
อย่างไรก็ตาม ถ้าเราจะทำการเลื่อนวันแปลกๆ เราก็ต้องย้อนไปสู่ฟังก์ชันพื้นฐานที่ทำได้ทุกอย่างตามใจ แต่อาจจะเขียนได้ยากกว่า ซึ่งเดี๋ยวเราจะลองค่อยๆ แกะว่าฟังก์ชันที่เขียนไว้มันแปลงเป็นอะไรได้บ้าง ที่ให้ผลเหมือนๆ กัน เพื่อที่จะได้สามารถดัดแปลงมาใช้ในเคสซับซ้อนได้ในอนาคต เช่น
TotalRevenue YTD2 = CALCULATE([TotalRevenue],DATESYTD(dDate[Date]))
มีค่าเทียบเท่ากับ การเปลี่ยน Filter มาเป็นช่วงเวลาระหว่างวันแรกของปี จนถึงวันสุดท้ายของ Filter Context ปัจจุบัน ซึ่งสามารถเขียนได้ด้วย DATESBETWEEN เป็นต้น
TotalRevenue YTD3 =
VAR currentLastdate = LASTDATE ( dDate[Date] )
VAR currentYear = YEAR ( currentLastdate )
VAR firstdayofYear = DATE ( currentYear, 1, 1 )
RETURN
CALCULATE ( [TotalRevenue],
DATESBETWEEN ( dDate[Date], firstdayofYear, currentLastdate )
)
และถ้าจะไม่ใช้ DATESBETWEEN อีก เราก็อาจจะต้องใช้ FILTER กับ ALL ร่วมกันดังนี้
TotalRevenue YTD4 =
VAR currentLastdate = LASTDATE ( dDate[Date] )
VAR currentYear = YEAR ( currentLastdate )
VAR firstdayofYear = DATE ( currentYear, 1, 1 )
RETURN
CALCULATE ( [TotalRevenue],
FILTER(ALL(dDate[Date]),dDate[Date]>=firstdayofYear && dDate[Date]<=currentLastdate)
)
แปลว่า 3 ตัวนี้ก็มีค่าเท่ากันทุกประการนั่นเอง
เอาล่ะ สำหรับการทำความเข้าใจฟังก์ชันพวก YTD ขอจบเท่านี้ก่อน ลองมาดูอีกกลุ่มนึงคือ การเทียบกับยอดในอีกช่วงเวลาบ้าง
เราสามารถใช้ Quick Measure ได้เช่นเดิม โดยเลือกดังนี้

มันจะออกมาเป็น
TotalRevenue YoY% =
VAR __PREV_YEAR =
CALCULATE ( [TotalRevenue], DATEADD ( 'dDate'[Date], -1, YEAR ) )
RETURN
DIVIDE ( [TotalRevenue] - __PREV_YEAR, __PREV_YEAR )
ซึ่งจะเป็น % การเปลี่ยนแปลง เทียบกับปีก่อนหน้า ดังนี้

แต่ถ้าเราไม่ได้อยากทำเป็น % เทียบ แต่อยากได้ค่ายอดขายของปีก่อนมา เราก็สามารถตัดสูตรให้เหลือแค่นี้ได้
TotalRevenueLY =
CALCULATE ( [TotalRevenue], DATEADD ( dDate[Date], -1, YEAR ) )

ซึ่งเจ้า DATEADD มีความสามารถในการเลื่อนวันออกไปตามช่วงเวลาที่ต้องการ ตามหน่วยที่กำหนด ซึ่งเลื่อนได้ทั้งหน่วย DAY MONTH QUARTER YEAR เลยล่ะ เลื่อนย้อนเวลาก็ติดลบ เลื่อนไปข้างหน้าก็เป็นเลขบวก

จะว่าไปมันก็ดูคล้ายๆ EDATE ใน Excel เลย แต่ EDATE เลื่อนได้ในหน่วยเดือนเท่านั้น และ DATEADD ให้ผลเป็น Table ซึ่งใช้ใน Filter ของ CALCULATE ได้ แต่ว่า EDATE ให้ผลเป็นค่าตัวเลข (ดังนั้นเราจึงไม่สามารถใช้ EDATE แบบ -12 เดือนในเคสนี้นะ)
ทีนี้ถ้าเราจะเลื่อนย้อน 1 ปี เพื่อเทียบกับยอดในปีก่อน เราสามารถใช้ฟังก์ชันสำเร็จรูปชื่อ SAMEPERIODLASTYEAR ได้เลย แบบนี้
TotalRevenueLY2 = CALCULATE([TotalRevenue], SAMEPERIODLASTYEAR(dDate[Date]))
และถ้าเราจะเขียนแบบ Manual เอง แบบไม่พึ่งพา Time Intelligence มันก็จะประมาณนี้
TotalRevenueLY3 =
VAR currentLastdate = LASTDATE (dDate[Date])
VAR currentFirstdate = FIRSTDATE(dDate[Date])
VAR LYLastdate= EDATE(currentLastdate,-12)
VAR LYFirstdate= EDATE(currentFirstdate,-12)
RETURN
CALCULATE ( [TotalRevenue],
FILTER(ALL(dDate[Date]),dDate[Date]>=LYFirstdate && dDate[Date]<=LYLastdate)
)
แปลว่า 3 ตัวนี้ก็มีค่าเท่ากันทุกประการนั่นเอง
ซึ่งแน่นอนว่า SAMEPERIODLASTYEAR นั้นมีความ Flexible น้อยที่สุด แต่ก็เขียนง่ายสุดด้วยนั่นเอง
ใครสนใจอยากเรียนเป็นคลิปวีดีโอ ผมมี

เราก็ได้เรียนรู้ความรู้พื้นฐานหลายๆ เรื่องไปแล้ว คราวนี้ขอพักเปลี่ยนหัวข้อมาดูเรื่องที่จะช่วยให้รายงานเรามีลูกเล่นเจ๋งๆ เพิ่มขึ้นด้วยการดึงค่าจาก Slicer มาคำนวณใน Report กันบ้างครับ (พอดีมีคน inbox มาถามด้วยล่ะ 55)
ใช้ไฟล์เดิมจากตอนก่อนหน้าได้เลยนะครับ หรือจะใช้อันนี้ก็ได้
ก่อนอื่น ลองสร้าง Table หรือ Matrix ขึ้นมาก่อน ลากสรุปผลยอดขายของแต่ละ ProductCategory ประมาณนี้

เคสนี้เป็นตัวอย่างที่ Simple ที่สุด เพราะว่าเราจะสร้าง Slicer ที่มีเลข 1 , 1000, 1000000 แค่ 3 ตัว แล้วพอเลือกตัวไหน เราก็จะเอาตัวเลขนั้นมาใช้เป็นตัวหารเลย
จะมี Slicer ได้ ก็ต้องมีคอลัมน์ก่อน แต่คอลัมน์นี้จะต้องไม่มา Filter ผลลัพธ์ของตารางของเราด้วย ดังนั้นเราจะสร้าง Table ที่แยกออกไปต่างหาก และไม่ต้องผูก Relationship กับตารางอื่นเลย
ให้ไปกด Enter Data แล้วใส่ประมาณนี้ลงไป

แล้วเราก็จะได้ตารางแยกออกมาเดี่ยวๆ ซึ่งเราไม่ต้องไปผูก Relationship กับใครนะ

จากนั้นเราลาก Field หัวหาร มาเป็น Slicer แบบ List

จากนั้นลองสร้าง Measure เพื่อดึงค่าจาก Field ตัวหารดู ซึ่งฟังก์ชันที่เรารู้จัก และพอใช้ได้ก็คือ VALUES หรือ DISTINCT ก็ได้
Measureดึงตัวหาร = VALUES('Tableตัวหาร'[ตัวหาร])

มันสามารถดึงค่ามาได้ดีเลย ทีนี้ถ้าเกิดเราสร้าง Measure ใหม่ ให้ TotalRevenue หารด้วย Measure ดึงตัวหาร เราก็จะเปลี่ยนหน่วยได้
TotalRevenueเปลี่ยนหน่วย = [TotalRevenue]/[Measureดึงตัวหาร]

อย่างไรก็ตาม วิธีที่เราเพิ่งทำไปมันมีจุดอ่อนอยู่ ก็คือ หากคนใช้ Report ดันแกล้งเลือกตัวหารมากกว่า 1 ตัว หรือแม้แต่การ clear การเลือก item ใน slicer ออกไปเลย (จะแปลว่าเลือกทุกตัว) ก็จะทำให้ เจ้า Visual ของเราพังทันที!
เมื่อกด See Details ก็จะพบว่า มัน Error เพราะว่า Measure ดึงตัวหารมันต้องการค่าเดียว แต่เราดันส่งให้มันหลายค่านั่นเอง…

ทางแก้ไขในการดึงค่าจาก Slicer ทำได้หลายแบบ เช่น
TotalRevenueเปลี่ยนหน่วย =
VAR divider =
IF (
COUNTROWS ( VALUES ( 'Tableตัวหาร'[ตัวหาร] ) ) = 1,
VALUES ( 'Tableตัวหาร'[ตัวหาร] ),
1
)
RETURN
[TotalRevenue] / divider
concept คือเช็คจำนวนแถวของ Tableตัวหาร'[ตัวหาร] ซะใช่ 1 แถวรึเปล่า? (ถ้าเลือก Slicer ค่าเดียว มันจะ Filter ตาราง Tableตัวหาร ให้เหลือแค่ 1 Row ซึ่งถ้า COUNTROWS ได้ 1 ก็จะให้ผลลัพธ์เป็น TRUE นั่นเอง
พอเป็น TRUE เราก็ค่อยให้เอาค่าจากคอลัมน์ Tableตัวหาร'[ตัวหาร] แต่พอไม่ใช่ TRUE เราก็ให้เป็นเลข 1 ไปก่อน ค่าที่ได้จะได้ไม่ Error

แต่ถ้าเราไม่อยากเขียนเทียบกับ 1 ให้ยุ่งยาก ก็สามารถใช้อีกฟังก์ชันนึงนั่นก็คือ HASONEVALUE ได้ ถ้าคอลัมน์นั้นมีแค่ค่าเดียว ก็จะได้ผลลัพธ์เป็น TRUE เช่นกัน ตามตัวอย่างข้างล่าง
TotalRevenueเปลี่ยนหน่วย =
VAR divider =
IF (
HASONEVALUE ( 'Tableตัวหาร'[ตัวหาร] ),
VALUES ( 'Tableตัวหาร'[ตัวหาร] ),
1
)
RETURN
[TotalRevenue] / divider
หรือถ้าขี้เกียจไปกว่านั้นอีก ก็มีฟังก์ชันอีกตัวที่ทำให้สูตรเขียนง่ายขึ้นไปอีก นั่นก็คือ
SELECTEDVALUE(<columnName>[, <alternateResult>])
SELECTEDVALUE มีความสามารถในการเช็คว่า <columnName> ที่ระบุ มีค่าเดียวรึเปล่า? ถ้ามีค่าเดียวก็จะให้ผลเป็นค่านั้นเลย แต่ถ้าไม่ได้มีค่าเดียวจะให้ผลเป็น <alternateResult> แทน เช่น
TotalRevenueเปลี่ยนหน่วย =
VAR divider =
SELECTEDVALUE ( 'Tableตัวหาร'[ตัวหาร], 1 )
RETURN
[TotalRevenue] / divider

และเพื่อให้อ่านผลได้เนียนขึ้น เราน่าจะต้องใส่หน่วยลงไปด้วย เช่น
TotalRevenueเปลี่ยนหน่วย =
VAR divider =
SELECTEDVALUE ( 'Tableตัวหาร'[ตัวหาร], 1 )
VAR numResult = [TotalRevenue] / divider
VAR textResult =
FORMAT ( numResult, "#,##0.00" )
RETURN
SWITCH (
divider,
1, numResult,
1000, textResult & " K",
1000000, textResult & " M"
)

ให้เราเตรียม Table สกุลเงินที่ต้องการจะเลือกเอาไว้ พร้อมกับตัวเลขอัตราแลกเปลี่ยนเมื่อเทียบกับเงินบาท ( Assume ว่าค่าตัวเลขปัจจุบันที่เราคำนวณอยู่ปกติเป็นสกุลบาทนะ)
ในบทความนี้ผมจะเอาอัตราแปลกเปลี่ยนมาจากในเว็บ SCB ละกัน test แล้วเร็วดี
https://www.scb.co.th/th/personal-banking/foreign-exchange-rates.html
สั่ง Get Data from Web ซะ

ที่นี้ Rate ของ JPY มันจะประหลาดตรงที่ว่ามันคิดต่อ 100 เยน ดังนั้นต้องระวังไว้ด้วย
ผมได้ทำ Query ให้แล้ว แต่ขอไม่อธิบายละเอียด หลักๆ คือเลือกเอาบางคอลัมน์ แล้ว Split เอาตัวย่อกับ Rate ออกมาใช้ เพื่อนๆ สามารถเอา Code นี้ใส่ไว้ใน Advanced Editor ได้เลย
let
Source = Web.Page(Web.Contents("https://www.scb.co.th/th/personal-banking/foreign-exchange-rates.html")),
Data2 = Source{2}[Data],
#"Changed Type" = Table.TransformColumnTypes(Data2,{{"FOREIGN CURRENCIES", type text}, {"BANK SELLS D/D & T/T", type text}, {"BANK SELLS NOTES", type text}, {"BANK BUYS TT", type text}, {"BANK BUYS EXPORT SIGHT BILL", type text}, {"BANK BUYS T/CHQS. & CHQS.", type text}, {"BANK BUYS NOTES", type text}}),
#"Removed Other Columns" = Table.SelectColumns(#"Changed Type",{"FOREIGN CURRENCIES", "BANK SELLS NOTES"}),
#"Removed Top Rows" = Table.Skip(#"Removed Other Columns",2),
#"Split Column by Delimiter" = Table.SplitColumn(#"Removed Top Rows", "FOREIGN CURRENCIES", Splitter.SplitTextByEachDelimiter({"#(lf)"}, QuoteStyle.Csv, false), {"FOREIGN CURRENCIES.1", "FOREIGN CURRENCIES.2"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"FOREIGN CURRENCIES.1", type text}, {"FOREIGN CURRENCIES.2", type text}}),
#"Trimmed Text" = Table.TransformColumns(#"Changed Type1",{{"FOREIGN CURRENCIES.1", Text.Trim, type text}, {"FOREIGN CURRENCIES.2", Text.Trim, type text}}),
#"Renamed Columns" = Table.RenameColumns(#"Trimmed Text",{{"FOREIGN CURRENCIES.1", "FXshort"}, {"FOREIGN CURRENCIES.2", "FXdesc"}, {"BANK SELLS NOTES", "Rate"}}),
#"Changed Type2" = Table.TransformColumnTypes(#"Renamed Columns",{{"Rate", type number}}),
#"Replaced Errors" = Table.ReplaceErrorValues(#"Changed Type2", {{"Rate", null}})
in
#"Replaced Errors"
จากนั้นตั้งชื่อ Query ว่า FXRateTable แล้ว Close & Apply ซะ

จากนั้นเราจะได้ FXRateTable ออกมาหน้าตาประมาณนี้

และใน Data Model ก็ไม่ได้ต้องไปผูก Relationship เช่นเคย

จากนั้นเราสร้าง Visual ที่มี Slicer เป็น FXshort ให้กดเลือกสกุลเงินที่ต้องการ (ผมปรับรูปแบบ Orientation ให้เป็น Horizontal เพื่อความสวยงาม)
จากนั้นสร้าง Measure ชื่อว่า FXRate ด้วยสูตรดังนี้ เพื่อใช้เป็นตัวดึงค่าจาก Slicer นั่นเอง
FXRate = SELECTEDVALUE(FXRateTable[Rate],1)
และสร้าง Measure ที่จะแสดง TotalRevenue แบบสกุลเงินอื่นได้ ชื่อว่า TotalRevenueFX
TotalRevenueFX =
VAR currentrate=[FXRate]
RETURN[TotalRevenue]/currentrate

ทีนี้เราต้องแก้เรื่อง JPY เล็กน้อย เพราะ Rate ที่ให้มามันเป็นการคิดต่อ 100 YEN ไม่ใช่ 1 YEN
ดังนั้นให้กลับไปแก้ Measure FX Rate ให้เป็นแบบนี้
FXRate =
IF (
SELECTEDVALUE ( FXRateTable[FXshort] ) = "JPY",
SELECTEDVALUE ( FXRateTable[Rate] ) / 100,
SELECTEDVALUE ( FXRateTable[Rate], 1 )
)
แปลว่าให้เช็คก่อนว่าเลือกมาเป็น JPY รึเปล่า? ถ้าใช่ให้เอา Rate ที่ได้ไปหาร 100 (เพื่อให้เป็น Rate ต่อ 1 YEN) นอกนั้นก็เช็คว่า Rate ที่ได้มีค่าเดียวรึเปล่า? ถ้ามีหลายค่าให้ทำ FXRate ให้เป็น 1 ซะ เพราะแปลว่าไม่ได้เลือกสกุลไหนเลย (หรือเลือกมาหลายอัน) นั่นเอง

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

เนื่องจาก Excel เป็นโปรแกรมที่ทำได้ครอบจักรวาล มันจึงมีเครื่องมือและฟังก์ชันที่เยอะมาก แถมช่วงหลังๆ ยังมีเครื่องมือใหม่ๆ อย่าง Power Query และ Power BI เพิ่มมาอีก ยิ่งงงเข้าไปใหญ่เลยว่าจะเรียนรู้อะไรก่อนหลังดี
ถ้าเอาแบบภาพรวมก็ดูรูปนี้ได้
เทพเอ็กเซลนำเสนอหลักสูตรอบรมที่ครอบคลุมตั้งแต่ระดับพื้นฐานไปจนถึงระดับสูง พร้อมด้วย Optional Knowledge สำหรับแต่ละหัวข้อเพื่อเสริมสร้างความรู้ในเชิงลึก

ซึ่งเอาจริงๆ ในส่วนของ Excel เองก็แยกเป็น 2 สายหลักๆ คือ สายเครื่องมือ กับสายเขียนสูตร ดังนี้

แต่ถ้าจะเอาแบบละเอียด ผมได้ทำการสรุปแผนที่การเรียนรู้เป็นรูปแบบคล้ายๆ กับแผนที่เส้นทางรถไฟ เพื่อที่ว่าแต่ละคนที่กำลังสนใจเรื่องไหน จะได้พอมีแนวทางในการศึกษาได้ครับ
โหลด PDF ได้ที่นี่ , โหลด Excel ได้ที่นี่ (แผนที่นี้ทำด้วยกราฟ Scatter Plot ตามคำแนะนำของ Excel Nana)
เส้นทางแต่ละสีคือเนื้อหาในคอร์สเรียนของผมครับ ถ้าพื้นฐานคือเริ่มที่ Excel Level Up
อย่างไรก็ตามแผนที่นี้เป็นแค่ Guideline ในความคิดเห็นของผมคนเดียวเท่านั้น เพื่อนๆ อาจมีวิธีหรือแนวทางอื่นที่คิดว่าเหมาะสมกว่าก็ได้นะ คิดเห็นยังไงก็ลอง Comment มาได้ครับ


เนื่องจากช่วงนี้ COVID-19 ยังคงระบาดอยู่ คอร์สเรียน Excel แบบ Online จึงเป็นวิธีที่น่าสนใจมากขึ้น แต่ผมเองก็มีคอร์สออนไลน์อยู่ในเว็บ SkillLane ด้วยหลายตัว (และจะเพิ่มขึ้นเรื่อยๆ) จึงอยากจะมาแนะนำว่าแต่ละอันสอนอะไร? และเหมาะกับใคร?
อย่างไรก็ตาม คอร์สของผมใน SkillLane ทุกคอร์สจะเป็นคลิปวีดีโอที่สามารถดูซ้ำกี่รอบก็ได้ ไม่มีวันหมดอายุอยู่แล้วนะครับ
ถ้าคุณคิดว่าเป็นคนที่พื้นฐาน Excel ok แล้ว อาจจะขาดแค่เรื่อง Pivot Table หรือคนที่อยากจะมุ่งเตรียมตัวไปใช้ Power Query / Power BI แบบด่วนๆ ผมก็แนะนำคอร์ส นะครับ เพราะเรียนจบเร็วดี (2 ชั่วโมงครึ่ง) แถมประหยัดด้วยแค่ 490 บาทเท่านั้น
แต่ถ้าคุณอยากได้เนื้อหา Excel แบบครบจริงๆ และอยากรู้เรื่องที่สำคัญให้ครบ ผมก็แนะนำคอร์ส นี่แหละครับ ยิ่งตอนนี้ลดราคาอยู่เหลือแค่ 1,090 เท่านั้นเอง (จาก 1,490) เรียกได้ว่าจ่ายแพงขึ้นอีกนิด แต่ได้เนื้อหาเยอะขึ้นมากเลย (2 ชั่วโมงครึ่งของ Pivot เทียบกับ 12 ชั่วโมงครึ่งของ Excel Level Up)
ใครรอคอร์สไหนอยู่ก็ Comment บอกได้นะครับ จะไม่มีในนี้ก็ได้นะ เผื่อผมจะได้ลองทำมาให้เพื่อนๆ ได้เรียนกัน