Панды, выбирающие по метке, иногда возвращают Series, иногда возвращают DataFrame

В 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

Ответ 1

Предположим, что поведение непоследовательно, но я думаю, что легко представить случаи, когда это удобно. В любом случае, чтобы каждый раз получать 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

Ответ 2

У вас есть индекс с тремя индексами 3. По этой причине df.loc[3] вернет фрейм данных.

Причина в том, что вы не указываете столбец. Таким образом, df.loc[3] выбирает три элемента всех столбцов (это столбец 0), а df.loc[3,0] возвращает строку. Например. df.loc[1:2] также возвращает dataframe, потому что вы нарезаете строки.

Выбор одной строки (как df.loc[1]) возвращает Серию с именами столбцов в качестве индекса.

Если вы хотите всегда иметь DataFrame, вы можете нарезать как df.loc[1:1]. Другим вариантом является булевское индексирование (df.loc[df.index==1]) или метод take (df.take([0]), но это используемое место не является ярлыками!).

Ответ 3

Используйте df['columnName'] чтобы получить Series, и df[['columnName']] чтобы получить Dataframe.

Ответ 4

Вы ответили в комментарии к joris:

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

Одна строка не преобразована в серии.
Это IS a Серия: No, I don't think so, in fact; see the edit

Лучший способ думать о структурах данных pandas является гибким контейнеры для данных с более низким размером. Например, DataFrame является контейнер для Series, а Panel - контейнер для объектов DataFrame. Мы хотели бы иметь возможность вставлять и удалять объекты из этих контейнеры в словарном стиле.

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

Была выбрана модель данных объектов pandas. Причина, конечно, заключается в том, что он обеспечивает некоторые преимущества, которые я не знаю (я не совсем понимаю последнее предложение цитаты, возможно, это причина)

.

Изменить: я не согласен со мной

DataFrame не может состоять из элементов, которые будут быть Серии, потому что следующий код дает один и тот же тип "Серии" также для строки как для столбца:

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 состоит из Серии, потому что то, что эти упомянутые Серии должны быть: столбцы или строки? Глупый вопрос и видение.

.

Тогда что такое 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 'docs говорит, что структуры данных Pandas лучше всего воспринимаются как контейнеры данных более низкого размера, мне показалось, что понимание того, почему будут найдены в характеристиках природы структур DataFrame.

Однако я понял, что этот цитируемый совет не следует рассматривать как точное описание характера структур данных Pandas.
Этот совет не означает, что DataFrame является контейнером Series.
Он выражает, что умственное представление DataFrame как контейнера Серии (либо строк, либо столбцов в соответствии с опцией, рассмотренной в один момент рассуждения) является хорошим способом рассмотрения 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 не входит в его структуру, это связано с тем, почему эта структура. Я предполагаю, что структура и функционирование структуры данных Pandas были выточены, чтобы быть максимально интуитивно понятными, и чтобы понять детали, нужно прочитать блог Уэса МакКинни.

Ответ 5

Если целью является получение подмножества набора данных с использованием индекса, лучше избегать использования 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

Ответ 6

Если вы также выберете индекс индекса данных, то результатом может быть либо DataFrame, либо Series, либо это может быть Series или скаляр (одно значение).

Эта функция гарантирует, что вы всегда получите список из вашего выбора (если действительны df, index и column):

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)