cummax,cummin,cumprod,cumsum
有時候我們需要求出從第一行開始截止到當前行的最大值、最小值,以及實現累乘、累和等等。
import pandas as pd
df = pd.DataFrame({"a": [10, 20, 15, 50, 40]})
# cummax:求出從第一行開始截止到當前行的最大值
# 第1行為10,第2行為20,第3行為15但是比20小所以還是20,第4行為50,同理第5行也是50
print(df["a"].cummax())
"""
0 10
1 20
2 20
3 50
4 50
Name: a, dtype: int64
"""
# 這個不需要解釋了
print(df["a"].cummin())
"""
0 10
1 10
2 10
3 10
4 10
Name: a, dtype: int64
"""
# 對每一行實現累乘
print(df["a"].cumprod())
"""
0 10
1 200
2 3000
3 150000
4 6000000
Name: a, dtype: int64
"""
# 對每一行實現累加
print(df["a"].cumsum())
"""
0 10
1 30
2 45
3 95
4 135
Name: a, dtype: int64
"""
shift:垂直方向移動
import pandas as pd
df = pd.DataFrame({"a": range(1, 10)})
print(df)
"""
a
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
"""
df["b"] = df["a"].shift(1)
df["c"] = df["a"].shift(-1)
print(df)
"""
a b c
0 1 NaN 2.0
1 2 1.0 3.0
2 3 2.0 4.0
3 4 3.0 5.0
4 5 4.0 6.0
5 6 5.0 7.0
6 7 6.0 8.0
7 8 7.0 9.0
8 9 8.0 NaN
"""
我們看到,我們某一列使用shift(n),可以使其達到向上或者向下的平移效果。n大於0,表示向上平移n個單位,n小於0表示向下平移n個單位。既然平移了,那么勢必就會出現NaN。
想象一個框,shift(1)表示框向上平移一個長度,那么框住的部分就是新的列。
如果我們有這樣一個需求,計算某一列的當前元素和上一個元素的差,該怎么做呢?
import pandas as pd
df = pd.DataFrame({"a": [10, 20, 15, 50, 40]})
print(df["a"] - df["a"].shift(1))
"""
0 NaN
1 10.0
2 -5.0
3 35.0
4 -10.0
Name: a, dtype: float64
"""
diff:垂直方向相減
這個功能我們已經實現了,可以使用shift平移之后手動相減。但是有一個更簡便的方法,也就是diff(n),n大於0,表示當前行與前第n行相減,n小於0,表示當前行與后第n行相減。
import pandas as pd
df = pd.DataFrame({"a": [10, 20, 15, 50, 40]})
df["b"] = df["a"].diff(1)
df["c"] = df["a"].diff(-1)
print(df)
"""
a b c
0 10 NaN -10.0
1 20 10.0 5.0
2 15 -5.0 -35.0
3 50 35.0 10.0
4 40 -10.0 NaN
"""
pct_change:垂直方向相減求比例
和diff(n)比較類似,但是在diff(n)基礎之上又做了一層操作。就是減完了之后,用差再除以原來減去的值。
import pandas as pd
df = pd.DataFrame({"a": [10, 20, 15, 50, 40]})
df["b"] = df["a"].diff(1)
df["b_pct"] = df["a"].pct_change(1)
df["c"] = df["a"].diff(-1)
df["c_pct"] = df["a"].pct_change(-1)
print(df)
"""
a b b_pct c c_pct
0 10 NaN NaN -10.0 -0.500000
1 20 10.0 1.000000 5.0 0.333333
2 15 -5.0 -0.250000 -35.0 -0.700000
3 50 35.0 2.333333 10.0 0.250000
4 40 -10.0 -0.200000 NaN NaN
"""
cut:分箱技術
有時候,我們需要對數據進行分類。比如考生成績,凡是小於60的歸類為不及格,大於等於60小於80的為不錯,大於等於80小於等於100為優秀
import pandas as pd
df = pd.DataFrame({"a": [60, 50, 80, 96, 75]})
# bins:為一個數組,從小到大。
df["b"] = pd.cut(df["a"], bins=[0, 59, 79, 100])
# 還可以指定labels,注意:len(labels) == len(bins) - 1,因為bins如果有n個元素,那么會形成n-1個區間
df["c"] = pd.cut(df["a"], bins=[0, 59, 79, 100], labels=["不及格", "不錯", "優秀"])
print(df)
"""
a b c
0 60 (59, 79] 不錯
1 50 (0, 59] 不及格
2 80 (79, 100] 優秀
3 96 (79, 100] 優秀
4 75 (59, 79] 不錯
"""
我們注意到:區間是左開右閉的,如果需要把右邊也改成開區間,那么加上right=False即可,默認是為True
rolling:窗口函數
假設我們有一年的歷史數據,我們需要對每8天求一次平均值該怎么做呢?比如:第一行是1~8
天的平均值,第二行是2~9
天的平均值,第三行是3~10
天的平均值。
import pandas as pd
df = pd.DataFrame({"a": [10, 20, 10, 60, 40, 20, 50]})
# 調用rolling(n)方法等於每n行開了一個窗口,然后對相應的窗口里面的數據進行操作。
# 然后就可以使用window求平均值了
# 這個n必須要大於0,否則報錯
window = df["a"].rolling(2)
# 我們說n大於0是往上,為2的話表示每一行往上數,加上本身數兩行,然后對這兩行求平均。所以第一行就是NaN了,因為上面沒有了。
print(window.mean())
"""
0 NaN
1 15.0
2 15.0
3 35.0
4 50.0
5 30.0
6 35.0
Name: a, dtype: float64
"""
# 同理n=3,表示往上數3行
# 那么第1行和第2行都會為NaN
print(df["a"].rolling(3).mean())
"""
0 NaN
1 NaN
2 13.333333
3 30.000000
4 36.666667
5 40.000000
6 36.666667
Name: a, dtype: float64
"""
# 當然不光可以求平均值,還可以求最大值,最小值,求和等等
df["b"] = df["a"].rolling(2).max()
df["c"] = df["a"].rolling(2).min()
df["d"] = df["a"].rolling(2).sum()
print(df)
"""
a b c d
0 10 NaN NaN NaN
1 20 20.0 10.0 30.0
2 10 20.0 10.0 30.0
3 60 60.0 10.0 70.0
4 40 60.0 40.0 100.0
5 20 40.0 20.0 60.0
6 50 50.0 20.0 70.0
"""
# 甚至還可以自定義函數
df["e"] = df["a"].rolling(2).sum()
# 每一個窗口可以理解為一個Series,df["a"].rolling(2).sum()等價於df["a"].rolling(2).agg("sum")
# 我們再單獨加上一個1
df["f"] = df["a"].rolling(2).agg(lambda x: sum(x) + 1)
print(df[["e", "f"]])
"""
e f
0 NaN NaN
1 30.0 31.0
2 30.0 31.0
3 70.0 71.0
4 100.0 101.0
5 60.0 61.0
6 70.0 71.0
"""
# 如果n=1,那么就是本身了
# 不管調用什么方法,都是它本身, 因為只有窗口大小為1
print((df["a"].rolling(1).sum() == df["a"].rolling(1).min()).all()) # True