Data Analytics | Insights & Research

PUAY101

Category: Uncategorized

  • หาความน่าจะเป็นจาก Contingency table

    หาความน่าจะเป็นจาก Contingency table

    ความน่าจะเป็นเบื้องต้น

    ความน่าจะเป็นของเหตุการณ์ = เหตุการณ์ที่สนใจ/โอกาศที่เกิดขึ้นทั้งหมด

    Probability formula.
    Using Contingency Tables to Calculate Probabilities – Statistics By Jim

    ทำตารางความถี่ -> แสดงจำนวนผู้โดยสารทั้งหมด -> แสดงเป็นสัดส่วน -> วิเคราะห์ความน่าจะเป็นร่วม ทั้งหมดนี้สามารถทำได้ใน R โดยไม่ต้องโหลด Package อะไรมาเลย! มาลองทำกัน

    # สร้าง contingency table
    tab <- xtabs(Freq ~ Sex + Survived, data = titanic_df)
    tab
    # [1]
    addmargins(ctab)
    # [2]
    prop.table(tab, margin = 1)
    # [3]
    prob.table(tab)

    ตาราง 1 แสดงจำนวนผู้โดยสารตามกลุ่ม Sex และ Survived

    • ผู้ชายเสียชีวิต 1,364 คน และรอด 367 คน
    • ผู้หญิงเสียชีวิต 126 คน และรอด 344 คน

    ตาราง 2 แสดงเป็นสัดส่วน (proportion) เพื่อให้เห็นชัดขึ้นว่าในแต่ละเพศมี “โอกาสรอด” เท่าไร เราสามารถใช้

    • ผู้ชายรอดประมาณ 21%
    • ผู้หญิงรอดประมาณ 73%

    ตาราง 3 ความน่าจะเป็นร่วม (Joint Probability)

    Joint probability คือ ความน่าจะเป็นที่เหตุการณ์สองอย่างเกิดพร้อมกัน เช่น “เป็นผู้หญิงและรอดชีวิต” (ก็คือเราเอาตัวเลขผู้หญิงที่รอดชีวิต(344)ไปหารกับรวมคนบนเรือทั้งหมด(2,201) จะได้ ประมาณ 15.6% เป็นต้น)

    สามารถสรุปออกมาได้ว่า:

    • ความน่าจะเป็นที่ “เป็นผู้หญิงและรอดชีวิต” = 0.156 หรือ 15.6%
    • ความน่าจะเป็นที่ “เป็นผู้ชายและเสียชีวิต” = 0.648 หรือ 64.8%

    References

    Using Contingency Tables to Calculate Probabilities – Statistics By Jim

    Joint Probability: Definition, Formula & Examples – Statistics By Jim

  • ทำนายแนวโน้มจะเลิกใช้บริการ ด้วย Random Forest

    ทำนายแนวโน้มจะเลิกใช้บริการ ด้วย Random Forest

    Introduction

    Business Problem

    ในโลกธุรกิจการแข่งขันสูงในปัจจุบัน การรักษาฐานลูกค้าไว้ให้มั่นคงเป็นหัวใจสำคัญสู่ความสำเร็จ

    การสูญเสียลูกค้าหรือที่เรียกว่า “Customer Churn” ไม่ได้หมายถึงแค่การสูญเสียรายได้ในปัจจุบัน แต่ยังรวมถึงโอกาสในอนาคตที่หายไปอีกด้วย

    คำถามคือ “เราจะรู้ได้อย่างไรว่าลูกค้าคนไหนมีแนวโน้มจะจากเราไปก่อนที่พวกเขาจะตัดสินใจ?”

    .

    มาทำความรู้จักกับ Random Forest กัน! บทความนี้จะพาไปดูวิธีใช้โมเดลประเภท Classification เพื่อทำนายว่าลูกค้าจะ “อยู่ต่อ” หรือ “ไป” (เป็น Binary Classification หรือการจำแนกแค่ 2 กลุ่มนั่นเอง)

    .

    Banking Customer Churn Prediction Dataset Dataset จากคุณ Saurabh Badole

    ขนาดข้อมูล: 10,000 rows × 14 columns

    คอลัมน์ทั้งหมด:

    • RowNumber (index), CustomerId, Surname → เป็น identifier (ไม่ใช้ในการ train)
    • CreditScore (คะแนนเครดิต)
    • Geography (ประเทศ: France, Spain, Germany)
    • Gender (ชาย/หญิง)
    • Age (อายุ)
    • Tenure (จำนวนปีที่เป็นลูกค้า)
    • Balance (ยอดเงินในบัญชี)
    • NumOfProducts (จำนวนผลิตภัณฑ์ที่ถือ)
    • HasCrCard (มีบัตรเครดิตหรือไม่)
    • IsActiveMember (ลูกค้ามีการใช้งานจริงหรือไม่)
    • EstimatedSalary (เงินเดือนโดยประมาณ)
    • Exited (Target → 1 = เลิกใช้บริการ, 0 = ยังอยู่)

    ประเภทข้อมูล:

    • Numeric (ตัวเลข): CreditScore, Age, Tenure, Balance, NumOfProducts, HasCrCard, IsActiveMember, EstimatedSalary, Exited
    • Categorical (แยกประเภท): Geography, Gender
    • Identifier: RowNumber, CustomerId, Surname

    Dataset นี้พร้อมใช้ได้เลย ไม่ต้อง handle กับ missing value

    แต่บางคอลัมน์ยังไม่ใช้ข้อมูลประเภทตัวเลข และบางคอลัมน์ไม่จำเป็นต้องเอามาใส่ใน Model อย่างเช่น (RowNumber, CustomerId, Surname)

    ลบ Column ที่เป็น identifier ออก


    Explore Data

    ดูจากกราฟนี้จะเห็นได้ชัดว่าข้อมูลของเรากำลังเจอกับปัญหาที่เรียกว่า Imbalanced Target (Proportion ของ Churn มีมากกว่า คนที่ Stay อยู่มาก)ซึ่งอาจจะทำให้โมเดลเกิด bias ได้

    การกระจายตัวของแต่ละ Feature แบ่งตาม Class (0 กับ 1)

    คนอายุเยอะมีโอกาส churn สูงกว่า

    ลูกค้าที่ balance สูงก็ churn มาก

    ลูกค้า Germany มี churn rate สูงกว่า France/Spain

    เพศหญิง churn rate สูงกว่าเพศชายเล็กน้อย

    import seaborn as sns
    import matplotlib.pyplot as plt
    sns.countplot(x="Exited",data=df)
    plt.title("Target Distribution (Exited = Churn)")
    plt.xlabel("Exited (1=Churn, 0=Stay)")
    plt.ylabel("Count")
    plt.show()
    plt.figure(figsize=(6,4))
    sns.histplot(data=df,x="Age",hue="Exited",multiple="stack", palette="viridis")
    plt.title("Age Distribution by Exited")
    plt.show()
    plt.figure(figsize=(6,4))
    sns.histplot(data=df,x="Balance",hue="Exited",bins=30, multiple="stack", palette="viridis")
    plt.title("Balance Distribution by Exited")
    plt.show()
    plt.figure(figsize=(6,4))
    sns.histplot(data=df,x="Geography",hue="Exited",multiple="stack", palette="viridis")
    plt.title("Churn by Geography")
    plt.show()
    plt.figure(figsize=(6,4))
    sns.countplot(x="Gender", hue="Exited", data=df, palette="viridis")
    plt.title("Churn by Gender")
    plt.show()
    view raw Visualize.py hosted with ❤ by GitHub

    Feature Encoding

    Model จะทำงานได้กับข้อมูลที่เป็นตัวเลข เพราะฉะนั้นเราจึงต้องแปลงข้อมูลที่เป็น String ให้เป็นตัวเลข ด้วยเทคนิคที่เรียกว่าการทำ Encoding

    คอลัมน์ Gender ใช้การ Label Encoding (e.g 0 = Male, 1 = Female)

    คอลัมน์ Geography ใช้วิธี One Hot Encoding เพราะ Geography เป็น Nominal categorical variable (ไม่มีลำดับชั้น)

    One Hot Encoding คืออะไร

    One-Hot Encoding = วิธีแปลงข้อมูล หมวดหมู่ (categorical) ให้เป็น ตัวเลขแบบ binary โดยสร้าง 1 คอลัมน์ต่อ 1 หมวด

    เช่น คอลัมน์ Geography ที่มีค่า {France, Germany, Spain} → จะกลายเป็น 3 คอลัมน์:

    FranceGermanySpain
    100
    010
    001

    ข้อดี: ไม่มีการบอกลำดับผิด ๆ เหมือน Label Encoding
    ใช้ได้ดีกับ nominal categorical ที่มีจำนวน category ไม่เยอะ

    ถ้าใช้ Label Encoding โมเดลอาจ เข้าใจผิดว่า 0 < 1 < 2 มีความหมายเชิงลำดับ

    from sklearn.preprocessing import LabelEncoder
    import pandas as pd
    encoder = LabelEncoder()
    df["Gender"] = encoder.fit_transform(df["Gender"])
    df = pd.get_dummies(data=df,columns=["Geography"]).astype("int")
    view raw encoding.py hosted with ❤ by GitHub

    Reference: What is One Hot Encoding and How to Implement it in Python? | Codecademy


    พอ Feature ของเราพร้อมที่จะนำมาเทรนโมเดลแล้ว

    ML ที่เราจะมาใช้ในวันนี้คือ Randomforest เป็นโมเดลจำพวก Rule Base

    Split Data

    แยกส่วนที่เป็น Feature กับ Target ออกจากกัน

    Recap อีกที ตัวแปร X มี (‘CreditScore’, ‘Gender’, ‘Age’, ‘Tenure’, ‘Balance’, ‘NumOfProducts’, ‘HasCrCard’, ‘IsActiveMember’, ‘EstimatedSalary’, ‘Geography_France’, ‘Geography_Germany’, ‘Geography_Spain’)

    ตัวแปร y คือ(Exited)

    X = df.drop(columns="Exited")
    y = df["Exited"]
    view raw Split_X_y.py hosted with ❤ by GitHub

    Split Data ออกเป็น Train set กับ Test set

    การแสดงแยกข้อมูลทั้งหมดออกเป็นชุดข้อมูลสำหรับฝึกอบรมและทดสอบ โดยมีข้อมูลฝึกอบรม 1,000 แถวและข้อมูลทดสอบ 300 แถว
    Image from Data Science Bootcamp11 by DataRockie
    from sklearn.model_selection import train_test_split
    x_train,x_test,y_train,y_test = train_test_split(X,y,test_size=0.2,stratify=y)

    Fitting Our Model

    from sklearn.ensemble import RandomForestClassifier
    rf = RandomForestClassifier()
    model = rf.fit(x_train,y_train)
    view raw fit_model.py hosted with ❤ by GitHub

    Finally !!

    Model Evaluation

    เมื่อเราได้ Model แล้วขั้นตอนต่อมาคือเอาไป Fit กับ Test Set ที่เรา Split ไว้ตอนแรก

    y_pred = rf.predict(x_test)
    view raw pred.py hosted with ❤ by GitHub

    ต่อไปคือการเอา y_pred ที่เราทำนายได้ ไปเทียบกับ y_test ซึ่งคือค่าจริงๆของมัน เพื่อดูว่า model ของเราทายถูก/ผิด เท่าไหร่

    from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score
    y_pred = rf.predict(x_test)
    y_proba = rf.predict_proba(x_test)[:,1]
    print("Classification Report:\n", classification_report(y_test, y_pred))
    print("Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
    print("ROC-AUC:", roc_auc_score(y_test, y_proba))

    Result:

    Classification Report:
    precision recall f1-score support
    0 0.91 0.88 0.89 1593
    1 0.58 0.66 0.62 407
    accuracy 0.83 2000
    macro avg 0.75 0.77 0.76 2000
    weighted avg 0.84 0.83 0.84 2000
    Confusion Matrix:
    [[1400 193]
    [ 137 270]]
    ROC-AUC: 0.864136864136864

    Confusion Matric บอกอะไรบ้าง คร่าวๆ?

    True Negative (TN) = 1400 → ทำนายถูกว่าไม่เลิก

    False Positive (FP) = 193 → ทำนายผิดว่าลูกค้าจะเลิก แต่จริง ๆ อยู่ต่อ

    False Negative (FN) = 137 → ทำนายผิดว่าลูกค้าจะอยู่ แต่จริง ๆ เลิก

    True Positive (TP) = 270 → ทำนายถูกว่าเลิก

    Accuracy = 0.83 (83%)
    → ฟังดูสูง แต่ต้องระวัง เพราะ dataset imbalanced (ส่วนใหญ่เป็น class 0)


    Variable Important

    ต่อไปเราจะมาดู ว่าตัวแปรไหนส่งผลต่อ Churn Rate มาก->น้อย ที่สุด

    import matplotlib.pyplot as plt
    import seaborn as sns
    # ดูความสำคัญของตัวแปร
    importances = rf.feature_importances_
    features = X.columns
    # สร้าง DataFrame สำหรับเรียงลำดับ
    feat_imp = pd.DataFrame({"Feature": features, "Importance": importances})
    feat_imp = feat_imp.sort_values(by="Importance", ascending=False)
    # Plot
    plt.figure(figsize=(10,6))
    sns.barplot(x="Importance", y="Feature",hue="Feature", data=feat_imp, palette="viridis")
    plt.title("Feature Importance (Random Forest)")
    plt.show()
    view raw VarImp.py hosted with ❤ by GitHub

    Insights จากการวิเคราะห์ Churn

    1. อายุ (Age)
      • ลูกค้าที่มีอายุมากกว่า 40–50 ปีขึ้นไปมีแนวโน้ม churn สูงกว่า กลุ่มอายุน้อย
      • สะท้อนว่า segment ลูกค้ารุ่นใหญ่ควรได้รับการดูแลพิเศษ เช่น บริการส่วนบุคคล
    2. NumOfProducts (จำนวนผลิตภัณฑ์ที่ถือครอง)
      • ลูกค้าที่มี เพียง 1 ผลิตภัณฑ์ มีแนวโน้ม churn สูง
      • ในขณะที่ผู้ที่ถือครองหลายผลิตภัณฑ์ (cross-sell) มี churn ต่ำกว่า → Cross-selling ช่วยลด churn
    3. Balance (ยอดเงินในบัญชี)
      • ลูกค้าที่มี ยอดเงินคงเหลือสูง (Balance สูง) มีโอกาส churn มากกว่ากลุ่มที่ balance เป็นศูนย์
      • ชี้ให้เห็นว่าลูกค้ากลุ่มนี้อาจมีบัญชี/บริการทางการเงินกับธนาคารอื่น → เสี่ยงย้าย

    สรุปเชิงกลยุทธ์

    • ควรเน้น Retention Program ไปที่
      • ลูกค้าอายุ 40–60 ปี
      • ลูกค้าที่ balance สูงแต่มีผลิตภัณฑ์น้อย
      • ลูกค้าที่ inactive
      • ลูกค้าใน Germany
    • กลยุทธ์ที่ควรทำ เช่น
      • Cross-sell/Up-sell ให้ลูกค้า balance สูงถือหลายผลิตภัณฑ์
      • Campaign สำหรับลูกค้า inactive → โปรโมชันใช้งานครั้งแรก/คืนค่าธรรมเนียม
      • Customer experience สำหรับลูกค้าอายุสูง เช่น call center เฉพาะกลุ่ม


    Reference

    Data Science Bootcamp 11 -Datarookie

    Feature Engineering | Codecademy

  • การบ้าน Data Transformation (R)

    การบ้าน Data Transformation (R)

    Data set Nycflights23

    1. โหลด Package
    2. โหลด Data set
    3. อธิบาย Dataset
    4. วิเคราะห์ข้อมูล

    โหลด Package

    install.packages("tidyverse")
    library(tidyverse)

    โหลด Data set

    ในที่นี้ใช้เป็น “nycflight23”

    install.packages("nycflights23")
    library(nycflights23)

    อธิบาย Dataset

    ข้อมูลที่มีอยู่ใน Dataset nycflights23 หลักๆ ประกอบด้วย:

    1. flights: ตารางหลักที่เก็บข้อมูลเที่ยวบินแต่ละเที่ยว ประกอบด้วยตัวแปรต่างๆ เช่น:
      • year, month, day: วันที่ออกเดินทาง
      • dep_time, sched_dep_time: เวลาออกเดินทางจริงและเวลาออกเดินทางตามตาราง (ในรูปแบบ hhmm)
      • dep_delay: ความล่าช้าในการออกเดินทาง (เป็นนาที)
      • arr_time, sched_arr_time: เวลาถึงที่หมายจริงและเวลาถึงที่หมายตามตาราง (ในรูปแบบ hhmm)
      • arr_delay: ความล่าช้าในการเดินทางถึงที่หมาย (เป็นนาที)
      • carrier: รหัสย่อของสายการบิน (อ้างอิงจากตาราง airlines)
      • flight: หมายเลขเที่ยวบิน
      • tailnum: หมายเลขทะเบียนเครื่องบิน (อ้างอิงจากตาราง planes)
      • origin: รหัสย่อของสนามบินต้นทาง (EWR, JFK, LGA) (อ้างอิงจากตาราง airports)
      • dest: รหัสย่อของสนามบินปลายทาง (อ้างอิงจากตาราง airports)
      • air_time: เวลาที่ใช้ในการบิน (เป็นนาที)
      • distance: ระยะทางที่บิน (เป็นไมล์)
      • hour, minute: ส่วนของชั่วโมงและนาทีของเวลาออกเดินทางตามตาราง
    2. airlines: ตารางที่เก็บข้อมูลเกี่ยวกับสายการบิน โดยมีรหัสย่อ (carrier) และชื่อเต็ม (name) ของสายการบิน
    3. airports: ตารางที่เก็บข้อมูลเกี่ยวกับสนามบิน โดยมีรหัสย่อ (faa), ชื่อเต็ม (name), ละติจูด (lat), ลองจิจูด (lon), ระดับความสูง (alt), และเขตเวลา (tz)
    4. planes: ตารางที่เก็บข้อมูลเกี่ยวกับเครื่องบิน โดยมีหมายเลขทะเบียน (tailnum), ปีที่ผลิต (year), ประเภทเครื่องยนต์ (engine), ผู้ผลิต (manufacturer), รุ่น (model), จำนวนที่นั่ง (seats), ความเร็วเครื่องยนต์ (speed), และประเภทเครื่องยนต์ (engine)
    5. weather: ตารางที่เก็บข้อมูลสภาพอากาศในแต่ละชั่วโมงสำหรับแต่ละสนามบินต้นทาง (EWR, JFK, LGA) ประกอบด้วยตัวแปรต่างๆ เช่น เวลา (time_hour), อุณหภูมิ (temp), จุดน้ำค้าง (dewp), ความชื้น (humid), ความกดอากาศ (pressure), ทิศทางลม (wind_dir), ความเร็วลม (wind_speed), ความแรงลม (wind_gust), ปริมาณน้ำฝน (precip), และสภาพอากาศ (visib)

    วิเคราะห์ข้อมูล

    สายการบินที่มีความล่าช้าเฉลี่ยในการเดินทางมากที่สุด

    Code:

    flights |>
      group_by(carrier)|>
      summarise(delay =mean(arr_delay,na.rm=T))|>
      arrange(-delay)|>
      head(10)|>
      left_join(airlines)|>
      select(2,3)

    เวลาที่ใช้ในการบิน (เป็นนาที)เฉลี่ยแต่ละรุ่นของเครื่องบิน

    Code:

    flights |>
      inner_join(planes)|>
      group_by(model)|>
      summarise(airTime=sum(air_time,na.rm=T))|>
      select(model,airTime)|>
      arrange(-airTime)

    ระยะทางการบินเฉลี่ย (เป็นไมล์) ของเครื่องบินแต่ละรุ่นในเดือนกันยายน

    Code:

    flights |>
      filter(month==9)|>
      left_join(planes)|>
      group_by(model)|>
      summarise(mean_distance=mean(distance,na.rm=T))

    ผู้ผลิตเจ้าไหนที่ผลิตเครื่องบินที่ถูกใช้มากที่สุด

    Code:

    flights |>
        inner_join(planes)|>
        group_by(manufacturer)|>
        summarise(count=n())

    สายการบินไหนมีความล่าช้าในการออกเดินทางมากที่สุด

    Code:

    flights |>
        left_join(airlines)|>
        group_by(name)|>
        summarise(most_dalay=max(dep_delay,na.rm=T))|>
        arrange(-most_dalay)|>
        head(10)