محلل السلاسل الزمنية
بيانات درجات الحرارة، حمول الخوادم، حركة مرور الويب ، كل شيء حقيقي يصل كتسلسل عبر الوقت، ويقضي المحللون أيامهم في فصل ما تفعله السلسلة فعلًا إلى ثلاثة إشارات: الانحراف البطيء (الاتجاه)، والإيقاع المتكرر (الموسمية)، والضوضاء المتبقية (الباقي). يبني هذا المشروع ذلك التفكيك من الصفر بـ pandas، ثم يستخدم الأجزاء: يتنبأ بال الأسبوع القادم بنموذج اتجاه-زائد-موسمية، ويُقيّم التنبؤ ضد حجز حقيقي، ويُعلّم التواريخ التي لا تتناسب مع النمط، ويربط سلسلتين في رسم يمكنك حفظها فعلًا.
يُفترض أساسيات بايثون ومعرفة pandas Series ، لا شيء من تحليل البيانات beyond ذلك. هذا اختياري وغير مُقيَّم؛ راجع المشاريع الواقعية للقائمة الكاملة المتنامية.
🎯 ما ستفعله
- ولّد سلسلة ضيوف مقهى واقعية مع فهرس datetime.
- فكّكها إلى مكونات اتجاه وموسمية وباقي يدويًا.
- تنبأ بالأسبوع القادم بنموذج اتجاه-زائد-موسمية.
- اختبر التنبؤ وقدّر خطأه على أيام محجوزة.
- اكتشف الشذوذ وارسم سلسلتين مرتبطتين في PNG.
أين تُشغّل هذا
محليًا باستخدام uv هو المسار الأساسي. pandas و NumPy تُثبّت بوضوح، وخلفية matplotlib غير التفاعلية Agg (الخطوة 5) تُ render الرسوم حتى بدون واجهة، وملفات الرسوم تصل فعلًا إلى مجلد المشروع.
Google Colab و Kaggle Notebooks و Binder تشغّل كل الخطوات بشكل متماثل ، المكتبات الثلاث مُثبّتة هناك. التنبيه الصادق كالعادة لمشاريع العرض: نظام ملفات الدفتر مؤقت، فالـ PNG المحفوظ وأي CSV قد لا ي survives إعادة تشغيل الجلسة. تعامل معها كمسارات جرّب وانتقل إلى uv المحلي حين تحتاج الملفات أن تبقى.
الإعداد
أنشئ المشروع وثبّت المكتبات الثلاث التي يُبنى عليها المحلل.
uv init time-series-analyzer
cd time-series-analyzer
uv add pandas numpy matplotlib
uv run python -c "import pandas, numpy, matplotlib; print('ok')"
pandas يتحكم في فهرس datetime، وإعادة العينات، و groupby المستخدمة للموسمية؛ numpy يوفر العشوائية المُخمّلة و polyfit للانحدار في الخطوة 3؛ matplotlib يرسم الناتج النهائي. تثبيت الثلاثة مسبقًا يُبقي كل خطوة لاحقة مُركّزة على أفكار السلسلة الزمنية.
✅ قائمة التحقق
- ✅
uv add pandas numpy matplotlibاكتمل وفحص الاستيراد طبعok. - ✅ مشروع
time-series-analyzer/جديد موجود بـpyproject.toml.
الخطوة 1: ابنِ وشكّل سلسلة بفهرس datetime
يعتمد تحليل السلاسل الزمنية أو يموت على الفهرس: كل نافذة، ويوم عمل، وتأخير في المُستقبل يفترض أن كل صف يعرف متى هو. تولّد هذه الخطوة سلسلة يومية واقعية وتعطيها DatetimeIndex سليمًا.
1.1 ولّد سلسلة ضيوف المقهى
👟 تلميح البداية : ابنِ السلسلة من ثلاثة أجزاء مسمّاة بعناية ، اتجاه خطي، وseasonality جيبية أسبوعية مفتّحة بـ dayofweek، وضوضاء مُخمّلة ، حتى يكون لدى التفكيك في الخطوة 2 بنية حقيقية للاسترجاع.
# series.py
import numpy as np
import pandas as pd
def make_cafe_guests(days: int = 365, seed: int = 42) -> pd.Series:
rng = np.random.default_rng(seed)
idx = pd.date_range("2025-01-01", periods=days, freq="D")
trend = np.linspace(100, 140, days)
weekly = 8 * np.sin(2 * np.pi * idx.dayofweek / 7)
noise = rng.normal(0, 5, days)
return pd.Series(trend + weekly + noise, index=idx, name="guests")
s = make_cafe_guests()
print(s.head(3))
print("index type:", type(s.index).__name__, "| dtype:", s.dtype)
idx.dayofweek هو أسلوب pandas الجوهري: يُعطي 0–6 (الإثنين–الأحد) لكل صف، وضربه في 2π/7 يضبط طور الجيب حتى تتبدل أيام العمل بين مرتفع ومنخفض ، أسبوعية حقيقية، لا اهتزاز عشوائي. np.random.default_rng(seed) هو واجهة بذرة NumPy الحديثة؛ البذرة الثابتة تجعل الضوضاء قابلة للتكرار. إرجاع Series بـ index=idx, name="guests" يعني أن كل دالة لاحقة (نوافذ متحركة، groupby بيوم العمل، الرسم) تحصل على الطوابع الزمنية مجانًا.
🎯 الناتج المتوقع : ثلاثة صفوف مؤرخة (تبدأ 2025-01-01)، قيم قريبة من 100، مع index type: DatetimeIndex | dtype: float64.
🩹 إذا لم يعمل : إذا طبع نوع الفهرس RangeIndex أو Index، فتعيين pd.Series(..., index=idx) مفقود والمعادلات اللاحقة بلا شيء لتُعلّق عليه. إذا كانت القيم قريبة من 1000 بدلاً من ~100–150، فقد قُلّب trend و weekly. إذا لم تكن السلسلة قابلة للتكرار عبر التشغيلات، فمعامل seed لا يصل إلى default_rng.
1.2 تحقق من شكل السلسلة
✅ قائمة التحقق
- ✅
sلديهاDatetimeIndexيغطي 365 يومًا بتردد يومي. - ✅
s.index.dayofweekيعمل 0–6 بشكل متكرر وs.dtypeعدد عشري. - ✅ البذرة نفسها تُنتج السلسلة المتطابقة في الاستدعاء الثاني.
🤔 سؤال (أسئلة) سقراطي(ة)
- بُنيت الموسمية من
dayofweek، فتتكرر أسبوعيًا. كيف سيختلف النموذج إذا استخدمت السلسلةidx.dayofyearبدلاً من ذلك، وأيهم ستثق به للأنماط السنوية (العطلات)؟ - القيم أعداد عشريّة بدقة ضمنية، لكن عدادات المقاهي الحقيقية أعداد صحيحة. متى يهم الحفاظ على الضوضاء العشرية للتفكيك، ومتى تُround إلى أعداد ضيوف صحيحة أولًا؟
الخطوة 2: فكّك إلى اتجاه وموسمية وباقي
الاتجاه هو “ما تفعله السلسلة ببطء”؛ الموسمية هي “الإيقاع المتكرر”؛ والباقي هو “كل ما سواها”. تحسب هذه الخطوة الثلاثة مباشرة ، متوسط متحرك للاتجاه، ومتوسطات أيام العمل للموسمية، وما يتبقى كباقي.
2.1 اكتب التفكيك الجمعي
👟 تلميح البداية : ترتيب حارس المتحف مهم ، الاتجاه أولًا (متوسط متحرك)، ثم series - trend للباقي المُزال من الاتجاه، ثم متوسطات أيام العمل لذلك الباقي كموسمية، ثم detrended - seasonal كباقي.
# series.py (continued)
def decompose(series: pd.Series, window: int = 14) -> tuple[pd.Series, pd.Series, pd.Series]:
trend = series.rolling(window, center=True).mean()
detrended = series - trend
seasonal = detrended.groupby(series.index.dayofweek).transform("mean")
residual = detrended - seasonal
return trend, seasonal, residual
trend, seasonal, residual = decompose(s)
print(seasonal.groupby(seasonal.index.dayofweek).first().to_string())
print("residual std: {:.2f}".format(residual.std()))
المتوسط المتحرك بـ center=True هو مُقدّر الاتجاه: كل نقطة تصبح متوسط جيرانها ±7 أيام، فيُophage الدورة الأسبوعية مع الحفاظ على الانحراف البطيء. طرحه (detrended) يُبقي الإيقاع الحقيقي مع الضوضاء، و groupby(dayofweek).transform("mean") هي حيلة الموسمية الأنيقة ، تحسب المتوسط لكل يوم عمل وتبثه عائدة لكل صف بنفس يوم العمل، فيكون seasonal بنفس طول series. الباقي هو كل ما نجا من الطرفيين، وانحرافه المعياري هو إشارة صحتك الأولى: يجب أن يكون أقل بكثير من std السلسلة الخام.
🎯 الناتج المتوقع : سبعة صفوف (واحد لكل يوم عمل) ل Offset الموسمية، مع residual std حوالي 4-6 ، أقل بوضوح من انتشار السلسلة الخام ~16.
🩹 إذا لم يعمل : إذا كان seasonal يحتوي صفوف NaN على الحواف، فنافذة center=True تترك أول/آخر 7 أيام غير مُعرّفة ، متوقع، اصفِ بـ .dropna(). إذا كان انحراف الباقي قريبًا من الصفر، فمصطلح الضوضاء لم يصل أبدًا إلى المولّد. إذا تباينت Offsets يوم العمل بشكل كبير بين صفوف يوم العمل نفسه، فقد استُبدل transform بـ apply ، transform هو ما يبث لكل صف.
2.2 تحقق من التفكيك
✅ قائمة التحقق
- ✅
trend + seasonal + residualيعيد بناء السلسلة الأصلية (ضمن خطأ العشري). - ✅ كل أيام السبعة لها قيمة موسمية واحدة بالضبط.
- ✅ انحراف الباقي المعياري أصغر من
std()السلسلة.
🤔 سؤال (أسئلة) سقراطي(ة)
- المتوسط المتحرك مرشح عالي المرور على السلسلة. ماذا يحدث لذروة حقيقية من مرة واحدة في
residualالخطوة 4 إذا كانت نافذة الاتجاه ضخمة (90 يومًا) بدلاً من 14 ، ومتى سيكون ذلك مفيدًا أو ضارًا؟ - القيمة الموسمية متوسط لكل يوم عمل، فتعامل الأيام الخمسة الإثنين في الشهر كمتطابقة. ماذا سيتغير إذا تنوّعت الموسمية نفسها عبر العام (الشتاء مقابل الصيف)؟
الخطوة 3: تنبأ بالاتجاه والموسمية معًا
يُvenna التفكيك هنا: بدلاً من تجهيز نموذج واحد فوق الضوضاء الخام، تمد الاتجاه المُتعلّم وتُعيد الإيقاع المُتعلّم. تتنبأ هذه الخطوة بالسبعة أيام القادمة من المكونين النظيفين.
3.1 جهّز خطًا على الاتجاه وأعد الموسمية
👟 تلميح البداية : جهّز np.polyfit من الدرجة الأولى على آخر 30 قيمة حقيقية، مدّ ذلك الخط 30→37 يومًا للأمام، ثم أضف Offset الموسمية لكل يوم عمل مقبل.
# series.py (continued)
def forecast_next(series: pd.Series, seasonal: pd.Series,
horizon: int = 7, window: int = 30) -> pd.Series:
X = np.arange(window)
y = series.tail(window).values
slope, intercept = np.polyfit(X, y, 1)
future = pd.date_range(series.index[-1] + pd.Timedelta(days=1),
periods=horizon, freq="D")
linear = intercept + slope * np.arange(window, window + horizon)
weekly = seasonal[future.dayofweek].values
return pd.Series(linear + weekly, index=future, name="forecast")
fc = forecast_next(s, seasonal)
print(fc.round(1).to_string())
np.polyfit(X, y, 1) يجد الخط المستقيم الأفضل عبر آخر window قيم حقيقية ، يحصل على الميل ويعيّن التقاطع. التنبؤ بعدها حسابي: مدّ ذلك الخط إلى مؤشرات window … window+horizon (مواقع المحور X بعد نافذة التدريب)، وأضف seasonal[future.dayofweek] حتى يركب إيقاع كل يوم الأسبوع فوق الخط. فعل الاتجاه والإيقاع بشكل منفصل ، بدلاً من التنبؤ بالقيم الضوضائية الخام بنموذج واحد ، هو جوهر الخطوة 3.
🎯 الناتج المتوقع : سبعة قيم مؤرخة، تقريبًا 140–160 و ليس صعودًا مستقيمًا ، أيام العمل تركب الجيب الأسبوعي بوضوح.
🩹 إذا لم يعمل : إذا كان التنبؤ ثابتًا، ف np.polyfit أعاد ميلًا ~صفر لأن window قصير جدًا أو y ليس الذيل. إذا كان التنبؤ ضوضاء خشنة، ف weekly لم يُضَف وبقي الخط فقط. إذا وقعت التواريخ قبل نهاية السلسلة، ف pd.Timedelta(days=1) offset مفقود.
3.2 تحقق من التنبؤ
✅ قائمة التحقق
- ✅ التنبؤ يغطي بالضبط السبعة أيام بعد تاريخ السلسلة الأخير.
- ✅ قيم التنبؤ تتبع الإيقاع الأسبوعي (قمم/وديان لكل يوم عمل)، لا خطًا مستقيمًا.
- ✅ مدّ الأفق إلى 14 يومًا لا يزال يقع بعد استمرار معقول.
🤔 سؤال (أسئلة) سقراطي(ة)
- تجهيز خط مستقيم يفترض معدل نمو ثابت. ما الشكل الذي سيأخذه التنبؤ إذا كان الاتجاه الحقيقي يتسارع، وأين يفشل افتراض الخط المستقيم بوضوح أكثر على بيانات حقيقية؟
- يستخدم التنبؤ ميل آخر 30 نقطة. كيف سيتغير تنبؤ الأسبوع القادم إذا جهّزت الخط على مكون الاتجاه لكامل العام ، وأيختيار يبدأ أكثر متانة، ولماذا؟
الخطوة 4: اختبر التنبؤ وقدّر الخطأ
تنبؤ لا يمكنك تقييمه هو تخمين. إعادة التجهيز يُعيد تجهيز النموذج على البيانات قبل أسبوع محجوز ومقارن توقعاته بالقيم التي أخذها ذلك الأسبوع فعلًا ، الطريقة الصادقة لمعرفة جودة نموذجك قبل أن تثق به للأمام.
4.1 قيّم التنبؤ ضد الحجز
👟 تلميح البداية : كرّر تنبؤ الخطوة 3 باستخدام series.iloc[:-horizon] للتدريب فقط، ثم احسب الخطأ المطلق المتوسط ضد الأسبوع المحجوز الأخير.
# series.py (continued)
def backtest(series: pd.Series, seasonal: pd.Series,
horizon: int = 7, window: int = 30) -> float:
train = series.iloc[:-horizon]
fc = forecast_next(train, seasonal, horizon=horizon, window=window)
actual = series.iloc[-horizon:]
mae = float((fc - actual).abs().mean())
return mae
print("MAE on held-out week: {:.2f} guests".format(backtest(s, seasonal)))
series.iloc[:-horizon] يقصّ الأسبوع الأخير ، النموذج لا يمكنه رؤية تلك الأيام حرفياً ، و forecast_next يعمل على ما يتبقى، فيكون المقارنة fc - actual اختبارًا خارج العينة حقيقيًا. الإبلاغ عن خطأ مطلق متوسط (abs().mean()) يُبقي الوحدات مقروءة: “بفارق ~4 ضيوف”، لا رقم مربع لا يشعر به أحد. المكون الموسمي يُمرّر دون تغيير: الاختصار الصادق هو أن الإيقاع تعلم من السلسلة الكاملة، بينما الاتجاه أُعيد تجهيزه على البيانات المقطوعة ، توثيق ممكن التحسين.
🎯 الناتج المتوقع : MAE في خانة واحدة (تقريبًا 3–6 ضيوف)، أقل باستمرار بكثير من تخمين بسيط كالمتوسط العام.
🩹 إذا لم يعمل : إذا ارتفع MAE إلى 20+، فالتنبؤ لا يزال يشمل الموسمية القادمة من السلسلة الكاملة لكن تجهيز الاتجاه يُحسب على إطار فارغ ، تحقق من أن train ليس فارغًا. إذا تباعدت fc و actual، ف forecast_next يُنتج تواريخًا تتجاوز train.index[-horizon] ، تأكد من offset days=1. إذا ترنّح التقييم بين التشغيلات، ف seasonal جاء من سلسلة ببذرة مختلفة.
4.2 تحقق من اختبار التقييم
✅ قائمة التحقق
- ✅
backtestيُبلّغ عن عدد عشري واحد بوحدة ضيوف. - ✅ مجموعة التدريب تنتهي قبل بداية الأسبوع المحجوز.
- ✅ إعادة التشغيل بـ
horizon=14تُنتج خطأ أكبر (أو مساوٍ) من 7.
🤔 سؤال (أسئلة) socrates)
- MAE يتعامل مع التنبؤ الزائد والنقصان بالتساوي. ماذا سيُبرز متوسط مربع الخطأ (RMSE) بدلاً من ذلك، ولماذا مقهى بها ذروات عطلات ضخمة تفضّله رغم أنه أقل بديهية؟
- أُعيد تجهيز الاتجاه على
trainلكن الموسمية تسرّبت من السلسلة الكاملة. في أي سير عمل حقيقي هذا التسرّب مقبول، وكيف ستُغلقه بالكامل إذا طلب العميل تقييمًا صارمًا؟
الخطوة 5: اكتشف الشذوذ وارسم الزوج
خطوتان ختاميتان تحوّلان المحلل إلى أداة جاهزة: علّم التواريخ التي لم تتناسب الواقع مع النموذج (مواكب كبيرة)، وارسم السلسلة مقابل زميل مرتبط ، محفوظ كملف يمكنك مشاركته.
5.1 علّم الشذوذ وارسم رسم الارتباط
👟 تلميح البداية : احسب z-score للباقي للعثور على القيم الشاذة، ثم اربط سلسلة الضيوف بسلسلة الإنفاق واحفظ التراكب كـ Agg backend بدون واجهة.
# series.py (continued)
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
def detect_anomalies(residual: pd.Series, threshold: float = 2.5) -> pd.Series:
z = (residual - residual.mean()) / residual.std()
return z[z.abs() > threshold]
anoms = detect_anomalies(residual.dropna())
print(f"anomalies: {len(anoms)} -> {anoms.index[:8].tolist()}")
def make_spend(seed: int = 7) -> pd.Series:
guests = make_cafe_guests(seed=seed)
rng = np.random.default_rng(seed)
return guests * 3.2 + rng.normal(0, 30, len(guests))
spend = make_spend()
print("correlation:", round(s.corr(spend), 2))
fig, ax = plt.subplots(figsize=(11, 4))
ax.plot(s.index, s.values, label="guests")
ax.plot(spend.index, spend.values / 3.2, alpha=0.5, label="spend / 3.2")
ax.legend(); ax.set_title("Café guests vs spend (scaled)")
fig.tight_layout()
fig.savefig("series.png", dpi=100)
(residual - residual.mean()) / residual.std() يُحوّل كل باقي إلى z-score ، “كم انحراف معياري عن النمط هذا اليوم؟” ، وحد > 2.5 يُبقي outliers صادقين (يوم 3-sigma) دون تعليم نصف الملف. الارتباط هو الإحصاء الملخص: s.corr(spend) يُعيد رقمًا واحدًا في [-1, 1]، وقيم قريبة من 0.9 تخبرك أن المقياسين يتحركان معًا. في الرسم، قسمة spend على مُضاعفه التقريبي يُركّب السلسلتين بنفس المقياس ، مطالبة بصرية يُؤكد رقم .corr() لاحقًا.
🎯 الناتج المتوقع : عدد شذوذ (بضعة على الأكثر)، وارتباط قريب من 0.9، وملف series.png يُظهر السلسلتين تتبعان بعضهما.
🩹 إذا لم يعمل : إذا علّم detect_anomalies عشرات الأيام، فقد فُكّكت البيانات بنافذة window صغيرة جدًا لتنعيم ضوضاء أيام الأسبوع ، وسّعها. إذا طبع الارتباط NaN، فلسلسلتين فهارس مختلفة بعد .dropna() ، حاذف بـ .align() أو احسب على الفهرس المشترك. إذا لم يظهر PNG، ف savefig يعمل من دليل عمل لا تراه ، اطبع Path("series.png").resolve() للتأكيد.
5.2 تحقق من المحلل النهائي
✅ قائمة التحقق
- ✅ عدد الشذوذ صغير (أرقام فردية لكل عام بيانات) والتواريخ المُعلّمة مفاجآت معقولة.
- ✅
s.corr(spend)عدد عشري أعلى بوضوح من 0.5. - ✅
series.pngموجود على القرص يُظهر السلسلتين تتحرك معًا. - ✅ خط التدفق بالكامل يعمل من الأعلى إلى الأسفل كسكربت واحد بدون تعديلات.
🤔 سؤال (أسئلة) socrates)
- بُنيت سلسلة الإنفاق من الضيوف، فالارتباط القريب من 1.0 مُهندس. ماذا يوحي ارتباط حقيقي أقل (0.4 مثلاً) بشأن خطط التوظيف من عدّاد الضيوف ، وماذا لا يُثبت عن سببية واحد للآخر؟
- علامات الشذوذ تُشير إلى فشل نموذج وأحداث حقيقية في آن. إذا أُغلق المقهى للصيانة، هل سيظهر كz-score موجب أو سالب، وكيف تُميّز “شذوذ مثير” عن “نموذج معطوب” دون الاتصال بالمقهى؟
⚠️ المآزق الشائعة
RangeIndexبسيط بدلاً منDatetimeIndex. النوافذ المتحركة لا تزال تعمل، لكنdayofweekوإعادة العينات وتوليد تواريخ المستقبل كلها تصلح. الإصلاح: ابنِ كل سلسلة بـindex=idxمن الخطوة 1 وتحقق منtype(s.index)مبكرًا.NaNمن النوافذ المركزية.rolling(center=True)تترك حواف غير مُعرّفة؛ إدخالها فيgroupbyأو الرسم يجعل الرسم والإحصائيات تحذف أيامًا بصمت. الإصلاح:.dropna()على الاتجاه والموسمية والباقي عند الحد الذي تحتاجه.- تجهيز الاتجاه فوق الضوضاء بدلاً من الذيل.
polyfitعلىwindowقصير جدًا يُنتج ميلًا معظمها ضوضاء. الإصلاح: جهّز على شهر واحد على الأقل من القيم الحقيقية (30+) ودع الموسمية تُضاف بعد الاتجاه، لا أثناءها. - موسمية تسرّبت إلى اختبار “صارم”. تمرير
seasonalالسلسلة الكاملة إلىbacktestيجعل التقييم مُ biography. الإصلاح: أعد حساب الموسمية منtrainداخل اختبار التقييم إذا كان الرقم لعميل. - ارتباط بفهارس غير مُحاذاة. بعد
.dropna()أو شرحة صباحية مصفاة، قد تختلف السلسلتان عن التواريخ ويعيد.corr()NaNأو رقمًا مُضللاً. الإصلاح:.align()أو اقطع كلتاهما على الفهرس المشترك قبل التقييم.
ما بنيته للتو
محلل سلاسل زمنية كامل: سلسلة يومية مولّدة، وتفكيك جمعي يدوي إلى اتجاه/موسمية/باقي، وتنبؤ اتجاه-زائد-موسمية بـ MAE مُختبر، واكتشاف شذوذ على الباقي، ورسم زوج مرتبط محفوظ على القرص. المهارة القابلة للنقل هي فصل الإشارة عن الضوضاء: أي تسلسل ضوضائي إلى انحراف بطيء وإيقاع متكرر وباقي، ثم تنبأ بالأجزاء وعلّم الباقي ، الوصفة ذاتها خلف تخطيط الطلب والمراقبة وسؤال “ما الذي تغيّر فعلًا؟”
شغّل نسخة أكمل بدون إعداد محلي
examples/time-series-analyzer/ في دورة الكود نسخة أكمل من الكود أعلاه، بتفكيك أربعة مكونات وتجهيز اتجاه على طراز SARIMA. استنسخها، أو افتح الدورة في GitHub Codespace وشغّلها من هناك.
إلى أين تذهب من هنا
- أضف المكون الرابع المفقود ، تأثيرات أيام العمل أو العطلات ، بتمرير
groupbyإضافي على الباقي. - استبدل تجهيز الخط اليدوي بـ
numpy.polyfitمن الدرجة الثانية واستخدم مقارنة AIC لتحديد ما إذا اكتسب المنحنى معامله الإضافية. - استقص
thresholdفيdetect_anomaliesمن 1.5 إلى 4 واطبع كم يومًا يُعلّم كل منها، حتى يتوقف البُعد عن أن يكون سحريًا. - اكتب التنبؤ مع z-scores في CSV واحد حتى يستطيع السكربت الذي يُرسل بريدًا لمدير المقهى قراءة ملف واحد، لا ثلاثة.
شارك مشروعك مع الفصل
بنيت شيئًا تفتخر به؟ examples/student-projects/ معرض لمشاريع قدمها طلاب آخرون ، و README يحتوي دليلًا كاملًا ومناسبًا للمبتدئين لإضافة مشروعك عبر طلب سحب، حتى لو لم تستخدم git من قبل: تفرّع المستودع، وإنشاء فرع، وعمل commit لملفاتك، وفتح الطلب، خطوة بخطوة. لا يُفترض خبرة git مسبقة.
أهلاً بكتابة بايثون خارج المتصفح. 🎓
أكمل كل خطوة ثم حدد المشروع كمكتمل لجمع نقاطه.