按标签选择的 Pandas 有时会返回 Series,有时会返回 DataFrame

Posted

技术标签:

【中文标题】按标签选择的 Pandas 有时会返回 Series,有时会返回 DataFrame【英文标题】:Pandas selecting by label sometimes return Series, sometimes returns DataFrame 【发布时间】:2013-12-21 10:16:48 【问题描述】:

在 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

【讨论】:

谢谢。值得注意的是,即使标签不在索引中,这也会返回一个 DataFrame。 仅供参考,具有非重复索引和单个索引器(例如单个标签),您总是会返回一个系列,这只是因为您在索引中有重复,它是一个 DataFrame . 请注意,还有一个问题:如果使用建议的解决方法,并且没有匹配的行,结果将是一个包含单行的 DataFrame,全部为 NaN。 保罗,你用的是什么版本的熊猫?在最新版本中,当我尝试 .loc[[nonexistent_label]] 时,我得到了 KeyError .loc 中使用列表比不使用列表要慢得多。为了仍然可读但也更快,最好使用df.loc[1:1]【参考方案2】:

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

原因是您没有指定列。所以df.loc[3] 选择所有列的三个项目(即列@​​987654324@),而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】:

TLDR

使用loc

df.loc[:] = 数据框

df.loc[int] = Dataframe 如果您有多个列,Series 如果您在数据框中只有 1 列

df.loc[:, ["col_name"]] = Dataframe(如果您有不止一行)和 Series(如果您只有 1 行)

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

不使用loc

df["col_name"] = 系列

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

【讨论】:

这是不正确的。如果只选择了一行,df.loc[:, ["col_name"]] 将返回一个系列。 对,如果数据框仅包含一行,因为 : 选择所有行 因此,由于关注结果的类型,也许您可​​以添加不同的部分,指定类型根据结果的基数而不同。【参考方案4】:

使用df['columnName'] 获取系列,使用df[['columnName']] 获取数据框。

【讨论】:

请注意复制原始 df。【参考方案5】:

您在对 joris 回答的评论中写道:

“我不懂设计 决定将单行转换成一个系列 - 为什么不 一行数据框?”

单行不会在系列中转换。 它一个系列: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 由 Series 组成是没有意义的,因为这些所说的 Series 应该是什么:列或行?愚蠢的问题和愿景。

.

那么什么是DataFrame?

在这个答案的先前版本中,我问了这个问题,试图在他的一个评论中找到对 OP 问题的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? 部分已由 Dan Allan 回答。

然后,正如上面引用的 Pandas 文档所说,pandas 的数据结构最好被视为低维数据的容器,在我看来,理解为什么 可以在 DataFrame 结构的性质特征中找到。

但是,我意识到不能将引用的建议视为对 Pandas 数据结构性质的精确描述。 这个建议并不意味着 DataFrame 是 Series 的容器。 它表示将 DataFrame 的心理表示为 Series 的容器(根据在推理的某个时刻考虑的选项,行或列)是考虑 DataFrame 的好方法,即使在现实中并非严格如此。 “好”意味着这个愿景能够高效地使用 DataFrame。就是这样。

.

那么什么是DataFrame对象呢?

DataFrame 类产生的实例具有源自 NDFrame 基类的特定结构,该基类本身派生自 PandasContainer 基类,即也是 Series 类的父类。 请注意,直到版本 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 数据结构的最佳方式是灵活 用于低维数据的容器。

从DataFRame 实例中提取数据的why 不在于其结构,而在于该结构的why。我猜 Pandas 的数据结构的结构和功能已经过精心设计,以便尽可能地直观直观,要了解细节,必须阅读 Wes McKinney 的博客。

【讨论】:

仅供参考,DataFrame 不是 ndarray 子类,也不是 Series(从 0.13 开始,但在此之前是这样)。这些更像是dict。 谢谢你通知我。我真的很感激,因为我是学习 Pandas 的新手。但我需要更多信息才能很好地理解。为什么在文档中写到 Series 是 ndarray 的子类? 它在 0.13 之前(即将发布),这里是开发文档:pandas.pydata.org/pandas-docs/dev/dsintro.html#series 好的。非常感谢。但是,它不会改变我推理和理解的基础,不是吗? - 在低于 0.13 的 Pandas 中,DataFrame 和其他 Pandas 的对象与 Series 不同:它们是什么子类? @Jeff 谢谢。我根据您的信息修改了我的答案。我很高兴知道您对我的编辑有何看法。【参考方案6】:

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

【讨论】:

语法result = df[df.index == idx] 是一个非常好的选择;完全符合我的目的。【参考方案7】:

如果您还选择了数据框的索引,则结果可以是数据框或系列它可以是系列或标量(单个值)。

此功能可确保您始终从您的选择中获取列表(如果 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)

【讨论】:

以上是关于按标签选择的 Pandas 有时会返回 Series,有时会返回 DataFrame的主要内容,如果未能解决你的问题,请参考以下文章

绘图与可视化--pandas中的绘图函数

Pandas TimeSeries 重新采样产生 NaN

在 Python Pandas 中将带有元素日期标签的年度财政数据元组转换为时间序列

Dataframe Pandas 聚合和/或 groupby

pandas DataFrame按数组中的元素选择

为啥运行 pandas_udf 时 Pyspark 失败?