microprediction / timemachines

Predict time-series with one line of code.
https://www.microprediction.com/blog/popular-timeseries-packages
MIT License
395 stars 51 forks source link

(Documentation) Note on mutability of state passed to skater #51

Closed jmrichardson closed 2 years ago

jmrichardson commented 2 years ago

Hi, I am not sure why predictions are not consistent. I ran the following:

from timemachines.skaters.simple.thinking import thinking_slow_and_fast
import numpy as np
y = np.cumsum(np.random.randn(1000))
s = {}
x = list()
for yi in y:
    xi, x_std, s = thinking_slow_and_fast(y=yi, s=s, k=3)

Then, wanted to verify predictions using state variable "s" and the same y using the following simple loop code:

for i in range(0, 10):
    x_new, x_std_new, s_new = thinking_slow_and_fast(y=yi, s=s, k=3)
    print(x_new)

I was expecting to see the same predictions for each loop given that I am using the same yi and s variables (ie they haven't changed). But got different predictions:

[-1.0763699106402587, -1.0762804377907655, -1.0761909649412726]
[-1.0848981995166735, -1.08481700115846, -1.0847358028002463]
[-1.0930360369514371, -1.0929623509702981, -1.092888664989159]
[-1.1007996104897944, -1.1007327443804924, -1.1006658782711902]
[-1.1082046070063825, -1.108143931623388, -1.108083256240394]
[-1.115266209255507, -1.1152111531047109, -1.1151560969539147]
[-1.1219990952068173, -1.1219491392351129, -1.1218991832634084]
[-1.128417439789925, -1.1283721126322885, -1.1283267854746521]
[-1.1345349187114502, -1.1344937923578347, -1.1344526660042193]
[-1.1403647140440984, -1.1403273998910912, -1.140290085738084]

Shouldn't using the same state "s" and the same "y" give us the same prediction?

microprediction commented 2 years ago

Can you try it making a deep copy of s? Python is pass by reference to object, and the state is a mutable dictionary.

microprediction commented 2 years ago

import copy s1 = copy.deepcopy(s) x_new, x_std_new, s_new = thinking_slow_and_fast(y=yi, s=s, k=3) x_prime, x_std_prime, s_newish = thinking_slow_and_fast(y=yi, s=s1, k=3)

jmrichardson commented 2 years ago

Yes, it does work. I also noticed that you must do a deep copy prior to each. This code snippet works:

for i in range(0, 10):
    s1 = copy.deepcopy(s)
    x_prime, x_std_prime, s_newish = thinking_slow_and_fast(y=yi, s=s1, k=3)
    print(x_prime)
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.17208556479699, 75.16299642564626, 75.15390728649554]

This does not:

s1 = copy.deepcopy(s)
for i in range(0, 10):
    x_prime, x_std_prime, s_newish = thinking_slow_and_fast(y=yi, s=s1, k=3)
    print(x_prime)
[75.17208556479699, 75.16299642564626, 75.15390728649554]
[75.30241054501099, 75.29423761273385, 75.28606468045672]
[75.41158896477022, 75.4043743792711, 75.39715979377199]
[75.5032450322357, 75.49692063825306, 75.49059624427044]
[75.58093886351598, 75.5753962430606, 75.56985362260521]
[75.6477567127686, 75.64288262630443, 75.63800853984024]
[75.70620517294356, 75.70189703172484, 75.69758889050611]
[75.75824066561717, 75.7544115365491, 75.75058240748105]
[75.80534998939467, 75.80192861818831, 75.79850724698197]
[75.84864166294314, 75.84557048199703, 75.84249930105092

Excellent, so it appears that I can save the state of the model by doing a deep copy. Thanks for the help!

microprediction commented 2 years ago

That's right, because the second way you are actually modifying s each time. Gotta love Python.

microprediction commented 2 years ago

There should be a comment in the docs about this. Let's leave it open until then.

jmrichardson commented 2 years ago

Awesome, loving this package! Will try to implement my use case and let you know how it goes on slack. Thanks again!