ไฟล์ csv ไฟล์เก็บข้อมูลแบบเบสิคง่ายๆ ที่เราใช้แลกเปลี่ยนข้อมูลกันไปมานั้น ลองมาดูกันว่าเมื่อมันทำงานคู่กับโค้ด python แล้วมันจะเจ๋งแจ๋วสักแค่ไหนกัน

csv หรือ comma-separated values แน่นอนเป็นไฟล์ที่ใช้เก็บข้อมูลประเภทตาราง แต่ละ row (บรรทัด) แสดงถึงข้อมูลแต่ละรายการ โดยใช้เครื่องหมาย , (comma) เป็นตัวคั่นแต่ละคอลัมน์ออกจากกัน

มั่นใจว่าคุณผู้ป่านทุกท่านน่าจะเคยผ่านหูผ่านตา เคยใช้งานกับไฟล์ชนิดนี้มากันแล้วอย่างน้อยซักครั้ง ยิ่งท่านใดที่ใช้ python น่าจะเคย load data มันขึ้นมา process กันบ้างแหละเน๊อะ

ตัวอย่าง เราได้ทำการอ่านไฟล์ csv: Social Sentiment Data ที่ได้มาจาก Kaggle และอ่านมันออกมาทุกคอลัมน์เลย

import csv

with open('sentimentdataset.csv', newline='', encoding='utf-8') as csvfile:
    reader = csv.reader(csvfile)        
    header = next(reader)         
    print("Columns:", header)

จะได้ผลลัพธ์ออกมาเป็นแบบนี้

Columns: ['', 'Unnamed: 0', 'Text', 'Sentiment', 'Timestamp', 'User', 'Platform', 'Hashtags', 'Retweets', 'Likes', 'Country', 'Year', 'Month', 'Day', 'Hour']

แล้ว Module csv ที่ว่าดีมันเป็นยังไง:

  1. มัน Detect ตัวคั่น delimiter จากไฟล์ได้
  2. ตรวจหาหัวตาราง Header Detection
  3. อ่านข้อมูลออกมาเป็น List
  4. ใส่ Quote ให้กับค่า Non-Numeric

1. มัน Detect ตัวคั่น delimiter จากไฟล์ได้

โดยปกติแล้วไฟล์ csv ถูกออกแบบมาใช้ , (comma) เป็นตัวคั่นของแต่ละคอลัมน์ แต่บางครั้งก็สามารถใช้เครื่องหมายอื่นได้ ไม่ว่าจะเป็น ; (semi-colon), \t (tab) หรือ | (pipe) ก็ได้

และหากเราใช้ method sniffer() จะตรวจหา delimiter ออกมาได้เลย โดยเราจะ sample data ออกมาไม่กี่แถวเพื่อดูว่าไฟล์นี้ใช้ตัวคั่นเป็นอะไรกันนะ

with open('sentimentdataset.csv', newline='', encoding='utf-8') as f:
    sample = f.read(2048)
    dialect = csv.Sniffer().sniff(sample, delimiters=[',',';','\t', '|'])
    print(f"Detected delimiter: {repr(dialect.delimiter)}")

ในโค้ดด้านบนนี้ เราจะแสดงตัวอย่างจากข้อมูลออกมาประมาณ 2048 ตัวอักษรแรก แล้วสามารถเพิ่มตัวคั่นอื่นๆ ที่ต้องการตรวจจับได้ใน list ของ parameter delimiters=[',', ';', '\t', '|'] นี้ ซึ่งโค้ดด้านบนได้ผลลัพธ์คือ

Detected delimiter: ','

2. ตรวจหาหัวตาราง Header Detection

Module csv ไม่ใช่แค่ตรวจหาตัวคั่นได้เท่านั้น แต่ยังตรวจหาว่าไฟล์นี้ “มีคอลัมน์หัวตารางมั้ย?” ได้ตามโค้ดนี้เลย

has_header = csv.Sniffer().has_header(sample)
print("Header detected?" , has_header)

ได้ผลลัพธ์เป็น

Header detected? True

ห๊ะ! แค่่นี้เองเหรอ? (ตอนแรกหนุ่มก็ถามคำถามนี้เหมือนกัน) และก็ไปหาข้อมูลมาเพิ่มก็พบกรณีที่สามารถใช้ได้ก็คือ เราสามารถมันตรวจไฟล์ csv ที่ไม่มีโครงสร้างที่แน่นอน หรือไฟล์ที่เราไม่แน่ใจว่ามีแถวส่วนหัว (header row) อยู่หรือไม่

  • ไฟล์ csv ไม่มีมาตรฐานที่ตายตัว บางไฟล์มี header บางไฟล์ไม่มี
  • หากเราพยายามอ่านไฟล์ csv ที่ไม่มี header ออกมา หรือพยายามใช้แถวแรก (ที่เป็นข้อมูล) แทนที่จะเป็นชื่อคอลัมน์ อาจจะประมวลข้อมูลผิดพลาดได้

3. อ่านข้อมูลออกมาเป็น List

โดยปกติตามความเคยชินของแอดเองจะอ่านไฟล์ csv ออกมาเป็น Pandas Data Frame เลย ซึ่งมันทำให้ใช้การวนซ้ำ (iterate) ได้ยาก พอเป็น List แล้วง่ายกว่าเยอะเลย มาดูโค้ดกันเลย

with open('sentimentdataset.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f, dialect)
    header = next(reader)
    for i, row in enumerate(reader):
        if i >= 1: break
        print(row)

ได้ผลลัพธ์เป็น

['0', '0', ' Enjoying a beautiful day at the park!              ', ' Positive  ', '2023-01-15 12:30:00', ' User123      ', ' Twitter  ', ' #Nature #Park                            ', '15.0', '30.0', ' USA      ', '2023', '1', '15', '12']

โดย csv.reader การอ่านข้อมูลแต่ละ row จะคืนค่าเป็น List หากการทำงานของเราไม่จำเป็นต้องใช้ชื่อคอลัมน์ ก็สามารถเข้าถึงข้อมูลแต่ละฟิลด์ด้วย index ของมันแทน (เช่น row[0], row[1]) ดังตัวอย่างคือการดึงข้อมูลออกมาแสดงผล

with open('sentimentdataset.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f, dialect)
    header = next(reader)
    for i, row in enumerate(reader):
        if i >= 5: break
        print(f"ID: {row[0]}, TextComment: {row[2].strip()}, Sentiment: {row[3].strip()}")

ได้ผลลัพธ์เป็น

ID: 0, TextComment: Enjoying a beautiful day at the park!, Sentiment: Positive
ID: 1, TextComment: Traffic was terrible this morning., Sentiment: Negative
ID: 2, TextComment: Just finished an amazing workout! 💪, Sentiment: Positive
ID: 3, TextComment: Excited about the upcoming weekend getaway!, Sentiment: Positive
ID: 4, TextComment: Trying out a new recipe for dinner tonight., Sentiment: Neutral

อีกตัวอย่างถ้าเป็น list แล้วเราสามารถตรวจสอบเงื่อนไขง่ายได้ด้วย List Comprehension เช่น ต้องการอยากรู้ว่ามีคอมเมนต์ประเภทไหนบ้าง

with open('sentimentdataset.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f, dialect)
    header = next(reader)
    all_rows = list(reader) # อ่านข้อมูลทั้งหมดจาก reader เข้ามาเก็บใน list เดียว
    
res_pos = [1 if row[3].strip() == 'Positive' else 0 for row in all_rows]
res_neg = [1 if row[3].strip() == 'Negative' else 0 for row in all_rows]
res_neu = [1 if row[3].strip() == 'Neutral' else 0 for row in all_rows]

print(f"Total number of Positive comments is {sum(res_pos)}")
print(f"Total number of Negative comments is {sum(res_neg)}")
print(f"Total number of Neutral comments is {sum(res_neu)}")

ได้ผลลัพธ์เป็น

Total number of Positive comments is 45
Total number of Negative comments is 4
Total number of Neutral comments is 18

4. ใส่ Quote ให้กับค่า Non-Numeric

ในไฟล์ csv แต่ลัฟิลด์สามารถมีเครื่องหมาย , (comma), " " (double quotes) หรือผสมระหว่างข้อความและตัวเลขก็ได้ ทำให้เมื่อเราต้อง export file csv ออกไปนั้นต้องใส่เครื่องหมาย " " (double quotes) ครอบเอาไว้กันความคลาดเคลื่อน ผิดพลาดเมื่อเรานำไปไฟล์ไปเปิดใช้งานเครื่องอื่น

เพื่อให้แน่ใจว่าสิ่งใดก็ตามภายในนั้น (แม้แต่เครื่องหมาย comma หรือการเว้นบรรทัด) จะถูกถือว่าเป็นส่วนหนึ่งของค่า ไม่ใช่เป็นตัวคั่น

INPUT = 'sentimentdataset.csv'
OUTPUT = 'quoted_nonnum.csv'

with open(INPUT, newline='', encoding='utf-8') as fin, \
     open(OUTPUT, 'w', newline='', encoding='utf-8') as fout:

    reader = csv.DictReader(fin)
    writer = csv.writer(fout, quoting=csv.QUOTE_NONNUMERIC)
    writer.writerow(['Text', 'Likes'])

    for i, row in enumerate(reader):
        if i >= 5: break
        writer.writerow([ row['Text'].strip() , float(row['Likes']) ])

ในโค้ดด้านบน เราเลือกคอลัมน์ Text และ Likes โดยใส่เครื่องหมายคำพูด " " (double quotes) ในค่าที่ไม่ใช่ตัวเลขทั้งหมด จากได้ผลลัพธ์เป็น:

"Text","Likes"
"Enjoying a beautiful day at the park!",30.0
"Traffic was terrible this morning.",10.0
"Just finished an amazing workout! 💪",40.0
"Excited about the upcoming weekend getaway!",15.0
"Trying out a new recipe for dinner tonight.",25.0

และถ้าต้องการใส่เครื่องหมายคำพูด " " (double quotes) ครอบทุกค่าเลยล่ะ

INPUT = 'Ref/sentimentdataset.csv'
OUTPUT = 'Ref/quoted_nonnum.csv'

with open(INPUT, newline='', encoding='utf-8') as fin, \
     open(OUTPUT, 'w', newline='', encoding='utf-8') as fout:

    reader = csv.DictReader(fin)
    writer = csv.writer(fout, quoting=csv.QUOTE_ALL)
    writer.writerow(['Text', 'Likes'])

    for i, row in enumerate(reader):
        if i >= 5: break
        writer.writerow([ row['Text'].strip() , float(row['Likes']) ])

เปลี่ยน parameter เป็น quoting=csv.QUOTE_ALL จะได้ผลลัพธ์เป็น

"Text","Likes"
"Enjoying a beautiful day at the park!","30.0"
"Traffic was terrible this morning.","10.0"
"Just finished an amazing workout! 💪","40.0"
"Excited about the upcoming weekend getaway!","15.0"
"Trying out a new recipe for dinner tonight.","25.0"

แล้วมีประโยชน์ยังไงล่ะ

  • เมื่อเราเปิดไฟล์ csv ในโปรแกรมสเปรดชีต เช่น Excel, Google Sheets, หรือโปรแกรมวิเคราะห์ข้อมูลบางชนิด โปรแกรมเหล่านี้จะพยายาม “เดา” ชนิดข้อมูลของแต่ละคอลัมน์
  • ถ้าค่าตัวเลขถูกเขียนโดยไม่มีเครื่องหมายคำพูด (เช่น 123, 45.67) โปรแกรมเหล่านี้มักจะตีความว่าเป็น ตัวเลข โดยอัตโนมัติ ซึ่งเหมาะสำหรับการคำนวณทางคณิตศาสตร์
  • แต่ถ้าค่านั้นเป็น string (ข้อความ) เช่น “Product ID”, “Apple” หรือ “123 Main Street” ถูกเขียนโดยมีเครื่องหมายคำพูดคร่อม (เช่น "Product ID", "Apple", "123 Main Street") โปรแกรมจะตีความว่าเป็น ข้อความ ทำให้ไม่สับสนระหว่างข้อความที่ดูเหมือนตัวเลขกับตัวเลขจริงๆ
  • สมมติคุณมีค่า 007 ซึ่งควรเป็น string (เช่น รหัสสินค้า ที่บางครั้งขึ้นต้นด้วย 0) ถ้าบันทึกโดยไม่มีเครื่องหมายคำพูด โปรแกรมสเปรดชีตบางตัวอาจตีความว่าเป็นตัวเลขและแปลงเป็น 7 ซึ่งทำให้ข้อมูลผิดพลาด

เปรียบเทียบ quoting=csv.QUOTE_NONNUMERIC กับโหมดอื่น ๆ

  • quoting=csv.QUOTE_ALL: ใส่ double quotes คร่อม ทุกค่า ในไฟล์ csv ไม่ว่าจะเป็นตัวเลขหรือข้อความ ("123", "Apple") ทำให้โปรแกรมตีความตัวเลขเป็นสตริง
  • quoting=csv.QUOTE_MINIMAL (default): ใส่ double quotes คร่อมเฉพาะค่าที่ จำเป็นต้อง quote เท่านั้น เช่น มีตัวแบ่งคอลัมน์ (delimiter), เครื่องหมายคำพูด (quotechar) หรืออักขระขึ้นบรรทัดใหม่ (newline) อยู่ในค่า
  • quoting=csv.QUOTE_NONE: ไม่ใส่เครื่องหมายคำพูดเลย แม้ว่าค่าจะมีตัวแบ่งคอลัมน์ก็ตาม ซึ่งมักใช้กับไฟล์ที่ข้อมูลไม่มีอักขระพิเศษ หรือเมื่อมี escapechar สำหรับ escape อักขระพิเศษแทน

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



Leave a Reply

Your email address will not be published. Required fields are marked *

Search

About

Feasible เว็บไซต์ที่นำเสนออาชีพปัจจุบันที่เรา (เจ้าของเว็บ) กำลังทำ ไม่ว่าจะเป็น นักวิเคราะห์ข้อมูล นักเรียน นักอ่าน นักฟาร์ม และอีกหลากหลายมุมมอง เรียกได้ว่าเป็น ‘แกงโฮะ’ เลยล่ะ ฮ่าๆๆ ติดตาม Content ที่จะทำออกมาได้เรื่อยๆ นะครับ ขอบคุณที่เข้ามาเยี่ยมกัน 😁✌️

Social Icons