เลือกแถวในแพนด้า MultiIndex DataFrame


147

สิ่งที่เป็นที่สุดวิธีหมีแพนด้าทั่วไปเพื่อเลือก / แถวกรองของdataframe ซึ่งเป็นดัชนี MultiIndex ?

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

สมมติฐานเพื่อความเรียบง่าย:

  1. อินพุตดาต้าเฟรมไม่มีคีย์ดัชนีที่ซ้ำกัน
  2. อินพุตดาต้าเฟรมด้านล่างมีสองระดับเท่านั้น (วิธีแก้ปัญหาส่วนใหญ่ที่แสดงไว้ที่นี่เป็นระดับ N)

ตัวอย่างการป้อนข้อมูล:

mux = pd.MultiIndex.from_arrays([
    list('aaaabbbbbccddddd'),
    list('tuvwtuvwtuvwtuvw')
], names=['one', 'two'])

df = pd.DataFrame({'col': np.arange(len(mux))}, mux)

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    u      5
    v      6
    w      7
    t      8
c   u      9
    v     10
d   w     11
    t     12
    u     13
    v     14
    w     15

คำถามที่ 1: การเลือกรายการเดียว

ฉันจะเลือกแถวที่มี "a" ในระดับ "หนึ่ง" ได้อย่างไร

         col
one two     
a   t      0
    u      1
    v      2
    w      3

นอกจากนี้ฉันจะลดระดับ "หนึ่ง" ในเอาต์พุตได้อย่างไร

     col
two     
t      0
u      1
v      2
w      3

คำถาม 1b
ฉันจะแบ่งแถวทั้งหมดที่มีค่า "t" ในระดับ "สอง" ได้อย่างไร

         col
one two     
a   t      0
b   t      4
    t      8
d   t     12

คำถามที่ 2: การเลือกหลายค่าในระดับ

ฉันจะเลือกแถวที่สอดคล้องกับรายการ "b" และ "d" ในระดับ "หนึ่ง" ได้อย่างไร

         col
one two     
b   t      4
    u      5
    v      6
    w      7
    t      8
d   w     11
    t     12
    u     13
    v     14
    w     15

คำถาม 2b
ฉันจะรับค่าทั้งหมดที่สอดคล้องกับ "t" และ "w" ในระดับ "สอง" ได้อย่างไร?

         col
one two     
a   t      0
    w      3
b   t      4
    w      7
    t      8
d   w     11
    t     12
    w     15

คำถามที่ 3: การแบ่งส่วนกากบาทเดี่ยว (x, y)

ฉันจะดึงข้อมูลส่วนตัดขวางเช่นแถวเดียวที่มีค่าเฉพาะสำหรับดัชนีได้dfอย่างไร โดยเฉพาะฉันจะดึงข้อมูลส่วนตัดขวางที่('c', 'u')กำหนดโดย

         col
one two     
c   u      9

คำถามที่ 4: การแบ่งส่วนข้ามหลาย ๆ ส่วน [(a, b), (c, d), ...]

ฉันจะเลือกสองแถวที่สอดคล้องกับ('c', 'u')และ('a', 'w')?

         col
one two     
c   u      9
a   w      3

คำถามที่ 5: ไอเทมหนึ่งชิ้นต่อระดับ

ฉันจะดึงแถวทั้งหมดที่ตรงกับ "a" ในระดับ "หนึ่ง" หรือ "t" ในระดับ "สอง" ได้อย่างไร

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    t      8
d   t     12

คำถามที่ 6: การหั่นตามอำเภอใจ

ฉันจะหั่นส่วนตัดขวางเฉพาะได้อย่างไร สำหรับ "a" และ "b" ฉันต้องการเลือกแถวทั้งหมดที่มีระดับย่อย "u" และ "v" และสำหรับ "d" ฉันต้องการเลือกแถวที่มีระดับย่อย "w"

         col
one two     
a   u      1
    v      2
b   u      5
    v      6
d   w     11
    w     15

คำถามที่ 7 จะใช้การตั้งค่าที่ไม่ซ้ำกันซึ่งประกอบด้วยระดับตัวเลข:

np.random.seed(0)
mux2 = pd.MultiIndex.from_arrays([
    list('aaaabbbbbccddddd'),
    np.random.choice(10, size=16)
], names=['one', 'two'])

df2 = pd.DataFrame({'col': np.arange(len(mux2))}, mux2)

         col
one two     
a   5      0
    0      1
    3      2
    3      3
b   7      4
    9      5
    3      6
    5      7
    2      8
c   4      9
    7     10
d   6     11
    8     12
    8     13
    1     14
    6     15

คำถามที่ 7: การกรองตามความไม่เท่าเทียมกันของตัวเลขในแต่ละระดับของดัชนี

ฉันจะรับแถวทั้งหมดที่ค่าในระดับ "สอง" มากกว่า 5 ได้อย่างไร

         col
one two     
b   7      4
    9      5
c   7     10
d   6     11
    8     12
    8     13
    6     15

หมายเหตุ: โพสต์นี้จะไม่อธิบายถึงวิธีการสร้าง MultiIndexes วิธีดำเนินการมอบหมายงานหรือการอภิปรายเกี่ยวกับประสิทธิภาพใด ๆ (เป็นหัวข้อแยกต่างหากสำหรับอีกครั้ง)

คำตอบ:


166

MultiIndex / การจัดทำดัชนีขั้นสูง

หมายเหตุ
โพสต์นี้จะมีโครงสร้างในลักษณะต่อไปนี้:

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

บันทึก (เช่นเดียวกับนี้) จะรวมไว้สำหรับผู้อ่านที่สนใจเรียนรู้เกี่ยวกับฟังก์ชันเพิ่มเติมรายละเอียดการใช้งานและข้อมูลอื่น ๆ ตามหัวข้อที่ต้องการ บันทึกเหล่านี้ได้รับการรวบรวมผ่านการกำจัดเอกสารและการเปิดเผยคุณสมบัติที่คลุมเครือต่างๆและจากประสบการณ์ของฉันเอง (ที่ยอมรับได้ในวง จำกัด )

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

นี่คือคำแนะนำเกี่ยวกับสำนวนทั่วไปบางส่วน (ต่อจากนี้ไปจะเรียกว่าสี่สำนวน) เราจะกลับมาเยี่ยมชมบ่อยๆ

  1. DataFrame.loc- โซลูชันทั่วไปสำหรับการเลือกตามฉลาก (+ pd.IndexSliceสำหรับการใช้งานที่ซับซ้อนมากขึ้นเกี่ยวกับชิ้นส่วน)

  2. DataFrame.xs - แยกส่วนตัดขวางเฉพาะจาก Series / DataFrame

  3. DataFrame.query- ระบุการดำเนินการแบ่งส่วนและ / หรือการกรองแบบไดนามิก (กล่าวคือเป็นนิพจน์ที่ได้รับการประเมินแบบไดนามิกสามารถใช้ได้กับบางสถานการณ์มากกว่าสถานการณ์อื่น ๆ ดูส่วนนี้ของเอกสารสำหรับการสืบค้นใน MultiIndexes

  4. การสร้างดัชนีบูลีนด้วยมาสก์ที่สร้างขึ้นโดยใช้MultiIndex.get_level_values(มักใช้ร่วมกับIndex.isinโดยเฉพาะอย่างยิ่งเมื่อกรองด้วยหลายค่า) นอกจากนี้ยังมีประโยชน์มากในบางสถานการณ์

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


คำถามที่ 1

ฉันจะเลือกแถวที่มี "a" ในระดับ "หนึ่ง" ได้อย่างไร

         col
one two     
a   t      0
    u      1
    v      2
    w      3

คุณสามารถใช้locเป็นโซลูชันวัตถุประสงค์ทั่วไปที่ใช้ได้กับสถานการณ์ส่วนใหญ่:

df.loc[['a']]

ณ จุดนี้ถ้าคุณได้รับ

TypeError: Expected tuple, got str

นั่นหมายความว่าคุณกำลังใช้แพนด้าเวอร์ชันเก่า พิจารณาอัปเกรด! df.loc[('a', slice(None)), :]มิฉะนั้นการใช้งาน

หรือคุณสามารถใช้xsที่นี่เนื่องจากเรากำลังแยกส่วนตัดขวางเดียว หมายเหตุlevelsและaxisอาร์กิวเมนต์ (สามารถสันนิษฐานค่าเริ่มต้นที่สมเหตุสมผลได้ที่นี่)

df.xs('a', level=0, axis=0, drop_level=False)
# df.xs('a', drop_level=False)

ที่นี่drop_level=Falseจำเป็นต้องใช้อาร์กิวเมนต์เพื่อป้องกันไม่ให้xsลดระดับ "หนึ่ง" ในผลลัพธ์ (ระดับที่เราแบ่งส่วน)

อีกทางเลือกหนึ่งคือการใช้query:

df.query("one == 'a'")

"ilevel_0 == 'a'"หากดัชนีไม่ได้มีชื่อคุณจะต้องเปลี่ยนสตริงการสืบค้นของคุณจะ

สุดท้ายใช้get_level_values:

df[df.index.get_level_values('one') == 'a']
# If your levels are unnamed, or if you need to select by position (not label),
# df[df.index.get_level_values(0) == 'a']

นอกจากนี้ฉันจะลดระดับ "หนึ่ง" ในเอาต์พุตได้อย่างไร

     col
two     
t      0
u      1
v      2
w      3

สามารถทำได้อย่างง่ายดายโดยใช้อย่างใดอย่างหนึ่ง

df.loc['a'] # Notice the single string argument instead the list.

หรือ,

df.xs('a', level=0, axis=0, drop_level=True)
# df.xs('a')

สังเกตว่าเราสามารถละเว้นdrop_levelอาร์กิวเมนต์ได้ (ถือว่าเป็นTrueค่าเริ่มต้น)

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

v = df.loc[['a']]
print(v)
         col
one two     
a   t      0
    u      1
    v      2
    w      3

print(v.index)
MultiIndex(levels=[['a', 'b', 'c', 'd'], ['t', 'u', 'v', 'w']],
           labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
           names=['one', 'two'])

คุณสามารถกำจัดระดับเหล่านี้ได้โดยใช้MultiIndex.remove_unused_levels:

v.index = v.index.remove_unused_levels()

print(v.index)
MultiIndex(levels=[['a'], ['t', 'u', 'v', 'w']],
           labels=[[0, 0, 0, 0], [0, 1, 2, 3]],
           names=['one', 'two'])

คำถาม 1b

ฉันจะแบ่งแถวทั้งหมดที่มีค่า "t" ในระดับ "สอง" ได้อย่างไร

         col
one two     
a   t      0
b   t      4
    t      8
d   t     12

โดยสัญชาตญาณคุณต้องการบางสิ่งที่เกี่ยวข้องกับslice():

df.loc[(slice(None), 't'), :]

มันใช้งานได้จริง! ™ แต่มันเป็นเรื่องที่น่าเบื่อ เราสามารถอำนวยความสะดวกให้กับไวยากรณ์การแบ่งส่วนที่เป็นธรรมชาติมากขึ้นโดยใช้pd.IndexSliceAPI ที่นี่

idx = pd.IndexSlice
df.loc[idx[:, 't'], :]

นี่คือมากและสะอาดกว่ามาก

หมายเหตุ
เหตุใดจึง:จำเป็นต้องมีชิ้นส่วนต่อท้ายในคอลัมน์ เนื่องจากlocสามารถใช้เพื่อเลือกและแบ่งตามทั้งสองแกน ( axis=0หรือ axis=1) หากไม่ต้องระบุให้ชัดเจนว่าแกนใดที่จะทำการแบ่งส่วนการดำเนินการจะคลุมเครือ ดูกล่องสีแดงขนาดใหญ่ในเอกสารประกอบการหั่นเอกสารเกี่ยวกับหั่น

หากคุณต้องการลบความคลุมเครือใด ๆ ให้locยอมรับaxis พารามิเตอร์:

df.loc(axis=0)[pd.IndexSlice[:, 't']]

หากไม่มีaxisพารามิเตอร์ (กล่าวคือเพียงแค่ทำdf.loc[pd.IndexSlice[:, 't']]) การแบ่งส่วนจะถือว่าอยู่บนคอลัมน์และ a KeyErrorจะถูกยกขึ้นในกรณีนี้

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

ด้วยxsนั้นก็คือ

df.xs('t', axis=0, level=1, drop_level=False)

ด้วยqueryนั้นก็คือ

df.query("two == 't'")
# Or, if the first level has no name, 
# df.query("ilevel_1 == 't'") 

และในที่สุดget_level_valuesคุณก็สามารถทำได้

df[df.index.get_level_values('two') == 't']
# Or, to perform selection by position/integer,
# df[df.index.get_level_values(1) == 't']

ทั้งหมดให้ผลเดียวกัน


คำถาม 2

ฉันจะเลือกแถวที่สอดคล้องกับรายการ "b" และ "d" ในระดับ "หนึ่ง" ได้อย่างไร

         col
one two     
b   t      4
    u      5
    v      6
    w      7
    t      8
d   w     11
    t     12
    u     13
    v     14
    w     15

การใช้ loc ทำได้ในลักษณะเดียวกันโดยระบุรายการ

df.loc[['b', 'd']]

ในการแก้ปัญหาข้างต้นในการเลือก "b" และ "d" คุณยังสามารถใช้query:

items = ['b', 'd']
df.query("one in @items")
# df.query("one == @items", parser='pandas')
# df.query("one in ['b', 'd']")
# df.query("one == ['b', 'd']", parser='pandas')

หมายเหตุ
ใช่โปรแกรมแยกวิเคราะห์เริ่มต้นคือ'pandas'แต่สิ่งสำคัญคือต้องเน้นไวยากรณ์นี้ไม่ใช่ python ตามอัตภาพ ตัวแยกวิเคราะห์ Pandas สร้างแผนภูมิการแยกวิเคราะห์ที่แตกต่างกันเล็กน้อยจากนิพจน์ สิ่งนี้ทำเพื่อให้การดำเนินการบางอย่างง่ายขึ้นในการระบุ สำหรับข้อมูลเพิ่มเติมโปรดอ่านโพสต์ของฉันใน แบบไดนามิกการแสดงออกในการประเมินผลโดยใช้หมีแพนด้า pd.eval ()

และด้วยget_level_values+ Index.isin:

df[df.index.get_level_values("one").isin(['b', 'd'])]

คำถาม 2 ข

ฉันจะรับค่าทั้งหมดที่สอดคล้องกับ "t" และ "w" ในระดับ "สอง" ได้อย่างไร

         col
one two     
a   t      0
    w      3
b   t      4
    w      7
    t      8
d   w     11
    t     12
    w     15

ด้วยlocนี้เป็นไปได้เพียงpd.IndexSliceร่วมกับ

df.loc[pd.IndexSlice[:, ['t', 'w']], :] 

ลำไส้ใหญ่แรก:ในpd.IndexSlice[:, ['t', 'w']]วิธีการที่จะข้ามชิ้นในระดับแรก เมื่อความลึกของระดับที่สอบถามเพิ่มขึ้นคุณจะต้องระบุชิ้นส่วนเพิ่มเติมโดยหนึ่งชิ้นต่อระดับจะถูกแบ่งออก อย่างไรก็ตามคุณไม่จำเป็นต้องระบุระดับเพิ่มเติมนอกเหนือจากระดับที่หั่นบาง ๆ

ด้วยqueryนี่คือ

items = ['t', 'w']
df.query("two in @items")
# df.query("two == @items", parser='pandas') 
# df.query("two in ['t', 'w']")
# df.query("two == ['t', 'w']", parser='pandas')

ด้วยget_level_valuesและIndex.isin(คล้ายกับด้านบน):

df[df.index.get_level_values('two').isin(['t', 'w'])]

คำถาม 3

ฉันจะดึงข้อมูลส่วนตัดขวางเช่นแถวเดียวที่มีค่าเฉพาะสำหรับดัชนีได้dfอย่างไร โดยเฉพาะฉันจะดึงข้อมูลส่วนตัดขวางที่('c', 'u')กำหนดโดย

         col
one two     
c   u      9

ใช้locโดยระบุทูเพิลคีย์:

df.loc[('c', 'u'), :]

หรือ,

df.loc[pd.IndexSlice[('c', 'u')]]

หมายเหตุ
ณ จุดนี้คุณอาจพบPerformanceWarningว่ามีลักษณะดังนี้:

PerformanceWarning: indexing past lexsort depth may impact performance.

นั่นหมายความว่าดัชนีของคุณไม่ได้ถูกจัดเรียง แพนด้าขึ้นอยู่กับดัชนีที่จัดเรียง (ในกรณีนี้คือตามศัพท์เพราะเรากำลังจัดการกับค่าสตริง) เพื่อการค้นหาและดึงข้อมูลที่เหมาะสมที่สุด การแก้ไขอย่างรวดเร็วจะมีการจัดเรียง DataFrame DataFrame.sort_indexของคุณล่วงหน้าโดยใช้ สิ่งนี้เป็นที่พึงปรารถนาอย่างยิ่งจากมุมมองด้านประสิทธิภาพหากคุณวางแผนที่จะทำการค้นหาหลาย ๆ คำค้นหาควบคู่กันไป:

df_sort = df.sort_index()
df_sort.loc[('c', 'u')]

คุณยังสามารถใช้MultiIndex.is_lexsorted()เพื่อตรวจสอบว่าดัชนีถูกจัดเรียงหรือไม่ ฟังก์ชันนี้จะส่งกลับTrueหรือFalseตามนั้น คุณสามารถเรียกใช้ฟังก์ชันนี้เพื่อพิจารณาว่าจำเป็นต้องมีขั้นตอนการเรียงลำดับเพิ่มเติมหรือไม่

ด้วยxsนี่เป็นเพียงการส่งทูเปิลตัวเดียวเป็นอาร์กิวเมนต์แรกอีกครั้งโดยมีอาร์กิวเมนต์อื่น ๆ ทั้งหมดตั้งเป็นค่าเริ่มต้นที่เหมาะสม:

df.xs(('c', 'u'))

ด้วยqueryสิ่งต่าง ๆ กลายเป็นเรื่องที่น่าเบื่อ:

df.query("one == 'c' and two == 'u'")

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

ด้วยการเข้าถึงที่ครอบคลุมหลายระดับget_level_valuesคุณยังสามารถใช้ได้ แต่ไม่แนะนำ:

m1 = (df.index.get_level_values('one') == 'c')
m2 = (df.index.get_level_values('two') == 'u')
df[m1 & m2]

คำถาม 4

ฉันจะเลือกสองแถวที่สอดคล้องกับ('c', 'u')และ('a', 'w')?

         col
one two     
c   u      9
a   w      3

ด้วยlocสิ่งนี้ยังคงเรียบง่ายเหมือน:

df.loc[[('c', 'u'), ('a', 'w')]]
# df.loc[pd.IndexSlice[[('c', 'u'), ('a', 'w')]]]

ด้วยqueryคุณจะต้องสร้างสตริงการสืบค้นแบบไดนามิกโดยการวนซ้ำข้ามส่วนและระดับของคุณ:

cses = [('c', 'u'), ('a', 'w')]
levels = ['one', 'two']
# This is a useful check to make in advance.
assert all(len(levels) == len(cs) for cs in cses) 

query = '(' + ') or ('.join([
    ' and '.join([f"({l} == {repr(c)})" for l, c in zip(levels, cs)]) 
    for cs in cses
]) + ')'

print(query)
# ((one == 'c') and (two == 'u')) or ((one == 'a') and (two == 'w'))

df.query(query)

100% ไม่แนะนำ! แต่มันเป็นไปได้


คำถาม 5

ฉันจะดึงแถวทั้งหมดที่ตรงกับ "a" ในระดับ "หนึ่ง" หรือ "t" ในระดับ "สอง" ได้อย่างไร

         col
one two     
a   t      0
    u      1
    v      2
    w      3
b   t      4
    t      8
d   t     12

นี่เป็นเรื่องยากมากที่จะทำlocในขณะที่ตรวจสอบความถูกต้องและยังคงรักษาความชัดเจนของโค้ด df.loc[pd.IndexSlice['a', 't']]ไม่ถูกต้องมันถูกตีความว่าdf.loc[pd.IndexSlice[('a', 't')]](กล่าวคือการเลือกส่วนตัดขวาง) คุณอาจนึกถึงวิธีแก้ไขpd.concatเพื่อจัดการป้ายกำกับแต่ละป้ายแยกกัน:

pd.concat([
    df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]
])

         col
one two     
a   t      0
    u      1
    v      2
    w      3
    t      0   # Does this look right to you? No, it isn't!
b   t      4
    t      8
d   t     12

แต่คุณจะสังเกตเห็นว่าแถวใดแถวหนึ่งซ้ำกัน เนื่องจากแถวนั้นเป็นไปตามเงื่อนไขการหั่นทั้งสองจึงปรากฏขึ้นสองครั้ง คุณจะต้องทำแทน

v = pd.concat([
        df.loc[['a'],:], df.loc[pd.IndexSlice[:, 't'],:]
])
v[~v.index.duplicated()]

แต่ถ้า DataFrame ของคุณมีดัชนีที่ซ้ำกันอยู่ (ที่คุณต้องการ) สิ่งนี้จะไม่คงไว้ ใช้ด้วยความระมัดระวังเป็นอย่างยิ่ง

ด้วยqueryสิ่งนี้ง่ายอย่างโง่เขลา:

df.query("one == 'a' or two == 't'")

ด้วยget_level_valuesสิ่งนี้ยังคงเรียบง่าย แต่ไม่หรูหราเท่า:

m1 = (df.index.get_level_values('one') == 'a')
m2 = (df.index.get_level_values('two') == 't')
df[m1 | m2] 

คำถามที่ 6

ฉันจะหั่นส่วนตัดขวางเฉพาะได้อย่างไร สำหรับ "a" และ "b" ฉันต้องการเลือกแถวทั้งหมดที่มีระดับย่อย "u" และ "v" และสำหรับ "d" ฉันต้องการเลือกแถวที่มีระดับย่อย "w"

         col
one two     
a   u      1
    v      2
b   u      5
    v      6
d   w     11
    w     15

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

โดยปกติแล้วปัญหาการแบ่งส่วนเช่นนี้จะต้องมีการส่งรายการคีย์ไปlocอย่างชัดเจน วิธีหนึ่งในการดำเนินการนี้คือ:

keys = [('a', 'u'), ('a', 'v'), ('b', 'u'), ('b', 'v'), ('d', 'w')]
df.loc[keys, :]

หากคุณต้องการบันทึกการพิมพ์บางส่วนคุณจะทราบว่ามีรูปแบบในการแบ่งส่วน "a", "b" และระดับย่อยของมันดังนั้นเราจึงสามารถแยกงานแบ่งออกเป็นสองส่วนและconcatผลลัพธ์:

pd.concat([
     df.loc[(('a', 'b'), ('u', 'v')), :], 
     df.loc[('d', 'w'), :]
   ], axis=0)

ข้อกำหนดการแบ่งส่วนสำหรับ "a" และ "b" นั้นสะอาดกว่าเล็กน้อย(('a', 'b'), ('u', 'v'))เนื่องจากระดับย่อยเดียวกันที่จัดทำดัชนีจะเหมือนกันสำหรับแต่ละระดับ


คำถามที่ 7

ฉันจะรับแถวทั้งหมดที่ค่าในระดับ "สอง" มากกว่า 5 ได้อย่างไร

         col
one two     
b   7      4
    9      5
c   7     10
d   6     11
    8     12
    8     13
    6     15

ซึ่งสามารถทำได้โดยใช้query,

df2.query("two > 5")

และget_level_values.

df2[df2.index.get_level_values('two') > 5]

หมายเหตุ
คล้ายกับตัวอย่างนี้เราสามารถกรองตามเงื่อนไขใด ๆ โดยใช้โครงสร้างเหล่านี้ โดยทั่วไปการจดจำสิ่งนั้นlocและxsมีไว้สำหรับการจัดทำดัชนีตามฉลากโดยเฉพาะในขณะที่queryและ get_level_valuesเป็นประโยชน์สำหรับการสร้างมาสก์เงื่อนไขทั่วไปสำหรับการกรอง


คำถามโบนัส

จะเกิดอะไรขึ้นถ้าฉันต้องการแบ่งMultiIndex คอลัมน์ ?

ที่จริงแล้วโซลูชันส่วนใหญ่สามารถใช้ได้กับคอลัมน์เช่นกันโดยมีการเปลี่ยนแปลงเล็กน้อย พิจารณา:

np.random.seed(0)
mux3 = pd.MultiIndex.from_product([
        list('ABCD'), list('efgh')
], names=['one','two'])

df3 = pd.DataFrame(np.random.choice(10, (3, len(mux))), columns=mux3)
print(df3)

one  A           B           C           D         
two  e  f  g  h  e  f  g  h  e  f  g  h  e  f  g  h
0    5  0  3  3  7  9  3  5  2  4  7  6  8  8  1  6
1    7  7  8  1  5  9  8  9  4  3  0  3  5  0  2  3
2    8  1  3  3  3  7  0  1  9  9  0  4  7  3  2  7

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

  1. ในการฝานlocให้ใช้

    df3.loc[:, ....] # Notice how we slice across the index with `:`. 

    หรือ,

    df3.loc[:, pd.IndexSlice[...]]
  2. ที่จะใช้ตามความเหมาะสมเพียงแค่ผ่านการโต้แย้งxsaxis=1

  3. df.columns.get_level_valuesคุณสามารถเข้าถึงค่าระดับคอลัมน์โดยตรงโดยใช้ จากนั้นคุณจะต้องทำสิ่งที่ชอบ

    df.loc[:, {condition}] 

    ในกรณีที่แสดงให้เห็นถึงสภาพบางส่วนสร้างขึ้นโดยใช้{condition}columns.get_level_values

  4. ในการใช้งานqueryตัวเลือกเดียวของคุณคือการเปลี่ยนข้อความค้นหาดัชนีและเปลี่ยนอีกครั้ง:

    df3.T.query(...).T

    ไม่แนะนำให้ใช้หนึ่งในตัวเลือกอื่น ๆ 3 ตัวเลือก


6

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

ฉันอยู่ไกลจากผู้เชี่ยวชาญ แต่ฉันพบวิธีแก้ปัญหาที่ไม่ได้ระบุไว้ในคำตอบที่ครอบคลุมด้านบน ฉันไม่รับประกันว่าโซลูชันจะดีที่สุดในทางใดทางหนึ่ง

นี่เป็นวิธีที่แตกต่างเพื่อให้ได้ผลลัพธ์ที่แตกต่างกันเล็กน้อยกับคำถาม # 6 ด้านบน (และอาจมีคำถามอื่น ๆ ด้วย)

โดยเฉพาะฉันกำลังมองหา:

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

ในฐานะที่เป็นประแจลิงในเฟือง (แต่สามารถแก้ไขได้ทั้งหมด):

  1. ดัชนีไม่มีชื่อ

ในกรอบข้อมูลของเล่นด้านล่าง:

    index = pd.MultiIndex.from_product([['a','b'],
                               ['stock1','stock2','stock3'],
                               ['price','volume','velocity']])

    df = pd.DataFrame([1,2,3,4,5,6,7,8,9,
                      10,11,12,13,14,15,16,17,18], 
                       index)

                        0
    a stock1 price      1
             volume     2
             velocity   3
      stock2 price      4
             volume     5
             velocity   6
      stock3 price      7
             volume     8
             velocity   9
    b stock1 price     10
             volume    11
             velocity  12
      stock2 price     13
             volume    14
             velocity  15
      stock3 price     16
             volume    17
             velocity  18

การใช้งานด้านล่างนี้แน่นอน:

    df.xs(('stock1', 'velocity'), level=(1,2))

        0
    a   3
    b  12

แต่ฉันต้องการผลลัพธ์ที่แตกต่างออกไปดังนั้นวิธีการของฉันเพื่อให้ได้ผลลัพธ์คือ:

   df.iloc[df.index.isin(['stock1'], level=1) & 
           df.index.isin(['velocity'], level=2)] 

                        0
    a stock1 velocity   3
    b stock1 velocity  12

และถ้าฉันต้องการสองค่าจากระดับหนึ่งและค่าเดียว (หรือ 2+) จากระดับอื่น:

    df.iloc[df.index.isin(['stock1','stock3'], level=1) & 
            df.index.isin(['velocity'], level=2)] 

                        0
    a stock1 velocity   3
      stock3 velocity   9
    b stock1 velocity  12
      stock3 velocity  18

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


2
ดีไม่รู้เรื่องlevelโต้แย้งIndex.isin!
cs95
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.