เรียนรู้วิธีการสร้าง Interactive Visualization ด้วย Plotly

หนึ่งในขั้นตอนของการวิเคราะห์ข้อมูลที่สำคัญคือการสำรวจและตรวจสอบข้อมูลเบื้องต้น อาทิ ข้อมูลเป็นข้อมูลประเภทไหน ข้อมูลมีการกระจายตัวเป็นอย่างไร แต่ละตัวแปรมีความสัมพันธ์อย่างไรเทียบกับข้อมูลทั้งหมด เพราะจะช่วยทำให้การตั้งสมมุติฐานของโจทย์ที่ต้องการวิเคราะห์ได้ดีขึ้น ซึ่งปกติแล้วจะนำข้อมูลมาแสดงผลในรูปแบบของภาพนิทัศน์ (visualization) เช่น แผนภูมิแท่ง (bar chart), แผนภูมิเส้น (line chart), แผนภูมิจุด (scatter plot) เป็นต้น

สำหรับเครื่องมือการสร้างแผนภูมิใน Python นั้น มีหลายวิธี สำหรับบทความที่ทางเว็บไซต์ได้เขียนไปในก่อนหน้านี้นั้น ได้ใช้การแสดงผลด้วย Matplotlib library เช่น แผนภูมิอนุกรมเวลา และแผนภูมิเส้น ซึ่งสามารถแสดงผลความสัมพันธ์เบื้องต้นได้ง่ายและรวดเร็ว
อย่างไรก็ตาม library ดังกล่าว มีข้อจำกัดในด้านการสร้างและแสดงผลที่โต้ตอบกับผู้ใช้งานได้ง่าย ถึงแม้ว่าจะมีความสามารถในการนำทางแบบโต้ตอบ (Interactive Toolbar) ของ Matplotlib library ที่ช่วยให้ผู้ใช้สามารถดูข้อมูล ขยายภาพเฉพาะจุด เก็บแผนภูมิดังกล่าวเป็นไฟล์รูป เทียบกับทาง Plotly library มีความสามารถที่น่าสนใจเพิ่มเติม เช่น กล่องข้อความหรือกลุ่มของข้อมูลสั้น ๆ (Tooltip) ทั้งแบบทีละจุดข้อมูลและแบบเปรียบเทียบข้อมูลทั้งหมด และการเลือกดูข้อมูลด้วยตัวกรองข้อมูล (Filter)


เครื่องมือการสร้างแผนภูมิใน Python ที่เป็นที่นิยมเช่น Matplotlib นั้น มีข้อจำกัดในด้านการแสดงผลที่โต้ตอบกับผู้ใช้งาน (interactive visualization) เช่น ไม่สามารถสร้างกล่องข้อความหรือกลุ่มของข้อมูลสั้น ๆ (Tooltip) ทั้งแบบทีละจุดข้อมูลและแบบเปรียบเทียบข้อมูลทั้งหมด และการเลือกดูข้อมูลด้วยตัวกรองข้อมูล (Filter)
ในบทความนี้ ผู้เขียนขอแนะนำ Plotly library ซึ่งเป็นเครื่องมือในการสร้างแผนภูมิที่มีความสามารถในการแสดงผลที่โต้ตอบกับผู้ใช้งาน ได้หลายหลาย มีแผนภูมิมากกว่า 40 ประเภท สามารถนำแผนภูมิที่ทำเสร็จสิ้นแล้วไปเพิ่มลงในเว็บไซด์ที่ต้องการได้ง่ายด้วย Python framework ที่ชื่อว่า “Plotly Dash” และที่สำคัญสามารถนำไปใช้งาน แก้ไข และเผยแพร่ได้อย่างเสรี (open source library)
การติดตั้ง library
สำหรับตัวอย่างในบทความนี้ ผู้เขียนได้ใช้งานบน Jupyter Notebook ซึ่งเป็นสิ่งแวดล้อมสำหรับการพัฒนาแบบเบ็ดเสร็จ (IDE) ที่นิยมสำหรับการวิเคราะห์ข้อมูลโดยการใช้ภาษา Python โดยสามารถทำติดตั้งได้โดยการใช้ command ด้านล่างหรืออ่านเพิ่มเติมได้ ที่นี่
!pip install plotly==4.14.3
# หากแผนภูมิไม่สามารถแสดงได้ใน jupyter notebook ให้ลอง run command นี้ดู
!pip install "notebook>=5.3" "ipywidgets>=7.5"
ข้อมูล
ผู้เขียนใช้ข้อมูลยอดขายของวีดิโอเกมจากเว็บไซต์ vgchartz.com โดยคัดเลือกเฉพาะวีดิโอเกมที่ขายได้มากกว่าหนึ่งแสนตลับ โดยสามารถอ่านรายละเอียดที่มาของข้อมูลได้ ที่นี่
import pandas as pd
data = pd.read_csv('https://github.com/Elliot-Taylor/VideoGameSalesPredictions/blob/master/vgsales.csv?raw=true')
data.head()
Rank | Name | Platform | Year | Genre | Publisher | NA_Sales | EU_Sales | JP_Sales | Other_Sales | Global_Sales |
1 | Wii Sports | Wii | 2006 | Sports | Nintendo | 41.49 | 29.02 | 3.77 | 8.46 | 82.74 |
2 | Super Mario Bros. | NES | 1985 | Platform | Nintendo | 29.08 | 3.58 | 6.81 | 0.77 | 40.24 |
3 | Mario Kart Wii | Wii | 2008 | Racing | Nintendo | 15.85 | 12.88 | 3.79 | 3.31 | 35.82 |
4 | Wii Sports Resort | Wii | 2009 | Sports | Nintendo | 15.75 | 11.01 | 3.28 | 2.96 | 33 |
5 | Pokemon Red/Pokemon Blue | GB | 1996 | Role-Playing | Nintendo | 11.27 | 8.89 | 10.22 | 1 | 31.37 |
จากการดูตัวอย่างข้อมูลจะพบว่า มีตัวแปรต่อเนื่องดังนี้ ลำดับของเกมที่ขายได้ตามจำนวนยอดขายทั้งหมด (Rank) ปีที่เกมดังกล่าวถูกวางขาย (Year) ยอดขายของทวีปอเมริกาเหนือ (NA_Sales) ยอดขายของทวีปยุโรป (EU_Sales) ยอดขายของประเทศญี่ปุ่น (JP_Sales) ยอดขายประเทศอื่น ๆ (Other_Sales) ยอดขายรวม (Global_Sales) และมีตัวแปรแบบจัดกลุ่มดังนี้ ชื่อเกม (Name) แพลตฟอร์มที่เกมถูกเอาไปเล่น (Platform) ประเภทของเกม (Genre) ชื่อบริษัทที่พัฒนาเกม (Publisher)
ตัวอย่างการใช้ plotly
สำหรับบทความนี้ ทางผู้เขียนสร้างแผนภูมิด้วย plotly.express ซึ่งเป็นตัว function ที่เขียนมาให้สร้างสร้างแผนภูมิ Plotly ได้ง่ายขึ้น โดยจะเริ่มต้นด้วยการวาดกราฟแท่ง (Bar Graph) โดยทางผู้เขียนต้องการทราบว่า บริษัทที่พัฒนาเกม (Publisher) ขนาดใหญ่ที่ขายเกมมากกว่า 500 เกม ชอบสร้างเกมประเภทไหน (Genre)
# นำเข้า plotly.express
import plotly.express as px
# คัดเลือกเฉพาะเกมที่ถูกพัฒนาโดยบริษัทที่พัฒนาเกม (Publisher) ที่ผลิตออกมาเกิน 500 เกม
data1 = data[data.groupby('Publisher')['Publisher'].transform('count').ge(500)]
# จัดกลุ่มข้อมูลก่อนนำไปสร้างแผนภูมิ
data1 = data1.groupby(["Publisher", "Genre"])['Name'].agg('count').reset_index(name="Count")
# กราฟแท่ง (Bar Graph)
fig = px.bar(data1, x="Publisher", y="Count", color="Genre",
labels=dict(Publisher='บริษัทที่พัฒนาเกม',Count="จำนวน",Genre='ประเภทของเกม'))
fig.show()

จากแผนภูมิตัวอย่าง จะเห็นจากแผนภูมิได้ว่า บริษัท Electronic Arts จะชอบสร้างเกมประเภท Sports เป็นพิเศษ ซึ่ง tooltip ช่วยให้ผู้ใช้สามารถโต้ตอบกับแผนภูมิด้วยเมาส์ และเห็นข้อมูลเพิ่มเติมในเฉพาะสิ่งที่สนใจได้อีกด้วย ในกรณีนี้ พบว่า Electronic Arts ได้ทำเกมประเภท Sports ออกมาแล้วกว่า 561 เกม ในขณะที่ บริษัท Namco Bandai Games จะชอบสร้างเกมประเภท Sports มีสัดส่วนการสร้างเกมประเภท Role-Playing เทียบกับเกมประเภทอื่นสูงกว่าบริษัทอื่น
ตัวอย่างต่อไปจะใช้กราฟเส้น (Line Graph) โดยในกราฟนี้ ทางผู้เขียนต้องการทราบว่า ในแต่ละปี (Year) เกมประเภทไหน (Genre) ถูกนำมาวางขายเยอะที่สุด
ตัวอย่างต่อไปจะใช้กราฟเส้น (Line Graph) โดยในกราฟนี้ ทางผู้เขียนต้องการทราบว่า ในแต่ละปี (Year) เกมประเภทไหน (Genre) ถูกนำมาวางขายเยอะที่สุด
# จัดกลุ่มข้อมูลก่อนนำไปสร้างแผนภูมิ
data2 = data.groupby(["Genre", "Year"])['Name'].agg('count').reset_index(name="Count")
# กราฟเส้น (Line Graph)
fig = px.line(data2, x="Year", y="Count", color='Genre',
labels=dict(Year='ปีที่เกมดังกล่าวถูกวางขาย',Count="จำนวน",Genre='ประเภทของเกม'))
fig.show()

จากแผนภูมิตัวอย่าง นอกจากการแสดงผลของแนวโน้มโดยรวมระหว่างความสัมพันธ์ของประเภทของเกมเทียบเป็นรายปี เมื่อทำการเลือกดูเฉพาะกลุ่มและทำการเลือกการเปรียบเทียบข้อมูลด้วย tooltip ทำให้เห็นได้ว่าเกมประเภท Racing กับ Platform ขายได้ดีและไปในทิศทางเดียวกันในช่วงระหว่างปี 2000 – 2005 หลังจากนั้นเกมประเภท Role-Playing กับ Puzzle กลับได้รับความนิยมมากกว่าและมียอดขายไปทิศทางเดียวกันในช่วงระหว่างปี 2005 – 2010
ตัวอย่างสุดท้ายจะวาดแผนภูมิจุด (Scatter Graph) ของเกมที่มียอดขายสูงสุด 50 อันดับแรก โดยดูความสัมพันธ์ระหว่างยอดขายของแต่ละเกมของประเทศญี่ปุ่น (JP_Sales) เทียบกับ ยอดขายประเทศอื่น ๆ (Other_Sales) โดยถูกแบ่งสีตามประเภทของเกม (Genre) และมีขนาดของจุดที่สัมพันธ์กับยอดขายรวม (Global_Sales)
# คัดเลือกข้อมูลเฉพาะเกมที่มียอดขายสูงสุด 50 อันดับแรก
data3 = data[data["Rank"] <= 50]
data3 = data3.sort_values('Genre')
# กราฟการกระจาย (Scatter Graph)
fig = px.scatter(data3, x="JP_Sales", y="Other_Sales",hover_name='Name',size="Global_Sales", color='Genre',
labels=dict(JP_Sales='ยอดขายของประเทศญี่ปุ่น',Other_Sales="ยอดขายประเทศอื่น ๆ",Global_Sales="ยอดขายรวม",Name="ชื่อ",Genre='ประเภทของเกม'))
fig.show()

แผนภูมิตัวอย่างได้ใช้การแสดงเทียบแกนข้อมูลแนวตั้งและแนวนอนแบบ toggle ซึ่งช่วยแสดงให้เห็นว่าเกมแนว action ขายได้ดีในกลุ่มประเทศอื่น เกมแนว sport ขายได้ดีในทุกประเทศ เกมแนว role-playing ขายได้ดีในประเทศญี่ปุ่น และในแนวเกมประเภท Shooter จะเกาะกลุ่มล่างซ้าย ซึ่งเป็นกลุ่มที่มีแต่เกมที่ทำยอดขายที่ไม่ดีในทุกประเทศ
จบไปแล้วนะครับสำหรับการใช้งาน Plotly เบื้องต้น หวังว่าบทความนี้จะเป็นไอเดียที่ดีในการที่ผู้อ่านจะนำไปปรับใช้ เพื่อให้แผนภูมิมีความน่าสนใจมากขึ้นและช่วยให้เราหา insight จากข้อมูลได้ง่ายขึ้นนะครับ หากผู้อ่านสนใจการสร้างแผนภูมิบน Plotly ที่อยู่นอกเหนือจากบทความนี้ สามารถเข้าไปดูได้ ที่นี่
เนื้อหาโดย ธนกร ทำอิ่นแก้ว ตรวจทานและปรับปรุงโดย ปพจน์ ธรรมเจริญพร