Pandas เลือกตามป้ายกำกับบางครั้งจะส่งคืน Series บางครั้งก็ส่งคืน DataFrame


98

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

ทำไมถึงเป็นเช่นนั้น? มีวิธีใดบ้างที่จะทำให้แน่ใจว่าฉันได้รับ data frame กลับมาเสมอ

In [1]: import pandas as pd

In [2]: df = pd.DataFrame(data=range(5), index=[1, 2, 3, 3, 3])

In [3]: type(df.loc[3])
Out[3]: pandas.core.frame.DataFrame

In [4]: type(df.loc[1])
Out[4]: pandas.core.series.Series

คำตอบ:


106

จริงอยู่ว่าพฤติกรรมไม่สอดคล้องกัน แต่ฉันคิดว่ามันง่ายที่จะจินตนาการถึงกรณีที่สะดวก อย่างไรก็ตามเพื่อให้ได้ DataFrame locทุกครั้งเพียงแค่ผ่านรายการไปยัง มีวิธีอื่น แต่ในความคิดของฉันนี่เป็นวิธีที่สะอาดที่สุด

In [2]: type(df.loc[[3]])
Out[2]: pandas.core.frame.DataFrame

In [3]: type(df.loc[[1]])
Out[3]: pandas.core.frame.DataFrame

6
ขอบคุณ. ที่น่าสังเกตว่าจะส่งคืน DataFrame แม้ว่าป้ายกำกับจะไม่อยู่ในดัชนีก็ตาม
jobevers

7
FYI ด้วยดัชนีที่ไม่ซ้ำกันและตัวสร้างดัชนีเดียว (เช่นฉลากเดียว) คุณจะได้รับซีรี่ส์กลับคืนมาเสมอเพราะคุณมีรายการที่ซ้ำกันในดัชนีซึ่งเป็น DataFrame
เจฟ

1
โปรดทราบว่ายังมี gotcha อีกอันหนึ่ง: หากใช้วิธีแก้ปัญหาที่แนะนำและไม่มีแถวที่ตรงกันผลลัพธ์จะเป็น DataFrame ที่มีแถวเดียวคือ NaN ทั้งหมด
Paul Oyster

2
พอลใช้แพนด้ารุ่นอะไรคะ? ในรุ่นล่าสุด, ฉันได้รับเมื่อฉันพยายามKeyError .loc[[nonexistent_label]]
Dan Allan

2
การใช้รายการใน.locนั้นช้ากว่าการไม่มีอยู่มาก เพื่อให้ยังคงอ่านได้ แต่ยังเร็วกว่าใช้งานได้ดีกว่ามากdf.loc[1:1]
Jonathan

16

3คุณมีดัชนีที่มีสามรายการดัชนี ด้วยเหตุนี้df.loc[3]จะส่งคืน dataframe

เหตุผลคือคุณไม่ได้ระบุคอลัมน์ ดังนั้นdf.loc[3]เลือกสามรายการของคอลัมน์ทั้งหมด (ซึ่งก็คือคอลัมน์0) ในขณะที่df.loc[3,0]จะส่งคืนซีรี่ส์ เช่นdf.loc[1:2]ส่งคืน dataframe เนื่องจากคุณแบ่งแถว

การเลือกแถวเดียว (as df.loc[1]) จะส่งกลับซีรี่ส์ที่มีชื่อคอลัมน์เป็นดัชนี

หากคุณต้องการให้แน่ใจว่ามักจะมี DataFrame df.loc[1:1]คุณสามารถเชือดเหมือน อีกทางเลือกหนึ่งคือการสร้างดัชนีบูลีน ( df.loc[df.index==1]) หรือวิธี Take ( df.take([0])แต่ตำแหน่งที่ใช้นี้ไม่ใช่ป้ายกำกับ!)


3
นั่นคือพฤติกรรมที่ฉันคาดหวัง ฉันไม่เข้าใจการตัดสินใจออกแบบสำหรับแถวเดียวเพื่อแปลงเป็นชุดข้อมูล - ทำไมไม่ใช้ data frame ที่มีแถวเดียว
jobevers

ทำไมการเลือกแถวเดียวจึงส่งกลับ Series ฉันไม่รู้จริงๆ
อริส


6

TLDR

เมื่อใช้ loc

df.loc[:]= Dataframe

df.loc[int]= Dataframeหากคุณมีคอลัมน์และซีรี่ส์มากกว่าหนึ่งคอลัมน์หากคุณมีเพียง 1 คอลัมน์ในดาต้าเฟรม

df.loc[:, ["col_name"]]= Dataframe

df.loc[:, "col_name"]= ซีรีส์

ไม่ใช้ loc

df["col_name"]= ซีรีส์

df[["col_name"]]= Dataframe


3

คุณเขียนในความคิดเห็นถึงคำตอบของ joris:

"ฉันไม่เข้าใจการตัดสินใจออกแบบสำหรับแถวเดียวในการแปลงเป็นชุดข้อมูล - ทำไมไม่ใช้ data frame ที่มีแถวเดียว"

แถวเดียวจะไม่ถูกแปลงในซีรี่ส์
มันเป็นซีรี่ส์:No, I don't think so, in fact; see the edit

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

http://pandas.pydata.org/pandas-docs/stable/overview.html#why-more-than-1-data-structure

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

.

แก้ไข: ฉันไม่เห็นด้วยกับฉัน

DataFrame ไม่สามารถประกอบด้วยองค์ประกอบที่เป็น Series ได้เนื่องจากรหัสต่อไปนี้ให้ "Series" ประเภทเดียวกันเช่นกันสำหรับแถวสำหรับคอลัมน์:

import pandas as pd

df = pd.DataFrame(data=[11,12,13], index=[2, 3, 3])

print '-------- df -------------'
print df

print '\n------- df.loc[2] --------'
print df.loc[2]
print 'type(df.loc[1]) : ',type(df.loc[2])

print '\n--------- df[0] ----------'
print df[0]
print 'type(df[0]) : ',type(df[0])

ผลลัพธ์

-------- df -------------
    0
2  11
3  12
3  13

------- df.loc[2] --------
0    11
Name: 2, dtype: int64
type(df.loc[1]) :  <class 'pandas.core.series.Series'>

--------- df[0] ----------
2    11
3    12
3    13
Name: 0, dtype: int64
type(df[0]) :  <class 'pandas.core.series.Series'>

ดังนั้นจึงไม่มีเหตุผลที่จะแสร้งทำเป็นว่า DataFrame ประกอบด้วย Series เพราะสิ่งเหล่านี้ควรจะเป็น: คอลัมน์หรือแถว? คำถามและวิสัยทัศน์โง่ ๆ

.

แล้ว DataFrame คืออะไร?

ในคำตอบรุ่นก่อนหน้านี้ฉันถามคำถามนี้โดยพยายามหาคำตอบสำหรับWhy is that?ส่วนของคำถามของ OP และการซักถามที่คล้ายกันsingle rows to get converted into a series - why not a data frame with one row?ในความคิดเห็นของเขา
ในขณะที่Is there a way to ensure I always get back a data frame?แดนอัลลันตอบในส่วนนั้น

จากนั้นตามที่เอกสารของ Pandas ที่อ้างถึงข้างต้นบอกว่าโครงสร้างข้อมูลของแพนด้านั้นถูกมองว่าดีที่สุดในฐานะคอนเทนเนอร์ของข้อมูลมิติที่ต่ำกว่าสำหรับฉันแล้วฉันก็เข้าใจว่าทำไมจึงพบในลักษณะของลักษณะของโครงสร้าง DataFrame

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

.

แล้ววัตถุ DataFrame คืออะไร?

DataFrameระดับผลิตกรณีที่มีโครงสร้างโดยเฉพาะอย่างยิ่งเกิดขึ้นในNDFrameชั้นฐานตัวเองมาจาก PandasContainerชั้นฐานที่ยังเป็นระดับที่แม่ของซีรีส์ระดับ
โปรดทราบว่าสิ่งนี้ถูกต้องสำหรับ Pandas จนถึงเวอร์ชัน 0.12 ในเวอร์ชันที่กำลังจะมาถึง 0.13 ซีรีส์จะมาจากคลาสNDFrameเท่านั้น

# with pandas 0.12

from pandas import Series
print 'Series  :\n',Series
print 'Series.__bases__  :\n',Series.__bases__

from pandas import DataFrame
print '\nDataFrame  :\n',DataFrame
print 'DataFrame.__bases__  :\n',DataFrame.__bases__

print '\n-------------------'

from pandas.core.generic import NDFrame
print '\nNDFrame.__bases__  :\n',NDFrame.__bases__

from pandas.core.generic import PandasContainer
print '\nPandasContainer.__bases__  :\n',PandasContainer.__bases__

from pandas.core.base import PandasObject
print '\nPandasObject.__bases__  :\n',PandasObject.__bases__

from pandas.core.base import StringMixin
print '\nStringMixin.__bases__  :\n',StringMixin.__bases__

ผลลัพธ์

Series  :
<class 'pandas.core.series.Series'>
Series.__bases__  :
(<class 'pandas.core.generic.PandasContainer'>, <type 'numpy.ndarray'>)

DataFrame  :
<class 'pandas.core.frame.DataFrame'>
DataFrame.__bases__  :
(<class 'pandas.core.generic.NDFrame'>,)

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

NDFrame.__bases__  :
(<class 'pandas.core.generic.PandasContainer'>,)

PandasContainer.__bases__  :
(<class 'pandas.core.base.PandasObject'>,)

PandasObject.__bases__  :
(<class 'pandas.core.base.StringMixin'>,)

StringMixin.__bases__  :
(<type 'object'>,)

ดังนั้นความเข้าใจของฉันคือตอนนี้อินสแตนซ์ DataFrame มีวิธีการบางอย่างที่สร้างขึ้นเพื่อควบคุมวิธีดึงข้อมูลจากแถวและคอลัมน์

วิธีการทำงานของวิธีการแยกข้อมูลเหล่านี้ได้อธิบายไว้ในหน้านี้: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing
เราพบวิธีการที่ Dan Allan กำหนดและวิธีการอื่น ๆ

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

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

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


1
FYI, DataFrame ไม่ใช่คลาสย่อย ndarray และไม่ใช่ Series (เริ่มต้นที่ 0.13 ก่อนหน้านั้น) สิ่งเหล่านี้เป็นเหมือนเผด็จการมากขึ้น
Jeff

ขอบคุณที่แจ้งให้ทราบ ฉันรู้สึกขอบคุณมากเพราะฉันยังใหม่ในการเรียนรู้ของนุ่น แต่ฉันต้องการข้อมูลเพิ่มเติมเพื่อทำความเข้าใจให้ดี เหตุใดจึงมีการเขียนในเอกสารว่า Series เป็นคลาสย่อยของ ndarray
eyquem

ก่อนหน้า 0.13 (เปิดตัวไม่นาน) นี่คือเอกสาร dev: pandas.pydata.org/pandas-docs/dev/dsintro.html#series
Jeff

ตกลง. ขอบคุณมาก. อย่างไรก็ตามมันไม่ได้เปลี่ยนพื้นฐานของการใช้เหตุผลและความเข้าใจของฉันใช่หรือไม่? - ใน Pandas ด้อยกว่า 0.13, DataFrame และวัตถุอื่น ๆ ของ Pandas ต่างจาก Series: พวกมันคือ subclass ของอะไร?
eyquem

@ เจฟฟ์ขอบคุณครับ ฉันแก้ไขคำตอบของฉันหลังจากข้อมูลของคุณ ฉันยินดีที่จะทราบว่าคุณคิดอย่างไรกับการแก้ไขของฉัน
eyquem

1

หากเป้าหมายคือการได้รับส่วนหนึ่งของชุดข้อมูลโดยใช้ดัชนีที่ดีที่สุดคือการหลีกเลี่ยงการใช้หรือloc ilocคุณควรใช้ไวยากรณ์ที่คล้ายกับสิ่งนี้แทน:

df = pd.DataFrame(data=range(5), index=[1, 2, 3, 3, 3])
result = df[df.index == 3] 
isinstance(result, pd.DataFrame) # True

result = df[df.index == 1]
isinstance(result, pd.DataFrame) # True

0

หากคุณเลือกดัชนีของดาต้าเฟรมด้วยผลลัพธ์อาจเป็น DataFrame หรือ Series หรืออาจเป็น Series หรือสเกลาร์ (ค่าเดียว)

ฟังก์ชันนี้ช่วยให้แน่ใจว่าคุณจะได้รับรายการจากการเลือกของคุณเสมอ (ถ้า df ดัชนีและคอลัมน์ถูกต้อง):

def get_list_from_df_column(df, index, column):
    df_or_series = df.loc[index,[column]] 
    # df.loc[index,column] is also possible and returns a series or a scalar
    if isinstance(df_or_series, pd.Series):
        resulting_list = df_or_series.tolist() #get list from series
    else:
        resulting_list = df_or_series[column].tolist() 
        # use the column key to get a series from the dataframe
    return(resulting_list)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.