【问题标题】:parameter "fill_value" of method "pandas.pivot_table" not working as expected方法“pandas.pivot_table”的参数“fill_value”未按预期工作
【发布时间】:2020-03-24 11:20:49
【问题描述】:

我正在使用 np.NaN 值旋转一个表,这些值我希望在转换后保留,目前还没有发生。

鉴于下表:

df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                         "bar", "bar", "bar", "bar"],
                   "B": ["one", "one", "one", "two", "two",
                         "one", "one", "two", "two"],
                   "C": ["small", "large", "large", "small",
                         "small", "large", "small", "small",
                         "large"],
                   "D": [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, 7],
                   "E": [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]})

现在应用数据透视表:

table = pd.pivot_table(df, values='D', index=['A', 'B'],
                    columns=['C'], aggfunc=np.sum, fill_value='HAHA').reset_index()

我得到的输出:

C   A    B     large     small
0   bar  one       0       0.0
1   bar  two       7       0.0
2   foo  one       0       0.0
3   foo  two    HAHA       0.0

我期待的输出

C   A    B     large     small
0   bar  one    HAHA      HAHA     
1   bar  two       7      HAHA     
2   foo  one    HAHA      HAHA     
3   foo  two    HAHA      HAHA     

问题: 为什么 pivot_table 方法只填充组“foo - two - large”,而其他组没有保留其 np.NaN,而是将其替换为零?

【问题讨论】:

  • 它按设计工作; np.NaN 元素的总和为 0 (df['E'].sum()==0),它只会填充您的数据透视图中根本不存在的元素,这是您输出中唯一为 nan 的元素
  • 这能回答你的问题吗? Sum across all NaNs in pandas returns zero?
  • 没错,我还没有意识到在这种情况下缺少组“foo - two - large”。我最初的想法是:因为下面的代码“np.sum([np.nan, np.nan])”给出了“nan”作为结果。我认为在这种方法中使用它时也会发生同样的情况。你知道为什么没有吗?
  • 在链接问题中引用的文档中,将其设为熊猫中的默认值似乎只是一个设计决定。我不知道确切地说为什么输出默认为0 而不是nan
  • @AfonsoSchulzAlbrecht 这是一个非常微妙的实施决策。基本上,当您将 np.sum 作为 aggfunc 传递时,pandas 会忽略它,而是使用 getattr(Series, 'sum')。它超级隐藏在源代码中,但在我的解决方案结束时,我放入了用于执行此“调度”的表。 np.sum、np.nansum 和 sum 都被分派到 DataFrame 或 Series 的 sum 属性。稍微相关:stackoverflow.com/questions/60647377/…

标签: python pandas


【解决方案1】:

这是np.sumgroupby 处理的结果。 pivot_table 的核心是一个 groupby 后跟 reshaping。我们可以看到不受欢迎的行为出现在哪里。

index=['A', 'B']
columns=['C']
keys = index+columns
aggfunc=np.sum

agged = df.groupby(keys).agg(aggfunc)
#                 D    E
#A   B   C              
#bar one large  0.0  0.0
#        small  0.0  0.0
#    two large  7.0  0.0
#        small  0.0  0.0
#foo one large  0.0  0.0
#        small  0.0  0.0
#    two small  0.0  0.0

对于groupby,默认是缺失数据求和为0,以后不会考虑NaN。但是 ('foo', 'two', 'large') 丢失了,因为它在您的原始 DataFrame 中从未有任何观察结果。稍后的重塑步骤会为该组提供NaN 值。

table = agged
if table.index.nlevels > 1:
    # Related GH #17123
    # If index_names are integers, determine whether the integers refer
    # to the level position or name.
    index_names = agged.index.names[: len(index)]
    to_unstack = []
    for i in range(len(index), len(keys)):
        name = agged.index.names[i]
        if name is None or name in index_names:
            to_unstack.append(i)
        else:
            to_unstack.append(name)
    table = agged.unstack(to_unstack)

print(table)
#            D           E      
#C       large small large small
#A   B                          
#bar one   0.0   0.0   0.0   0.0
#    two   7.0   0.0   0.0   0.0
#foo one   0.0   0.0   0.0   0.0
#    two   NaN   0.0   NaN   0.0

那么你如何获得你想要的行为呢?您需要设置dropna=False。 Pandas 也很聪明,并尝试将"dispatch" 大部分基本操作转换为优化的等效项。我们需要使用 lambda 来避免这种情况。

pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'], 
               aggfunc=lambda x: sum(x),
               dropna=False,
               fill_value='HAHA')

        large small
A   B              
bar one  HAHA  HAHA
    two     7  HAHA
foo one  HAHA  HAHA
    two  HAHA  HAHA

我们可以看到sumnp.nansumnp.sum 都得到了Series.sum 属性的别名。 lambda 是避免这种情况的最简单方法。

[func for func, attr in pd.DataFrame()._cython_table.items() if attr == 'sum']
#[<function sum>, <function numpy.sum>, <function numpy.nansum>]

【讨论】:

    猜你喜欢
    • 2016-05-03
    • 2019-04-22
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-19
    • 2016-07-01
    • 1970-01-01
    相关资源
    最近更新 更多