通过标签选择的熊猫有时返回Series,有时返回DataFrame


95

在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

Answers:


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。
人员

7
仅供参考,使用非重复索引和单个索引器(例如,单个标签),您将始终会获得一个Series,这仅是因为索引中有重复项即它是一个DataFrame。
Jeff

1
请注意,还有另一个陷阱:如果使用建议的解决方法,并且没有匹配的行,则结果将是一个具有单个行的DataFrame,全为NaN。
Paul Oyster 2014年

2
保罗,您使用的是哪个版本的熊猫?在最新版本中,KeyError尝试会得到一个提示.loc[[nonexistent_label]]
Dan Allan

2
使用列表中的列表.loc比没有列表中的列表要慢得多。要保持可读性,还要更快,更好地使用df.loc[1:1]
乔纳森(Jonathan),

15

您有一个包含三个索引项的索引3。因此,df.loc[3]将返回一个数据帧。

原因是您未指定列。因此,df.loc[3]选择所有列中的三个项目(即column 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]),但是此使用的位置不是标签!)。


3
那就是我所期望的行为。我不了解将单行转换为系列的设计决策-为什么不将一行数据框转换为数据?
2013年

啊,为什么不选择一行就返回系列,我真的不知道。
joris

6

TLDR

使用时 loc

df.loc[:]=数据

df.loc[int]=数据框(如果您有多于一列)和系列(如果您在数据框中只有一列)

df.loc[:, ["col_name"]]=数据

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

不使用 loc

df["col_name"]= 系列

df[["col_name"]]=数据


5

使用df['columnName']得到一个系列,并df[['columnName']]得到一个数据帧。


1
当心,它需要原始df的副本。
smci

3

您在评论joris的答案中写道:

“我不理解将单行转换为系列的设计决策-为什么不将一行数据框呢?”

单行不会在系列中转换
一个系列:No, I don't think so, in fact; see the edit

考虑熊猫数据结构的最佳方法是将其作为低维数据的灵活容器。例如,DataFrame是Series的容器,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?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?Dan Allan 对此部分做了回答。

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

但是,我意识到,不应将引用的建议作为对熊猫数据结构本质的精确描述。
此建议并不意味着DataFrame是Series的容器。
它表示,将DataFrame作为Series的容器的心理表示形式(根据推理的某个时刻考虑的选项是行还是列)是考虑DataFrame的一种好方法,即使实际上并非严格如此。“良好”意味着该愿景可以高效地使用DataFrame。就这样。

那么什么是DataFrame对象?

所述数据帧类产生具有特定结构起源于实例NDFrame基类,本身从派生 PandasContainer基类,也是一个父类的系列类。
请注意,这对于0.12版之前的Pandas才是正确的。在即将发布的0.13版本中,Series也将仅从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
我们在其中找到了Dan Allan给出的方法和其他方法。

为什么这些提取方法是照原样制作的?
当然,这是因为它们被认为是提供更好的可能性和简化数据分析的方法。
这句话正是这样表达的:

考虑熊猫数据结构的最佳方法是将其作为低维数据的灵活容器。

为什么数据从数据帧的实例提取的不在于它的结构,它位于为什么这种结构。我猜想,Pandas数据结构的结构和功能已经过精心设计,以便尽可能地提高智力上的直观性,并且要了解详细信息,必须阅读Wes McKinney的博客。


1
仅供参考,DataFrame不是ndarray子类,也不是Series(从0.13开始,尽管之前是)。这些比任何东西都更像字典。
杰夫,

谢谢你通知我。我真的很感激,因为我是学习熊猫的新手。但是我需要更多信息才能很好地理解。为什么在文档中将Series定义为ndarray的子​​类?
eyquem 2013年

它在0.13之前(即将发布),这是开发文档:pandas.pydata.org/pandas-docs/dev/dsintro.html#series
Jeff

好。非常感谢你。但是,它不会改变我的推理和理解的基础,对吗?-在低于0.13的Pandas中,DataFrame和其他Pandas的对象与Series不同:它们的子类是什么?
eyquem

@Jeff谢谢。根据您的信息,我修改了答案。我很高兴知道您对我的编辑的看法。
eyquem

1

如果目标是使用索引获取数据集的子集,则最好避免使用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

0

如果您还选择数据框的索引,则结果可以是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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.