φυβλαςのβλογ
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
モンゴル語
言語学
maya
確率論
日本での日記
中国での日記
-- 北京での日記
-- 香港での日記
-- 澳門での日記
台灣での日記
北欧での日記
他の国での日記
qiita
その他の記事

記事の類別



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

  記事を検索

  おすすめの記事

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

ไทย

日本語

中文