การสร้าง Data Visualization ด้วย Dash ตอนที่ 2 : Layout

กำหนดหน้าที่และรูปแบบ

หลังจากกำหนดหน้าที่ของ web page ที่จะสร้างแล้ว การทำ layout คือพิจารณาหน้าตาของ web page จะออกมาเป็นอย่างไร จะนำเอา component อะไรมานำมาแสดงไว้ตรงตำแหน่งไหน เลือกเฉพาะที่สำคัญ ไม่ควรมีเยอะ และอาจมีกำหนด behavior ของแต่ละ component ด้วยก็ได้

การทำ Layout โยงกับการรู้จัก HTML Tag, CSS และ logic สำหรับ Dash แล้ว HTML Tag คือ python classes ที่บรรจุอยู่ใน package ชื่อ dash_html_components และ dash_core_components ท่านสามารถดูรายละเอียด components ที่รองรับได้จาก [1][2]

ยกตัวอย่างการทำ dashboard เพื่อรายงานข้อมูลจำนวนผู้ติดเชื้อและจำนวนผู้รับวัคซีน Covid-19 ของประเทศในกลุ่ม South East Asia ณ วันที่ 5 มิถุนายน 2564 ข้อมูลที่ใช้ได้มาจาก OWID ข้อมูลที่ต้องการให้ผู้อ่านได้ทราบมีดังนี้
  • จำนวนประชากร
  • reproduction rate
  • จำนวนผู้ติดเชื้อสะสมต่อประชากร 1 ล้านคน
  • จำนวนผู้ติดเชื้อใหม่ต่อประชากร 1 ล้านคน
  • จำนวนผู้เสียชีวิตต่อประชากร 1 ล้านคน
  • จำนวนผู้รับวัคซีนแล้วต่อประชากร 1 ร้อยคน
ทำการร่าง layout ออกมาดังรูปที่ 1

รูปที่ 1


แปลงภาพร่างไปเป็น โครงสร้างของ HTML tag ดังรูปที่ 2

รูปที่ 2


แปลงไปสู่ Dash code ในส่วนของ layout (ดูเรื่อง Dash application structure จากตอนที่แล้ว) ดังนี้

app.layout = html.Div(
			children=[
					html.Div(
							children=[
								html.Div(),
								html.Table(children=[
									html.THead(),
									html.TBody()
								])
							]
					),
					html.Div(
						children=[							
							# -- box 1
							html.Div(
									children=[
										html.Div(),
										dcc.Graph() 
								]
							),	
							# -- box 2
							html.Div(
									children=[
										html.Div(),
										dcc.Graph(
											) 
									]
							),
							
							# -- box 3
							html.Div(
									children=[
										html.Div(),
										dcc.Graph() 
									]
							),									
					]) # -- end of grid
			])
    
    

เติม Style

Dash ใช้ CSS แบบ external file โดยมี Bootstrap component ที่สามารถติดตั้งผ่าน pip ได้ [3] แต่หากต้องการใช้ CSS อื่นก็สามารถกำหนดให้ Dash นำเข้ามาใน application ได้ โดยกำหนด URL ของ CSS file ให้กับ attribute ชื่อ "external_stylesheets" เช่น ต้องการใช้ tailwind จาก cdnjs ก็ไปกำหนดค่าดังนี้

ext_css = ['https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.1.2/tailwind.min.css']
app = dash.Dash(__name__,external_stylesheets=ext_css)    
    
สังเกตุว่าจะมีการกำหนด URL ของ CSS file ไว้ใน list นั่นคือ สามารถอ้างถึง CSS file ได้มากกว่า 1 file

การกำหนด style ให้กับ component จะกำหนดผ่าน attribute ชื่อ "className" ค่าที่นำมาใช้คือชื่อ class ที่กำหนดไว้ใน CSS file เช่น

html.Div(className="grid grid-cols-2 rounded-md p-6 rounded-lg bg-gray-50 ... ")    

หมายเหตุ สำหรับท่านที่ไม่คุ้นกับการใช้ CSS อาจศึกษาต่อได้จากที่นี่

เชื่อมโยงแหล่งข้อมูลและสร้างกราฟ

การเชื่อมโยงกับแหล่งข้อมูลจะกล่าวถึงรายละเอียดในตอนหลัง สำหรับเนื้อหาในตอนนี้จะกล่าวถึงว่า ใช้ข้อมูลแหล่งข้อมูลภายนอกที่เก็บในรูปของ csv file นำเข้ามาผ่าน pandas [4] ดังนี้

df = pd.read_csv("./data/demo.csv")    
    

ข้อมูลถูกนำเข้าไปเก็บไว้ในตัวแปร df ต่อไปเป็นขั้นตอนการสร้างกราฟ

ในตัวอย่างนี้จะใช้ Bar graph ในการสร้างต้องอาศัย 2 components คือ bar() จาก plotly.express และ Graph() จาก dash_core_components ดังนี้

new_fig = px.bar(df, x="location", y='new_cases_per_million', color="location")
dth_fig = px.bar(df, x="location", y="new_deaths_per_million", color="location")
vac_fig = px.bar(df, x="location", y="total_vaccinations_per_hundred", color="location")    

new_fig,dth_fig และ vac_fig ตัวแปรทั้งสามเรียกว่า figure ทำหน้าที่เป็นตัวเชื่อมระหว่าง bar() และ Graph()

เติมรายละเอียดลงใน Code

ก่อนจะมาถึงขั้นตอนการลงมือเขียน Python code ได้แนะนำขั้นตอนการทำงานเริ่มจาก กำหนดหน้าที่ กำหนดรูปแบบที่จะนำเสนอ กำหนด component ที่จะใช้ กำหนด style และเชื่อมโยงแหล่งข้อมูลและเตรียมสร้างกราฟ ขั้นตอนต่อไปคือการทำทั้งหมดเขียนออกมาเป็น code ดังตัวอย่าง

import dash
import dash_core_components as dcc
import dash_html_components as html

import plotly.express as px
import pandas as pd

# -- (1) external css file
ext_css = ['https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.1.2/tailwind.min.css']

# -- (2) initiate Dash application
app = dash.Dash(__name__,external_stylesheets=ext_css)

# -- (3) option function helps creating table

def generate_table(dataframe, max_rows=10):
	return html.Table(className='table-auto',children=[
		html.Thead(
			html.Tr([html.Th(className="border px-5 py-1 text-right",
				children=col) for col in 
					["Location","Population","Reproduction Rate","Total cases(per million)"]])
		),
		html.Tbody([
			html.Tr([
				html.Td(className="border px-5 py-1 text-right text-xs",
					children=dataframe.iloc[i][col]) for col in dataframe.columns
			]) for i in range(min(len(dataframe), max_rows))
		])
	])

# -- (4) data and graphing
df = pd.read_csv("./data/demo.csv")
new_fig = px.bar(df, x="location", y='new_cases_per_million', color="location")
dth_fig = px.bar(df, x="location", y="new_deaths_per_million", color="location")
vac_fig = px.bar(df, x="location", y="total_vaccinations_per_hundred", color="location")

# -- (5) layout
app.layout = html.Div(className="bg-white-200 items-center p-2",			
			children=[
				#-- table
				html.Div(className="grid grid-cols-1 rounded-md shadow p-6 rounded-lg bg-gray-50 items-center text-center ",
							children=[
								html.Div(className="text-lg font-bold",
										children="General Information"	),
								generate_table(df.loc[:,['location','population',"reproduction_rate","total_cases_per_million"]])	
							]
					),
				html.Div(className='grid grid-cols-3 gap-4 p-4 items-center',
						# -- grid
						children=[							
							# -- box 1
							html.Div(className="rounded-md shadow p-6 rounded-lg bg-gray-50 p-1.5",
									children=[
										html.Div(className="text-lg font-bold",
											children="New Cases (per million)"),
										dcc.Graph(
											id='new-case-graph',
											figure=new_fig
											) # -- end graph
									]
									),	
							# -- box 2
							html.Div(className="rounded-md shadow p-6 rounded-lg bg-gray-50 p-1.5",
									children=[
										html.Div(className="text-lg font-bold",
											children="New Death (per million)"),
										dcc.Graph(
											id='dth-case-graph',
											figure=dth_fig
											) # -- end graph
									]
									),
							
							# -- box 3
							html.Div(className="rounded-md shadow p-6 rounded-lg bg-gray-50 p-1.5",
									children=[
										html.Div(className="text-lg font-bold",
											children="Vaccinated (per hundred)"),
										dcc.Graph(
											id='vac-case-graph',
											figure=vac_fig
											) # -- end graph
									]
									),									
							]) # -- end of grid
				])
                
# -- (6) run program
if __name__ == '__main__':
    app.run_server('0.0.0.0',debug=True)     
     

ผลที่ได้ตามรูปที่ 3
รูปที่ 3


โดยสรุปแล้วเนื้อหาในตอนนี้เป็นการแนะนำให้รู้จักกับ Dash layout ซึ่งโดยหลักแล้วจะเป็นงานด้านการออกแบบ เป็นส่วนต่อมาจากการกำหนดหน้าที่ เป้าหมายของ application องค์ความรู้หลักในขั้นตอนนี้การออกแบบ user interface หรือ dash board ในตอนต่อไปจะเป็นกล่าวถึงเรื่องการใช้ callback function


Previous
Next Post »