0%
SciPy: الحوسبة العلمية المتقدمة في Python

SciPy: الحوسبة العلمية المتقدمة في Python

دليل شامل إلى SciPy للتحسين والإحصاء وتكامل المعادلات التفاضلية العادية والاستيفاء ومعالجة الإشارات والمصفوفات المتناثرة في Python.

I

InSkillCoach

· دقيقة

SciPy: الحوسبة العلمية المتقدمة في Python

SciPy هي مكتبة الحوسبة العلمية التي تمتد بها NumPy عبر خوارزميات عددية موثوقة: التحسين، وملاءمة المنحنيات، والإحصاء، وتكامل المعادلات التفاضلية، والاستيفاء، ومعالجة الإشارات، والمصفوفات المتناثرة. فحيثما توفر NumPy بنية البيانات (ndarray) والعمليات الأولية، توفر SciPy الطرق العددية عالية المستوى، وهي في معظمها مبنية على مكتبات مرجعية مكتوبة بلغتَي Fortran وC (LAPACK وMINPACK وFFTPACK). يستعرض هذا الدليل الوحدات الفرعية الأكثر استخدامًا مع أمثلة كاملة قابلة للتنفيذ.

SciPy مقارنةً بـ NumPy

الحد الفاصل بينهما سهل التذكر:

  • NumPy: المصفوفة ndarray، والحساب المتجهي، والجبر الخطي الأساسي، وتوليد الأعداد العشوائية.
  • SciPy: الخوارزميات التي تعمل على هذه المصفوفات، بتطبيقات متينة ثمرة عقود من الحوسبة العلمية.

تعتمد SciPy على NumPy، ويتم استيرادها وحدةً فرعيةً تلو الأخرى:

import numpy as np
from scipy import optimize, stats, integrate, interpolate, signal, sparse

# Vérifier la version installée
import scipy
print(scipy.__version__)     # 1.14.1 (par exemple)

توجد بعض التداخلات (فـ scipy.linalg توسّع numpy.linalg بتحليلات إضافية، وscipy.fft تحل بجدارة محل numpy.fft)، وفي هذه الحالات تكون نسخة SciPy عادةً أكثر اكتمالًا.

scipy.optimize: التصغير وملاءمة المنحنيات

تصغير دالة باستخدام minimize

تعثر minimize على نهاية صغرى محلية لدالة قياسية ذات عدة متغيرات.

from scipy.optimize import minimize

# Fonction de Rosenbrock, un banc d'essai classique
def rosenbrock(p):
    x, y = p
    return (1 - x) ** 2 + 100 * (y - x ** 2) ** 2

resultat = minimize(rosenbrock, x0=[0.0, 0.0], method="Nelder-Mead")

print(resultat.success)      # True
print(resultat.x)            # [0.99998736 0.99997369] -> minimum en (1, 1)
print(resultat.fun)          # 5.9e-10 (valeur au minimum, quasi nulle)
print(resultat.nit)          # nombre d'itérations

يعتمد اختيار الطريقة على المسألة: فـ "Nelder-Mead" لا تتطلب تدرجًا، و"BFGS" (الافتراضية دون قيود) تقدّره بالفروق المنتهية، و"L-BFGS-B" تقبل الحدود، و"SLSQP" تتعامل مع القيود العامة.

# Minimisation avec bornes : x dans [0, 2], y dans [0, 2]
res = minimize(rosenbrock, x0=[0.5, 0.5], method="L-BFGS-B",
               bounds=[(0, 2), (0, 2)])
print(res.x)                 # [1. 1.]

للعثور على جذر معادلة بدلًا من نهاية صغرى، استخدم optimize.root_scalar أو optimize.brentq.

ملاءمة نموذج للبيانات باستخدام curve_fit

تقدّر curve_fit معاملات نموذج بطريقة المربعات الصغرى غير الخطية. مثال: ملاءمة تناقص أسّي لقياسات تحتوي على ضجيج.

from scipy.optimize import curve_fit

# Modèle : la première variable est toujours x, les suivantes sont les paramètres
def modele(x, a, tau, c):
    return a * np.exp(-x / tau) + c

# Données synthétiques bruitées
rng = np.random.default_rng(42)
x_donnees = np.linspace(0, 10, 50)
y_vrai = modele(x_donnees, a=3.0, tau=2.5, c=0.5)
y_donnees = y_vrai + rng.normal(0, 0.15, size=x_donnees.size)

# Ajustement (p0 : estimation initiale des paramètres)
params, covariance = curve_fit(modele, x_donnees, y_donnees, p0=[1, 1, 0])

a_est, tau_est, c_est = params
incertitudes = np.sqrt(np.diag(covariance))

print(f"a   = {a_est:.3f} ± {incertitudes[0]:.3f}")   # a   = 3.011 ± 0.052
print(f"tau = {tau_est:.3f} ± {incertitudes[1]:.3f}") # tau = 2.487 ± 0.098
print(f"c   = {c_est:.3f} ± {incertitudes[2]:.3f}")   # c   = 0.503 ± 0.031

يعطي قطر مصفوفة التغاير تباين كل معامل مُقدَّر، وجذره التربيعي يمثل الارتياب بانحراف معياري واحد. قدّم دائمًا قيمة p0 معقولة للنماذج غير الخطية، وإلا فقد يتقارب الحل إلى قيمة مثلى خاطئة.

scipy.stats: التوزيعات والاختبارات الإحصائية

العمل مع التوزيعات

يوفر كل توزيع الواجهة نفسها: pdf (دالة الكثافة)، وcdf (دالة التوزيع التراكمي)، وppf (الكميات، أي معكوس cdf)، وrvs (توليد العينات).

from scipy import stats

# Loi normale de moyenne 100 et d'écart-type 15
loi = stats.norm(loc=100, scale=15)

print(loi.pdf(100))          # 0.02659... (densité au sommet)
print(loi.cdf(115))          # 0.8413... (P(X <= 115))
print(loi.ppf(0.975))        # 129.399... (quantile à 97,5 %)
print(loi.rvs(size=3, random_state=42))   # [107.45  84.44 109.72]

# Autres lois du même moule : stats.t, stats.chi2, stats.binom,
# stats.poisson, stats.expon, stats.uniform...

اختبار t: مقارنة متوسطين

rng = np.random.default_rng(1)
groupe_a = rng.normal(loc=52, scale=8, size=40)   # ex. scores avec méthode A
groupe_b = rng.normal(loc=48, scale=8, size=40)   # ex. scores avec méthode B

t_stat, p_valeur = stats.ttest_ind(groupe_a, groupe_b)
print(f"t = {t_stat:.3f}, p = {p_valeur:.4f}")
# t = 2.279, p = 0.0255 (par exemple)

if p_valeur < 0.05:
    print("Différence significative au seuil de 5 %")
# Variante appariée (mesures avant/après sur les mêmes sujets) : stats.ttest_rel
# Variances inégales : ttest_ind(..., equal_var=False) (test de Welch)

اختبار مربع كاي: استقلال متغيرين فئويين

# Tableau de contingence : lignes = formation suivie, colonnes = réussite
observations = np.array([[45, 15],    # avec formation : 45 réussites, 15 échecs
                         [30, 30]])   # sans formation : 30 réussites, 30 échecs

chi2, p, ddl, attendus = stats.chi2_contingency(observations)
print(f"chi2 = {chi2:.3f}, p = {p:.4f}, ddl = {ddl}")
# chi2 = 6.806, p = 0.0091, ddl = 1
print(attendus)              # effectifs attendus sous l'hypothèse d'indépendance
# [[37.5 22.5]
#  [37.5 22.5]]

الارتباطات

x = rng.normal(size=100)
y = 0.7 * x + rng.normal(scale=0.5, size=100)

r, p = stats.pearsonr(x, y)          # corrélation linéaire
print(f"Pearson  r = {r:.3f}, p = {p:.2e}")   # r = 0.812, p = 1.5e-24

rho, p = stats.spearmanr(x, y)       # corrélation de rangs (relations monotones)
print(f"Spearman rho = {rho:.3f}")   # rho = 0.795

استخدم ارتباط سبيرمان عندما تكون العلاقة رتيبة لكنها غير خطية، أو عند وجود قيم شاذة.

scipy.integrate: التكاملات والمعادلات التفاضلية

التكامل العددي باستخدام quad

from scipy.integrate import quad

# Intégrale de exp(-x²) entre 0 et l'infini (vaut sqrt(pi)/2)
valeur, erreur = quad(lambda x: np.exp(-x ** 2), 0, np.inf)
print(valeur)                        # 0.8862269254527579
print(np.sqrt(np.pi) / 2)            # 0.8862269254527579
print(erreur)                        # 7.1e-09 (estimation de l'erreur)

حل معادلة تفاضلية عادية باستخدام solve_ivp

مثال: المهتز التوافقي المخمّد، الموصوف بمعادلة من الرتبة الثانية x” + 2ζω x’ + ω² x = 0، وقد أُعيدت كتابتها على شكل جملة معادلات من الرتبة الأولى.

from scipy.integrate import solve_ivp

omega = 2.0    # pulsation propre
zeta = 0.15    # taux d'amortissement

def oscillateur(t, y):
    x, v = y                              # position, vitesse
    return [v, -2 * zeta * omega * v - omega ** 2 * x]

solution = solve_ivp(
    oscillateur,
    t_span=(0, 20),                       # intervalle d'intégration
    y0=[1.0, 0.0],                        # position 1, vitesse nulle à t=0
    t_eval=np.linspace(0, 20, 400),       # points où évaluer la solution
    method="RK45",                        # Runge-Kutta adaptatif (défaut)
)

print(solution.success)                   # True
print(solution.y.shape)                   # (2, 400) -> position et vitesse
print(solution.y[0, :3])                  # [1.  0.995 0.980] (position amortie)

# Tracé de la position au cours du temps
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(solution.t, solution.y[0])
ax.set_xlabel("Temps (s)")
ax.set_ylabel("Position")
ax.set_title("Oscillateur harmonique amorti")
plt.show()

بالنسبة إلى الأنظمة الصلبة (stiff) ذات الثوابت الزمنية المتباينة جدًا، استخدم method="Radau" أو method="BDF".

scipy.interpolate: استيفاء البيانات

يبني الاستيفاء دالة مستمرة تمر بنقاط القياس المنفصلة.

from scipy.interpolate import CubicSpline, interp1d

# Points de mesure épars
x_mesures = np.array([0, 1, 2, 3, 4, 5], dtype=float)
y_mesures = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])

# Spline cubique : lisse, dérivable, recommandée
spline = CubicSpline(x_mesures, y_mesures)

x_fin = np.linspace(0, 5, 100)
y_interpole = spline(x_fin)

print(spline(2.5))                   # 0.5546... (valeur interpolée entre 2 et 3)
print(spline(2.5, 1))                # dérivée première au même point

# Interpolation linéaire simple
lineaire = interp1d(x_mesures, y_mesures, kind="linear")
print(lineaire(2.5))                 # 0.5 (moyenne des points voisins)

تنبيه: لا يكون الاستيفاء موثوقًا إلا داخل نطاق القياسات. وللاستقراء الخارجي أو لتمليس بيانات تحتوي على ضجيج، فضّل ملاءمة نموذج (curve_fit) أو شريحة تمليس (scipy.interpolate.make_smoothing_spline).

scipy.signal وscipy.fft: معالجة الإشارات

التحليل الطيفي باستخدام FFT

from scipy.fft import fft, fftfreq

# Signal échantillonné : somme de deux sinusoïdes (5 Hz et 50 Hz) plus du bruit
fe = 500                              # fréquence d'échantillonnage (Hz)
duree = 2.0
t = np.linspace(0, duree, int(fe * duree), endpoint=False)
rng = np.random.default_rng(7)
signal_brut = (np.sin(2 * np.pi * 5 * t)
               + 0.5 * np.sin(2 * np.pi * 50 * t)
               + 0.3 * rng.normal(size=t.size))

# Transformée de Fourier
spectre = fft(signal_brut)
frequences = fftfreq(t.size, d=1 / fe)

# Amplitudes sur les fréquences positives
moitie = t.size // 2
amplitudes = 2 / t.size * np.abs(spectre[:moitie])

pics = frequences[:moitie][amplitudes > 0.3]
print(pics)                           # [ 5. 50.] -> les deux fréquences retrouvées

الترشيح باستخدام مرشّح Butterworth

from scipy.signal import butter, filtfilt

# Filtre passe-bas d'ordre 4, fréquence de coupure 20 Hz
b, a = butter(N=4, Wn=20, btype="low", fs=fe)

# filtfilt applique le filtre dans les deux sens : aucun déphasage
signal_filtre = filtfilt(b, a, signal_brut)

# La composante à 50 Hz et le bruit haute fréquence sont supprimés,
# la sinusoïde à 5 Hz est préservée.

توفر scipy.signal أيضًا الكشف عن القمم (find_peaks)، والمخططات الطيفية (ShortTimeFFT)، والالتفاف، وتصميم أنواع عديدة من المرشحات (تشيبيشيف، والإهليلجية، وFIR).

from scipy.signal import find_peaks

pics_indices, proprietes = find_peaks(signal_filtre, height=0.5, distance=fe // 10)
print(len(pics_indices))              # 10 -> un pic par période de la sinusoïde 5 Hz

scipy.sparse: المصفوفات المتناثرة

عندما تحتوي مصفوفة على غالبية ساحقة من الأصفار (الرسوم البيانية، وأنظمة المعادلات الناتجة عن الشبكات الحسابية، ومصفوفات المصطلحات والمستندات)، فإن تخزينها بصورة كثيفة يهدر الذاكرة والحساب.

from scipy import sparse

# Matrice creuse au format COO (construction) puis CSR (calcul)
lignes = np.array([0, 1, 2, 2])
colonnes = np.array([1, 2, 0, 2])
valeurs = np.array([3.0, 4.0, 5.0, 6.0])

m = sparse.coo_array((valeurs, (lignes, colonnes)), shape=(3, 3)).tocsr()
print(m.toarray())
# [[0. 3. 0.]
#  [0. 0. 4.]
#  [5. 0. 6.]]

print(m.nnz)                          # 4 éléments non nuls stockés (sur 9)

# Produit matrice-vecteur : seuls les éléments non nuls sont parcourus
v = np.array([1.0, 2.0, 3.0])
print(m @ v)                          # [ 6. 12. 23.]

# Résolution d'un système linéaire creux
from scipy.sparse.linalg import spsolve
b = np.array([6.0, 12.0, 23.0])
x = spsolve(m.tocsc(), b)
print(x)                              # [1. 2. 3.]

لتقدير الحجم: ستشغل مصفوفة كثيفة بحجم 100 000 × 100 000 من نوع float64 ما مقداره 80 غيغابايت، أما المصفوفة نفسها بنسبة 0.01 % من العناصر غير الصفرية فتتسع في بضعة ميغابايتات بصيغة CSR.

جدول ملخّص للوحدات الفرعية

الوحدة الفرعيةالمجالأبرز الدوال
scipy.optimizeالتحسين، والملاءمة، والجذورminimize, curve_fit, root_scalar, linprog
scipy.statsالتوزيعات، والاختبارات، والارتباطاتnorm, ttest_ind, chi2_contingency, pearsonr
scipy.integrateالتكاملات، والمعادلات التفاضليةquad, solve_ivp
scipy.interpolateالاستيفاء، والشرائح (splines)CubicSpline, interp1d, make_smoothing_spline
scipy.signalالترشيح، والقمم، والمخططات الطيفيةbutter, filtfilt, find_peaks
scipy.fftتحويلات فورييهfft, ifft, fftfreq, rfft
scipy.sparseالمصفوفات المتناثرةcsr_array, coo_array, sparse.linalg.spsolve
scipy.linalgالجبر الخطي الموسّعlu, svd, expm, solve_banded
scipy.spatialالهندسة، والمسافاتKDTree, distance.cdist, ConvexHull
scipy.ndimageمعالجة الصور متعددة الأبعادgaussian_filter, label, rotate

أفضل الممارسات

  • استورد الوحدات الفرعية صراحةً (from scipy import optimize): فالأمر import scipy وحده لا يحمّل الوحدات الفرعية.
  • تحقق دائمًا من resultat.success بعد minimize أو solve_ivp: فالخوارزمية التي لم تتقارب تُرجع نتيجة مع ذلك.
  • قدّم تقديرًا أوليًا p0 معقولًا إلى curve_fit: فالمربعات الصغرى غير الخطية تتقارب إلى قيمة مثلى محلية تتوقف على نقطة البداية.
  • فسّر قيم p بحذر: فقيمة p الأقل من 0.05 لا تقيس حجم الأثر ولا أهميته العملية؛ أورِد كذلك المتوسطات وفترات الثقة.
  • اختر الطريقة المناسبة للمسألة: Radau أو BDF للمعادلات التفاضلية الصلبة، وL-BFGS-B متى وُجدت حدود، واختبار Welch عندما تختلف التباينات.
  • لا تستقرئ باستخدام الاستيفاء: فخارج نطاق القياسات تتباعد الشريحة التكعيبية بسرعة.
  • انتقل إلى المصفوفات المتناثرة متى انخفضت كثافة العناصر غير الصفرية إلى بضعة بالمئة في المصفوفات الكبيرة.

الخاتمة

تحوّل SciPy لغة Python إلى بيئة حوسبة علمية متكاملة: التحسين وملاءمة النماذج مع optimize، والاستدلال الإحصائي مع stats، وحل المعادلات التفاضلية مع integrate، وإعادة بناء الدوال مع interpolate، والتحليل الترددي والترشيح مع signal وfft، والتوسع إلى الأحجام الكبيرة مع sparse. وتحتضن كل وحدة فرعية خوارزميات مرجعية جرى التحقق منها على مدى عقود، مما يغنيك عن إعادة تنفيذ طرق عددية دقيقة. وبالاقتران مع NumPy لبنى البيانات، وPandas للجداول ذات التسميات، وMatplotlib للتصوير البياني، تكمل SciPy صندوق الأدوات القياسي لكل عمل علمي أو هندسي في Python.

InSkillCoach

نبذة عن InSkillCoach

خبير في التدريب والتقنيات

مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.

الشهادات:

  • AWS Certified Solutions Architect – Professional
  • شهادات Google Cloud
  • Microsoft Certified: DevOps Engineer Expert
  • Certified Kubernetes Administrator (CKA)
  • CompTIA Security+
1.2k
74

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!