φυβλαςのβλογ
phyblas的博客



จัดการข้อมูลด้วย pandas เบื้องต้น บทที่ ๑๔: การจัดกลุ่มข้อมูล
เขียนเมื่อ 2016/09/25 15:54
แก้ไขล่าสุด 2021/09/28 16:42
pandas ได้เตรียมคำสั่งที่สะดวกสำหรับใช้ในการแบ่งกลุ่มข้อมูลที่อยู่ภายในตารางเพื่อจะนำมารวบยอดหรือทำอะไรก็ตามแบบแยกเป็นกลุ่มๆ

การแยกกลุ่มของข้อมูลสามารถทำได้ด้วยเมธอด groupby ซึ่งมีอยู่ทั้งในซีรีส์และเดตาเฟรม มีหน้าที่แบ่งกลุ่มข้อมูลเพื่อนำมาทำอะไรต่ออีกที

การใช้ groupby นั้นค่อนข้างมีความซับซ้อนเข้าใจยากอยู่แต่หากเข้าใจและใช้เป็นแล้วจะเป็นประโยชน์ในการจัดการข้อมูลเป็นอย่างมาก



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

ตัวอย่างเช่น ลองดูตารางข้อมูลของโปเกมอน ๙ สายพันธุ์
import pandas as pd
pokemon = pd.DataFrame([
        ['ฟุชิงิดาเนะ','พืช/พิษ',0.7,6.9],
        ['ฟุชิงิโซว','พืช/พิษ',1.0,13.0],
        ['ฟุชิงิบานะ','พืช/พิษ',2.4,155.5],
        ['ฮิโตคาเงะ','ไฟ',0.6,8.5],
        ['ลิซาร์โด','ไฟ',1.1,19.0],
        ['ลิซาร์ดอน','ไฟ/บิน',1.7,101.5],
        ['เซนิงาเมะ','น้ำ',0.5,9.0],
        ['คาเมล','น้ำ',1.0,22.5],
        ['คาเม็กซ์','น้ำ',1.6,101.1]],
    columns=['สายพันธุ์','ชนิด','ส่วนสูง','น้ำหนัก'],
    index=[1,2,3,4,5,6,7,8,9])
print(pokemon)



ได้
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
1 ฟุชิงิดาเนะ พืช/พิษ 0.7 6.9
2 ฟุชิงิโซว พืช/พิษ 1.0 13.0
3 ฟุชิงิบานะ พืช/พิษ 2.4 155.5
4 ฮิโตคาเงะ ไฟ 0.6 8.5
5 ลิซาร์โด ไฟ 1.1 19.0
6 ลิซาร์ดอน ไฟ/บิน 1.7 101.5
7 เซนิงาเมะ น้ำ 0.5 9.0
8 คาเมล น้ำ 1.0 22.5
9 คาเม็กซ์ น้ำ 1.6 101.1

แต่ละตัวมีชนิดเหมือนกันบ้างต่างกันบ้าง เราลองมาแบ่งกลุ่มตามชนิดดูด้วย groupby
print(pokemon.groupby('ชนิด'))

ได้
 

ผลที่ได้ออกมาจะเป็นออบเจ็กต์ชนิด pandas.core.groupby.DataFrameGroupBy อย่างที่เห็น ซึ่งถึงสั่ง print ก็ไม่แสดงผลลัพธ์อะไร

ต่อจากนี้ไปจะเรียกออบเจ็กชนิดนี้ว่า "ข้อมูลที่จัดกลุ่มแล้ว"

เราสามารถดูผลการจัดกลุ่มได้ด้วยการเติม .groups ต่อท้าย ผลที่ได้จะออกมาเป็นดิกชันนารีที่แสดงดัชนีของแถวที่อยู่ในแต่ละกลุ่ม
print(pokemon.groupby('ชนิด').groups)

ได้
{'ไฟ/บิน': [6], 'ไฟ': [4, 5], 'น้ำ': [7, 8, 9], 'พืช/พิษ': [1, 2, 3]}

เท่านี้ก็จะเห็นได้ว่าข้อมูลถูกแบ่งกลุ่มออกตามชนิดของโปเกมอน

หากแค่ต้องการจำนวนแถวในแต่ละกลุ่มก็ใช้เมธอด size
print(pokemon.groupby('ชนิด').size())

จะได้ซีรีส์ของจำนวน
ชนิด
น้ำ        3
พืช/พิษ    3
ไฟ         2
ไฟ/บิน     1
dtype: int64

และอาจสามารถดูเนื้อในได้ด้วยการแปลงเป็น list
print(list(pokemon.groupby('ชนิด')))

ได้
[('น้ำ',    สายพันธุ์ ชนิด  ส่วนสูง  น้ำหนัก
  7  เซนิงาเมะ  น้ำ      0.5      9.0
  8      คาเมล  น้ำ      1.0     22.5
  9   คาเม็กซ์  น้ำ      1.6    101.1),
 ('พืช/พิษ',      สายพันธุ์     ชนิด  ส่วนสูง  น้ำหนัก
  1  ฟุชิงิดาเนะ  พืช/พิษ      0.7      6.9
  2    ฟุชิงิโซว  พืช/พิษ      1.0     13.0
  3   ฟุชิงิบานะ  พืช/พิษ      2.4    155.5),
 ('ไฟ',    สายพันธุ์ ชนิด  ส่วนสูง  น้ำหนัก
  4  ฮิโตคาเงะ   ไฟ      0.6      8.5
  5   ลิซาร์โด   ไฟ      1.1     19.0),
 ('ไฟ/บิน',    สายพันธุ์    ชนิด  ส่วนสูง  น้ำหนัก
  6  ลิซาร์ดอน  ไฟ/บิน      1.7    101.5)]



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

ตัวอย่าง
for chue,klum in pokemon.groupby('ชนิด'):
    print('<<'+chue+'>>')
    print(klum)

ได้
<<น้ำ>>
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
7 เซนิงาเมะ น้ำ 0.5 9.0
8 คาเมล น้ำ 1.0 22.5
9 คาเม็กซ์ น้ำ 1.6 101.1

<<พืช/พิษ>>
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
1 ฟุชิงิดาเนะ พืช/พิษ 0.7 6.9
2 ฟุชิงิโซว พืช/พิษ 1.0 13.0
3 ฟุชิงิบานะ พืช/พิษ 2.4 155.5

<<ไฟ>>
 
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
4 ฮิโตคาเงะ ไฟ 0.6 8.5
5 ลิซาร์โด ไฟ 1.1 19.0

<<ไฟ/บิน>>
  สายพันธุ์ ชนิด ส่วนสูง น้ำหนัก
6 ลิซาร์ดอน ไฟ/บิน 1.7 101.5



จัดการข้อมูลที่ถูกจัดกลุ่มแล้วด้วยเมธอดต่างๆ
ข้อมูลที่ถูกจัดกลุ่มแล้วนี้สามารถใช้เมธอดต่างๆที่ใช้สำหรับการรวบยอดข้อมูลได้

เช่น max
print(pokemon.groupby('ชนิด').max())

ได้
  สายพันธุ์ ส่วนสูง น้ำหนัก
ชนิด      
น้ำ เซนิงาเมะ 1.6 101.1
พืช/พิษ ฟุชิงิโซว 2.4 155.5
ไฟ ฮิโตคาเงะ 1.1 19.0
ไฟ/บิน ลิซาร์ดอน 1.7 101.5

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

sum ก็ทำนองเดียวกัน เป็นการคำนวณผลรวม แต่ sum จะแสดงเฉพาะคอลัมน์ที่เป็นข้อมูลตัวเลข เพราะตัวหนังสือไม่สามารถคำนวณได้
print(pokemon.groupby('ชนิด').sum())

ได้
  ส่วนสูง น้ำหนัก
ชนิด    
น้ำ 3.1 132.6
พืช/พิษ 4.1 175.4
ไฟ 1.7 27.5
ไฟ/บิน 1.7 101.5

แล้วก็ยังมี min, prod, mean, median, std, var, ฯลฯ เหมือนกับที่เขียนไว้ในบทที่ ๘

อนึ่ง ในนี้ยังมีเมธอด count ซึ่งมีไว้หาจำนวนสมาชิกที่ไม่เป็น NaN อาจดูเหมือนคล้ายกับ size แต่ count จะคืนค่าเป็นเดตาเฟรมซึ่งนับจำนวนข้อมูลที่ไม่ใช่ NaN ในแต่ละคอลัมน์ ซึ่งถ้าหากทุกคอลัมน์ไม่มี NaN อยู่ก็จะได้ค่าทุกคอลัมน์เท่ากันหมด
print(pokemon.groupby('ชนิด').count())

ได้
  สายพันธุ์ ส่วนสูง น้ำหนัก
ชนิด      
น้ำ 3 3 3
พืช/พิษ 3 3 3
ไฟ 2 2 2
ไฟ/บิน 1 1 1

ข้อมูลที่จัดกลุ่มแล้วสามารถตามด้วย [ชื่อคอลัมน์] เพื่อแยกเป็นซีรีส์ของแต่ละคอลัมน์ก่อนจึงค่อยใช้เมธอดก็ได้
print(pokemon.groupby('ชนิด')['น้ำหนัก'])

ได้
 

แบบนี้จะได้ออบเจ็กต์ประเภท pandas.core.groupby.SeriesGroupBy ซึ่งก็เรียกว่าเป็นข้อมูลที่แยกกลุ่มแล้วเหมือนกัน แต่ต่างจากก่อนเติม [] ตรงที่ว่าเป็น SeriesGroupBy ไม่ใช่ DataFrameGroupBy

แต่คุณสมบัติก็คล้ายๆกัน เอามาใช้เมธอดได้เช่นเดียวกัน เพื่อจัดการกับข้อมูลในคอลัมน์นั้น

เช่นหาค่าเฉลี่ยภายในกลุ่มต่างๆในคอลัมน์นั้น
print(pokemon.groupby('ชนิด')['น้ำหนัก'].mean())

ได้
ชนิด
น้ำ         44.200000
พืช/พิษ     58.466667
ไฟ          13.750000
ไฟ/บิน     101.500000
Name: น้ำหนัก, dtype: float64



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

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

ตัวอย่างเช่นหากต้องการแบ่งข้อมูลออกเป็น ๓ กลุ่ม ชื่อกลุ่ม 1,2 และ 3
xyz = [1,2,3,1,2,3,1,2,3]
# หรือ xyz = pd.Series([1,2,3,1,2,3,1,2,3],index=range(1,10))
print(pokemon.groupby(xyz).groups)

ได้
{1: [1, 4, 7], 2: [2, 5, 8], 3: [3, 6, 9]}

ข้อมูลจะถูกแบ่งกลุ่มตามลิสต์ที่ใส่ไป

แท้จริงแล้วตัวอย่างก่อนหน้าที่เขียนเป็น pokemon.groupby('ชนิด') นั้นมีค่าเท่ากับเขียนเป็น
pokemon.groupby(pokemon['ชนิด'])

นั่นคือแบ่งโดยใช้ซีรีส์ของชนิดโปเกมอน เพียงแต่ว่ากรณีที่ใช้คอลัมน์เป็นตัวแบ่งสามารถเขียนให้ง่ายขึ้นดังที่เห็นนี้

เราอาจจะประยุกต์เป็นแบ่งโดยใช้ฟังก์ชันเพื่อสร้างลิสต์หรือซีรีส์สำหรับคัดกรองแบ่งกลุ่มขึ้นมา

เช่นลองแบ่งกลุ่มตามอักษร ๔ ตัวแรกของชื่อสายพันธุ์
xxxx = pokemon['สายพันธุ์'].map(lambda x:x[:4])
print(pokemon.groupby(xxxx).groups)
for chue,klum in pokemon.groupby(xxxx):
    print('%s: %s'%(chue,list(klum['สายพันธุ์'])))

ได้
{'เซนิ': [7], 'ฮิโต': [4], 'ฟุชิ': [1, 2, 3], 'คาเม': [8, 9], 'ลิซา': [5, 6]}
คาเม: ['คาเมล', 'คาเม็กซ์']
ฟุชิ: ['ฟุชิงิดาเนะ', 'ฟุชิงิโซว', 'ฟุชิงิบานะ']
ลิซา: ['ลิซาร์โด', 'ลิซาร์ดอน']
ฮิโต: ['ฮิโตคาเงะ']
เซนิ: ['เซนิงาเมะ']



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

เช่นแบ่งตามดัชนีเลขคู่เลขคี่
def khukhi(x):
    if(x%2): return 'คี่'
    else: return 'คู่'
print(pokemon.groupby(khukhi).groups)
for chue,klum in pokemon.groupby(khukhi):
    print('%s: %s'%(chue,list(klum['สายพันธุ์'])))

ได้
{'คี่': [1, 3, 5, 7, 9], 'คู่': [2, 4, 6, 8]}
คี่: ['ฟุชิงิดาเนะ', 'ฟุชิงิบานะ', 'ลิซาร์โด', 'เซนิงาเมะ', 'คาเม็กซ์']
คู่: ['ฟุชิงิโซว', 'ฮิโตคาเงะ', 'ลิซาร์ดอน', 'คาเมล']

ที่จริงแล้ว pokemon.groupby(khukhi) ก็มีค่าเท่ากับ
pokemon.groupby(pokemon.index.map(khukhi))

เพียงแต่ในกรณีที่จะใช้ฟังก์ชันทำกับดัชนีแล้วสามารถแค่ใส่ตัวฟังก์ชันลงไปเลยโดยตรงแบบนี้ได้เลยซึ่งจะดูง่ายกว่า

ในตัวอย่างก่อนหน้าที่แบ่งตามอักษร ๔ ตัวแรกของชื่อสายพันธุ์เราอาจเขียนใหม่ได้โดยตั้งให้สายพันธุ์เป็นดัชนี จากนั้นก็จะสามารถใส่ฟังก์ชันที่ทำกับชื่อสายพันธุ์ลงไปใน  groupby ได้เลย
pokemon = pokemon.set_index('สายพันธุ์')
print(pokemon)
print(pokemon.groupby(lambda x:x[:4]).groups)

ได้
  ชนิด ส่วนสูง น้ำหนัก
สายพันธุ์      
ฟุชิงิดาเนะ พืช/พิษ 0.7 6.9
ฟุชิงิโซว พืช/พิษ 1.0 13.0
ฟุชิงิบานะ พืช/พิษ 2.4 155.5
ฮิโตคาเงะ ไฟ 0.6 8.5
ลิซาร์โด ไฟ 1.1 19.0
ลิซาร์ดอน ไฟ/บิน 1.7 101.5
เซนิงาเมะ น้ำ 0.5 9.0
คาเมล น้ำ 1.0 22.5
คาเม็กซ์ น้ำ 1.6 101.1

{'เซนิ': ['เซนิงาเมะ'], 'ฮิโต': ['ฮิโตคาเงะ'], 'ฟุชิ': ['ฟุชิงิดาเนะ', 'ฟุชิงิโซว', 'ฟุชิงิบานะ'], 'คาเม': ['คาเมล', 'คาเม็กซ์'], 'ลิซา': ['ลิซาร์โด', 'ลิซาร์ดอน']}



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

ตัวอย่าง
pokemon = pd.DataFrame([
        ['มาดัตสึโบมิ','พืช','พิษ',0.7,4.0],
        ['อุตสึดง','พืช','พิษ',1.0,6.4],
        ['อุตสึบ็อต','พืช','พิษ',1.7,15.5],
        ['เมโนคุราเงะ','น้ำ','พิษ',0.9,45.5],
        ['โดคุคุราเงะ','น้ำ','พิษ',1.6,55.0],
        ['ยาดอน','น้ำ','พลังจิต',1.2,36.0],
        ['ยาโดรัน','น้ำ','พลังจิต',1.6,78.5],
        ['ทามะทามะ','พืช','พลังจิต',0.4,2.5],
        ['นัสซี','พืช','พลังจิต',2.0,120.0]],
    columns=['สายพันธุ์','ชนิด1','ชนิด2','ส่วนสูง','น้ำหนัก'],
    index=[69,70,71,72,73,79,80,102,103])
print(pokemon)



ได้
  สายพันธุ์ ชนิด1 ชนิด2 ส่วนสูง น้ำหนัก
69 มาดัตสึโบมิ พืช พิษ 0.7 4.0
70 อุตสึดง พืช พิษ 1.0 6.4
71 อุตสึบ็อต พืช พิษ 1.7 15.5
72 เมโนคุราเงะ น้ำ พิษ 0.9 45.5
73 โดคุคุราเงะ น้ำ พิษ 1.6 55.0
79 ยาดอน น้ำ พลังจิต 1.2 36.0
80 ยาโดรัน น้ำ พลังจิต 1.6 78.5
102 ทามะทามะ พืช พลังจิต 0.4 2.5
103 นัสซี พืช พลังจิต 2.0 120.0

โปเกมอนแต่ละตัวในข้อมูลนี้มี "ชนิด1" และ "ชนิด2" ต่างกัน หากลองใช้เป็นคีย์แบ่งดู
print(pokemon.groupby(['ชนิด1','ชนิด2']).groups)
print(pokemon.groupby(['ชนิด1','ชนิด2']).size())

ได้
{('น้ำ', 'พิษ'): [72, 73], ('พืช', 'พิษ'): [69, 70, 71], ('น้ำ', 'พลังจิต'): [79, 80], ('พืช', 'พลังจิต'): [102, 103]}
ชนิด1  ชนิด2
น้ำ    พลังจิต    2
       พิษ        2
พืช    พลังจิต    2
       พิษ        3
dtype: int64

ผลที่ได้จะอยู่ในรูปของดัชนีซ้อน

เมื่อนำมาใช้กับ for ส่วนของชื่อก็จะอยู่ในรูปของลิสต์อีกทีด้วย
for chue,klum in pokemon.groupby(['ชนิด1','ชนิด2']):
    print('<< '+chue[0]+' + '+chue[1]+' >>')
    print(klum)

ได้
<< น้ำ + พลังจิต >>
  สายพันธุ์ ชนิด1 ชนิด2 ส่วนสูง น้ำหนัก
79 ยาดอน น้ำ พลังจิต 1.2 36.0
80 ยาโดรัน น้ำ พลังจิต 1.6 78.5

<< น้ำ + พิษ >>
  สายพันธุ์ ชนิด1 ชนิด2 ส่วนสูง น้ำหนัก
72 เมโนคุราเงะ น้ำ พิษ 0.9 45.5
73 โดคุคุราเงะ น้ำ พิษ 1.6 55.0

<< พืช + พลังจิต >>
  สายพันธุ์ ชนิด1 ชนิด2 ส่วนสูง น้ำหนัก
102 ทามะทามะ พืช พลังจิต 0.4 2.5
103 นัสซี พืช พลังจิต 2.0 120.0

<< พืช + พิษ >>
  สายพันธุ์ ชนิด1 ชนิด2 ส่วนสูง น้ำหนัก
69 มาดัตสึโบมิ พืช พิษ 0.7 4.0
70 อุตสึดง พืช พิษ 1.0 6.4
71 อุตสึบ็อต พืช พิษ 1.7 15.5



อ้างอิง


<< บทที่แล้ว      บทถัดไป >>
หน้าสารบัญ


-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> pandas

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

目录

从日本来的名言
模块
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
机器学习
-- 神经网络
javascript
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  查看日志

  推荐日志

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ