看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
在业务数据分析中,特别是和时间相关的数据,会经常要判断数据的变化情况,比如是否是增长还是降低,或是持平。在本需求中,我们需要以数据中最后的月份为基础,来看它最近的数据变化,并将变化情况标记在本行的最后一列中。
以下为我们需求的源数据:
import pandas as pd
import numpy as np
np.random.seed(4096)
df = pd.DataFrame(np.random.randint(0, 100, (8, 6)),
columns=pd.array([*'123456'])+'月'
)
df.iloc[4, 1:] = df.iloc[4,1]
df
'''
1月 2月 3月 4月 5月 6月
0 36 40 89 40 90 88
1 23 78 70 62 30 17
2 23 42 68 49 87 96
3 66 75 45 96 61 20
4 22 71 71 71 71 71
5 2 41 64 72 89 96
6 35 93 45 42 75 18
7 99 83 1 50 85 64
'''
在以上数据中,一列是一个月份的数据,共有6个月份的数据,6 月是最近的数据,现在要以6月为基础,看每个行 6 月前的数据相对 6 月是否有上升、下降或持平,并且还要说明这三种情况的持续时间。
比如,索引 0 行,6 月为 88,相对 5 月有下降,再看 5 月相对 4 月是上升,因此,本行连续1个月降低。
再比如,索引 4 行,从 2 月到 6 月是连续持平的,共持平了四次。
又比如,索引 2 行,从 4 月到 6 月是连续增长的,共增长了两次。
整体思路是写一个函数,传入每一行(axis 为 1)数据,返回要标记的文本内容。
这个函数在处理时,可以按以下顺序进行:
接下来我们来按这个思路写代码。
在编写函数时,我们先取一行的数据作为实验,通过后再将其封装为函数。
由于传入是一行,我们随机取索引 1 行数据:
temp_ser = df.loc[1]
temp_ser
'''
1月 23
2月 78
3月 70
4月 62
5月 30
6月 17
Name: 1, dtype: int64
'''
求位差,看数据变化情况:
temp_ser.diff()
'''
1月 NaN
2月 55.0
3月 -8.0
4月 -8.0
5月 -32.0
6月 -13.0
Name: 1, dtype: float64
'''
对数值符号化:
temp_ser = temp_ser.diff().apply(np.sign)
temp_ser
'''
1月 NaN
2月 1.0
3月 -1.0
4月 -1.0
5月 -1.0
6月 -1.0
Name: 1, dtype: float64
'''
判断与最新月份的符号是否相同,同时,根据最新月份的符号转为趋势变化的文本:
type_str = {1: '增长', -1: '降低', 0: '持平'}.get(temp_ser.iloc[-1])
type_str
# '降低'
temp_ser == temp_ser.iloc[-1]
'''
1月 False
2月 False
3月 True
4月 True
5月 True
6月 True
Name: 1, dtype: bool
'''
为了方便计算累乘,我们将数据反转再计算:
temp_ser = (temp_ser == temp_ser.iloc[-1]).iloc[::-1]
temp_ser.cumprod()
'''
6月 1
5月 1
4月 1
3月 1
2月 0
1月 0
Name: 1, dtype: int64
'''
最后,将所有的 1 加起来:
num = temp_ser.cumprod().sum()
num
# 4
最后构造标记文本:
f'连续{num}个月{type_str}'
# '连续4个月降低'
最后,将以上过程封装为一个函数:
def func(ser: pd.Series):
ser = ser.diff().apply(np.sign)
type_str = {1: '增长', -1: '降低', 0: '持平'}.get(ser.iloc[-1])
ser = (ser == ser.iloc[-1]).iloc[::-1]
num = ser.cumprod().sum()
return f'连续{num}个月{type_str}'
调用这个函数来增加标记列:
df.assign(连续增减=df.apply(func, axis=1))
'''
1月 2月 3月 4月 5月 6月 连续增减
0 36 40 89 40 90 88 连续1个月降低
1 23 78 70 62 30 17 连续4个月降低
2 23 42 68 49 87 96 连续2个月增长
3 66 75 45 96 61 20 连续2个月降低
4 22 71 71 71 71 71 连续4个月持平
5 2 41 64 72 89 96 连续5个月增长
6 35 93 45 42 75 18 连续1个月降低
7 99 83 1 50 85 64 连续1个月降低
'''
得到了结果。
另外我们还可以编写迭代方法来处理数据,同样可以得到结果。如:
def func(ser: pd.Series):
ser = iter(ser.iloc[::-1])
cur, nxt = next(ser), next(ser)
type_str = {1: '增长', -1: '降低', 0: '持平'}.get(cur)
n = 1
while cur == nxt:
n += 1
cur = nxt
nxt = next(ser)
return f'连续{n}个月{type_str}'
(
df.diff(axis=1)
.applymap(np.sign)
.apply(func, axis=1)
)
'''
0 连续1个月降低
1 连续4个月降低
2 连续2个月增长
3 连续2个月降低
4 连续4个月持平
5 连续5个月增长
6 连续1个月降低
7 连续1个月降低
dtype: object
'''
还可以通过第一种方法用累加的思路完成:
(
df.diff(axis=1)
.applymap(np.sign)
.set_index('6月', drop=False)
.apply(lambda x: x!=x.iloc[-1], axis=1)
.apply(lambda x: x.iloc[::-1].cumsum(), axis=1)
.apply(lambda x: sum(x==0), axis=1)
.rename({1: '增长', -1: '降低', 0: '持平'})
)
'''
6月
降低 1
降低 4
增长 2
降低 2
持平 4
增长 5
降低 1
降低 1
dtype: int64
'''
df.assign(连续增减='连续'+_.astype(str).values+'个月'+_.index)
# ...
这样就完成了以上的需求。
(完)
更新时间:2024-08-18 16:06:40 标签:pandas python 变化 连续