By Arnon Puitrakul - 11 เมษายน 2018
วันนี้จะพามารู้จัก สัตว์ อีกชนิดหนึ่ง (นอกจาก งู 🐍) ที่จะช่วยให้เราทำงานกับข้อมูลใน Python ได้ง่ายขึ้นนั่นคือ Pandas 🐼
Pandas เป็นหมีชนิดนึงที่มักอาศัยอยู่ อยู่ตามภูเขาในประเทศจีน ไม่ใช่ละ คนละแพนด้าละ !!! แพนด้าที่เราจะพูดถึงในวันนี้เป็น Library ที่มี Data Structure และ Function สำหรับการจัดการและเตรียมข้อมูลใน Python ที่กำลังเป็นที่นิยมโคตร ๆ
ถามว่า Pandas เข้ามาอยู่ตรงไหนของการทำงานเรา ก็คงตอบได้ว่า มันอยู่ในช่วงแรกของ Workflow เลยก็ว่าได้ (ถ้าเราต้องการเอาข้อมูลมาทำ Classification และวิเคราห์ในขั้นตอนต่อ ๆ ไป)
หลังจากเราเข้าใจแล้วว่า Pandas คืออะไร เรามาลองเริ่มใช้กันเลยดีกว่า โดยเริ่มจากการติดตั้งเข้าหมียักษ์ตัวนี้เข้าไปในเครื่องของเรากันก่อน โดยเราสามารถติดตั้งได้หลายวิธีมาก ๆ แต่วันนี้ผมจะใช้ pip ในการติดตั้งบน Python3 ละกัน โดยใช้คำสั่งดังนี้ (สำหรับ Python 2.7 ก็สามารถใช้ pip ได้เช่นกัน แค่เอาเลข 3 ออก)
pip3 install pandas
รันแค่คำสั่งเดียว เราก็ได้หมียักษ์เข้ามาในเครื่องเราเป็นที่เรียบร้อยแล้ว หลังจากนั้น เวลาจะใช้เราก็ต้อง Import มันเข้ามาในไฟล์ของเราโดยใช้คำสั่ง
import pandas as pd
ก็จริง ๆ แล้วตรง as pd ข้างหลังไม่ต้องใส่มาก็ได้ แต่เพื่อความพิมพ์แล้วมือไม่พังซะก่อน กับทำให้ Code เราดูสั้นขึ้นเขียนแบบนี้ก็ไม่เลวเท่าไหร่ ง่ายดี 😃
ทีนี้เรามาดูกันบ้างดีกว่าว่า ใน Pandas นั่นมี Data Structure อะไรมาให้เราเลือกใช้บ้าง ?
ในตัวของหมียักษ์มันจะมีข้อมูลอยู่ 2 ประเภทด้วยกัน อย่างแรกคือ Series ให้เรานึกภาพถึง 1D Array ที่เราสามารถเข้าถึงตำแหน่งไหนก็ได้ของข้อมูล
กับอีกอันคือ DataFrame อันนี้น่าจะเป็นอะไรที่เราน่าจะใช้บ่อยมาก ๆ ใน Pandas แล้ว อันนี้ให้เรานึกถึง Grid เหมือนใน Excel เลยที่จะมี Row และ Column เป็น 2D
ถ้าเราบอกว่า DataFrame มันเป็นข้อมูลที่เป็นตาราง 2D ที่เราเคยเห็นกันในกระดาษ มันก็ไม่น่าจะแปลกเท่าไหร่ ถ้าเราตัดสัก 1 Column ออกมาแล้วเราจะได้ตารางออกมา ตรงนี้อยากให้จำไว้หน่อย เพราะถ้า งง ตรงนี้ เวลาเราไปอ่าน Document แล้วไปเจอบางคำสั่งที่เราเอา DataFrame เข้าแต่ได้ Series ออกมาแทน
โดยที่ทั้ง 2 Data Structure นี้มันจะมีคำสั่งให้เราสามารถจัดการข้อมูลได้เยอะมาก ๆ ถ้าใครเคยใช้ SQL ใน Database มาก่อน ให้นึกถึง Query ได้เลย เพราะมันทำได้ขนาดนั้นเลยละใรแง่ของการ Select, Aggregate และ Search ข้อมูล
เราน่าจะเข้าใจกันไปแล้วว่าข้อมูลที่หมียักษ์มันเก็บหน้าตาเป็นยังไง ตอนนี้เรามาลองใช้กันเลยดีกว่า !
Pandas นอกจากที่เราจะจัดการกับข้อมูลได้แล้ว เรายังสามารถ Import ข้อมูลจากไฟล์ได้ในตัวเลย โดยไม่ต้องลง Library เพิ่ม โดยผ่าน Function ที่ชื่อว่า read_csv
Data = pd.read_csv('./happiness_2017.csv')
เพียงเท่านี้เราก็ได้ข้อมูลมาแล้ว ~ แต่บางทีข้อมูลที่เราเอาเข้าอาจจะไม่ใช่ CSV พูดง่าย ๆ คือมันไม่ได้ใช้ Comma ในการเว้นช่อง ที่อาจจะใช้ Space ในการเว้นช่อง เราก็สามารถบอกมันให้อ่าน Space เป็นช่องแทนได้ โดยการเพิ่ม sep ดั่งตัวอย่างนี้
Data = pd.read_csv('./happiness_2017.csv', sep=" ")
โดยที่ข้อมูลที่เอาเข้ามา มันจะตัดแถวแรกออกไปเป็น Header หรือหัวตารางโดยอัตโนมัติ แต่ถ้าข้อมูลของเราไม่มีหัวตาราง เราก็สามารถใช้ header ในการบอกว่าเราไม่มีหัวตาราง หรือหัวตารางเราอยู่ตรงไหนได้ และเรายังสามารถกำหนดหัวตารางเองได้อีกด้วย
Data = pd.read_csv('./happiness_2017.csv', header=None, names=['col1', 'col2', 'col3'])
จะเห็นว่าผมมีการเติม Option เข้าไปอีก 2 ตัวคือ header และ names สำหรับตัวแรกเป็นการบอกว่าหัวตารางของเราอยู่แถวที่เท่าไหร่ และตัวสุดท้ายคือจะใส่เมื่อเราต้องการใส่ชื่อหัวตารางเอง
คำเตือน! ถ้าเราใส่ Option names ลงไป เราต้องเติม header=None ลงไปด้วย เพราะเราต้องการกำหนดหัวตารางเองโดยที่ไม่เอาแถวแรกมาเป็นหัวตาราง
ปัญหามันจะเริ่มเกิด เมื่อปริมาณข้อมูลของเราเยอะมากเกินกว่าที่ Ram เราจะเอาอยู่ การใช้ read_csv เฉย ๆ เกรงว่าจะไม่น่ารอด จึงมีอีก Pattern ในการอ่านไฟล์ดังนี้
chunk_size = 10000
chunks = []
for chunk in read_csv('./happiness_2017.csv', chunksize=chunksize) :
//do sth
chunks.append(chunk)
data.concat(chunks, axis=0)
จากตัวอย่างด้านบนนี้ทำให้เราเห็นว่า แทนที่เราจะ Import มาทั้งก้อน เราก็แยกมันเป็น Chunk ก่อน ๆ เพื่อให้เราทำอะไรสักอย่างในการตัด Data ที่เราไม่ได้ใช้ออกก่อน เพื่อไม่ให้ Memory เต็ม หรือ เราสามารถกำหนดได้เลยว่า เราอยากให้ Data เข้ามากี่ Chunk ก็ได้เช่นกัน
เท่านี้เราก็สามารถเอาข้อมูลเข้าปากหมีได้ละ ! 🎍 นอกจากนั้นเรายังสามารถที่จะดูได้ด้วยว่าใน DataFrame ของเรามี Column อะไรบ้างโดยใช้คำสั่งดังนี้
print(Data.columns)
เราก็จะได้อะไรแบบด้านล่างมา
Index(['Country', 'Happiness.Rank', 'Happiness.Score', 'Whisker.high',
'Whisker.low', 'Economy..GDP.per.Capita.', 'Family',
'Health..Life.Expectancy.', 'Freedom', 'Generosity',
'Trust..Government.Corruption.', 'Dystopia.Residual'],
dtype='object')
ใช่ครับ เราจะได้ออกมาเป็น Array โดยที่เราสามารถเข้าถึงชื่อผ่าน Index ได้เลยโดยตรง พูดถึง Column แล้ว เราก็ยังสามารถเอาข้อมูลใน Column นั้น ๆ ออกมาได้ด้วย
print(Data['Economy..GDP.per.Capita.']
โดยที่เราก็ใส่ชื่อ Column ลงไปเหมือนเราเรียก Array ได้เลย หรือเราสามารถใช้ชื่อตามด้วยจุดและชื่อ Columns ก็ได้เช่นกัน แต่ถ้าเป็นในกรณีตัวอย่างนี้มันจะพัง เพราะในชื่อ Column ของเรามันดันมีจุดอยู่ มันจะนึกว่ามันไปเรียกลูกของมันทำให้พังได้ วิธีที่ชัวร์ที่สุดคือการเข้าถึงเหมือน Array ที่ยกตัวอย่างนี่แหละ
ถ้าเราต้องการเข้าถึงข้อมูลเป็น Row ก็ได้เช่นกัน โดยเราอาจจะบอกว่า เราอยากได้ข้อมูลใน Row ที่ 0 ถึง 10 เราก็สามารถทำได้ดังนี้
print(Data[0:10:5])
เราก็จะได้ Data ตั้งแต่ Row ที่ 0-10 ทีนี้เลข 5 ตัวสุดท้ายมาจากไหน มันคือตัวเลขที่บอกว่า เราจะ Sampling ทีละเท่าไหร่ เช่นถ้าเราใส่ 5 มันก็จะเอาข้อมูลตัวที่ 0,5 ออกมาให้เราแทนที่จะเป็น 0,1,2,...,9 ให้เรานั่นเอง ทีน้ีถามว่า ถ้าเราอยากจะเอา Row เดิมนี่แหละ แต่อยากได้แค่ Column เดียวเราจะทำยังไง
print(Data[0:10:5]['Country'])
ก็ทำได้ง่ายมาก ๆ โดยการเติมอีกกล่องเข้าไปข้างหลังหรือใช้จุดเหมือนกับที่ได้เล่าไปในตอนที่เราเข้าถึง Column
แล้วถ้า Row และ Column ของเรามันไม่มีชื่อละ ?
ปัญหานี้มันจะเกิดเมื่อ Dataset ที่เราเอาเข้ามา มันไม่มีหัวตารางฉะนั้น เราก็จะไม่สามารถใช้ชื่อในการเข้าถึงได้แล้ว อีกวิธีหนึ่งที่เหล่าโปรแกรมเมอร์ชอบกันก็คือการเข้าถึงผ่าน Index โดยตรงเลย ซึ่งใน Pandas ก็สามารถทำได้โดยการใช้คำสั่ง iloc
# Example
# DataFrame.iloc[FromRow:ToRow, FromCol:toCol]
# Print The First Row with 20 Columns
print(Data.iloc[0:1,0:20])
จากตัวอย่างด้านบนนี้ เราก็จะได้ข้อมูลใน Row แรกที่มี 20 Columns ออกมา และแน่นอนว่า เราสามารถเรียก Row โดยที่เราสามารถใช้การบอกตำแหน่ง 3 หลักในการบอก Row ได้เหมือนกับตัวอย่างด้านบนเลย
หมายเหตุ จริง ๆ แล้วมันยังมีคำสั่ง loc ธรรมดาอยู่สำหรับการเข้าถึง Index ที่เป็น String และ ix สำหรับการเข้าถึง Index ที่เป็นทั้ง String และตัวเลข
ฟิลลิ่งตรงนี้ ถ้าเป็นคนที่เขียนโปรแกรมมาก่อน น่าจะรู้สึกแปลกนิด ๆ เพราะเหมือนมันเรียก Function อะไรสักอย่าง แล้วแทนที่เราจะป้อน Argument ที่ครอบอยู่ในวงเล็บเข้าไป กลายเป็น [] ซะงั้น 😫
ถ้าเราบอกว่า เราต้องการเลือกข้อมูลตาม เงื่อนไขบางอย่าง เราก็ทำได้เช่นกัน จากตัวอย่างด้านล่างนี้
print(data.loc[lambda data: data.Country == 'Norway', :])
จากใน Code ด้านบน เราจะเห็นว่าเราสามารถใส่ Callable ลงไปในการสร้างเงื่อนไขเพื่อเลือกข้อมูลขึ้นมาได้ เช่นในตัวอย่างด้านบนที่ผมต้องการเลือก Row มี Country เป็น Norway นั่นเอง ซึ่งเราก็สามารถนำเทคนิคตรงนี้ไปใช้งานได้อย่างมากมาย ถ้าใช้คล่อง ๆ ก็เหมือนเรา Query ข้อมูลจาก Database โดยใช้ SQL เลยละ
ตอนนี้เราลองมาดูกันดีกว่า ว่า Data ของเราเป็นยังไง โดยที่เราสามารถดึง n rows แรกออกของข้อมูลออกมาได้
Data.head(n)
นอกจากนั้น ถ้าเราอยากได้ Statistics ของแต่ละ Column เราก็สามารถใช้คำสั่ง describe ในการดูได้ โดยผลจะออกมาเป็นแบบนี้
Happiness.Rank Happiness.Score Whisker.high Whisker.low
count 155.000000 155.000000 155.000000 155.000000
mean 78.000000 5.354019 5.452326 5.255713
std 44.888751 1.131230 1.118542 1.145030
min 1.000000 2.693000 2.864884 2.521116
25% 39.500000 4.505500 4.608172 4.374955
50% 78.000000 5.279000 5.370032 5.193152
75% 116.500000 6.101500 6.194600 6.006527
max 155.000000 7.537000 7.622030 7.479556
โดยที่เราจะได้ count, mean, std, min, 25%, 50%, 75% และ max ออกมา โดยที่ 25%, 50% และ 75% คือค่าของแค่ละใน Quartiles นั่นเอง (ถ้า งง ก็กลับไปอ่าน Statistics ก่อนนะ)
ใน Dataset บางตัวมันก็จะแอบมี Missing Data อยู่ เราก็สามารถดูในช่อง count ได้ โดยการที่เราดูว่า จำนวนของ count เท่ากับจำนวนของข้อมูลรึยัง ได้เช่นกัน
แน่นอนว่า มันแสดง mean กับ std ออกมาได้ มันก็ต้องคำนวณออกมาให้เราเป็นค่าเปล่า ๆ ได้เหมือนกันผ่านคำสั่งในตัวอย่างนี้
mean_val = Data['Whisker.high'].mean()
std_val = Data['Whisker.high'].std()
แน่นอนว่ากับ max และ min ก็ไม่ต่างเลย
จากหัวข้อก่อนหน้านี้ได้มีการพูดถึงในเรื่องของการ หา Missing Data ไปแล้ว ตอนนี้เรามาดูกันว่า เราจะสามารถเติมส่วนที่หายไปได้อย่างไรบ้าง
ซึ่งในทางสถิติแล้ว เรื่องของการเติมข้อมูลที่หายไปนั้นทำได้หลายวิธีมาก แนะนำให้ลองไปอ่านดู แต่ในวันนี้ผมจะลองทำในแบบที่ง่ายที่สุดกันนั่นคือการเอา Mean เข้าไปใส่ตรง ๆ เลย ซึ่งมันก็มีข้อเสียที่สามารถใช้ได้กับแค่ข้อมูลที่เป็นตัวเลขเท่านั้น สมมุติว่าเราต้องการที่จะ Impute ช่อง Freedom เราก็สามารถทำได้ดังนี้
data.Freedom.fillna(data.Freedom.mean(), inplace=True)
แล้วทีนี้เราถ้าลองเช็คหา Missing Data ของช่อง Freedom มันก็จะหายไปหมดแล้ว เพราะเราแทนที่มันด้วยค่า Mean ไปเรียบร้อยแล้ว ซึ่งวิธีการเติมข้อมูลอื่น ๆ ก็จะใช้ลักษณะนี้ในการจัดการ คล้าย ๆ กัน ขึ้นกับความซับซ้อนในการเติม
หรือถ้าไม่อยากจะ Fill ข้อมูลเข้าไป เราก็สามารถเอามันออกไปได้เช่นกัน โดยผ่านคำสั่งดังนี้
data.Freedom.dropna()
มันก็จะเอา Row ที่ในช่อง Freedom ว่างออกไปจากข้อมูลเรา
แต่ถ้าเราต้องการที่จะทำ Smoothing เช่นการบวก 1 เข้าไปในทุก Row ของสัก Column นึงก็ทำได้เช่นกัน
def smooth_data (number) :
return number + 1
Data.Price = Data.price.apply(smooth_data)
# OR
Data.Price = Data.Price.apply(lambda price: price + 1)
หรือถ้าเราต้องการที่จะ Filter บางอย่าง เราก็แค่เติม Condition ลงไปใน Function เราก็เป็นอันจบแล้ว
Pandas เป็นหมียักษ์หลายตัว ที่อาศัยอยู่แถบตอนกลางของประเทศจีน (ยังอีก ยังไม่เลิกเล่นอีก !! 🐼) ก็เป็น Library ที่ช่วยให้เราสามารถจัดการกับ Dataset ของเราได้ง่ายขึ้น ตั้งแต่ Import ยัน Data Preparation กันเลยทีเดียว จริง ๆ แล้ว Pandas ยังมีอีกหลายความสามารถมาก ๆ ที่ไม่ได้ยกมาเล่าในวันนี้ แนะนำให้ลองไปอ่าน Documentation ดูแล้วจะรู้ว่า หมียักษ์ตัวนี้มันทำอะไรได้มากกว่ากิน กับนอน และ Entertain ผู้คนได้ วันนี้สวัสดีฮ่ะ 😃
เราเป็นคนที่อ่านกับซื้อหนังสือเยอะมาก ปัญหานึงที่ประสบมาหลายรอบและน่าหงุดหงิดมาก ๆ คือ ซื้อหนังสือซ้ำเจ้าค่ะ ทำให้เราจะต้องมีระบบง่าย ๆ สักตัวในการจัดการ วันนี้เลยจะมาเล่าวิธีการที่เราใช้ Obsidian ในการจัดการหนังสือที่เรามีกัน...
หากเราเรียนลงลึกไปในภาษาใหม่ ๆ อย่าง Python และ Java โดยเฉพาะในเรื่องของการจัดการ Memory ว่าเขาใช้ Garbage Collection นะ ว่าแต่มันทำงานยังไง วันนี้เราจะมาเล่าให้อ่านกันว่า จริง ๆ แล้วมันทำงานอย่างไร และมันมีเคสใดที่อาจจะหลุดจนเราต้องเข้ามาจัดการเองบ้าง...
ก่อนหน้านี้เราเปลี่ยนมาใช้ Zigbee Dongle กับ Home Assistant พบว่าเสถียรขึ้นเยอะมาก อุปกรณ์แทบไม่หลุดออกจากระบบเลย แต่การติดตั้งมันเข้ากับ Synology DSM นั้นมีรายละเอียดมากกว่าอันอื่นนิดหน่อย วันนี้เราจะมาเล่าวิธีการเพื่อใครเอาไปทำกัน...
เมื่อหลายวันก่อนมีพี่ที่รู้จักกันมาถามว่า เราจะโหลด CSV ยังไงให้เร็วที่สุด เป็นคำถามที่ดูเหมือนง่ายนะ แต่พอมานั่งคิด ๆ ต่อ เห้ย มันมีอะไรสนุก ๆ ในนั้นเยอะเลยนี่หว่า วันนี้เราจะมาเล่าให้อ่านกันว่า มันมีวิธีการอย่างไรบ้าง และวิธีไหนเร็วที่สุด เหมาะกับงานแบบไหน...