熊猫按标签选择有时返回 Series,有时返回 DataFrame

在熊猫中,当我选择一个标签,只有一个条目在索引中,我得到一个系列,但是当我选择一个条目有一个以上的条目,我得到一个数据帧。

为什么? 有没有办法确保我总是能拿回一个数据帧?

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
59018 次浏览

你有一个包含三个索引项目的索引 3。因为这个原因,df.loc[3]将返回一个数据帧。

原因是您没有指定列。因此,df.loc[3]从所有列中选择三个项(即 0列) ,而 df.loc[3,0]将返回 Series。例如,df.loc[1:2]也返回一个数据框架,因为你要对行进行切割。

选择一行(作为 df.loc[1])将返回一个以列名作为索引的 Series。

如果希望确保始终有一个 DataFrame,可以像 df.loc[1:1]那样进行切片。另一个选项是布尔索引(df.loc[df.index==1])或 take 方法(df.take([0]),但这里使用的是位置而不是标签!).

虽然这种行为是不一致的,但是我认为很容易想象这种方便的情况。无论如何,为了每次都得到一个 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

你在评论中写道 Joris 的回答:

“我不明白这个设计 决定单行到 改变信仰成为一个系列-为什么不是一个 只有一行的资料框? 」

在系列中,单行不是 转变了
是的系列: No, I don't think so, in fact; see the edit

考虑熊猫数据结构的最佳方式是灵活的 用于低维数据的容器。例如,DataFrame 是一个 Panel 是 DataFrame 对象的容器。 我们希望能够插入和删除这些对象 以类似字典的方式存放容器。

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

熊猫对象的数据模型就是这样选择的。原因当然在于它确保了一些我不知道的优势(我不完全理解引文的最后一句,也许这就是原因)

.

编辑: 我不同意

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 应该是什么: 列还是行?愚蠢的问题和远见。

.

那么什么是数据框架?

在这个答案的前一个版本中,我提出了这个问题,试图在他的一个评论中找到这个问题的 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?部分则由丹 · 艾伦回答。

然后,正如上面引用的大熊猫的文档所说,大熊猫的数据结构最好看作是低维数据的 容器,在我看来,对 为什么的理解可以从 DataFrame 结构的本质特征中找到。

然而,我意识到,这个被引用的建议不能被视为对熊猫数据结构性质的精确描述。
这个建议并不意味着 DataFrame 是 Series 的容器。
它表示,将数据框架作为 Series 的容器(根据推理中的某个时刻考虑的行或列)是考虑数据框架的一个好方法,即使严格来说实际情况并非如此。数据框架的心智表征是:。“好”意味着这个愿景能够高效地使用 DataFrames。仅此而已。

.

那么什么是 DataFrame 对象?

数据框架类生成的实例具有起源于 NDFrame基类的特定结构,NDFrame基类本身派生自 熊猫容器基类,熊猫容器基类也是 系列类的父类。
请注意,这是正确的熊猫,直到版本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
我们发现在它的方法给出了丹艾伦和其他方法。

为什么这些提取方法被精心制作成这样?
这当然是因为它们被评价为在数据分析方面提供了更好的可能性和便利性。
这正是这句话所表达的:

考虑熊猫数据结构的最佳方式是灵活的 低维数据容器。

从 DataFRame 实例中提取数据的 为什么不在于它的结构,而在于这个结构的 为什么。我猜想大熊猫数据结构的结构和功能已经被雕刻,以便尽可能多的智力直觉,为了理解细节,人们必须阅读 Wes McKinney 的博客。

如果目标是使用索引获取数据集的子集,那么最好避免使用 lociloc。相反,你应该使用类似下面这样的语法:

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

使用 df['columnName']获得 Series,使用 df[['columnName']]获得 Dataframe。

如果您也选择数据框架的索引,那么结果可以是 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)

TLDR

使用 loc

df.loc[:] = 数据框架

如果有多个列,则 df.loc[int] = 数据框架; 如果数据框中只有1个列,则 系列

如果有多于一行,则 df.loc[:, ["col_name"]] = 数据框架; 如果所选内容中只有一行,则 系列

df.loc[:, "col_name"] = 系列

没有使用 loc

df["col_name"] = 系列

df[["col_name"]] = 数据框架

每次我们把 [['column name']]它返回熊猫数据帧对象, 如果我们把 ['column name']我们得到了熊猫系列物体