[英]How to apply a function not returning a numeric value to a pandas rolling Window?
我有一個 dtype 的日期時間系列:float64。 我正在嘗試將自定義 function 應用於系列上的滾動 window。 我希望這個 function 返回字符串。 但是,這會產生一個 TypeError。 為什么這會產生錯誤,有沒有辦法通過一個 function 的應用程序直接使其工作?
這是一個例子:
import numpy as np
import pandas as pd
np.random.seed(1)
number_series = pd.Series(np.random.randint(low=1,high=100,size=100),index=[pd.date_range(start='2000-01-01',freq='W',periods=100)])
number_series = number_series.apply(lambda x: float(x))
def func(s):
if s[-1] > s[-2] > s[-3]:
return 'High'
elif s[-1] > s[-2]:
return 'Medium'
else:
return 'Low'
new_series = number_series.rolling(5).apply(func)
結果是以下錯誤:
TypeError: must be real number, not str
我目前采用的解決方法是將 func 修改為 output 整數到系列,然后將另一個 function 應用於該系列以生成新系列。 按照下面的例子:
def func_float(s):
if s[-1] > s[-2] > s[-3]:
return 1
elif s[-1] > s[-2]:
return 2
else:
return 3
float_series = number_series.rolling(5).apply(func_float)
def func_text(s):
if s == 1:
return 'High'
elif s == 2:
return 'Medium'
else:
return 'Low'
new_series = float_series.apply(func_text)
這給出了生成錯誤的初始代碼的預期結果:
new_series
2000-01-02 Low
2000-01-09 Low
2000-01-16 Low
2000-01-23 Low
2000-01-30 Medium
...
2001-10-28 Low
2001-11-04 Medium
2001-11-11 High
2001-11-18 High
2001-11-25 Low
Length: 100, dtype: object
Note that the apply
function for a Rolling
object is different from the apply
function for a Series
object and I agree with you that this is a bit confusing. 據我了解,應用於滾動 windows 的函數通常用於聚合數據(例如sum
、 count
等)。
但是,您可以將滾動 windows 轉換為列表並將 function 應用於該列表(感謝此討論)。
所以我的方法是:
import numpy as np
import pandas as pd
np.random.seed(1)
number_series = pd.Series(np.random.randint(low=1,high=100,size=100),index=[pd.date_range(start='2000-01-01',freq='W',periods=100)])
number_series = number_series.apply(lambda x: float(x))
def func(s):
if len(s) > 2:
if s[-1] > s[-2] > s[-3]:
return 'High'
elif s[-1] > s[-2]:
return 'Medium'
else:
return 'Low'
else:
return ''
list = [func(window) for window in list(number_series.rolling(5))]
new_series = pd.Series(list, index=number_series.index)
另請注意, func
需要以不同方式處理第一項,否則索引將超出范圍。
一種方法是:
WindowIndexer
或rolling()
方法。func
返回一個字符串並將結果存儲為一個列表import numpy as np
import pandas as pd
np.random.seed(1)
number_series = pd.Series(np.random.randint(low=1,high=100,size=100),index=[pd.date_range(start='2000-01-01',freq='W',periods=100)])
number_series = number_series.apply(lambda x: float(x))
def func(s):
if (len(s) >= 3) and (s[-1] > s[-2] > s[-3]):
return 'High'
elif (len(s) >= 2) and s[-1] > s[-2]:
return 'Medium'
else:
return 'Low'
# Step 1: Get the window indexer
window_indexer = number_series.rolling(5)._get_window_indexer()
start, end = window_indexer.get_window_bounds(num_values=len(number_series))
# Step 2: Apply func
results = [func(number_series.iloc[slice(s, e)]) for s, e in zip(start, end)]
# Step 3: Get results back to a pandas Series
new_series = pd.Series(results, index=number_series.index)
new_series
>>>
2000-01-02 Low
2000-01-09 Low
2000-01-16 Medium
2000-01-23 Low
2000-01-30 Medium
...
2001-10-28 Low
2001-11-04 Medium
2001-11-11 High
2001-11-18 High
2001-11-25 Low
Length: 100, dtype: object
這是使用帶有列表和 pd.Series 構造函數的 boolean 'or' 技巧的另一種方法:
import numpy as np
import pandas as pd
np.random.seed(1)
number_series = pd.Series(np.random.randint(low=1,high=100,size=100),index=[pd.date_range(start='2000-01-01',freq='W',periods=100)])
number_series = number_series.apply(lambda x: float(x))
def func(s):
if s[-1] > s[-2] > s[-3]:
return 'High'
elif s[-1] > s[-2]:
return 'Medium'
else:
return 'Low'
l = []
new_series = number_series.rolling(5).apply(lambda x: l.append(func(x)) or 0)
pd.Series(l, index=number_series.index[:len(l)])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.