เทคนิคที่ 052 · PYTHON / JAVASCRIPT

เส้นโค้งแอนดรูส์Andrews Curves

แปลงข้อมูลหลายมิติเป็นคลื่นเพื่อหากลุ่ม

5
เฉพาะทางควรรู้ก่อน · คุ้นกับข้อมูลหลายมิติและเข้าใจแนวคิดการปรับมาตรฐาน
ลองเล่น

01ภาพรวมของกราฟนี้

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

เปรียบเทียบให้เห็นภาพ

เหมือนแปลงลายนิ้วมือให้เป็นเสียง คนที่ลายนิ้วมือคล้ายกันจะได้เสียงที่คล้ายกัน เราจึงจับกลุ่มด้วยการฟังแทนการดู

ใช้ที่ไหนบ้าง — หากลุ่มในข้อมูลที่มี 5-10 ตัวแปร ตรวจว่าการแบ่งกลุ่มที่มีอยู่แยกกันจริงไหม หรือหาแถวที่ผิดปกติในข้อมูลหลายมิติ

สร้างอย่างไร

  1. ขั้นที่ 1ปรับมาตรฐานทุกตัวแปรให้อยู่ในสเกลเดียวกัน
  2. ขั้นที่ 2แปลงแต่ละแถวเป็นฟังก์ชันคลื่น
  3. ขั้นที่ 3วาดเส้นทุกเส้นบนแกนเดียวกัน
  4. ขั้นที่ 4ระบายสีตามกลุ่มที่รู้อยู่แล้ว

ตัวอย่างคลาสสิก · David Andrews เสนอวิธีนี้ในปี 1972

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

Wikipedia · Andrews plot

02อธิบายภาษาคนเข้าใจง่าย ๆ

ข้อมูลที่มี 6 ตัวแปรวาดบนกระดาษสองมิติตรง ๆ ไม่ได้ วิธีที่ใช้กันคือลดมิติด้วย PCA หรือวาดทุกคู่ด้วย scatter matrix ซึ่งทั้งสองวิธีมีข้อเสีย

Andrews curves ใช้วิธีที่ต่างออกไป คือแปลงแต่ละแถวให้เป็นสูตรคลื่นหนึ่งสูตร โดยเอาค่าของแต่ละตัวแปรมาเป็นสัมประสิทธิ์ของไซน์และโคไซน์

คุณสมบัติทางคณิตศาสตร์ที่ทำให้วิธีนี้ใช้ได้คือ ถ้าสองแถวมีค่าใกล้กันในทุกมิติ เส้นคลื่นของทั้งสองจะใกล้กันตลอดช่วง ดังนั้นกลุ่มที่เกาะกันในข้อมูลจะปรากฏเป็นมัดเส้นที่เกาะกัน

ประโยชน์จริงคือการตรวจว่ากลุ่มที่เราตั้งไว้แยกกันจริงไหม ถ้าระบายสีตามกลุ่มแล้วเส้นแต่ละสีเกาะกันเป็นมัดแยกจากกัน แปลว่าการแบ่งกลุ่มนั้นมีฐานในข้อมูลจริง

ข้อจำกัดที่หนักคือลำดับของตัวแปรเปลี่ยนรูปร่างของเส้น เพราะตัวแปรแรกได้สัมประสิทธิ์ที่ต่างจากตัวอื่น สลับคอลัมน์แล้วภาพจะไม่เหมือนเดิม จึงต้องลองหลายลำดับก่อนสรุป

03ข้อมูลที่ต้องใช้

รูปทรงของข้อมูลตารางแบบกว้าง (หนึ่งแถวหนึ่ง entity)
จำนวนมิติที่เข้ารหัสได้3 มิติ
จำนวนแถวที่เหมาะ20–150 แถว × 4–8 ตัวแปรขั้นต่ำ 15 แถว
เส้นฐานศูนย์ไม่เกี่ยวข้องแกนตั้งคือค่าของฟังก์ชันคลื่นซึ่งไม่มีหน่วยและไม่มีความหมายในตัวเอง

คอลัมน์ที่ต้องเตรียม

คอลัมน์ทำหน้าที่ชนิดเข้ารหัสด้วยจำนวนค่าที่เหมาะหมายเหตุ
ราคาตัวแปรตัวเลขตัวที่ 1ตัวเลขต่อเนื่องpathต่อเนื่องทุกคอลัมน์ตัวเลขถูกใช้เป็นสัมประสิทธิ์ของคลื่น
ความเร็วตัวแปรตัวเลขตัวที่ 2ตัวเลขต่อเนื่องpathต่อเนื่องควรมี 3–8 คอลัมน์ตัวเลข
group
ไม่บังคับ
กลุ่มที่รู้อยู่แล้วหมวดหมู่color<= 3 กลุ่มใช้ระบายสีเพื่อตรวจว่ากลุ่มแยกกันจริงไหม

ต้องเตรียมข้อมูลอย่างไรก่อนวาด

  1. ★ ปรับมาตรฐานทุกตัวแปรให้อยู่ในสเกลเดียวกัน ไม่อย่างนั้นตัวแปรที่มีหน่วยใหญ่จะครองรูปร่างทั้งหมด
  2. ตัดสินใจลำดับของตัวแปรและลองหลายลำดับ
  3. ตัดแถวที่ไม่ครบ

ค่าที่หายไป — แถวที่ขาดค่าใดค่าหนึ่งแปลงเป็นคลื่นไม่ได้ ต้องตัดออกและรายงาน

สเกลของแกน — ตัวแปรทุกตัวต้องปรับมาตรฐานก่อน เพราะสัมประสิทธิ์ของคลื่นมาจากค่าดิบโดยตรง

กราฟจะเริ่มพังเมื่อไร

เกิน 200 เส้น ภาพจะกลายเป็นมัดทึบที่แยกอะไรไม่ได้

หน้าตาไฟล์ที่ต้องเตรียม

item,group,ราคา,ความเร็ว,ความจุ,น้ำหนัก,คะแนนรีวิว
รุ่น A,รุ่นประหยัด,167,48.5,17.6,30.9,3.17
รุ่น B,รุ่นมาตรฐาน,301,55.7,30.4,20,4.09

04อ่านแล้วเห็นอะไร

สิ่งที่เห็นได้จากภาพ

  • มัดเส้นที่เกาะกันซึ่งคือกลุ่มในข้อมูล
  • เส้นที่แยกตัวออกไปซึ่งคือแถวผิดปกติ
  • ว่ากลุ่มที่ระบายสีไว้แยกกันจริงหรือปนกัน

สิ่งที่กราฟนี้ตอบไม่ได้

  • ★ อ่านค่าของตัวแปรใดตัวแปรหนึ่งจากเส้นไม่ได้เลย
  • รูปร่างของเส้นเปลี่ยนตามลำดับของตัวแปร จึงไม่ใช่คุณสมบัติของข้อมูลล้วน ๆ

อ่านตามลำดับนี้

  1. มองหามัดเส้นที่เกาะกัน
  2. ดูว่าเส้นแต่ละสีเกาะกันเป็นมัดของตัวเองไหม
  3. หาเส้นที่แยกตัวออกจากทุกมัด

คำถามที่ตอบได้

  • ข้อมูลแบ่งเป็นกี่กลุ่มโดยธรรมชาติ
  • การแบ่งกลุ่มที่เราตั้งไว้มีฐานในข้อมูลไหม
  • มีแถวไหนที่ผิดปกติในหลายมิติพร้อมกัน

สัญญาณว่ามีอะไรผิด

  • ไม่ได้ปรับมาตรฐานตัวแปรก่อน ทำให้ตัวแปรหน่วยใหญ่ครองรูปร่างทั้งหมด
  • เส้นเยอะจนเป็นมัดทึบ

05เหมาะกับการนำเสนอแบบไหน

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

ทำอย่างไรให้คนอ่านได้ข้อสรุปโดยไม่ต้องบรรยาย

  1. อธิบายว่าแกนตั้งไม่มีหน่วยและเส้นคือตัวแทนของแถว
  2. ระบุลำดับของตัวแปรที่ใช้
  3. ระบายสีตามกลุ่มเพื่อให้ตรวจสอบได้

รูปแบบชื่อกราฟที่ควรใช้

กลุ่ม <กลุ่ม> เกาะกันเป็นมัดแยกจากกลุ่มอื่นชัดเจน ซึ่งสนับสนุนว่าการแบ่งกลุ่มมีฐานในข้อมูล

06เอาไปใช้ต่อ

โค้ดทุกชิ้นอ่านไฟล์ andrews-curves-sample.csv ชุดเดียวกับที่ Playground ใช้ และตัวเลขในโค้ดเปลี่ยนตามค่าที่คุณปรับด้านล่าง

# ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*"
# ดาวน์โหลด andrews-curves-sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์
import matplotlib
matplotlib.use("Agg")   # รันแบบไม่มีหน้าจอ ถ้ารันในเครื่องตัวเองลบบรรทัดนี้ได้
import matplotlib.pyplot as plt
import pandas as pd
from pandas.plotting import andrews_curves
from sklearn.preprocessing import StandardScaler  # pip install scikit-learn

C1, C2, C3 = "#2a78d6", "#eb6834", "#1baf7a"   # palette ของคลัง เปลี่ยนเป็นสีองค์กรได้
INK, SURFACE = "#0b0b0b", "#fcfcfb"

df = pd.read_csv("andrews-curves-sample.csv")
df["ราคา"] = pd.to_numeric(df["ราคา"], errors="coerce")
df["ความเร็ว"] = pd.to_numeric(df["ความเร็ว"], errors="coerce")
df["ความจุ"] = pd.to_numeric(df["ความจุ"], errors="coerce")
df["น้ำหนัก"] = pd.to_numeric(df["น้ำหนัก"], errors="coerce")
df["คะแนนรีวิว"] = pd.to_numeric(df["คะแนนรีวิว"], errors="coerce")
NOTE = "bundles"   # ← ค่าจาก Playground

fig, ax = plt.subplots(figsize=(7.6, 4.2), facecolor=SURFACE)
ax.set_facecolor(SURFACE)

cols = ["ราคา", "ความเร็ว", "ความจุ", "น้ำหนัก", "คะแนนรีวิว"]
scaled = df.copy()
scaled[cols] = StandardScaler().fit_transform(df[cols])   # ★ ต้องปรับมาตรฐานก่อนเสมอ
andrews_curves(scaled[cols + ["group"]], "group", ax=ax, color=[C1, C2, C3], linewidth=1.4, alpha=0.65)
ax.set_xlabel("t"); ax.set_ylabel("ค่าของฟังก์ชันคลื่น (ไม่มีหน่วย)")
ax.legend(frameon=False, title="กลุ่ม")

ax.spines[["top", "right"]].set_visible(False)   # ลดเส้นที่ไม่ได้ให้ข้อมูล
fig.tight_layout()
fig.savefig("andrews-curves.png", dpi=160)
print("เขียน andrews-curves.png แล้ว ·", len(df), "แถว")

ติดตั้ง: pip install "matplotlib==3.10.*" "pandas==2.*" · ทดสอบกับเวอร์ชัน 3.10

07ลองเล่นเพื่อเข้าใจ

ข้อมูลตัวอย่าง · 45 แถว · seed 197● คำนวณใหม่ในเบราว์เซอร์ทันทีที่ปรับค่า
เส้นสีเดียวกันควรเกาะกันถ้ากลุ่มแยกจริง
รุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียม
-4.00-2.000.002.004.006.00-2.00.02.0tรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียมรุ่นประหยัดรุ่นมาตรฐานรุ่นพรีเมียม

ลำดับของตัวแปรเปลี่ยนรูปร่างของเส้น — สลับคอลัมน์แล้วภาพจะไม่เหมือนเดิม

จำนวนตัวแปร5
จำนวนเส้น45
จำนวนกลุ่ม3
ดูข้อมูลเป็นตาราง (3 แถว)
Andrews curves 45 เส้น 3 กลุ่ม
กลุ่มจำนวนเส้น
รุ่นประหยัด15
รุ่นมาตรฐาน15
รุ่นพรีเมียม15

ลองแล้วจะเห็นอะไร

  • สังเกตอะไร: ดูว่าเส้นแต่ละสีเกาะกันเป็นมัดของตัวเองไหม ถ้าเกาะแปลว่าการแบ่งกลุ่มมีฐานในข้อมูล ถ้าปนกันแปลว่ากลุ่มที่ตั้งไว้อาจไม่ตรงกับโครงสร้างจริง

ข้อควรระวัง

  1. ★ ไม่ปรับมาตรฐานตัวแปรก่อน ทำให้ตัวแปรที่มีหน่วยใหญ่ครองรูปร่างของเส้นทั้งหมด
  2. สรุปจากลำดับตัวแปรเดียวโดยไม่ลองสลับ ทั้งที่รูปร่างเปลี่ยนตามลำดับ
  3. พยายามอ่านค่าของตัวแปรจากเส้น ซึ่งทำไม่ได้เลย
  4. ใส่เส้นมากเกินไปจนกลายเป็นมัดทึบ

เมื่อไหร่ไม่ควรใช้กราฟนี้

  • มีตัวแปรน้อยกว่า 4 ตัวใช้แทน: scatter-matrix ที่อ่านค่าได้ตรง
  • ต้องการอ่านค่าของแต่ละตัวแปรใช้แทน: parallel-coordinates
  • ต้องการลดมิติเพื่อดูโครงสร้างใช้แทน: pca-biplot หรือ embedding-projection

กราฟที่เกี่ยวข้อง

เทคนิคถัดไป · กราฟวงกลม