1
\$\begingroup\$

I want to perform some calculations over rolling periods of a pandas DataFrame and to abstract all this in a dedicated class. The job of the class below is simply, given backward and forward looking parameters to slice a column, perform some calculations and append the results as new columns of the original DataFrame.

I implemented the following:

import pandas as pd
import numpy as np
import random
random.seed(0)
np.random.seed(0)


class RollingEvaluator:

    def __init__(self, backward_window_size, forward_window_size):
        self.backward_window_size_ = backward_window_size
        self.forward_window_size_ = forward_window_size

    def run(self, data, column_name, func):

        n_rows = data.shape[0]
        output = []
        for i in range(self.backward_window_size_, n_rows-self.forward_window_size_):
            data_slice = data.iloc[(
                i-self.backward_window_size_):(i+1+self.forward_window_size_)]
            res = func(data_slice[column_name].values)
            current_row = {**res,
                           **data.iloc[i]}
            output.append(current_row)

        return pd.DataFrame(output)


if __name__ == "__main__":

    def custom_function(l):
        return {"Percentage (custom)": l[-1] / l[0] - 1}

    data = pd.read_csv("AAPL.csv", nrows=10)
    re = RollingEvaluator(1, 0)

    print(data)
    res = re.run(data, "Adj Close", custom_function)

    res["Percentage (default)"] = res["Adj Close"].pct_change()
    print(res)

Is there a more pythonic / pandas oriented way to do this ?

The data looks like:

Date,High,Low,Open,Close,Volume,Adj Close
2000-01-03,1.0044642686843872,0.9079241156578064,0.9363839030265808,0.9994419813156128,535796800.0,0.8594232797622681
2000-01-04,0.9877232313156128,0.9034598469734192,0.9665178656578064,0.9151785969734192,512377600.0,0.786965012550354
2000-01-05,0.9871651530265808,0.9196428656578064,0.9263392686843872,0.9285714030265808,778321600.0,0.7984815835952759
2000-01-06,0.9553571343421936,0.8482142686843872,0.9475446343421936,0.8482142686843872,767972800.0,0.7293821573257446
2000-01-07,0.9017857313156128,0.8526785969734192,0.8616071343421936,0.8883928656578064,460734400.0,0.7639315724372864
2000-01-10,0.9129464030265808,0.8459821343421936,0.9107142686843872,0.8727678656578064,505064000.0,0.7504956722259521
2000-01-11,0.8872767686843872,0.8080357313156128,0.8565848469734192,0.828125,441548800.0,0.7121071815490723
2000-01-12,0.8526785969734192,0.7723214030265808,0.8482142686843872,0.7784598469734192,976068800.0,0.6694000959396362
2000-01-13,0.8816964030265808,0.8258928656578064,0.8436104655265808,0.8638392686843872,1032684800.0,0.7428181767463684
2000-01-14,0.9129464030265808,0.8872767686843872,0.8928571343421936,0.8967633843421936,390376000.0,0.7711297273635864
\$\endgroup\$
3
  • \$\begingroup\$ Why would you have custom_function at all when pct_change does the exact same thing, only vectorised? \$\endgroup\$
    – Reinderien
    Jan 14, 2022 at 3:13
  • \$\begingroup\$ Well, the custom_function was just an illustration of what I can do, the point of percentage was to make sure that I can reproduce the pct_change function. I actually use this class to fit models on slices of the data \$\endgroup\$
    – RUser4512
    Jan 14, 2022 at 9:11
  • \$\begingroup\$ Ok, but Code Review doesn't operate on illustrations - please show your actual use case \$\endgroup\$
    – Reinderien
    Jan 14, 2022 at 13:20

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.