重新计算行之间的值并将季度数据转换为月度数据
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
我有两部分问题想寻求帮助,即根据每个项目和年季度在行之间进行减法,然后将我的数据平均分配到月度基础上。我只是不知道如何编写代码以这种方式运行。
这是一个数据框。实际数据将由数百项和近 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