סדרות עתיות ושיטות חיזוי – הרצאה 2

התקדמות: 0 / 100 נקודות 0%

חלק א': משימות תכנות בפייתון (Python)

1. מפעל רעש (Noise Factory)

15 נק'

מה עושים?
צרו שתי סדרות IID באורך 500:
א. התפלגות אחידה: Uniform(-0.5, 0.5)
ב. התפלגות נורמלית (גאוסית): N(0, 1).
ציירו את הסדרה העתית עבור כל אחת מהן. לאחר מכן, ציירו תרשים פיזור (Lag plot) עבור Lag=1 ועבור Lag=5 לכל סדרה.

על מה לענות?
מה אתם מצפים לראות ב-Lag plot של סדרת IID, והאם התוצאות תואמות לציפייה?

רמז:

השתמשו ב-np.random.uniform וב-np.random.normal. כדי לצייר Lag plot של lag=1, ציירו את x[:-1] מול x[1:]. עבור lag=5, ציירו x[:-5] מול x[5:].

פתרון:

בסדרת IID, אין תלות בין תצפית לתצפית אחרת, לכן ה-Lag plot ייראה כמו "ענן" אקראי של נקודות ללא כיוון או צורה מובהקת (אין קורלציה).

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0) # For reproducibility

N = 500
u_noise = np.random.uniform(-0.5, 0.5, N)
g_noise = np.random.normal(0, 1, N)

def plot_lags(series, title):
    fig, axes = plt.subplots(1, 3, figsize=(15, 4))
    
    # Time series plot
    axes[0].plot(series)
    axes[0].set_title(f"{title} - Time Series")
    axes[0].set_xlabel("Time (t)")
    axes[0].set_ylabel("Value")
    
    # Lag 1 plot
    axes[1].scatter(series[:-1], series[1:], alpha=0.5)
    axes[1].set_title("Lag 1 Plot")
    axes[1].set_xlabel("x[t]")
    axes[1].set_ylabel("x[t+1]")
    
    # Lag 5 plot
    axes[2].scatter(series[:-5], series[5:], alpha=0.5)
    axes[2].set_title("Lag 5 Plot")
    axes[2].set_xlabel("x[t]")
    axes[2].set_ylabel("x[t+5]")
    
    plt.tight_layout()
    plt.show()

plot_lags(u_noise, "Uniform IID")
plot_lags(g_noise, "Gaussian IID")

Take-Home Message:

בסדרות IID (ובפרט רעש לבן), העבר לא מלמד כלום על העתיד. ויזואלית, תרשימי Lag יציגו פיזור אקראי לחלוטין (ענן כדורי/ריבועי) לכל פיגור (Lag) שנבחר.

2. בלש/ית הילוך מקרי (Random Walk Detective)

15 נק'

מה עושים?
צרו סדרת הילוך מקרי (Random Walk) באורך N=500 על ידי סכום מצטבר (Cumulative Sum) של רעש גאוסי תקני.
ציירו שתי סדרות: 1. הסדרה המקורית. 2. ההפרש הראשון (First Difference: xt - xt-1).

על מה לענות?
השוו ויזואלית בין שני הגרפים. איזו סדרה נראית "יותר סטציונרית" ולמה?

רמז:

השתמשו בפונקציה np.cumsum() כדי ליצור את ההילוך המקרי מתוך הרעש. כדי לחשב את ההפרש הראשון, השתמשו ב-np.diff().

פתרון:

הסדרה של ההפרש הראשון היא בדיוק הרעש הגאוסי המקורי (שהוא IID ולכן סטציונרי חלש). הילוך מקרי אינו סטציונרי כי התוחלת/השונות שלו תלויה בזמן ומשוטטת ללא חזרה לממוצע קבוע.

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0) # For reproducibility

N = 500
noise = np.random.normal(0, 1, N)
random_walk = np.cumsum(noise)
first_difference = np.diff(random_walk)

fig, axes = plt.subplots(2, 1, figsize=(10, 6))

axes[0].plot(random_walk, color='blue')
axes[0].set_title("Random Walk (Non-Stationary)")
axes[0].set_ylabel("Value")

axes[1].plot(first_difference, color='green')
axes[1].set_title("First Difference (Stationary / White Noise)")
axes[1].set_ylabel("Difference")
axes[1].set_xlabel("Time (t)")

plt.tight_layout()
plt.show()

Take-Home Message:

גזירה/הפרשה (Differencing) היא פעולה מתמטית עוצמתית שיכולה להפוך סדרה לא-סטציונרית (כמו הילוך מקרי) לסדרה סטציונרית (כמו רעש לבן). זו טכניקת בסיס במודלי ARIMA.

3. בונים ACF (Building ACF)

15 נק'

מה עושים?
ממשו מאפס (ללא ספריות מוכנות של ACF) פונקציה acf(x, H) המחזירה את האוטוקורלציה ρ(h) מ-h=0 עד h=H.
הפעילו את הפונקציה (עבור H=20) פעם אחת על סדרת IID ופעם אחת על Random Walk. הציגו את התוצאות בגרף עמודות (Bar plot).

על מה לענות?
מה ההבדל הניכר בצורת ה-ACF בין שתי הסדרות?

רמז:

שלבים לאלגוריתם: 1. הפחיתו את ממוצע הסדרה (Center). 2. חשבו את γ(0) (שונות המדגם). 3. בלולאה עבור h, חשבו את סכום המכפלות xt · xt-h חלק ב-N. 4. חלקו כל γ(h) ב-γ(0) כדי לקבל את ρ(h).

פתרון:

ה-ACF של סדרת IID נופל לאפס מיד אחרי Lag 0 (קורלציה עצמית טריוויאלית של 1). לעומת זאת, ה-ACF של הילוך מקרי דועך לאט מאוד, כי כל ערך קרוב מאוד לערך הקודם שלו (זכרון ארוך).

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0) # For reproducibility

def acf(x, H):
    N = len(x)
    x_mean = np.mean(x)
    x_centered = x - x_mean
    
    # Calculate gamma_0 (variance)
    gamma_0 = np.sum(x_centered**2) / N
    
    rhos = []
    for h in range(H + 1):
        # Calculate gamma_h
        gamma_h = np.sum(x_centered[h:] * x_centered[:N-h]) / N
        rho_h = gamma_h / gamma_0
        rhos.append(rho_h)
        
    return np.array(rhos)

# Data from previous tasks
N = 500
iid_noise = np.random.normal(0, 1, N)
rw = np.cumsum(iid_noise)

H = 20
acf_iid = acf(iid_noise, H)
acf_rw = acf(rw, H)

# Plotting
lags = np.arange(H + 1)
fig, axes = plt.subplots(1, 2, figsize=(12, 4))

axes[0].bar(lags, acf_iid, color='purple')
axes[0].set_title("ACF of IID Noise")
axes[0].set_xlabel("Lag (h)")
axes[0].set_ylabel("ACF")

axes[1].bar(lags, acf_rw, color='orange')
axes[1].set_title("ACF of Random Walk")
axes[1].set_xlabel("Lag (h)")

plt.tight_layout()
plt.show()

Take-Home Message:

צורת ה-ACF היא כלי האבחון המרכזי שלנו. דעיכה איטית מאוד (Slow decay) מעידה באופן מובהק על חוסר סטציונריות (כמו מגמה או הילוך מקרי). נפילה חדה מעידה על רעש או סדרה ללא זכרון.

4. משימת הסרת מגמה (Detrending)

15 נק'

מה עושים?
צרו דאטה סינתטי של "מכירות שמפו" (100 תצפיות): שלבו מגמה ליניארית (למשל 0.5 · t), רעש גאוסי, וקפיצה פתאומית ב-t=50.
1. התאימו קו מגמה (באמצעות numpy.polyfit).
2. הציגו גרף של הסדרה המקורית עם קו המגמה.
3. חשבו והציגו את השאריות (Detrended series - הסדרה פחות המגמה).

בונוס מתקדם: השוו בין שאריות אלו לבין ביצוע Differencing פשוט על הסדרה המקורית.

רמז:

צרו t = np.arange(100). המגמה: trend = 0.5 * t. הקפיצה: הוסיפו למערך מ-index 50 מספר קבוע (למשל 20). להתאמה ליניארית: p = np.polyfit(t, data, 1) ואז trend_est = np.polyval(p, t).

פתרון:

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(0) # For reproducibility

t = np.arange(100)
trend = 0.5 * t
noise = np.random.normal(0, 3, 100)
jumps = np.zeros(100)
jumps[50:] = 20  # Sudden marketing success!

data = trend + noise + jumps

# Fit linear trend
coeffs = np.polyfit(t, data, 1)
trend_est = np.polyval(coeffs, t)

# Detrending
detrended = data - trend_est

fig, axes = plt.subplots(2, 1, figsize=(10, 8))

axes[0].plot(t, data, label="Original Data (Sales)")
axes[0].plot(t, trend_est, color='red', linestyle='--', label="Fitted Trend")
axes[0].set_title("Time Series with Linear Trend Fit")
axes[0].legend()

axes[1].plot(t, detrended, color='green')
axes[1].axhline(0, color='black', linestyle='--')
axes[1].set_title("Detrended Series (Residuals)")

plt.tight_layout()
plt.show()

Take-Home Message:

הסרת מגמה דטרמיניסטית (Detrending) משאירה אותנו עם התנודות (והקפיצות) סביב הקו. חשוב לשים לב שמודל ליניארי פשוט לא תמיד מטפל טוב בקפיצות פתאומיות (Shocks), ואפשר לראות זאת בדפוס שנוצר בשאריות.

5. קזינו – הישרדות (Gambler's Ruin) - רשות

10 נק'

מה עושים?
בצעו סימולציה למשחק בו מהמר מתחיל עם 50 שקלים. בכל צעד יש הסתברות p להרוויח שקל ו-1-p להפסיד שקל. המשחק נעצר אם מגיעים ל-0 (פשיטת רגל) או ל-100 (יעד).
הריצו 1000 סימולציות עבור p=0.49 ועבור p=0.51.

על מה לענות?
מהו אחוז הפעמים בהם המהמר הגיע ליעד (100) בכל אחד מהמקרים? האם שינוי קטן ב-p משפיע הרבה?

רמז:

צרו לולאת for עבור הסימולציות. בכל סימולציה, לולאת while balance > 0 and balance < 100 שתעדכן את היתרה (balance += 1 if np.random.rand() < p else -1).

פתרון:

שינוי קטן בהסתברות גורר שינוי אדיר בסיכויי ההישרדות. עם פחות מ-50% סיכוי, התוחלת יורדת והסיכוי להגיע ליעד קטן משמעותית.

import numpy as np

np.random.seed(0) # For reproducibility

def simulate_casino(p, num_sims=1000):
    success_count = 0
    for _ in range(num_sims):
        balance = 50
        while 0 < balance < 100:
            if np.random.rand() < p:
                balance += 1
            else:
                balance -= 1
        if balance == 100:
            success_count += 1
    return success_count / num_sims * 100

print(f"Win probability (p=0.49): {simulate_casino(0.49)}%")
print(f"Win probability (p=0.51): {simulate_casino(0.51)}%")

Take-Home Message:

תהליכים סטוכסטיים מצטברים (כמו הילוך מקרי מוטה) רגישים מאוד לתוחלת הצעד הבודד. "Drift" קטן (מגמה) מצטבר עם הזמן ומכריע את גורל התהליך.

חלק ב': משימות תיאוריה וחישוב

1. תכונות רעש IID

5 נק'

הניחו סדרה עתית Xt שהיא IID (משתנים בלתי תלויים ושווי פילוג) עם תוחלת 0 ושונות σ2.
חשבו והסבירו במילים: מהו פונקציית האוטוקווריאנס γ(h) ופונקציית האוטוקורלציה ρ(h) עבור h=0 ועבור h ≠ 0?

רמז:

זכרו שמשתנים בלתי תלויים מקיימים קווריאנס אפס ביניהם. עבור h=0, האוטוקווריאנס הוא בעצם השונות. האוטוקורלציה היא נרמול של האוטוקווריאנס ב-γ(0).

פתרון:

- עבור h=0: γ(0) = Cov(Xt, Xt) = Var(Xt) = σ2. האוטוקורלציה היא ρ(0) = γ(0)/γ(0) = 1.
- עבור h ≠ 0: מכיוון שהמשתנים בלתי תלויים, השונות המשותפת היא 0, ולכן γ(h) = 0. בהתאמה, ρ(h) = 0.

Take-Home Message:

לרעש "טהור" (IID) אין שום זכרון. התצפית של היום לא מלמדת אותנו כלום על התצפית של מחר. גרף ה-ACF שלו הוא "ספייק" יחיד ב-Lag 0 ואפס בכל שאר הפיגורים.

2. IID מול White Noise

5 נק'

בהרצאה למדנו ש-
IID ⇒ White Noise
אך ההפך אינו נכון:
White Noise ⇏ IID
הסבירו טענה זו במילים פשוטות וקצרות.

רמז:

White noise דורש רק חוסר קורלציה ליניארית (Covariance = 0) ותוחלת/שונות קבועה. IID דורש חוסר תלות מוחלט והתפלגות זהה לחלוטין. האם חוסר קורלציה אומר חוסר תלות?

פתרון:

סדרה שהיא IID היא בלתי תלויה לחלוטין (ולכן הקווריאנס הוא 0) ובעלת התפלגות זהה, לכן היא עונה על תנאי White Noise (תוחלת ושונות קבועה, קורלציה 0).
מנגד, White Noise רק דורש חוסר קורלציה ליניארית (אך יכולה להיות תלות לא-ליניארית בין התצפיות) ויכול להיות מורכב מהתפלגויות שונות בעלות אותה שונות בדיוק (לכן אינו מחייב Identically Distributed ואינו מחייב Independent).

Take-Home Message:

קורלציה אפס אינה מעידה בהכרח על חוסר תלות (למעט בהתפלגות גאוסית נורמלית משותפת). White Noise הוא תנאי "חלש" יותר מ-IID.

3. חוסר סטציונריות בהילוך מקרי

5 נק'

נתון הילוך מקרי: St = St-1 + et כאשר et הוא רעש לבן עם שונות σ2 (ונניח S0 = 0).
למה הסדרה לא סטציונרית? מה קורה לשונות Var(St) כאשר הזמן t גדל?

רמז:

רשמו את St כסכום מפורש של הרעשים: St = e1 + e2 + ... + et. מכיוון שהרעשים חסרי קורלציה, מה קורה כשסוכמים את השונויות שלהם?

פתרון:

ניתן לרשום St = Σi=1t ei.
השונות של סכום משתנים חסרי קורלציה היא סכום השונויות: Var(St) = Σi=1t Var(ei) = t · σ2.
כיוון שהשונות תלויה בזמן t וגדלה לאינסוף, הסדרה אינה סטציונרית (שהרי סטציונריות דורשת שונות קבועה שאינה תלויה בזמן).

Take-Home Message:

בהילוך מקרי (Random Walk) השגיאות מצטברות. ככל שעובר הזמן, אי-הודאות לגבי המיקום שלנו גדלה באופן ליניארי. אי אפשר לבצע חיזוי בטוח לטווח ארוך לסדרה כזו.

חלק ג': שאלות רב-ברירה (MCQ)

שאלה 1: הגדרת סטציונריות

5 נק'

מה מהבאים אינו מהווה תנאי להגדרת סדרה עתית כ"סטציונרית חלשה" (Weakly Stationary)?

רמז:

שימו לב להבדל בין "סטציונריות חלשה" (Weak) שמתעסקת רק במומנטים ראשונים ושניים, לבין "סטציונריות חזקה" (Strict).

פתרון:

התשובה הנכונה היא ב'. התנאי של התפלגות משותפת זהה מתייחס ל"סטציונריות חזקה" (Strict Stationarity). סטציונריות חלשה מסתפקת בתוחלת, שונות, ואוטוקווריאנס קבועים.

Take-Home Message:

בקורס שלנו (וברוב המקרים המעשיים), כשאומרים "סטציונריות" מתכוונים לסטציונריות חלשה – טיפול בתוחלת, שונות וקווריאנס בלבד, שזה מספיק למודלים ליניאריים.

שאלה 2: IID ו-White Noise

5 נק'

איזו מהאמירות הבאות נכונה בהקשר של סדרות עתיות?

רמז:

היזכרו בשאלת התיאוריה ממקודם. מיהו התנאי ה"מחמיר" יותר שגורר את התנאי ה"מקל" יותר?

פתרון:

התשובה הנכונה היא ג'. IID דורש חוסר תלות מוחלט וזהות בהתפלגות, מה שגורר מיידית קווריאנס 0 ותוחלת/שונות קבועות – אלו בדיוק הדרישות ל-White Noise. ההפך אינו נכון (שולל את א' ו-ד').

Take-Home Message:

White Noise מרמז רק על העדר קורלציה קווית (Linear predictability). עדיין ייתכנו תלויות לא-ליניאריות מורכבות שמודלים מתקדמים (כמו GARCH) יכולים ללמוד.

שאלה 3: זיהוי ע"פ ACF

5 נק'

חוקרת שרטטה גרף ACF לסדרה עתית כלשהי וראתה ש-ρ(1) ≈ 0.98, ρ(2) ≈ 0.95, ρ(3) ≈ 0.93, והדעיכה נמשכת לאט מאוד. איזו מהסדרות הבאות הכי סביר שהיא חוקרת?

רמז:

איזו סדרה מאופיינת ב"זכרון ארוך" מאוד ובכך שכל תצפית קרובה מאוד לתצפית שלפניה?

פתרון:

התשובה הנכונה היא ב'. הילוך מקרי (או סדרה עם מגמה חזקה שלא טופלה) מאופיין ב-ACF שדועך באטיות רבה, כי xt ו-xt-k עדיין דומים מאוד. לשאר האופציות אמור להיות ACF שנופל לאזור האפס מיד אחרי Lag 0.

Take-Home Message:

ACF שדועך לאט הוא סימן אזהרה קלאסי לחוסר סטציונריות בממוצע (לרוב, הילוך מקרי או קיום מגמה). הצעד הבא יהיה בדרך כלל לבצע Differencing ולבדוק שוב.