重新计算行之间的值并将季度数据转换为月度数据

Recalculate values between row and convert quarterly data to monthly basis

我有两部分问题想寻求帮助,即根据每个项目和年季度在行之间进行减法,然后将我的数据平均分配到月度基础上。我只是不知道如何编写代码以这种方式运行。

这是一个数据框。实际数据将由数百项和近 50 列组成。时间范围是20年的历史数据。 Number 是每一列已经计算的累计值。比如Q1是一月到三月的数据,Q2是一月到六月的数据,所有项目都是按季度记录的。

data = [['kitkat', '2020Q4', 500000, 350000], ['kitkat', '2020Q3', 400000, 250000], ['kitkat', '2020Q2', 200000, 100000], ['kitkat', '2020Q1', 100000, 50000],
       ['kitkat', '2019Q4', 700000, 450000],['kitkat', '2019Q3', 500000, 300000],['kitkat', '2019Q1', 300000, 150000], 
       ['oreo', '2020Q4', 500000, 350000], ['oreo', '2020Q3', 400000, 250000], ['oreo', '2020Q2', 200000, 100000], ['oreo', '2020Q1', 100000, 50000],
       ['oreo', '2019Q4', 700000, 450000],['oreo', '2019Q3', 500000, 300000],['oreo', '2019Q2', 300000, 150000],['oreo', '2019Q1', 200000, 100000]]
df = pd.DataFrame(data, columns = ['Item', 'CloseBook', "Income", "Expense"])

第 1 部分
因此,对于每个项目和每年。 Q4中的数字应该只包含10月到12月的数据。因此,每年的计算显示遵循以下规则:
Q4 = Q4-Q3
Q3 = Q3-Q2
Q2 = Q2-Q1
Q1 = Q1。
但是有可能会丢失某些季度。如果是这样,它必须减去上一个季度,例如。如果缺少 Q2,则 Q3-Q1。 这第一部分的结果应该是

第 2 部分
继续第 1 部分的计算。我需要添加新列以按月记录时间。并且根据计算的方式,需要将先前计算的结果除以 3、6 或 9。 要求是如果
它计算为 Q4-Q3/Q3-Q2/Q2-Q1 那么它必须除以 3,
如果计算为 Q4-Q2/Q3-Q1 那么它必须除以 6,
如果计算为 Q4-Q1 那么它必须除以 9.

kitkat 项目的最终数据框应如下所示

或者,是否有更短、更合适的方法来编写代码来计算从原始数据到我需要的最终结果?

解决方案使用 groupby().apply(),所以不会很快。如果你有数百万行,我们需要找到一个更高效的解决方案:

def f(group):
    idx = pd.IndexSlice
    cols = ["Income", "Expense"]

    # Part 1
    # Every quarter except Q1 is the delta between current row and previous row
    group.loc[idx[:, :, 2:], cols] = group[cols].diff()

    # Part 2
    year = group.index.get_level_values("Year")
    quarter = group.index.get_level_values("Quarter").to_series()

    times = []
    divisors = []
    for y, q, prev_q in zip(year, quarter, quarter.shift()):
        if q == 1:
            # Q1 always have 3 months
            m = pd.date_range(f"{y}-01-01", f"{y}-03-01", freq="MS")
        else:
            # Other quarters have varying number of months due to gap with
            # previous quarter
            m = pd.date_range(f"{y}-{int(prev_q)*3+1}-01", f"{y}-{q*3}-01", freq="MS")

        times.append(m)
        
        # If the gap is n month from previous quarter, the divisors are n
        # repeated n times
        divisors.append([len(m)] * len(m))

    group["Time"] = times
    group["Divisor"] = divisors
    group = group.explode(["Time", "Divisor"])

    group[cols] /= group["Divisor"].to_numpy()[:, None]
    return group

s = pd.PeriodIndex(df["CloseBook"], freq="Q")
result = (
    df.set_index(["Item", s.year.rename("Year"), s.quarter.rename("Quarter")])
    .sort_index()
    .groupby(level=[0, 1])
    .apply(f)
    .droplevel([1,2,3,4])
    .drop(columns="Divisor")
    .reset_index()
)

结果(trim 并对列和索引重新排序以品尝):

      Item CloseBook        Income       Expense       Time
0   kitkat    2019Q1      100000.0       50000.0 2019-01-01
1   kitkat    2019Q1      100000.0       50000.0 2019-02-01
2   kitkat    2019Q1      100000.0       50000.0 2019-03-01
3   kitkat    2019Q3  33333.333333       25000.0 2019-04-01
4   kitkat    2019Q3  33333.333333       25000.0 2019-05-01
5   kitkat    2019Q3  33333.333333       25000.0 2019-06-01
6   kitkat    2019Q3  33333.333333       25000.0 2019-07-01
7   kitkat    2019Q3  33333.333333       25000.0 2019-08-01
8   kitkat    2019Q3  33333.333333       25000.0 2019-09-01
9   kitkat    2019Q4  66666.666667       50000.0 2019-10-01
10  kitkat    2019Q4  66666.666667       50000.0 2019-11-01
11  kitkat    2019Q4  66666.666667       50000.0 2019-12-01
12  kitkat    2020Q1  33333.333333  16666.666667 2020-01-01
13  kitkat    2020Q1  33333.333333  16666.666667 2020-02-01
14  kitkat    2020Q1  33333.333333  16666.666667 2020-03-01
15  kitkat    2020Q2  33333.333333  16666.666667 2020-04-01
16  kitkat    2020Q2  33333.333333  16666.666667 2020-05-01
17  kitkat    2020Q2  33333.333333  16666.666667 2020-06-01
18  kitkat    2020Q3  66666.666667       50000.0 2020-07-01
19  kitkat    2020Q3  66666.666667       50000.0 2020-08-01
20  kitkat    2020Q3  66666.666667       50000.0 2020-09-01
21  kitkat    2020Q4  33333.333333  33333.333333 2020-10-01
22  kitkat    2020Q4  33333.333333  33333.333333 2020-11-01
23  kitkat    2020Q4  33333.333333  33333.333333 2020-12-01
24    oreo    2019Q1  66666.666667  33333.333333 2019-01-01
25    oreo    2019Q1  66666.666667  33333.333333 2019-02-01
26    oreo    2019Q1  66666.666667  33333.333333 2019-03-01
27    oreo    2019Q2  33333.333333  16666.666667 2019-04-01
28    oreo    2019Q2  33333.333333  16666.666667 2019-05-01
29    oreo    2019Q2  33333.333333  16666.666667 2019-06-01
30    oreo    2019Q3  66666.666667       50000.0 2019-07-01
31    oreo    2019Q3  66666.666667       50000.0 2019-08-01
32    oreo    2019Q3  66666.666667       50000.0 2019-09-01
33    oreo    2019Q4  66666.666667       50000.0 2019-10-01
34    oreo    2019Q4  66666.666667       50000.0 2019-11-01
35    oreo    2019Q4  66666.666667       50000.0 2019-12-01
36    oreo    2020Q1  33333.333333  16666.666667 2020-01-01
37    oreo    2020Q1  33333.333333  16666.666667 2020-02-01
38    oreo    2020Q1  33333.333333  16666.666667 2020-03-01
39    oreo    2020Q2  33333.333333  16666.666667 2020-04-01
40    oreo    2020Q2  33333.333333  16666.666667 2020-05-01
41    oreo    2020Q2  33333.333333  16666.666667 2020-06-01
42    oreo    2020Q3  66666.666667       50000.0 2020-07-01
43    oreo    2020Q3  66666.666667       50000.0 2020-08-01
44    oreo    2020Q3  66666.666667       50000.0 2020-09-01
45    oreo    2020Q4  33333.333333  33333.333333 2020-10-01
46    oreo    2020Q4  33333.333333  33333.333333 2020-11-01
47    oreo    2020Q4  33333.333333  33333.333333 2020-12-01