Вибір панд за міткою іноді повертає Серію, іноді повертає DataFrame


97

У Pandas, коли я вибираю мітку, яка має лише один запис в індексі, я повертаю серію, але коли я вибираю запис, який має більше одного запису, я отримую кадр даних.

Чому так? Чи є спосіб забезпечити, щоб я завжди повертав фрейм даних?

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

Відповіді:


101

Звичайно, поведінка суперечлива, але я думаю, що легко уявити випадки, коли це зручно. У будь-якому випадку, щоб кожного разу отримувати 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
Зверніть увагу, що існує ще одна помилка: якщо ви використовуєте запропонований обхідний шлях і немає відповідних рядків, результатом буде DataFrame з одним рядком, всім NaN.
Пол Ойстер,

2
Поле, яку версію панд ти використовуєш? На останню версію, я отримую, KeyErrorколи я намагаюся .loc[[nonexistent_label]].
Dan Allan

2
Використання списку в .locнабагато повільніше, ніж без нього. Щоб все ще було читабельно, але також набагато швидше, краще використовуватиdf.loc[1:1]
Джонатан

16

У вас є індекс із трьома елементами індексу 3. З цієї причини df.loc[3]поверне фрейм даних.

Причина полягає в тому, що ви не вказуєте стовпець. Отже, df.loc[3]вибирає три елементи з усіх стовпців (який є стовпцем 0), а df.loc[3,0]повертає серію. Наприклад, df.loc[1:2]також повертається фрейм даних, оскільки ви нарізаєте рядки.

Вибір одного рядка (як df.loc[1]) повертає Серію з назвами стовпців як індекс.

Якщо ви хочете бути впевненим, що завжди маєте DataFrame, ви можете нарізати як df.loc[1:1]. Іншим варіантом є логічне індексування ( df.loc[df.index==1]) або метод take (( df.take([0])але це використовуване місце розташування не мітки!).


3
Ось такої поведінки я би очікував. Я не розумію дизайнерського рішення для перетворення окремих рядків у ряд - чому б не фрейм даних з одним рядком?
jobevers

Ах, чому вибір одного рядка повертає Серію, я насправді не знаю.
joris

6

Використовуйте, df['columnName']щоб отримати серію та df[['columnName']]отримати Dataframe.


1
Обережно, копіює оригінал df.
smci

6

TLDR

При використанні loc

df.loc[:]= Кадр даних

df.loc[int]= Кадр даних, якщо у вас більше одного стовпця, і Серія, якщо у вас є лише 1 стовпець у фреймі даних

df.loc[:, ["col_name"]]= Кадр даних

df.loc[:, "col_name"]= Серія

Не використовуючи loc

df["col_name"]= Серія

df[["col_name"]]= Кадр даних


3

Ви написали в коментарі до відповіді Йоріса:

"Я не розумію дизайнерського рішення для перетворення окремих рядків у ряд - чому б не фрейм даних з одним рядком?"

Один рядок не перетворюється в серію.
Це IS Серія:No, I don't think so, in fact; see the edit

Найкращий спосіб думати про структури даних pandas - це гнучкі контейнери для даних менших розмірів. Наприклад, DataFrame є контейнером для серії, а 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, оскільки якими б мали бути ці згадані Series: стовпцями чи рядками? Дурне питання та бачення.

.

Тоді що таке DataFrame?

У попередній версії цієї відповіді я задав це запитання, намагаючись знайти відповідь на Why is that?частину питання про ОП та подібний допит 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 - це контейнер Series.
Він висловлює, що уявне представлення DataFrame як контейнера Series (або рядків, або стовпців відповідно до опції, розглянутої в один момент аргументації) є хорошим способом розглянути DataFrames, навіть якщо це не так, насправді. "Добре" означає, що це бачення дозволяє ефективно використовувати DataFrames. Це все.

.

Тоді що таке об’єкт DataFrame?

Клас DataFrame створює екземпляри, які мають певну структуру, що походить від базового класу NDFrame , який сам походить від базового класу PandasContainer, який також є батьківським класом класу Series .
Зауважте, що це правильно для 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
Ми знаходимо в ньому метод, заданий Деном Алланом та іншими методами.

Чому ці методи вилучення були розроблені як вони були?
Це, безумовно, тому, що вони були оцінені як такі, що надають кращі можливості та полегшують аналіз даних.
Це саме те, що виражено в цьому реченні:

Найкращий спосіб думати про структури даних pandas - це гнучкі контейнери для даних менших розмірів.

Чому екстракції даних з екземпляра DataFRame не лежить в його структурі, вона лежить в чому цієї структури. Я припускаю, що структура та функціонування структури даних Панд були розроблені для того, щоб бути якомога інтелектуально інтуїтивнішим, і що, щоб зрозуміти деталі, потрібно прочитати блог Уеса Мак-Кінні.


1
FYI, DataFrame НЕ є підкласом ndarray, а також не є Series (починаючи з 0,13, до цього він був). Вони більше подібні до дикту, ніж будь-що.
Джефф

Дякую, що повідомили мене. Я дуже ціную, бо я новачок у вивченні панд. Але мені потрібно більше інформації, щоб добре зрозуміти. Чому в документах написано, що Серія є підкласом ndarray?
eyquem

це було до 0,13 (випуск незабаром), ось документи розробників: pandas.pydata.org/pandas-docs/dev/dsintro.html#series
Джефф

ГАРАЗД. Велике спасибі. Однак це не змінює основи моїх міркувань та розуміння, правда? - У Pandas нижче 0,13, DataFrame та інші об’єкти Pandas, що відрізняються від Series: до чого вони належать?
eyquem

@Jeff Дякую. Я змінив свою відповідь після вашої інформації. Мені було б приємно знати, що ви думаєте про мою редакцію.
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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.