Memilih panda berdasarkan label terkadang mengembalikan Seri, terkadang mengembalikan DataFrame

98

Di Pandas, ketika saya memilih label yang hanya memiliki satu entri dalam indeks, saya mendapatkan kembali Seri, tetapi ketika saya memilih entri yang memiliki lebih dari satu entri, saya mendapatkan kembali bingkai data.

Mengapa demikian? Apakah ada cara untuk memastikan saya selalu mendapatkan kembali bingkai data?

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
pemberi kerja
sumber

Jawaban:

106

Memang perilakunya tidak konsisten, tetapi saya pikir mudah untuk membayangkan kasus-kasus yang nyaman. Bagaimanapun, untuk mendapatkan DataFrame setiap saat, cukup teruskan daftar ke loc. Ada cara lain, tapi menurut saya ini yang paling bersih.

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

In [3]: type(df.loc[[1]])
Out[3]: pandas.core.frame.DataFrame
Dan Allan
sumber
6
Terima kasih. Perlu dicatat bahwa ini mengembalikan DataFrame meskipun labelnya tidak ada dalam indeks.
pekerja
7
FYI, dengan indeks non-duplikat, dan pengindeks tunggal (misalnya label tunggal), Anda SELALU akan mendapatkan kembali Seri, hanya karena Anda memiliki duplikat dalam indeks, itu adalah DataFrame.
Jeff
1
Perhatikan bahwa ada gotcha lain: jika menggunakan solusi yang disarankan, dan tidak ada baris yang cocok, hasilnya adalah DataFrame dengan satu baris, semuanya NaN.
Paul Oyster
2
Paul, panda versi apa yang kamu gunakan? Pada versi terbaru, saya mendapatkan KeyErrorketika saya mencoba .loc[[nonexistent_label]].
Dan Allan
2
Menggunakan daftar di .locjauh lebih lambat daripada tanpa itu. Agar tetap dapat dibaca tetapi juga lebih cepat, lebih baik digunakandf.loc[1:1]
Jonathan
16

Anda memiliki indeks dengan tiga item indeks 3. Untuk alasan ini df.loc[3]akan mengembalikan dataframe.

Alasannya adalah Anda tidak menentukan kolomnya. Jadi df.loc[3]pilih tiga item dari semua kolom (yaitu kolom 0), sementara df.loc[3,0]akan mengembalikan Seri. Misalnya df.loc[1:2]juga mengembalikan dataframe, karena Anda mengiris baris.

Memilih satu baris (sebagai df.loc[1]) mengembalikan Seri dengan nama kolom sebagai indeks.

Jika Anda ingin memastikan selalu memiliki DataFrame, Anda dapat mengiris seperti df.loc[1:1]. Pilihan lainnya adalah boolean indexing ( df.loc[df.index==1]) atau metode take ( df.take([0]), tapi lokasi yang digunakan ini bukan label!).

joris
sumber
3
Itulah perilaku yang saya harapkan. Saya tidak mengerti keputusan desain untuk baris tunggal untuk diubah menjadi seri - mengapa tidak bingkai data dengan satu baris?
pekerja pada
Ah, mengapa memilih satu baris mengembalikan Seri, saya tidak begitu tahu.
joris
7

Gunakan df['columnName']untuk mendapatkan Seri dan df[['columnName']]untuk mendapatkan Dataframe.

pengguna4422
sumber
1
Berhati-hatilah karena mengambil salinan df asli.
smci
6

TLDR

Ketika menggunakan loc

df.loc[:]= Dataframe

df.loc[int]= Dataframe jika Anda memiliki lebih dari satu kolom dan Seri jika Anda hanya memiliki 1 kolom dalam dataframe

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

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

Tidak menggunakan loc

df["col_name"]= Seri

df[["col_name"]]= Dataframe

Colin Anthony
sumber
3

Anda menulis di komentar untuk jawaban joris:

"Saya tidak mengerti keputusan desain untuk baris tunggal untuk diubah menjadi seri - mengapa tidak bingkai data dengan satu baris?"

Satu baris tidak diubah menjadi Seri.
Ini ADALAH Seri:No, I don't think so, in fact; see the edit

Cara terbaik untuk memikirkan struktur data panda adalah sebagai wadah fleksibel untuk data berdimensi lebih rendah. Misalnya, DataFrame adalah wadah untuk Seri, dan Panel adalah wadah untuk objek DataFrame. Kami ingin dapat menyisipkan dan menghapus objek dari kontainer ini dengan cara seperti kamus.

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

Model data objek Pandas telah dipilih seperti itu. Alasannya pasti terletak pada fakta bahwa itu memastikan beberapa keuntungan yang saya tidak tahu (saya tidak sepenuhnya memahami kalimat terakhir dari kutipan, mungkin itu alasannya)

.

Edit: Saya tidak setuju dengan saya

DataFrame tidak dapat terdiri dari elemen yang akan menjadi Seri, karena kode berikut memberikan jenis "Seri" yang sama juga untuk baris seperti untuk kolom:

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

hasil

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

Jadi, tidak masuk akal untuk berpura-pura bahwa DataFrame terdiri dari Seri karena Seri seperti apa yang seharusnya: kolom atau baris? Pertanyaan dan visi bodoh.

.

Lalu apa itu DataFrame?

Pada jawaban versi sebelumnya saya mengajukan pertanyaan ini, mencoba mencari jawaban atas Why is that?bagian pertanyaan OP dan interogasi serupa single rows to get converted into a series - why not a data frame with one row?di salah satu komentarnya,
sedangkan Is there a way to ensure I always get back a data frame?bagian tersebut telah dijawab oleh Dan Allan.

Kemudian, seperti yang dikatakan oleh dokumen Pandas di atas bahwa struktur data panda paling baik dilihat sebagai wadah data dimensi yang lebih rendah, bagi saya tampaknya pemahaman tentang mengapa dapat ditemukan dalam karakteristik sifat struktur DataFrame.

Namun, saya menyadari bahwa saran yang dikutip ini tidak boleh dianggap sebagai deskripsi yang tepat tentang sifat struktur data Pandas.
Saran ini tidak berarti bahwa DataFrame adalah wadah Seri.
Ini mengungkapkan bahwa representasi mental dari DataFrame sebagai wadah Seri (baik baris atau kolom sesuai dengan opsi yang dipertimbangkan pada satu momen penalaran) adalah cara yang baik untuk mempertimbangkan DataFrames, meskipun sebenarnya tidak benar-benar terjadi. "Baik" artinya visi ini memungkinkan penggunaan DataFrames dengan efisiensi. Itu saja.

.

Lalu apa itu objek DataFrame?

Kelas DataFrame menghasilkan instance yang memiliki struktur tertentu yang berasal dari kelas dasar NDFrame , yang berasal dari kelas dasar PandasContainer yang juga merupakan kelas induk dari kelas Seri .
Perhatikan bahwa ini benar untuk Pandas hingga versi 0.12. Pada versi 0.13 mendatang, Seri juga akan diturunkan dari kelas NDFrame saja.

# 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__

hasil

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

Jadi pemahaman saya sekarang adalah contoh DataFrame memiliki metode tertentu yang telah dibuat untuk mengontrol cara data diekstraksi dari baris dan kolom.

Cara kerja metode ekstraksi ini dijelaskan di halaman ini: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing
Kami menemukan di dalamnya metode yang diberikan oleh Dan Allan dan metode lainnya.

Mengapa metode ekstraksi ini dibuat sebagaimana mestinya?
Hal itu tentunya karena mereka dinilai sebagai yang memberikan kemungkinan dan kemudahan yang lebih baik dalam analisis data.
Persis apa yang diungkapkan dalam kalimat ini:

Cara terbaik untuk memikirkan struktur data panda adalah sebagai wadah fleksibel untuk data berdimensi lebih rendah.

The mengapa dari ekstraksi data dari contoh DataFRame tidak terletak pada struktur, itu terletak pada mengapa struktur ini. Saya kira bahwa struktur dan fungsi struktur data Panda telah dipahat agar sedapat mungkin intuitif secara intelektual, dan untuk memahami detailnya, seseorang harus membaca blog Wes McKinney.

eyquem
sumber
1
FYI, DataFrame BUKAN sub-kelas ndarray, begitu pula Seri (mulai 0,13, sebelum itu meskipun). Ini lebih dikt-seperti itu.
Jeff
Terima kasih telah memberi tahu saya. Saya sangat menghargai karena saya baru dalam belajar tentang Pandas. Tetapi saya membutuhkan lebih banyak informasi untuk memahami dengan baik. Mengapa tertulis di dokumen bahwa Seri adalah subkelas ndarray?
eyquem
sebelum 0,13 (segera dirilis), berikut adalah dokumen dev: pandas.pydata.org/pandas-docs/dev/dsintro.html#series
Jeff
BAIK. Terima kasih banyak. Namun itu tidak mengubah dasar pemikiran dan pemahaman saya, bukan? - Dalam Panda yang lebih rendah dari 0,13, DataFrame dan objek Panda lain berbeda dari Seri: apa subkelasnya?
eyquem
@Jeff Terima kasih. Saya mengubah jawaban saya setelah informasi Anda. Saya akan senang mengetahui pendapat Anda tentang suntingan saya.
eyquem
1

Jika tujuannya adalah untuk mendapatkan subset dari kumpulan data menggunakan indeks, sebaiknya hindari penggunaan locatau iloc. Sebagai gantinya Anda harus menggunakan sintaks yang mirip dengan ini:

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
Ajit
sumber
0

Jika Anda juga memilih indeks frame data maka hasilnya bisa berupa DataFrame atau Seri atau bisa Seri atau skalar (nilai tunggal).

Fungsi ini memastikan bahwa Anda selalu mendapatkan daftar dari pilihan Anda (jika df, indeks, dan kolom valid):

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)
Wouter
sumber