سرنخ — خودآموز برنامه‌نویسی برای هوش مصنوعی (مقدماتی)

فصل ۲ از ۱۰

پیشرفت ترم
۰٪

ترم ۲ · داده: دیدن، تمیز کردن، فهمیدن

کارِ برداری: حلقه ننویس

فصل ۲پیش‌نمایش رایگان

در این فصل چه یاد می‌گیری#

فصلِ قبل دیدی که np_array * 2 بدونِ هیچ حلقه‌ای کلِ آرایه را دو برابر کرد. این یک ترفندِ سرعتی نیست؛ یک روشِ فکرکردن است. در این فصل یاد می‌گیری به‌جای «برای هر عنصر، این کار را بکن» بنویسی «این کار را روی همه بکن» — و مهم‌تر از آن، با ماسکِ شرطی فقط عنصرهایی را جدا کنی که شرطی را دارند. این تنها مهارتی است که در کلِ ترمِ ۲ و ۳ بیشترین بار را می‌کشد؛ اگر یک فصل را باید دو بار بخوانی، این است.

یک مهرِ بزرگ که هم‌زمان روی همهٔ خانه‌های یک شبکه فشار می‌آورد، در کنارِ یک قالبِ سوراخ‌دار که فقط بعضی خانه‌ها را عبور می‌دهد

آخر این فصل می‌توانی:

  • روی کلِ یک آرایه عملیات ریاضی و مقایسه‌ای انجام بدهی
  • قاعدهٔ broadcasting را بگویی و پیش‌بینی کنی کِی کار می‌کند
  • با ماسکِ شرطی زیرمجموعه‌ای از داده را جدا و بشماری
  • با np.where بر اساسِ شرط مقدارِ تازه بسازی
  • با axis روی ردیف یا ستونِ یک جدول تجمیع کنی

قبل از شروع#

از فصل ۱: np.array، shape، dtype، و برشِ دوبعدی با arr[:, 0].

از ترمِ ۱ فصل ۵: عملگرهای مقایسه‌ای و and/or — که اینجا شکلِ تازه‌ای پیدا می‌کنند.

📓 نوت‌بوک: نوت‌بوک این فصل را در Colab باز کن — همهٔ کدهای این فصل آماده و به‌ترتیب داخلش هست.

۱. عملیات روی کلِ آرایه#

import numpy as np

prices = np.array([850000, 1200000, 430000, 2100000, 650000])

print(prices * 2)
print(prices + 50000)
print(prices / 1000)
print(prices - prices.mean())
[1700000 2400000  860000 4200000 1300000]
[ 900000 1250000  480000 2150000  700000]
[ 850. 1200.  430. 2100.  650.]
[-196000.  154000. -616000. 1054000. -396000.]

هر عملیات عنصربه‌عنصر روی همهٔ آرایه اجرا شد. این را vectorization می‌گویند: به‌جای گفتنِ «چطور»، فقط می‌گویی «چه».

خطِ آخر جالب‌ترین است: prices - prices.mean() فاصلهٔ هر قیمت از میانگین را داد. عددِ منفی یعنی ارزان‌تر از میانگین، مثبت یعنی گران‌تر. همین یک خط، هستهٔ کاری است که در فصلِ ۷ ترمِ ۳ با نامِ نرمال‌سازی می‌بینی.

🌱 ریشه‌اش کجاست: «فاصله از میانگین» شهودِ پشتِ انحرافِ معیار است — عددی که می‌گوید داده‌ها به‌طورِ متوسط چقدر از مرکز دورند. اگر می‌خواهی بدانی چرا این عدد این‌طور تعریف شده و چرا مربع می‌شود، ریشه، ترم ۶ فصل ۶ — میانگین، میانه، پراکندگی دقیقاً همین را می‌سازد.

۲. broadcasting: وقتی شکل‌ها یکی نیستند#

در بخشِ قبل prices + 50000 کار کرد، هرچند prices پنج عنصر داشت و 50000 یک عدد بود. numpy عددِ تکی را روی همهٔ عنصرها پخش کرد. این قاعده اسم دارد: broadcasting.

table = np.array([
    [10, 20, 30],
    [40, 50, 60],
])
column_factor = np.array([1, 2, 3])

print(table * 2)
print(table * column_factor)
print(table.shape, column_factor.shape)
[[ 20  40  60]
 [ 80 100 120]]
[[ 10  40  90]
 [ 40 100 180]]
(2, 3) (3,)

آرایهٔ (2, 3) با آرایهٔ (3,) جمع شد، چون آخرین بُعدشان می‌خواند: هر دو سه ستون دارند، پس column_factor روی هر ردیف تکرار شد.

قاعده در یک جمله: دو آرایه با هم سازگارند اگر برای هر بُعد، اندازه‌شان یا برابر باشد یا یکی‌شان ۱ باشد — و ابعاد از راست به هم تطبیق داده می‌شوند.

برای همین در فصلِ قبل (3,) و (4,) خطا داد: نه برابرند و نه هیچ‌کدام ۱ است.

قبل از ادامه، جواب بده: آرایه‌ای (100, 3) داری و می‌خواهی هر ردیف را در عددِ متفاوتی ضرب کنی. آرایهٔ ضریب‌ها با shape برابرِ (100,) کار می‌کند؟ جواب: نه. تطبیق از راست است، پس (100,) با آخرین بُعد یعنی 3 مقایسه می‌شود و نمی‌خواند. باید شکلش را به (100, 1) تبدیل کنی — با reshape(-1, 1). متدِ reshape شکلِ تازه را می‌گیرد و همان عنصرها را در آن قالب می‌ریزد، و آن -1 یعنی «این بُعد را خودت حساب کن»: می‌گویی یک ستون می‌خواهم، numpy خودش می‌فهمد که پس ۱۰۰ ردیف لازم است. این دقیقاً همان جایی است که در بلوکِ 🔧 فصلِ قبل هشدار دادیم.

۳. ماسکِ شرطی: مهم‌ترین بخشِ این فصل#

عملگرهای مقایسه هم برداری‌اند و نتیجه‌شان آرایه‌ای از True و False است:

print(prices > 800000)
print((prices > 800000).sum())
print((prices > 800000).mean())
[ True  True False  True False]
3
0.6

به آن آرایهٔ True/False می‌گویند ماسک (mask). دو ترفندِ بسیار پرکاربرد رویش:

  • .sum() روی ماسک تعدادِ Trueها را می‌دهد، چون True مثلِ ۱ و False مثلِ ۰ جمع می‌شود.
  • .mean() روی ماسک نسبتشان را می‌دهد — اینجا ۰٫۶ یعنی شصت درصدِ قیمت‌ها بالای ۸۰۰ هزارند.

این دو خط، پرکاربردترین ابزارِ «داده را ببین» در کلِ دوره‌اند.

و حالا کارِ اصلی: ماسک را داخلِ کروشه بگذار تا فقط همان عنصرها را بگیری.

expensive = prices[prices > 800000]

print(expensive)
print(f"تعداد: {len(expensive)}")
print(f"میانگینِ گران‌ها: {expensive.mean()}")
[ 850000 1200000 2100000]
تعداد: 3
میانگینِ گران‌ها: 1383333.3333333333

prices[prices > 800000] را بلند بخوان: «از prices، آن‌هایی که بزرگ‌تر از هشتصدهزارند». همین. هیچ حلقه‌ای، هیچ appendی.

چک کن: سه عدد باید برگردند و هر سه واقعاً بالای ۸۰۰٬۰۰۰ باشند. اگر پنج عدد گرفتی، احتمالاً پرانتز را جا انداخته‌ای و کلِ آرایه برگشته.

ترکیبِ چند شرط هم می‌شود، ولی با نمادِ دیگری:

mid_range = prices[(prices > 500000) & (prices < 1500000)]
print(mid_range)

not_cheap = prices[~(prices < 500000)]
print(not_cheap)
[ 850000 1200000  650000]
[ 850000 1200000 2100000  650000]

⚠️ مواظب باش: اینجا & و | و ~ به‌کار می‌روند، نه and و or و not. دلیلش این است که and می‌خواهد کلِ آرایه را یک True یا False واحد ببیند و نمی‌تواند، پس خطا می‌دهد: ValueError: The truth value of an array with more than one element is ambiguous. و پرانتزها اجباری‌اند — چون & تقدمِ بالاتری از > دارد و بدونِ پرانتز، پایتون اول اشتباهی عددها را با هم & می‌کند.

۴. np.where: بر اساسِ شرط، مقدار بساز#

ماسک عنصرها را جدا می‌کند. گاهی نمی‌خواهی جدا کنی — می‌خواهی برچسب بزنی:

labels = np.where(prices > 800000, "گران", "ارزان")
capped = np.where(prices > 1000000, 1000000, prices)

print(labels)
print(capped)
['گران' 'گران' 'ارزان' 'گران' 'ارزان']
[ 850000 1000000  430000 1000000  650000]

np.where(شرط، اگر درست، اگر نادرست) را می‌شود «ifِ برداری» خواند. مثالِ دوم یک کارِ خیلی رایج است: سقف گذاشتن روی مقدارهای خیلی بزرگ.

۵. axis: تجمیع روی ردیف یا ستون#

روی جدول، تجمیع یک سؤالِ اضافه دارد: در چه جهتی؟

sales = np.array([
    [12, 15, 9],
    [7, 22, 14],
    [30, 4, 11],
    [18, 19, 25],
])

print("مجموع کل:", sales.sum())
print("مجموع هر ستون:", sales.sum(axis=0))
print("مجموع هر ردیف:", sales.sum(axis=1))
print("میانگین هر ستون:", sales.mean(axis=0))
print("بیشینهٔ هر ردیف:", sales.max(axis=1))
مجموع کل: 186
مجموع هر ستون: [67 60 59]
مجموع هر ردیف: [36 43 45 62]
میانگین هر ستون: [16.75 15.   14.75]
بیشینهٔ هر ردیف: [15 22 30 25]

قاعدهٔ axis را همه یک بار اشتباه می‌فهمند، پس این‌طور به‌خاطر بسپار: axis بُعدی است که از بین می‌رود.

sales شکلش (4, 3) است. sum(axis=0) بُعدِ صفر (ردیف‌ها) را جمع می‌کند و از بین می‌برد، پس نتیجه (3,) می‌شود — یک عدد برای هر ستون. و axis=1 ستون‌ها را از بین می‌برد و یک عدد برای هر ردیف می‌دهد.

اگر شک کردی، shape نتیجه را چاپ کن؛ همیشه جوابِ قطعی می‌دهد.

📏 اندازه بگیر: «میانگینِ هر ستون» را نگاه کن: [16.75, 15.0, 14.75]. سه عدد خیلی نزدیک به هم. حالا به sum(axis=1) نگاه کن: [36, 43, 45, 62] — ردیفِ آخر تقریباً دو برابرِ ردیفِ اول است. یک تجمیع می‌تواند تفاوتِ واقعی را کاملاً پنهان کند. قبل از اینکه از روی میانگین نتیجه بگیری، همیشه بپرس: میانگینِ چه چیزی، در کدام جهت، و آیا پراکندگی‌اش را هم دیده‌ام؟ فصلِ ۸ همین ترم دقیقاً به این می‌پردازد.

۶. یک خطای واقعی: and روی آرایه#

print(prices[(prices > 500000) and (prices < 1500000)])
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

پیام دقیقاً همان چیزی را می‌گوید که در بلوکِ ⚠️ بخشِ ۳ خواندی: پایتون می‌خواهد and را روی یک True/False واحد اجرا کند، ولی طرفینش آرایه‌اند و «درست بودنِ» یک آرایهٔ پنج‌عنصری معنا ندارد.

🔧 اگر کار نکرد: هر وقت این پیام را دیدی، دنبالِ and، or یا not بگرد که روی آرایه یا ستون به‌کار رفته باشد و به &، |، ~ تبدیلش کن — و پرانتزها را فراموش نکن. این پیام را در فصلِ بعد روی pandas هم می‌بینی، با همان علت و همان راهِ حل.

🤖 از دستیارت بپرس: «قاعدهٔ broadcasting در numpy را با سه مثال توضیح بده که یکی‌شان کار نکند، و بگو چرا.» جوابش را با قاعدهٔ بخشِ ۲ بسنج، و هر سه مثال را خودت اجرا کن. مثالِ خراب مهم‌ترینشان است: اگر دستیار مثالی داد که برخلافِ ادعایش کار کرد، همان‌جا یاد می‌گیری که خروجی را باید امتحان کرد.

واژه‌های تازهٔ این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
vectorization وکتورایزیشِن اجرای یک عملیات روی کلِ آرایه، بدونِ حلقه
broadcasting برادکستینگ قاعدهٔ تطبیقِ شکل‌ها هنگامِ عملیات بینِ دو آرایه
mask ماسک آرایه‌ای از True/False که برای فیلترکردن به‌کار می‌رود
axis اَکسیس جهتِ تجمیع؛ همان بُعدی که از بین می‌رود

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

آرایه برای عددِ خالص عالی است، ولی دادهٔ واقعی فقط عدد نیست: ستونِ نام دارد، ستونِ شهر دارد، خانهٔ خالی دارد، و مهم‌تر از همه ستون‌هایش اسم دارند، نه شماره. فصلِ بعد pandas را می‌آورد و DataFrame را — جدولی که هر ستونش نام و نوعِ خودش را دارد. و از همان فصل، دیگر با دادهٔ ساختگیِ چهارخطی کار نمی‌کنی: یک فایلِ واقعی با ۲۴۴ ردیف و همهٔ کثیفی‌های دنیای واقعی.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.