Pandas 按标签选择有时会返回 Series,有时会返回 DataFrame
- 2025-04-16 08:57:00
- admin 原创
- 19
问题描述:
在 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:
TLDR
使用时loc
df.loc[:]
=数据框
df.loc[int]
=数据框(如果数据框中有多个列)和系列(如果数据框中只有 1 列)
df.loc[:, ["col_name"]]
= Dataframe(如果选择中有多个行)和Series(如果选择中只有 1 行)
df.loc[:, "col_name"]
=系列
不使用loc
df["col_name"]
=系列
df[["col_name"]]
=数据框
解决方案 3:
您的索引包含三个索引项3
。因此df.loc[3]
将返回一个数据框。
原因是你没有指定列。因此,df.loc[3]
它会选择所有列(即 column 0
)中的三个项目,同时df.loc[3,0]
返回一个 Series。例如,df.loc[1:2]
由于你对行进行了切片,因此也会返回一个 DataFrame。
选择单行(如df.loc[1]
)将返回以列名作为索引的系列。
如果要确保始终拥有 DataFrame,可以像 一样进行切片df.loc[1:1]
。另一个选项是布尔索引 ( df.loc[df.index==1]
) 或 take 方法 ( df.take([0])
,但这使用的是位置而不是标签!)。
解决方案 4:
用于df['columnName']
获取系列并df[['columnName']]
获取数据框。
解决方案 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-struct
Pandas 对象的数据模型就是这样选择的。原因当然在于它能带来一些我不知道的优势(我不太理解引文最后一句的意思,也许这就是原因)。
。
编辑:我不同意我的观点
DataFrame 不能由 Series 元素组成,因为以下代码为行和列提供了相同的类型“Series”:
import pandas as pd
df = pd.DataFrame(data=[11,12,13], index=[2, 3, 3])
print '-------- df -------------'
print df
print '
------- df.loc[2] --------'
print df.loc[2]
print 'type(df.loc[1]) : ',type(df.loc[2])
print '
--------- 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 结构性质的特征中找到。
然而,我意识到,这条建议并不能被理解为对 Pandas 数据结构本质的准确描述。
这条建议并不意味着 DataFrame 是 Series 的容器。
它表明,将 DataFrame 视为 Series 的容器(根据推理过程中的某个时刻的选项,可以是行,也可以是列)是一种理解 DataFrame 的好方法,即使现实情况并非如此。“好”是指这种设想能够高效地使用 DataFrame。仅此而已。
。
那么什么是 DataFrame 对象?
DataFrame类生成的实例具有源自NDFrame基类的特定结构,而 NDFrame 基类本身派生自 PandasContainer 基类,而 PandasContainer基类也是Series类的父类。
请注意,在 Pandas 0.12 版本之前,此方法是正确的。在即将推出的 0.13 版本中,Series也将仅派生自NDFrame类。
# with pandas 0.12
from pandas import Series
print 'Series :
',Series
print 'Series.__bases__ :
',Series.__bases__
from pandas import DataFrame
print '
DataFrame :
',DataFrame
print 'DataFrame.__bases__ :
',DataFrame.__bases__
print '
-------------------'
from pandas.core.generic import NDFrame
print '
NDFrame.__bases__ :
',NDFrame.__bases__
from pandas.core.generic import PandasContainer
print '
PandasContainer.__bases__ :
',PandasContainer.__bases__
from pandas.core.base import PandasObject
print '
PandasObject.__bases__ :
',PandasObject.__bases__
from pandas.core.base import StringMixin
print '
StringMixin.__bases__ :
',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 给出的方法和其他方法。
为什么要设计这些提取方法?
当然是因为它们被认为能够提供更好的可能性,并且简化数据分析。
这句话表达的正是这一点:
思考熊猫数据结构的最佳方式是将其作为低维数据的灵活容器。
从 DataFRame 实例中提取数据的原因不在于其结构,而在于这种结构本身的意义。我猜想,Pandas 数据结构的结构和功能已经经过精心设计,以便尽可能直观易懂。要理解其中的细节,可以阅读 Wes McKinney 的博客。
解决方案 6:
如果目标是使用索引获取数据集的子集,最好避免使用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
解决方案 7:
每次我们放入[['column name']]
它都会返回 Pandas DataFrame 对象,如果我们放入['column name']
我们得到 Pandas Series 对象
解决方案 8:
如果您还选择数据框的索引,那么结果可以是数据框或系列,也可以是系列或标量(单个值)。
此函数可确保您始终从您的选择中获取一个列表(如果 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)
扫码咨询,免费领取项目管理大礼包!