1. 程式人生 > 實用技巧 >【12月DW打卡】joyful-pandas - 02 - Pandas 基礎 (包含基本函式、視窗物件、擴張視窗等知識點)

【12月DW打卡】joyful-pandas - 02 - Pandas 基礎 (包含基本函式、視窗物件、擴張視窗等知識點)

第二章 pandas 基礎

簡單回顧和總結

  • 重新回頭跟著教程走了一遍,又又又複習了一次,這次感覺效果更好了。
  • 後面的第二個練習用結構化的語言寫了一遍,對上了答案,花了快兩個多小時,哎,手生了。
  • 附ipynb轉md博文命令: jupyter nbconvert --to markdown E:\PycharmProjects\TianChiProject\00_山楓葉紛飛\competitions\008_joyful-pandas\02-pandas基礎.ipynb

準備:導包


import numpy as np
import pandas as pd

準備:升級pandas

pip install pandas --upgrade

pd.__version__
'1.1.5'

一、檔案的讀取和寫入

  • 在使用 read_table 的時候需要注意,引數 sep 中使用的是正則表示式,因此需要對 | 進行轉義變成 | ,否則無法讀取到正確的結果。
  • 一般在資料寫入中,最常用的操作是把 index 設定為 False,去除索引。
  • 如果想要把表格快速轉換為 markdown 和 latex 語言,可以使用 to_markdown 和 to_latex 函式,此處需要安裝 tabulate 包。
In [20]: print(df_csv.to_markdown())
|    |   col1 | col2   |   col3 | col4   | col5     |
|---:|-------:|:-------|-------:|:-------|:---------|
|  0 |      2 | a      |    1.4 | apple  | 2020/1/1 |
|  1 |      3 | b      |    3.4 | banana | 2020/1/2 |
|  2 |      6 | c      |    2.5 | orange | 2020/1/5 |
|  3 |      5 | d      |    3.2 | lemon  | 2020/1/7 |

二、基本資料結構

  • 一維的Series
  • 二維的DataFrame

1. Series

Series 一般由四個部分組成,分別是序列的值 data 、索引 index 、儲存型別 dtype 、序列的名字 name 。其中,索引也可以指定它的名字,預設為空。

s = pd.Series(data = [100, 'a', {'dic1':5}],
            index = pd.Index(['id1', 20, 'third'], name='my_idx'),
            dtype = 'object',
            name = 'my_name')
s
my_idx
id1              100
20                 a
third    {'dic1': 5}
Name: my_name, dtype: object

2. DataFrame

ataFrame 在 Series 的基礎上增加了列索引,一個數據框可以由二維的 data 與行列索引來構造:

df = pd.DataFrame(data = {'col_0': [1,2,3], 'col_1':list('abc'),
                           'col_2': [1.2, 2.2, 3.2]},
                   index = ['row_%d'%i for i in range(3)])

df

col_0 col_1 col_2
row_0 1 a 1.2
row_1 2 b 2.2
row_2 3 c 3.2

三、常用基本函式

prefix_path = 'E:\\PycharmProjects\\DatawhaleChina\\joyful-pandas\\data'
df = pd.read_csv(prefix_path+'\\learn_pandas.csv')
### 0. 查詢基本列名稱 (屬性)
df.columns
Index(['School', 'Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer',
       'Test_Number', 'Test_Date', 'Time_Record'],
      dtype='object')
  1. 彙總函式

    • df.head(2)
    • df.tail(3)
    • info() 返回表的資訊概況;
    • describe() 返回表中數值列對應的主要統計量;
  2. 特徵統計函式

    • 常見的如:sum, mean, median, var, std, max, min等
    • 分位數:quantile,示例df.quantile(0.75)
    • 非缺失值個數:count()
    • 最大值對應的索引: idxmax()
    • 小結:上述操作返回的都是標量,所以又稱為聚合函式,他們有一個公共引數axis,預設為0代表逐列來聚合,1代表逐行聚合!
  3. 唯一值函式

    • unique:得到其唯一值組成的列表
    • nunique:得到其唯一值組成的列表的大小
    • value_counts 可以得到唯一值和其對應出現的頻數:
    • drop_duplicates :刪除重複資料,其中的關鍵引數是 keep ,預設值 first 表示每個組合保留第一次出現的所在行, last 表示保留最後一次出現的所在行, False 表示把所有重複組合所在的行剔除。
    • duplicated 和 drop_duplicates 的功能類似,但前者返回了是否為唯一值的布林列表,其 keep 引數與後者一致。其返回的序列,把重複元素設為 True ,否則為 False 。 drop_duplicates 等價於把 duplicated 為 True 的對應行剔除。
  4. 替換函式 (針對某一個列進行的)

    • 對映替換:
    • replace函式,可以通過字典構造,或者傳入兩個列表來進行替換
    • 另外, replace 還有一種特殊的方向替換,指定 method 引數為 ffill 則為用前面一個最近的未被替換的值進行替換, bfill 則使用後面最近的未被替換的值進行替換。如: s.replace([1, 2], method='ffill')
    • 正則替換,暫時不可用~
df['Gender']
0      Female
1        Male
2        Male
3      Female
4        Male
        ...  
195    Female
196    Female
197    Female
198      Male
199      Male
Name: Gender, Length: 200, dtype: object
df['Gender'].replace({'Female': 0, 'Male': 1}).head()
0    0
1    1
2    1
3    0
4    1
Name: Gender, dtype: int64
  • 邏輯替換
  • 包括了 where 和 mask ,這兩個函式是完全對稱的: where 函式在傳入條件為 False 的對應行進行替換,而 mask 在傳入條件為 True 的對應行進行替換,當不指定替換值時,替換為缺失值。
s = pd.Series([-1, 1.2345, 100, -50, 999])
s.where(s<0)
0    -1.0
1     NaN
2     NaN
3   -50.0
4     NaN
dtype: float64
s.where(s<0, 100)
0     -1.0
1    100.0
2    100.0
3    -50.0
4    100.0
dtype: float64



- 需要注意的是,傳入的條件只需是與被呼叫的 Series 索引一致的布林序列即可
s_condition= pd.Series([True,False,False,True, True],index=s.index)
s.where(s<0, 6666)
0      -1.0
1    6666.0
2    6666.0
3     -50.0
4    6666.0
dtype: float64

數值替換包含了 round, abs, clip 方法,它們分別表示取整、取絕對值和截斷(比大小,超出返回的用上屆或者下屆來依次替代):

s = pd.Series([-1, 1.2345, 100, -50])
print('取整 保留兩位', s.round(2))

print('取abs', s.abs())

print('clip大法:按照大小進行範圍擷取\n', s.clip(0, 1))
# print('\n', s.clip(0, 2))
# print('\n', s.clip(0, 3))

取整 保留兩位 0     -1.00
1      1.23
2    100.00
3    -50.00
dtype: float64
取abs 0      1.0000
1      1.2345
2    100.0000
3     50.0000
dtype: float64
clip大法:按照大小進行範圍擷取
 0    0.0
1    1.0
2    1.0
3    0.0
dtype: float64
  1. 排序函式

    • 排序共有兩種方式,其一為值排序,其二為索引排序,對應的函式是 sort_values() 和 sort_index() 。
    • 預設引數 ascending=True; eg: df.sort_values('Height', ascending=False).head()
    • 在排序中,進場遇到多列排序的問題,比如在體重相同的情況下,對身高進行排序,並且保持身高降序排列,體重升序排列;eg: df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()
    • 索引排序的用法和值排序完全一致,只不過元素的值在索引中,此時需要指定索引層的名字或者層號,用引數 level 表示。另外,需要注意的是字串的排列順序由字母順序決定。eg: df_demo.sort_index(level=['Grade','Name'],ascending=[True,False]).head()
  2. apply 方法

    • apply的引數往往是一個以序列為輸入的函式。
    • apply效能較慢,自由度比較高。
df_demo = df[['Height', 'Weight']]
def my_mean(x):
    return x.mean()
# axis=0,預設按列作為一個整體進行計算; axis=1,表示按行計算
df_demo.apply(my_mean, axis=0)
df_demo.apply(lambda x:x.mean(), axis=0)
Height    163.218033
Weight     55.015873
dtype: float64

四. 視窗物件

pandas中有三類視窗,分別是滑動視窗 rolling 、擴張視窗 expanding 以及指數加權視窗 ewm 。

4.1 滑動視窗物件 rolling

  • 滑動視窗,引數為視窗大小 window,每次滾動包括當前行,前window-1行是沒有結果的。
  • 使用 .rolling 得到滑窗物件roller,roller類似於一個分組,再搭配聚合函式進行輸出
  • 類似的還有滑動相關係數或滑動協方差的計算,如roller.cov(s)或者roller.corr(s)
  • 還支援後接上apply()方法
  • 支援shift, diff, pct_change 這一組的類滑窗函式,它們的公共引數為 periods=n ,預設為 1,分別表示取向前第 n
    個元素的值、與向前第 n 個元素做差(與 Numpy中的diff不同,後者表示 n 階差分)、與向前第 n 個元素相比計
    算增長率。這裡的 n 可以為負,表示反方向的類似操作。
s = pd.Series([2,4,6,8,10])
s_roller_mean = s.rolling(window=3).mean()

s2 = pd.Series([1,2,6,16,30])
roller = s2.rolling(window = 3)
roller.cov(s)
roller.corr(s)
0         NaN
1         NaN
2    0.944911
3    0.970725
4    0.995402
dtype: float64

4.2 擴張視窗 expanding (從起點開始依次累加擴張指定的大小視窗——執行聚合函式)

擴張視窗又稱累計視窗,可以理解為一個動態長度的視窗,其視窗的大小就是從序列開始處到具體操作的對
應位置,其使用的聚合函式會作用於這些逐步擴張的視窗上。具體地說,設序列為 a1, a2, a3, a4,則其每個
位置對應的視窗即 [a1]、[a1, a2]、[a1, a2, a3]、[a1, a2, a3, a4]。

s = pd.Series([1, 3, 6, 10])
s.expanding().mean()
0    1.000000
1    2.000000
2    3.333333
3    5.000000
dtype: float64

4.3 練一練

cummax, cumsum, cumprod 函式是典型的類擴張視窗函式,請使用 expanding 物件依次實現它們。

#### 4.3.1 cummax
s = pd.Series([1, 3, 6, 10])
ret1 = s.cummax()
ret2 = s.expanding().max()
print('after cummax or expanding.max():')
print(ret1)
print(ret2)
#### 4.3.1 cumsum
#### 4.3.1 cumprod
# 略
after cummax or expanding.max():
0     1
1     3
2     6
3    10
dtype: int64
0     1.0
1     3.0
2     6.0
3    10.0
dtype: float64

五 練習

2.5.1 Ex1:口袋妖怪資料集

現有一份口袋妖怪的資料集,下面進行一些背景說明:

  • 代表全國圖鑑編號,不同行存在相同數字則表示為該妖怪的不同狀態
  • 妖怪具有單屬性和雙屬性兩種,對於單屬性的妖怪,Type 2 為缺失值
  • Total, HP, Attack, Defense, Sp. Atk, Sp. Def, Speed 分別代表種族值、體力、物攻、防禦、特攻、特防、速度,其中種族值為後 6 項之和。

題目

  1. 對 HP, Attack, Defense, Sp. Atk, Sp. Def, Speed 進行加總,驗證是否為 Total 值。
df = pd.read_csv('E:\\PycharmProjects\\DatawhaleChina\\joyful-pandas\\data\\pokemon.csv')
df.head(3)
list_cols = ['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']
df['my_total'] = 0
for x in list_cols:
    df['my_total'] = df['my_total'] + df[x]
if df[df['my_total']!=df['Total']].shape[0] > 0:
    print('存在錯誤資料!', df[df['my_total']!=df['Total']])
else:
    print('暫無錯誤資料')
del df['my_total']
暫無錯誤資料
  1. 對於 # 重複的妖怪只保留第一條記錄,解決以下問題:
  • (a) 求第一屬性的種類數量和前三多數量對應的種類
  • (b) 求第一屬性和第二屬性的組合種類
  • (c) 求尚未出現過的屬性組合
print(df.columns)
df = df.drop_duplicates('#', keep='first')
Index(['#', 'Name', 'Type 1', 'Type 2', 'Total', 'HP', 'Attack', 'Defense',
       'Sp. Atk', 'Sp. Def', 'Speed'],
      dtype='object')
print('第一屬性的種類數量:', set(df['Type 1']).__len__())
print(df['Type 1'].value_counts()[:3])
df['Type 1'].value_counts().index[:3]
#.sort_values(by='count',ascending=False)['Type 1'][0:3]
第一屬性的種類數量: 18
Water     105
Normal     93
Grass      66
Name: Type 1, dtype: int64





Index(['Water', 'Normal', 'Grass'], dtype='object')
print('多列去重 -- 求第一屬性和第二屬性的組合種類')
df_2 = df.drop_duplicates(['Type 1', 'Type 2'])
df_2.shape[0]
多列去重 -- 求第一屬性和第二屬性的組合種類





143
print('可以使用set做一個差集 -- 求尚未出現過的屬性組合')
type1_name_set = set(df['Type 1'])
type2_name_set = set(df['Type 2'])
all_type_pair, now_type_pair = set(), set()
for x in type1_name_set:
    for y in type2_name_set:
        all_type_pair.add((x, y))
for x,y in zip(df['Type 1'], df['Type 2']):
    now_type_pair.add((x,y))
print('列印差集: ')
name_diff = all_type_pair.difference(now_type_pair)
print(len(name_diff), name_diff)
可以使用set做一個差集 -- 求尚未出現過的屬性組合
列印差集: 
199 {('Fighting', 'Fighting'), ('Ghost', 'Ground'), ('Dragon', 'Ghost'), ('Grass', 'Dragon'), ('Flying', 'Electric'), ('Ice', 'Electric'), ('Dark', 'Fairy'), ('Grass', 'Grass'), ('Electric', 'Psychic'), ('Grass', 'Water'), ('Electric', 'Poison'), ('Bug', 'Psychic'), ('Grass', 'Bug'), ('Psychic', 'Psychic'), ('Normal', 'Poison'), ('Psychic', 'Normal'), ('Grass', 'Fire'), ('Steel', 'Dark'), ('Poison', 'Psychic'), ('Bug', 'Dragon'), ('Grass', 'Ghost'), ('Fairy', 'Fighting'), ('Psychic', 'Steel'), ('Poison', 'Normal'), ('Bug', 'Ice'), ('Ground', 'Normal'), ('Ghost', 'Fairy'), ('Dragon', 'Normal'), ('Poison', 'Steel'), ('Electric', 'Grass'), ('Electric', 'Water'), ('Psychic', 'Water'), ('Flying', 'Fighting'), ('Ice', 'Fighting'), ('Steel', 'Electric'), ('Bug', 'Bug'), ('Dragon', 'Steel'), ('Normal', 'Fighting'), ('Poison', 'Grass'), ('Ground', 'Grass'), ('Rock', 'Electric'), ('Ground', 'Water'), ('Fire', 'Grass'), ('Dragon', 'Grass'), ('Poison', 'Fire'), ('Dark', 'Electric'), ('Fighting', 'Rock'), ('Dragon', 'Water'), ('Ground', 'Fire'), ('Poison', 'Ghost'), ('Electric', 'Dark'), ('Psychic', 'Dark'), ('Fire', 'Fire'), ('Grass', 'Normal'), ('Fairy', 'Ice'), ('Fire', 'Ghost'), ('Electric', 'Ground'), ('Psychic', 'Ground'), ('Rock', 'Rock'), ('Fire', 'Dark'), ('Ghost', 'Electric'), ('Dragon', 'Dark'), ('Dark', 'Rock'), ('Flying', 'Ice'), ('Ice', 'Ice'), ('Ground', 'Ground'), ('Normal', 'Electric'), ('Fairy', 'Rock'), ('Psychic', 'Poison'), ('Bug', 'Normal'), ('Flying', 'Bug'), ('Poison', 'Poison'), ('Ground', 'Poison'), ('Fire', 'Poison'), ('Flying', 'Rock'), ('Ice', 'Rock'), ('Fighting', 'Dragon'), ('Dragon', 'Poison'), ('Fighting', 'Ice'), ('Normal', 'Rock'), ('Bug', 'Fairy'), ('Ghost', 'Fighting'), ('Electric', 'Fighting'), ('Steel', 'Ice'), ('Poison', 'Fairy'), ('Fighting', 'Grass'), ('Ground', 'Fairy'), ('Fighting', 'Bug'), ('Fairy', 'Psychic'), ('Fire', 'Fairy'), ('Dragon', 'Fairy'), ('Grass', 'Electric'), ('Fighting', 'Fire'), ('Fighting', 'Ghost'), ('Dragon', 'Fighting'), ('Bug', 'Dark'), ('Fairy', 'Dragon'), ('Fairy', 'Steel'), ('Flying', 'Flying'), ('Flying', 'Psychic'), ('Flying', 'Normal'), ('Ice', 'Normal'), ('Dark', 'Bug'), ('Fairy', 'Grass'), ('Ghost', 'Ice'), ('Ice', 'Dragon'), ('Fairy', 'Water'), ('Fairy', 'Bug'), ('Flying', 'Steel'), ('Electric', 'Electric'), ('Ice', 'Steel'), ('Psychic', 'Electric'), ('Normal', 'Dragon'), ('Normal', 'Ice'), ('Fairy', 'Fire'), ('Fairy', 'Ghost'), ('Poison', 'Electric'), ('Flying', 'Grass'), ('Ice', 'Grass'), ('Ghost', 'Bug'), ('Flying', 'Water'), ('Ice', 'Bug'), ('Fire', 'Electric'), ('Fighting', 'Poison'), ('Fighting', 'Normal'), ('Normal', 'Bug'), ('Ghost', 'Rock'), ('Flying', 'Fire'), ('Ice', 'Fire'), ('Water', 'Bug'), ('Electric', 'Rock'), ('Flying', 'Ghost'), ('Water', 'Fire'), ('Fairy', 'Ground'), ('Rock', 'Normal'), ('Flying', 'Dark'), ('Steel', 'Steel'), ('Fighting', 'Fairy'), ('Dark', 'Poison'), ('Dark', 'Normal'), ('Fighting', 'Water'), ('Dragon', 'Rock'), ('Fairy', 'Poison'), ('Flying', 'Ground'), ('Ground', 'Fighting'), ('Steel', 'Grass'), ('Fairy', 'Normal'), ('Steel', 'Water'), ('Steel', 'Bug'), ('Steel', 'Fire'), ('Ghost', 'Psychic'), ('Dark', 'Grass'), ('Flying', 'Poison'), ('Ice', 'Poison'), ('Dark', 'Water'), ('Ghost', 'Normal'), ('Rock', 'Fire'), ('Fairy', 'Fairy'), ('Normal', 'Normal'), ('Rock', 'Ghost'), ('Water', 'Normal'), ('Ghost', 'Steel'), ('Grass', 'Rock'), ('Electric', 'Dragon'), ('Fighting', 'Ground'), ('Psychic', 'Dragon'), ('Electric', 'Ice'), ('Psychic', 'Ice'), ('Normal', 'Steel'), ('Fighting', 'Electric'), ('Poison', 'Ice'), ('Flying', 'Fairy'), ('Ghost', 'Water'), ('Dark', 'Dark'), ('Ice', 'Fairy'), ('Ground', 'Ice'), ('Fire', 'Dragon'), ('Electric', 'Bug'), ('Fire', 'Ice'), ('Psychic', 'Bug'), ('Dragon', 'Dragon'), ('Fairy', 'Dark'), ('Water', 'Water'), ('Dark', 'Ground'), ('Electric', 'Fire'), ('Ghost', 'Ghost'), ('Steel', 'Poison'), ('Psychic', 'Rock'), ('Normal', 'Fire'), ('Steel', 'Normal'), ('Normal', 'Ghost'), ('Ground', 'Bug'), ('Rock', 'Poison'), ('Fire', 'Bug'), ('Poison', 'Rock'), ('Ice', 'Dark'), ('Dragon', 'Bug'), ('Normal', 'Dark'), ('Fairy', 'Electric')}
  1. 按照下述要求,構造 Series :

    • (a) 取出物攻,超過 120 的替換為 high ,不足 50 的替換為 low ,否則設為 mid
    • (b) 取出第一屬性,分別用 replace 和 apply 替換所有字母為大寫
    • (c) 求每個妖怪六項能力的離差,即所有能力中偏離中位數最大的值,新增到 df 並從大到小排序
ret_a = df['Attack'].mask(df['Attack']>120, 'high')\
.mask(df['Attack']<50, 'low')\
.mask((50<=df['Attack'])&(df['Attack']<=120), 'mid')
ret_a
0       low
1       mid
2       mid
4       mid
5       mid
       ... 
793    high
794     mid
795     mid
797     mid
799     mid
Name: Attack, Length: 721, dtype: object
# df['Type 1'].replace({i:str.upper(i) for i in df['Type 1'].unique()})
# 等價於
print(df['Type 1'])
for i in df['Type 1'].unique():
    df['Type 1'].replace({i : str(i).upper()}, inplace=True)
df['Type 1']
0        Grass
1        Grass
2        Grass
4         Fire
5         Fire
        ...   
793       Dark
794     Dragon
795       Rock
797    Psychic
799       Fire
Name: Type 1, Length: 721, dtype: object





0        GRASS
1        GRASS
2        GRASS
4         FIRE
5         FIRE
        ...   
793       DARK
794     DRAGON
795       ROCK
797    PSYCHIC
799       FIRE
Name: Type 1, Length: 721, dtype: object
df['Type 1'].apply(lambda x : str(x).lower())
0        grass
1        grass
2        grass
4         fire
5         fire
        ...   
793       dark
794     dragon
795       rock
797    psychic
799       fire
Name: Type 1, Length: 721, dtype: object
list_cols = ['HP', 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed']
df['max_devision'] = \
    df[list_cols].apply(lambda x : np.max((x-x.median()).abs()), 1)
df.sort_values('max_devision', ascending=False).head()

# Name Type 1 Type 2 Total HP Attack Defense Sp. Atk Sp. Def Speed max_devision
230 213 Shuckle BUG Rock 505 20 10 230 10 230 5 215.0
121 113 Chansey NORMAL NaN 450 250 5 5 35 105 50 207.5
261 242 Blissey NORMAL NaN 540 255 10 10 75 135 55 190.0
217 202 Wobbuffet PSYCHIC NaN 405 190 33 58 33 58 33 144.5
223 208 Steelix STEEL Ground 510 75 85 200 55 65 30 130.0

六 練習

Ex2:指數加權視窗

  1. 作為擴張視窗的ewm視窗

在擴張視窗中,使用者可以使用各類函式進行歷史的累計指標統計,但這些內建的統計函式往往把視窗中的所有元素賦予了同樣的權重。事實上,可以給出不同的權重來賦給視窗中的元素,指數加權視窗就是這樣一種特殊的擴張視窗。

其中,最重要的引數是alpha,它決定了預設情況下的視窗權重為\(w_i=(1−\alpha)^i,i\in\{0,1,...,t\}\),其中\(i=t\)表示當前元素,\(i=0\)表示序列的第一個元素。

從權重公式可以看出,離開當前值越遠則權重越小,若記原序列為\(x\),更新後的當前元素為\(y_t\),此時通過加權公式歸一化後可知:

\[\begin{split}y_t &=\frac{\sum_{i=0}^{t} w_i x_{t-i}}{\sum_{i=0}^{t} w_i} \\ &=\frac{x_t + (1 - \alpha)x_{t-1} + (1 - \alpha)^2 x_{t-2} + ... + (1 - \alpha)^{t} x_{0}}{1 + (1 - \alpha) + (1 - \alpha)^2 + ... + (1 - \alpha)^{t-1}}\\\end{split} \]

對於Series而言,可以用ewm物件如下計算指數平滑後的序列:

np.random.seed(0)
s = pd.Series([-1, -1, -2, -2, -2])
s.head()
0   -1
1   -1
2   -2
3   -2
4   -2
dtype: int64
s.ewm(alpha=0.2).mean().head()
0   -1.000000
1   -1.000000
2   -1.409836
3   -1.609756
4   -1.725845
dtype: float64

請用expanding視窗實現 s.ewm(alpha=0.2).mean()

def my_ewm_fun(lst_p, alpha=0.2):
    lst = list(lst_p)
    sums = 0
    devide = 0
    ret_lst = []
    for idx in range(len(lst)-1, -1, -1):
        x = lst[idx]
        weight = (1-alpha)**(len(lst) - idx-1)
        sums += x*(weight)
        devide += weight
        # print(x,  x*(weight))
    # TypeError: must be real number, not list
    # print('**** ', sums)
    # print('-------', sums, ' / ', devide)
    return sums/devide
s.expanding().apply(my_ewm_fun).head()
def ewm_func(x, alpha=0.2):
    win = (1-alpha)**np.arange(x.shape[0])[::-1]
    res = (win*x).sum()/win.sum()
    # print('**** ', (win*x))
    # print((win*x).sum(), '/ weights:', win.sum())
    return res
s.expanding().apply(ewm_func).head()
0   -1.000000
1   -1.000000
2   -1.409836
3   -1.609756
4   -1.725845
dtype: float64
  1. 作為滑動視窗的ewm視窗

從第1問中可以看到,ewm作為一種擴張視窗的特例,只能從序列的第一個元素開始加權。現在希望給定一個限制視窗n,只對包含自身最近的n個視窗進行滑動加權平滑。請根據滑窗函式,給出新的wiyt的更新公式,並通過rolling視窗實現這一功能。

s.rolling(window=4).apply(my_ewm_fun).head()
0         NaN
1         NaN
2         NaN
3   -1.609756
4   -1.826558
dtype: float64
s.rolling(window=4).apply(ewm_func).head() # 無需對原函式改動
0         NaN
1         NaN
2         NaN
3   -1.609756
4   -1.826558
dtype: float64