φυβλαςのβλογ
บล็อกของ phyblas



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

แต่พอเป็นเดตาเฟรมมีคอลัมน์ด้วยจึงเพิ่มเป็นสองมิติ คือต้องระบุทั้งดัชนีและคอลัมน์

ซีรีส์ที่มีดัชนีซ้อนกันสองตัวก็ถือว่ามีสองมิติเช่นกัน เพราะต้องระบุดัชนีทั้ง ๒ ตัวเพื่อเข้าถึงข้อมูล

ดังนั้นหากมองเช่นนี้แล้ว ซีรีส์ที่มีดัชนีสองตัวกับเดตาเฟรมนั้นก็เป็นอะไรที่คล้ายๆกัน

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

ตัวอย่าง สร้างตารางข้อมูลของโปเกมอนซึ่งแต่ละแถวแสดงชนิดของโปเกมอน แต่ละคอลัมน์แสดงระดับวิวัฒนาการ (ร่าง) ของโปเกมอน
import pandas as pd
pokemon = pd.DataFrame([
        ['ชิโครีตา','เบย์ลีฟ','เมกาเนียม'],
        ['ฮิโนอาราชิ','แม็กมาราชิ','บักฟูน'],
        ['วานิโนโกะ','อาลิเกตซ์','ออร์ไดล์']],
    index=pd.Series(['พืช','ไฟ','น้ำ'],name='ชนิด'),
    columns=pd.Series([1,2,3],name='ร่าง'))
print(pokemon)





ได้
ร่าง 1 2 3
ชนิด      
พืช ชิโครีตา เบย์ลีฟ เมกาเนียม
ไฟ ฮิโนอาราชิ แม็กมาราชิ บักฟูน
น้ำ วานิโนโกะ อาลิเกตซ์ ออร์ไดล์

จากนั้นลองใช้ stack เพื่อแปลงเป็นซีรีส์ที่มีดัชนี ๒ ตัว
print(pokemon.stack())

ได้
ชนิด  ร่าง
พืช   1         ชิโครีตา
      2          เบย์ลีฟ
      3        เมกาเนียม
ไฟ    1       ฮิโนอาราชิ
      2       แม็กมาราชิ
      3           บักฟูน
น้ำ   1        วานิโนโกะ
      2        อาลิเกตซ์
      3         ออร์ไดล์
dtype: object

เท่านี้คอลัมน์ของเดตาเฟรมก็กลายมาเป็นดัชนีตัวที่สองของซีรีส์ที่เกิดขึ้นมาใหม่

ส่วนกระบวนการตรงกันข้าม ก็คือการแปลงจากซีรีส์ที่มีดัชนีสองตัวมาเป็นเดตาเฟรมจะใช้เมธอด unstack

ถ้าลอง
print(pokemon.stack().unstack())

แบบนี้ผลที่ได้ก็คือเดตาเฟรมตัวเดิม

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

ดังนั้นถ้าจะเลือกดึง "ชนิด" ขึ้นมาก็ใส่เป็น
print(pokemon.stack().unstack('ชนิด'))
# หรือ print(pokemon.stack().unstack(0))

ได้
ชนิด พืช ไฟ น้ำ
ร่าง      
1 ชิโครีตา ฮิโนอาราชิ วานิโนโกะ
2 เบย์ลีฟ แม็กมาราชิ อาลิเกตซ์
3 เมกาเนียม บักฟูน ออร์ไดล์

คอลัมน์กับดัชนีจะกลับกันจากข้อมูลตอนแรก



กรณีข้อมูลสามมิติ
stack นั้นไม่ได้ใช้แค่กับเดตาเฟรมที่มีดัชนีแค่ตัวเดียว แต่กับแบบหลายดัชนีก็สามารถใช้ได้ด้วย

และ unstack นั้นไม่ใช่แค่ใช้กับซีรีส์แต่ยังใช้กับเดตาเฟรมได้ด้วย

ลองดูตัวอย่างโดยใช้โปเกมอนชุดเดิม แต่เพิ่มข้อมูลหมายเลขเข้าไป
pokemon = pd.DataFrame({
        'สายพันธุ์':[
            'ชิโครีตา'
,'เบย์ลีฟ','เมกาเนียม',
            'ฮิโนอาราชิ'
,'แม็กมาราชิ','บักฟูน',
            'วานิโนโกะ','อาลิเกตซ์','ออร์ไดล์'],
        'หมายเลข':[152,153,154,155,156,157,158,159,160]},
    index=[pd.Series(['พืช','พืช','พืช','ไฟ','ไฟ','ไฟ','น้ำ','น้ำ','น้ำ'],name='ชนิด'),
           pd.Series([1,2,3,1,2,3,1,2,3],name='ร่าง')])
print(pokemon)

แบบนี้จะเป็นข้อมูลที่มีมิติเป็นสามมิติ คือมี "ชนิด", "ร่าง" แล้วก็มีแยกว่าเป็นข้อมูลสายพันธุ์หรือหมายเลข (ไม่ได้ตั้งชื่อ)
    สายพันธุ์ หมายเลข
ชนิด ร่าง    
พืช 1 ชิโครีตา 152
2 เบย์ลีฟ 153
3 เมกาเนียม 154
ไฟ 1 ฮิโนอาราชิ 155
2 แม็กมาราชิ 156
3 บักฟูน 157
น้ำ 1 วานิโนโกะ 158
2 อาลิเกตซ์ 159
3 ออร์ไดล์ 160

ลอง stack ดู
print(pokemon.stack())

จะได้
ชนิด  ร่าง
พืช   1     สายพันธุ์      ชิโครีตา
            หมายเลข             152
      2     สายพันธุ์       เบย์ลีฟ
            หมายเลข             153
      3     สายพันธุ์     เมกาเนียม
            หมายเลข             154
ไฟ    1     สายพันธุ์    ฮิโนอาราชิ
            หมายเลข             155
      2     สายพันธุ์    แม็กมาราชิ
            หมายเลข             156
      3     สายพันธุ์        บักฟูน
            หมายเลข             157
น้ำ   1     สายพันธุ์     วานิโนโกะ
            หมายเลข             158
      2     สายพันธุ์     อาลิเกตซ์
            หมายเลข             159
      3     สายพันธุ์      ออร์ไดล์
            หมายเลข             160
dtype: object

ตอนนี้กลายเป็นซีรีส์ที่มีดัชนีถึง ๓ ตัวไปแล้ว และข้อมูลทั้งหมดถูกกางแผ่ออกในแนวนอนทั้งหมดกลายเป็น 3*3*2=18 แถว

ในทางกลับกันลองใช้ unstack ดู
print(pokemon.unstack())

จะได้
  สายพันธุ์ หมายเลข
ร่าง 1 2 3 1 2 3
ชนิด            
น้ำ วานิโนโกะ อาลิเกตซ์ ออร์ไดล์ 158 159 160
พืช ชิโครีตา เบย์ลีฟ เมกาเนียม 152 153 154
ไฟ ฮิโนอาราชิ แม็กมาราชิ บักฟูน 155 156 157

กลายเป็นเดตาเฟรมที่มีดัชนีตัวเดียว แต่กลายเป็นว่าคอลัมน์เพิ่มเป็น ๒ ตัว

นอกจากนี้แล้วก็ยัง unstack ซ้ำได้อีก
print(pokemon.unstack().unstack())

และผลที่ได้ก็กลายเป็นซีรีส์เหมือนกัน แต่ลำดับจะต่างจากซีรีส์ที่ได้จากการ stack
           ร่าง  ชนิด
สายพันธุ์  1     น้ำ      วานิโนโกะ
                 พืช       ชิโครีตา
                 ไฟ      ฮิโนอาราชิ
           2     น้ำ      อาลิเกตซ์
                 พืช        เบย์ลีฟ
                 ไฟ      แม็กมาราชิ
           3     น้ำ       ออร์ไดล์
                 พืช      เมกาเนียม
                 ไฟ          บักฟูน
หมายเลข    1     น้ำ            158
                 พืช            152
                 ไฟ             155
           2     น้ำ            159
                 พืช            153
                 ไฟ             156
           3     น้ำ            160
                 พืช            154
                 ไฟ             157
dtype: object



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

แต่ถ้าข้อมูลไม่ครบสมบูรณ์แบบนั้น เมื่อมีการแปลงไปมาก็จะเกิด NaN ขึ้นได้ ตัวอย่างเช่นถ้าข้อมูลเป็นแบบนี้
pokemon = pd.Series(
    ['คิโมริ','จุปเทิล','จูไคน์',
     'อาชาโม'
,'วากะชาโม','บาชาโม',
     'มิซึโงโรว'
,'นุมาครอว์','ลากลาร์จ'],
    index=[
        pd.Series(
            ['พืช','พืช','พืช',
             'ไฟ','ไฟ/ต่อสู้','ไฟ/ต่อสู้',
             'น้ำ'
,'น้ำ/ดิน','น้ำ/ดิน'],
            name='ชนิด'),
        pd.Series([1,2,3,1,2,3,1,2,3],name='ร่าง')])
print(pokemon)





ได้
ชนิด       ร่าง
พืช        1          คิโมริ
           2         จุปเทิล
           3          จูไคน์
ไฟ         1          อาชาโม
ไฟ/ต่อสู้  2        วากะชาโม
           3          บาชาโม
น้ำ        1       มิซึโงโรว
น้ำ/ดิน    2       นุมาครอว์
           3        ลากลาร์จ
dtype: object

ในตัวอย่างนี้หากจับคู่ระหว่าง "ร่าง" กับ "ชนิด" แล้วละก็จะพบว่าไม่สามารถจับคู่กันได้ครบ

เช่นมี "ไฟ" ร่าง 1 แต่ไม่มี "ไฟ" ร่าง 2 มี "ไฟ/ต่อสู้" ร่าง 2 แต่ไม่มี "ไฟ/ต่อสู้" ร่าง 1

เมื่อใช้ unstack
print(pokemon.unstack())

ผลที่ได้จะออกมาเป็น
ร่าง 1 2 3
ชนิด      
น้ำ มิซึโงโรว None None
น้ำ/ดิน None นุมาครอว์ ลากลาร์จ
พืช คิโมริ จุปเทิล จูไคน์
ไฟ อาชาโม None None
ไฟ/ต่อสู้ None วากะชาโม บาชาโม

จะเห็นได้ว่ามี None อยู่หลายจุดในส่วนที่ข้อมูลขาดไป

แต่ถ้าไม่อยากให้เป์น None เราสามารถเพิ่มคีย์เวิร์ด fill_value ลงไปเพื่อกำหนดค่าที่มาแทนข้อมูลที่ว่างเปล่าได้ เช่น
print(pokemon.unstack(fill_value='---'))

ได้
ร่าง 1 2 3
ชนิด      
น้ำ มิซึโงโรว --- ---
น้ำ/ดิน --- นุมาครอว์ ลากลาร์จ
พืช คิโมริ จุปเทิล จูไคน์
ไฟ อาชาโม --- ---
ไฟ/ต่อสู้ --- วากะชาโม บาชาโม

พอเป็นแบบนี้หากใช้ stack ซ้อนลงไปก็จะกลายเป็นซีรีส์ที่มีแถวเพิ่มมาเป็น ๑๕ แถว
print(pokemon.unstack(fill_value='---').stack())

ได้
ชนิด       ร่าง
น้ำ        1       มิซึโงโรว
           2             ---
           3             ---
น้ำ/ดิน    1             ---
           2       นุมาครอว์
           3        ลากลาร์จ
พืช        1          คิโมริ
           2         จุปเทิล
           3          จูไคน์
ไฟ         1          อาชาโม
           2             ---
           3             ---
ไฟ/ต่อสู้  1             ---
           2        วากะชาโม
           3          บาชาโม
dtype: object

แต่หากปล่อยให้เป็น None อยู่แบบนี้แล้ว stack ซ้อนเข้าไปอีกก็จะได้ซีรีส์อันเดิมกลับมา โดยที่ None ที่เกิดขึ้นมานี้ก็หายไปด้วย เพราะปกติเวลา stack ข้อมูลที่เป็น None หรือ NaN จะละไป
print(pokemon.unstack().stack())

แต่หากไม่ต้องการให้ตัด None หรือ NaN ทิ้งก็ให้เพิ่มคีย์เวิร์ด dropna=0 (จากที่ปกติเป็น 1 คือลบ NaN ทิ้งเสมอ)

ตัวอย่าง
print(pokemon.unstack().stack(dropna=0))

ได้
ชนิด       ร่าง
น้ำ        1       มิซึโงโรว
           2            None
           3            None
น้ำ/ดิน    1            None
           2       นุมาครอว์
           3        ลากลาร์จ
พืช        1          คิโมริ
           2         จุปเทิล
           3          จูไคน์
ไฟ         1          อาชาโม
           2            None
           3            None
ไฟ/ต่อสู้  1            None
           2        วากะชาโม
           3          บาชาโม
dtype: object

แต่ในกรณีที่ใช้ unstack จะไม่มีการตัดข้อมูลที่เป็น None และไม่มีตัวเลือกให้ตัดทิ้งด้วย
print(pokemon.unstack().unstack())

ได้
ร่าง  ชนิด
1     น้ำ          มิซึโงโรว
      น้ำ/ดิน           None
      พืช             คิโมริ
      ไฟ              อาชาโม
      ไฟ/ต่อสู้         None
2     น้ำ               None
      น้ำ/ดิน      นุมาครอว์
      พืช            จุปเทิล
      ไฟ                None
      ไฟ/ต่อสู้     วากะชาโม
3     น้ำ               None
      น้ำ/ดิน       ลากลาร์จ
      พืช             จูไคน์
      ไฟ                None
      ไฟ/ต่อสู้       บาชาโม
dtype: object



อ้างอิง


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


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

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

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

หมวดหมู่

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

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



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

  ค้นหาบทความ

  บทความแนะนำ

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

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文