Skip to content

Capstone: Data Type Translator

นี่คือโปรเจกต์จบของ Data Types Path ทั้งหมด — คุณจะเอาทุกอย่างที่เรียนมาใช้จริง ตั้งแต่ binary ไปจนถึง type contracts

เวลาที่ใช้: ~120 นาที

ใช้ข้อมูลนี้เป็น input — copy แล้ว save เป็น messy_students.csv:

student_id,name,phone,price,enrolled_on,active,note
1234,Somchai Jaidee,0812345678,"2,500.00",15/01/2025,yes,
001235,Somsri Deejai,+66812345679,1800.50,2025-01-16,TRUE,นักเรียนทุน
1236,Somying Sabaidi,,0,Jan 17 2025,1,
001237,Somkid Jaijing,081-234-5680,"3,200.00",2025-01-18,Y,note ปกติ
001238,Sompong Jaiyen,"","1,500.00",19/01/2025,yes,""
39,Somrak Deemak,+66898765432,"999.99",2025-01-20,TRUE,
001240,Somsak Jairai,0876543210,"4,100.00",21 Jan 2025,true,VIP
1241,Somwang Sawasdee,,"750.00",2025-01-22,FALSE,ยกเลิก
001242,Somporn Jaidee,081-999-8888,"2,200.50",23/01/2025,Yes,
001243,Somjit Jaijaroen,+66834567890,0,2025-01-24,no,ยังไม่จ่าย

ปัญหาที่ต้องแก้

Section titled “ปัญหาที่ต้องแก้”
#ปัญหาColumn ที่เกี่ยวความรู้จาก Module
1Leading zeros หายstudent_idModule 0 (binary), Module 2 (text)
2Date formats ปนกันenrolled_onModule 3 (date/time)
3Empty vs null vs zerophone, note, priceModule 1 (blank/empty/null)
4Text ที่ดูเหมือนตัวเลขpriceModule 0.5 (numbers)
5Boolean ไม่ consistentactiveModule 1 (core categories)
6Phone format ไม่ consistentphoneModule 2 (text)

เขียน type contract ก่อนเริ่ม clean — ใช้เป็นแผนที่สำหรับทุก deliverable:

ดูตัวอย่าง Type Contract
ColumnTypeRequiredConstraints / หมายเหตุ
student_idVARCHAR(6)Yesตัวเลข 6 หลัก, เติม leading zeros, ห้ามเป็น integer
nameVARCHAR(100)Yes
phoneVARCHAR(20)Nodigits + '+' เท่านั้น, normalize เป็น +66XXXXXXXXX
priceNUMERIC(10,2)Yes>= 0, ลบ comma ก่อน convert
enrolled_onDATEYesISO 8601 YYYY-MM-DD เท่านั้น
activeBOOLEANYesnormalize yes/y/1/trueTRUE, default TRUE
noteTEXTNoempty string ""NULL

กฎ Null:

  • phone = NULL หมายถึง “ไม่มีเบอร์” (ไม่ใช่ empty string)
  • note = NULL หมายถึง “ไม่มีหมายเหตุ” (empty string "" ต้องแปลงเป็น NULL)
  • price = 0 หมายถึง “ฟรี” ไม่ใช่ null

  1. Import CSV — File > Import > Upload > เลือก “Do not auto-detect” สำหรับ column types

  2. Fix student_id — ใช้สูตรเพื่อเติม leading zeros

  3. Fix price — ลบ comma, ตรวจสอบว่าเป็นตัวเลข

  4. Fix dates — normalize ทุก format ให้เป็น YYYY-MM-DD

  5. Fix active — normalize ให้เป็น TRUE / FALSE

  6. Fix phone — normalize format, แปลง empty string เป็นช่องว่าง

  7. ตั้ง Data Validation — สำหรับทุก column

ดูตัวอย่าง Sheets formulas
// Fix student_id (สมมติ raw อยู่ column A, cleaned อยู่ column H)
=TEXT(VALUE(A2), "000000")
// Fix price (ลบ comma)
=VALUE(SUBSTITUTE(D2, ",", ""))
// Fix dates (ถ้ารู้ว่า format ปนกัน ต้องจัดการทีละ format)
// สำหรับ DD/MM/YYYY:
=DATE(RIGHT(E2,4), MID(E2,4,2), LEFT(E2,2))
// สำหรับ ISO YYYY-MM-DD — ไม่ต้องแปลง ใช้ DATEVALUE
=DATEVALUE(E2)
// Fix active — normalize
=IF(OR(LOWER(F2)="yes", LOWER(F2)="y", F2="1", LOWER(F2)="true"), TRUE, FALSE)
// Fix phone — empty string → blank
=IF(C2="", , C2)
// Data Validation สำหรับ student_id:
// Custom formula: =AND(LEN(H2)=6, ISNUMBER(VALUE(H2)))

ผลลัพธ์ที่ถูกต้อง:

student_idnamephonepriceenrolled_onactivenote
001234Somchai Jaidee08123456782500.002025-01-15TRUE
001235Somsri Deejai+668123456791800.502025-01-16TRUEนักเรียนทุน
001236Somying Sabaidi0.002025-01-17TRUE
001237Somkid Jaijing08123456803200.002025-01-18TRUEnote ปกติ
001238Sompong Jaiyen1500.002025-01-19TRUE
000039Somrak Deemak+66898765432999.992025-01-20TRUE
001240Somsak Jairai08765432104100.002025-01-21TRUEVIP
001241Somwang Sawasdee750.002025-01-22FALSEยกเลิก
001242Somporn Jaidee08199988882200.502025-01-23TRUE
001243Somjit Jaijaroen+668345678900.002025-01-24FALSEยังไม่จ่าย

ดูตัวอย่าง Python notebook
import pandas as pd
import numpy as np
# ===== STEP 1: อ่าน CSV (ทุก column เป็น string ก่อน) =====
df = pd.read_csv('messy_students.csv', dtype='string')
print("Raw data:")
print(df)
print(f"\nShape: {df.shape}")
# ===== STEP 2: Clean student_id — เติม leading zeros =====
df['student_id'] = df['student_id'].str.strip().str.zfill(6)
print("\nFixed student_id:")
print(df['student_id'])
# ===== STEP 3: Clean price — ลบ comma, แปลงเป็น float =====
df['price'] = (
df['price']
.str.replace(',', '', regex=False)
.str.replace('"', '', regex=False)
.astype(float)
)
print("\nFixed price:")
print(df['price'])
# ===== STEP 4: Clean enrolled_on — normalize dates =====
df['enrolled_on'] = pd.to_datetime(df['enrolled_on'], format='mixed', dayfirst=True)
print("\nFixed enrolled_on:")
print(df['enrolled_on'])
# ===== STEP 5: Clean active — normalize boolean =====
bool_map = {
'true': True, 'yes': True, '1': True, 'y': True,
'false': False, 'no': False, '0': False, 'n': False,
}
df['active'] = df['active'].str.strip().str.lower().map(bool_map)
print("\nFixed active:")
print(df['active'])
# ===== STEP 6: Clean phone — normalize empty → NaN =====
df['phone'] = df['phone'].replace({'': pd.NA, '""': pd.NA})
# ลบ dash ออก
df['phone'] = df['phone'].str.replace('-', '', regex=False)
print("\nFixed phone:")
print(df['phone'])
# ===== STEP 7: Clean note — empty string → NaN =====
df['note'] = df['note'].replace({'': pd.NA, '""': pd.NA})
print("\nFixed note:")
print(df['note'])
# ===== STEP 8: Run Import Checklist =====
print("\n" + "="*50)
print("IMPORT CHECKLIST")
print("="*50)
# Check 1: Row count
print(f"\n1. Row count: {len(df)} rows")
# Check 2: Nulls per column
print("\n2. Nulls per column:")
print(df.isnull().sum())
# Check 3: Leading zeros
bad_ids = df[df['student_id'].str.len() != 6]
print(f"\n3. IDs not 6 chars: {len(bad_ids)}")
# Check 4: Dates parsed
print(f"\n4. enrolled_on dtype: {df['enrolled_on'].dtype}")
# Check 5: Total price
print(f"\n5. Total price: {df['price'].sum():.2f}")
# ===== STEP 9: Export cleaned CSV =====
df.to_csv('cleaned_students.csv', index=False)
print("\nExported: cleaned_students.csv")
# ===== Final Result =====
print("\n" + "="*50)
print("CLEANED DATA")
print("="*50)
print(df)
print(df.dtypes)

ดูตัวอย่าง SQL
-- ===== SCHEMA (ตรงกับ Type Contract) =====
CREATE TABLE students (
student_id VARCHAR(6) NOT NULL,
name VARCHAR(100) NOT NULL,
phone VARCHAR(20),
price NUMERIC(10,2) NOT NULL CHECK (price >= 0),
enrolled_on DATE NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
note TEXT
);
-- ===== LOAD DATA (จาก cleaned CSV) =====
INSERT INTO students VALUES ('001234', 'Somchai Jaidee', '0812345678', 2500.00, '2025-01-15', TRUE, NULL);
INSERT INTO students VALUES ('001235', 'Somsri Deejai', '+66812345679', 1800.50, '2025-01-16', TRUE, 'นักเรียนทุน');
INSERT INTO students VALUES ('001236', 'Somying Sabaidi', NULL, 0.00, '2025-01-17', TRUE, NULL);
INSERT INTO students VALUES ('001237', 'Somkid Jaijing', '0812345680', 3200.00, '2025-01-18', TRUE, 'note ปกติ');
INSERT INTO students VALUES ('001238', 'Sompong Jaiyen', NULL, 1500.00, '2025-01-19', TRUE, NULL);
INSERT INTO students VALUES ('000039', 'Somrak Deemak', '+66898765432', 999.99, '2025-01-20', TRUE, NULL);
INSERT INTO students VALUES ('001240', 'Somsak Jairai', '0876543210', 4100.00, '2025-01-21', TRUE, 'VIP');
INSERT INTO students VALUES ('001241', 'Somwang Sawasdee', NULL, 750.00, '2025-01-22', FALSE, 'ยกเลิก');
INSERT INTO students VALUES ('001242', 'Somporn Jaidee', '0819998888', 2200.50, '2025-01-23', TRUE, NULL);
INSERT INTO students VALUES ('001243', 'Somjit Jaijaroen', '+66834567890', 0.00, '2025-01-24', FALSE, 'ยังไม่จ่าย');
-- ===== VERIFICATION QUERIES =====
-- Check 1: Row count
SELECT COUNT(*) AS row_count FROM students;
-- Expected: 10
-- Check 2: Nulls per column
SELECT
COUNT(*) - COUNT(student_id) AS null_id,
COUNT(*) - COUNT(name) AS null_name,
COUNT(*) - COUNT(phone) AS null_phone,
COUNT(*) - COUNT(price) AS null_price,
COUNT(*) - COUNT(enrolled_on) AS null_date,
COUNT(*) - COUNT(active) AS null_active,
COUNT(*) - COUNT(note) AS null_note
FROM students;
-- Expected: 0, 0, 3, 0, 0, 0, 4
-- Check 3: IDs — ทุก ID ต้อง 6 หลัก
SELECT student_id FROM students WHERE LENGTH(student_id) != 6;
-- Expected: 0 rows
-- Check 4: Date range check
SELECT MIN(enrolled_on), MAX(enrolled_on) FROM students;
-- Expected: 2025-01-15 to 2025-01-24
-- Check 5: Total price
SELECT SUM(price) AS total_price FROM students;
-- Expected: 17050.99
-- Bonus: cross-check ยอดตรงกับ pandas
SELECT
SUM(price) AS total,
COUNT(CASE WHEN active THEN 1 END) AS active_count,
COUNT(CASE WHEN NOT active THEN 1 END) AS inactive_count
FROM students;

ก่อนส่ง ตรวจว่าทั้ง 3 ระบบให้ผลลัพธ์ตรงกัน:

ตรวจอะไรGoogle SheetspandasSQL
Row count101010
SUM(price)17,050.9917050.9917050.99
IDs ที่ 6 หลัก10/1010/1010/10
Active count888
Null phones333

ใช้ตารางนี้ตรวจงานของตัวเองก่อนส่ง:

เกณฑ์คะแนนตรวจ
Row counts match ทั้ง 3 ระบบ20%Sheets = pandas = SQL = 10 rows?
IDs preserved (6 หลัก, leading zeros)15%001234 ไม่ใช่ 1234? ทุกระบบ?
Dates correct (ISO 8601)20%2025-01-15 ไม่ใช่ 15/01/2025? ทุกระบบ?
Numeric types correct (ยอดตรง)20%SUM ตรงกัน? ไม่มี comma ใน price?
Type contract complete25%ทุก column มี type + nullable + constraints?