【问题标题】:Pivot a pandas dataframe and get the non-axis columns as a series旋转 pandas 数据框并将非轴列作为一个系列
【发布时间】:2014-01-16 10:32:48
【问题描述】:

我有一个使用 pandas.io.sql.read_frame 从数据库中提取的数据集,看起来像这样

      Period Category    Projected       Actual     Previous
 0   2013-01 A          1214432.94   3175516.32   3001149.50  
 1   2013-01 B           624010.78    867729.20    866639.38
 2   2013-01 C          2533443.36   2314765.87   2482210.68
 3   2013-01 D          5616228.49   5672648.92   5918737.79
 4   2013-01 E           492184.31   1009281.36    990499.75
 5   2013-01 F         32824689.07  29610034.26  32248832.59
 6   2013-01 G            94192.33    152839.03    189061.80
 7   2013-01 H          1271544.89   1545591.40   1054648.58
 8   2013-01 I          8273369.88   8656894.51   8691683.73
 9   2013-01 J          8540953.73   8012622.14   8671895.07
 10  2013-01 K          8016059.13   8530401.75   9953181.37
 11  2013-01 L          1190095.56    512354.65    459954.82
 12  2013-01 M           850057.11   1077172.22   1097503.89
 13  2013-02 A          1227779.01   2850482.70   3070764.66
 14  2013-02 B           636124.55    822016.04    866802.59
 15  2013-02 C          2581194.49   2471194.78   2681301.30
 16  2013-02 D          5970719.17   5179206.09   5872806.59
 17  2013-02 E           477820.01   1199334.74   1330452.48
 18  2013-02 F         34537100.44  29082997.97  31982248.04
 19  2013-02 G            92523.45     75865.03     93782.83
 ...

如果我使用 D.pivot_table(rows="Category", cols="Period", aggfunc="sum") 旋转表格,我会得到一个看起来像这样的多索引数据框

<class 'pandas.core.frame.DataFrame'>
Index: 13 entries, A ...
Data columns (total 33 columns):
(Projected, 2013-01)    13  non-null values
(Projected, 2013-02)    13  non-null values
(Projected, 2013-03)    13  non-null values
(Projected, 2013-04)    13  non-null values
(Projected, 2013-05)    13  non-null values
(Projected, 2013-06)    13  non-null values
(Projected, 2013-07)    13  non-null values
(Projected, 2013-08)    13  non-null values
(Projected, 2013-09)    13  non-null values
(Projected, 2013-10)    13  non-null values
(Projected, 2013-11)    12  non-null values
(Actual, 2013-01)       13  non-null values
(Actual, 2013-02)       13  non-null values
(Actual, 2013-03)       13  non-null values
(Actual, 2013-04)       13  non-null values
(Actual, 2013-05)       13  non-null values
(Actual, 2013-06)       13  non-null values
(Actual, 2013-07)       13  non-null values
(Actual, 2013-08)       13  non-null values
(Actual, 2013-09)       13  non-null values
(Actual, 2013-10)       13  non-null values
(Actual, 2013-11)       12  non-null values
(Previous, 2013-01)     13  non-null values
(Previous, 2013-02)     13  non-null values
(Previous, 2013-03)     13  non-null values
(Previous, 2013-04)     13  non-null values
(Previous, 2013-05)     13  non-null values
(Previous, 2013-06)     13  non-null values
(Previous, 2013-07)     13  non-null values
(Previous, 2013-08)     13  non-null values
(Previous, 2013-09)     13  non-null values
(Previous, 2013-10)     13  non-null values
(Previous, 2013-11)     12  non-null values
dtypes: float64(33)     

但我不希望列上的分层索引。相反,我希望将非枢轴列(Projected、Actual 和 Previous)作为三元组(即系列)形式的值,因此最终表看起来像这样

Period 2013-01 2013-02 2013-03 ...
Group
A       Series  Series  Series ...
B       Series  Series  Series ...
C       Series  Series  Series ...
D       Series  Series  Series ...
...

其中每个“系列”是一个由三个数字组成的 pandas 系列,分别是(预计、实际和先前)聚合值。

我查看了数据透视表中的 stack、unstack、各种组合或行、cols 和 values 参数以及 pandas.core.reshape 中的 melt 函数,但它们似乎都没有达到我想要的效果。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    我相信您是在向我们介绍 XY problem,因为包含 Series 的结果数据集没有实际适用性。

    也许您正在寻找一个 groupby 对象而不是枢轴?

    >>> df.groupby(["Category", 'Period']).get_group(('A', '2013-01'))
        Period Category   Projected      Actual   Previous
    0  2013-01        A  1214432.94  3175516.32  3001149.5
    >>> df.groupby(["Category", 'Period']).get_group(('A', '2013-01'))[['Projected', 'Actual', 'Previous']].sum()
    Projected    1214432.94
    Actual       3175516.32
    Previous     3001149.50
    dtype: float64
    

    【讨论】:

    • 我需要表格格式的数据,以便通过 django 模板语言方便地遍历,即我想遍历行。最终,我希望表格中的每个单元格都有三个值,用逗号分隔。因此想要以旋转格式的元组。如果我使用 group_by,我基本上必须在 python 中构建表。如果我能让 pandas 做到这一点,而不是在 python 中,这很慢,我会考虑实际适用性。我可以在这里做些什么来避免 XY 问题?
    • @sirlark,看看下面我的回答。我一直在其他一些情况下使用这种解决方案,并且效果很好。如果不尝试几种不同的方式,我永远无法记住语法,但基本上一旦你有一个系列列表(通过下面的列表插值完成),你可以zip(*[list_of_series]) 创建元组。
    【解决方案2】:

    我相信@alko 在正确的轨道上建议在开头使用groupby,然后是sum。如果您的目标是在每个地方都有一个可迭代的,那么您可以使用zip 创建一列元组。这个怎么样:

    import pandas as pd
    import numpy as np
    from itertools import product
    
    np.random.seed(1)
    
    periods = range(0,3)
    categories = list('ABC')
    
    rows = list(product(periods, categories)) * 2
    n = len(rows)
    
    df = pd.DataFrame({'Projected': np.random.randn(n), 
                       'Actual': np.random.randn(n), 
                       'Previous': np.random.randn(n)},
                      index = pd.MultiIndex.from_tuples(rows))
    df.index.names = ['Period', 'Category']
    summed = df.groupby(level=['Period', 'Category']).sum()
    summed['tuple'] = zip(*[summed[c] for c in ['Projected', 'Actual', 'Previous']])
    result = summed['tuple'].unstack('Period')
    

    给予


    为了完整起见,您可以返回其他方式,尽管有点痛苦:

    andback = result.stack().apply(lambda t: pd.Series({'Projected': t[0],
                                                  'Actual': t[1],
                                                  'Previous': t[2]}))
    

    给予


    只是为了帮助 cmets 中的某个人。以下是我添加小计和总计的方法:

    def add_subtotal(g):
        category = g.index.get_level_values('Category')[0]
        g.loc[(category, 'subtotal'), :] = g.sum()
        return g
    
    with_subtotals = andback.groupby(level='Category', axis=0).transform(add_subtotal)
    
    with_subtotals.loc[('Grand', 'Total'), :] = with_subtotals\
        .loc[with_subtotals.index.get_level_values('Period')=='subtotal', :]\
        .sum()
    

    这给出了:

    【讨论】:

    • 我一直在寻找一种方法来做到这一点,而不会闯入 python 来形成元组,但找不到,所以看起来这是我要得到的最接近的,我会接受这个答案。至少 zip 不直接涉及 python 循环,并且对于我的情况来说相当快。
    • 很高兴它有帮助。另一个注意事项:使用 DataFrame 内容中的元组,您可以轻松地将单个列转换为 MultiIndex,例如:df.index = pd.MultiIndex.from_tuples(df['tuple_column'])
    • 您知道是否可以为每个类别(A、B、C)添加小计?那么最后有一个最终的总数吗?
    • @8one6 “后退”之一。理想情况下,在每个类别下都有一个小计,或者如果这是不可能的。将“A 总计”、“B 总计”、“C 总计”和“总计”添加到期间列中
    • 这应该是一个单独的问题,但我在上面至少提出了一种方法。
    猜你喜欢
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 2023-04-04
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-09
    相关资源
    最近更新 更多