Gấu trúc chọn theo nhãn đôi khi trả về Chuỗi, đôi khi trả về DataFrame


97

Trong Pandas, khi tôi chọn một nhãn chỉ có một mục nhập trong chỉ mục, tôi nhận lại một Chuỗi, nhưng khi tôi chọn một mục nhập có nhiều mục nhập hơn thì tôi nhận lại một khung dữ liệu.

Tại sao vậy? Có cách nào để đảm bảo tôi luôn lấy lại khung dữ liệu không?

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

Câu trả lời:


101

Cho rằng hành vi là không nhất quán, nhưng tôi nghĩ rất dễ hình dung những trường hợp mà điều này thuận tiện. Dù sao, để có được DataFrame mỗi lần, chỉ cần chuyển một danh sách đến loc. Có những cách khác, nhưng theo tôi cách này là sạch nhất.

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
Cảm ơn. Cần lưu ý rằng điều này trả về một DataFrame ngay cả khi nhãn không có trong chỉ mục.
jobevers

7
FYI, với một chỉ mục không trùng lặp và một chỉ mục duy nhất (ví dụ: một nhãn duy nhất), bạn LUÔN LUÔN lấy lại một Chuỗi, chỉ vì bạn có các bản sao trong chỉ mục mà nó là DataFrame.
Jeff

1
Lưu ý rằng có một lỗi khác: nếu sử dụng giải pháp được đề xuất và không có hàng nào phù hợp, kết quả sẽ là DataFrame với một hàng duy nhất, tất cả là NaN.
Paul Oyster

2
Paul, bạn đang sử dụng phiên bản gấu trúc nào? Trên phiên bản mới nhất, tôi nhận được một KeyErrorkhi tôi thử .loc[[nonexistent_label]].
Dan Allan

2
Sử dụng một danh sách .locchậm hơn nhiều so với không có nó. Vẫn có thể đọc được nhưng cũng nhanh hơn nhiều, sử dụng tốt hơndf.loc[1:1]
Jonathan

16

Bạn có một chỉ mục với ba mục chỉ mục 3. Vì lý do df.loc[3]này sẽ trả về một khung dữ liệu.

Lý do là bạn không chỉ định cột. Vì vậy, hãy df.loc[3]chọn ba mục của tất cả các cột (là cột 0), trong khi df.loc[3,0]sẽ trả về một Chuỗi. Ví dụ: df.loc[1:2]cũng trả về một khung dữ liệu, vì bạn cắt các hàng.

Việc chọn một hàng (dưới dạng df.loc[1]) sẽ trả về một Chuỗi có tên cột làm chỉ mục.

Nếu bạn muốn chắc chắn luôn có DataFrame, bạn có thể cắt như sau df.loc[1:1]. Một tùy chọn khác là lập chỉ mục boolean ( df.loc[df.index==1]) hoặc phương thức take ( df.take([0])nhưng vị trí được sử dụng này không phải là nhãn!).


3
Đó là hành vi mà tôi mong đợi. Tôi không hiểu quyết định thiết kế để các hàng đơn được chuyển đổi thành một chuỗi - tại sao không phải là một khung dữ liệu có một hàng?
jobevers

À, tại sao việc chọn một hàng lại trả về một Series, tôi thực sự không biết.
joris

6

Sử dụng df['columnName']để lấy Chuỗi và df[['columnName']]lấy Khung dữ liệu.


1
Hãy coi chừng có một bản sao của df gốc.
smci

6

TLDR

Khi đang sử dụng loc

df.loc[:]= Khung dữ liệu

df.loc[int]= Dataframe nếu bạn có nhiều cột và Series nếu bạn chỉ có 1 cột trong dataframe

df.loc[:, ["col_name"]]= Khung dữ liệu

df.loc[:, "col_name"]= Dòng

Không sử dụng loc

df["col_name"]= Dòng

df[["col_name"]]= Khung dữ liệu


3

Bạn đã viết một bình luận cho câu trả lời của joris:

"Tôi không hiểu quyết định thiết kế để các hàng đơn lẻ được chuyển đổi thành một chuỗi - tại sao không phải là một khung dữ liệu có một hàng?"

Một hàng không được chuyển đổi trong một Chuỗi.
Đó một loạt:No, I don't think so, in fact; see the edit

Cách tốt nhất để nghĩ về cấu trúc dữ liệu của gấu trúc là các vùng chứa linh hoạt cho dữ liệu có chiều thấp hơn. Ví dụ: DataFrame là vùng chứa cho Series và Panel là vùng chứa cho các đối tượng DataFrame. Chúng tôi muốn có thể chèn và xóa các đối tượng khỏi các vùng chứa này theo cách giống như từ điển.

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

Mô hình dữ liệu của các đối tượng Pandas đã được lựa chọn như vậy. Lý do chắc chắn nằm ở chỗ nó đảm bảo một số ưu điểm mà tôi không biết (tôi không hiểu hết câu cuối cùng của trích dẫn, có thể đó là lý do)

.

Chỉnh sửa: Tôi không đồng ý với tôi

Một DataFrame không được bao gồm các phần tử sẽ Chuỗi, vì mã sau cung cấp cùng loại "Chuỗi" cho một hàng cũng như cho một cột:

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])

kết quả

-------- 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'>

Vì vậy, không có ý nghĩa gì khi giả vờ rằng một DataFrame bao gồm Chuỗi vì những gì mà những Chuỗi đã nói này được cho là: cột hay hàng? Câu hỏi ngu ngốc và tầm nhìn.

.

Vậy thì DataFrame là gì?

Trong phiên bản trước của câu trả lời này, tôi đã hỏi câu hỏi này, cố gắng tìm câu trả lời cho Why is that?phần câu hỏi của OP và câu hỏi tương tự single rows to get converted into a series - why not a data frame with one row?trong một trong những bình luận của anh ấy,
trong khiIs there a way to ensure I always get back a data frame? phần đã được trả lời bởi Dan Allan.

Sau đó, như các tài liệu của gấu trúc được trích dẫn ở trên nói rằng cấu trúc dữ liệu của gấu trúc được xem tốt nhất là các thùng chứa dữ liệu chiều thấp hơn, đối với tôi dường như sự hiểu biết về lý do tại sao sẽ được tìm thấy trong các đặc điểm về bản chất của cấu trúc DataFrame.

Tuy nhiên, tôi nhận ra rằng lời khuyên được trích dẫn này không được coi là mô tả chính xác về bản chất cấu trúc dữ liệu của Pandas.
Lời khuyên này không có nghĩa là DataFrame là một vùng chứa của Series.
Nó thể hiện rằng biểu diễn tinh thần của DataFrame như một vùng chứa Chuỗi (hàng hoặc cột theo tùy chọn được xem xét tại một thời điểm lý luận) là một cách tốt để xem xét DataFrame, ngay cả khi nó không hoàn toàn như vậy trong thực tế. "Tốt" nghĩa là tầm nhìn này cho phép sử dụng DataFrames một cách hiệu quả. Đó là tất cả.

.

Vậy thì đối tượng DataFrame là gì?

Lớp DataFrame tạo ra các cá thể có cấu trúc cụ thể bắt nguồn từ lớp cơ sở NDFrame , bản thân nó có nguồn gốc từ lớp cơ sở PandasContainer cũng là lớp cha của lớp Series .
Lưu ý rằng điều này đúng với Gấu trúc cho đến phiên bản 0.12. Trong phiên bản 0.13 sắp tới, Series cũng sẽ chỉ xuất phát từ lớp 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__

kết quả

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'>,)

Vì vậy, hiểu biết của tôi là bây giờ một cá thể DataFrame có một số phương thức nhất định đã được tạo ra để kiểm soát cách dữ liệu được trích xuất từ ​​các hàng và cột.

Cách thức hoạt động của các phương pháp giải nén này được mô tả trong trang này: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing
Chúng tôi tìm thấy trong đó phương pháp do Dan Allan đưa ra và các phương pháp khác.

Tại sao những phương pháp trích xuất này lại được chế tạo như cũ?
Đó là chắc chắn bởi vì chúng đã được đánh giá là những thứ mang lại khả năng tốt hơn và dễ dàng hơn trong việc phân tích dữ liệu.
Đó chính xác là những gì được diễn đạt trong câu này:

Cách tốt nhất để nghĩ về cấu trúc dữ liệu của gấu trúc là các vùng chứa linh hoạt cho dữ liệu có chiều thấp hơn.

do tại sao trích xuất dữ liệu từ một cá thể DataFRame không nằm ở cấu trúc của nó, nó nằm ở lý do tại sao cấu trúc này. Tôi đoán rằng cấu trúc và chức năng của cấu trúc dữ liệu của Pandas đã được đục đẽo để trở nên trực quan nhất có thể, và để hiểu chi tiết, người ta phải đọc blog của Wes McKinney.


1
FYI, DataFrame KHÔNG phải là một lớp con ndarray, cũng không phải là một Series (bắt đầu từ 0,13, trước đó là mặc dù vậy). Đây là những thứ giống như bất cứ điều gì.
Jeff

Cảm ơn bạn đã thông báo cho tôi. Tôi thực sự đánh giá cao vì tôi là người mới trong việc tìm hiểu về Gấu trúc. Nhưng tôi cần thêm thông tin để hiểu rõ. Tại sao trong tài liệu lại viết rằng Series là một lớp con của ndarray?
eyquem

trước 0:13 (phát hành trong thời gian ngắn), đây là tài liệu dành cho nhà phát triển: pandas.pydata.org/pandas-docs/dev/dsintro.html#series
Jeff

ĐỒNG Ý. Cảm ơn rât nhiều. Tuy nhiên, nó không thay đổi cơ sở lý luận và hiểu biết của tôi, phải không? - Trong Pandas thấp hơn 0,13, DataFrame và các đối tượng khác của Pandas khác với Series: chúng là lớp con gì?
eyquem

@Jeff Cảm ơn bạn. Tôi đã sửa đổi câu trả lời của mình sau thông tin của bạn. Tôi rất vui khi biết bạn nghĩ gì về bản chỉnh sửa của tôi.
eyquem

1

Nếu mục tiêu là lấy một tập hợp con của tập dữ liệu bằng cách sử dụng chỉ mục, thì tốt nhất là tránh sử dụng lochoặc iloc. Thay vào đó, bạn nên sử dụng cú pháp tương tự như sau:

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

Nếu bạn cũng chọn trên chỉ mục của khung dữ liệu thì kết quả có thể là DataFrame hoặc Chuỗi hoặc nó có thể là Chuỗi hoặc vô hướng (giá trị đơn).

Hàm này đảm bảo rằng bạn luôn nhận được danh sách từ lựa chọn của mình (nếu df, chỉ mục và cột hợp lệ):

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.