pandas groupby-apply行为,返回一个Series(不一致的输出类型)

时间:2016-06-09 00:52:35

标签: python pandas

当app函数返回一个系列时,我很好奇pandas groupby-apply的行为。

当系列具有不同的长度时,它会返回一个多索引系列。

In [1]: import pandas as pd

In [2]: df1=pd.DataFrame({'state':list("AABBB"),
   ...:                 'city':list("vwxyz")})

In [3]: df1
Out[3]:
  city state
0    v     A
1    w     A
2    x     B
3    y     B
4    z     B

In [4]: def f(x):
   ...:         return pd.Series(x['city'].values,index=range(len(x)))
   ...:

In [5]: df1.groupby('state').apply(f)
Out[5]:
state
A      0    v
       1    w
B      0    x
       1    y
       2    z
dtype: object

这将返回一个Series对象。

但是,如果每个系列都具有相同的长度,那么它会将其转换为DataFrame

In [6]: df2=pd.DataFrame({'state':list("AAABBB"),
   ...:                 'city':list("uvwxyz")})

In [7]: df2
Out[7]:
  city state
0    u     A
1    v     A
2    w     A
3    x     B
4    y     B
5    z     B

In [8]: df2.groupby('state').apply(f)
Out[8]:
       0  1  2
state
A      u  v  w
B      x  y  z

这真的是预期的行为吗?如果我们使用这种方式,我们是否打算检查退货类型?或apply中有一个我不欣赏的选项吗?

如果您感到好奇,在我的实际使用案例中,返回的系列将与组的长度相同。这似乎是transform的理想情况,除了我发现返回系列的apply在大型数据集上实际上要快一个数量级。这可能是另一个话题。

编辑:松散地根据Parfait的答案,我们当然可以这样做:

X=df.groupby('state').apply(f)
if not isinstance(X,pd.Series):
    X=X.stack()
X

这将为df=df1df=df2提供相同的输出类型。我想我只是在问这是否真的是处理这个问题的正常或首选方式。

2 个答案:

答案 0 :(得分:5)

本质上,数据帧由等长系列组成(技术上是Series对象的字典容器)。如pandas split-apply-combine文档中所述,运行 groupby()指的是以下一个或多个

  
      
  • 根据某些条件将数据拆分成组
  •   
  • 将功能独立应用于每个组
  •   
  • 将结果合并到数据结构中
  •   

请注意,这并不表示始终生成数据框,而是通用的数据结构。因此,groupby()操作可以向下转换为系列,或者如果给定系列作为输入,则可以向上转换为数据帧。

对于您的第一个数据帧,您运行不相等的分组(或不等的索引长度),强制执行"组合"处理不足以产生数据帧。由于数据帧不能组合不同的长度序列,因此它产生多索引序列。您可以在定义的函数中使用打印语句查看此内容,state==A组的长度为2,组{3}为B

def f(x):
    print(x)
    return pd.Series(x['city'].values, index=range(len(x)))

s1 = df1.groupby('state').apply(f)

print(s1)
#   city state
# 0    v     A
# 1    w     A
#   city state
# 0    v     A
# 1    w     A
#   city state
# 2    x     B
# 3    y     B
# 4    z     B
# state   
# A      0    v
#        1    w
# B      0    x
#        1    y
#        2    z
# dtype: object

但是,您可以通过重置索引并从而调整其层次级别来操纵多索引系列结果:

df = df1.groupby('state').apply(f).reset_index()
print(df)

#   state  level_1  0
# 0     A        0  v
# 1     A        1  w
# 2     B        0  x
# 3     B        1  y
# 4     B        2  z

但与您的需求更相关的是unstack(),它会转动索引标签的一个级别,从而产生一个数据框。考虑fillna()填写None结果。

df = df1.groupby('state').apply(f).unstack()
print(df)

#        0  1     2
# state            
# A      v  w  None
# B      x  y     z

答案 1 :(得分:3)

您不是在函数f中执行.xcodeproj,而是 可以index=range(len(x))来防止这种不良行为