簡體   English   中英

如何應用 function 而不將數值返回給 pandas 滾動 Window?

[英]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 的函數通常用於聚合數據(例如sumcount等)。

但是,您可以將滾動 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需要以不同方式處理第一項,否則索引將超出范圍。

一種方法是:

  1. 獲取WindowIndexerrolling()方法。
  2. 應用func返回一個字符串並將結果存儲為一個列表
  3. 將結果轉換回系列。
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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM