مقایسه و منطق بولی
در بخش مربوط به توابع عمومی با توابعی که روی آرایهها عمل ریاضی انجام میدادند آشنا شدیم. همچنین با عملگر متناظر با هر تابع نیز آشنا شدیم. عملیات رایج دیگری که روی آرایهها انجام میشود عمل مقایسه آرایهها با هم است. که این عمل هم توسط توابعی و هم توسط عملگرهای متناظر با آن توابع انجام میشود. در واقع کار این توابع و عملگرهای مقایسهای مقایسه عضو به عضو آرایهها با هم است.
در جدول زیر با چند مورد از توابع مقایسهای و عملگرهای مقایسهای آشنا میشویم.
توضیح کار تابع یا عملگر | عملگر متناظر | تابع متناظر |
مساوی | == | np.equal |
نامساوی | != | np.not_equal |
کوچکتر | > | np.less |
کوچکتر مساوی | <= | np.less_equal |
بزرگتر | > | np.greater |
بزرگتر مساوی | >= | np.greater_equal |
خروجی مقایسه دو آرایه یک آرایه از نوع مقدار بولی (True و False) است. که هر خانه آن حاصل مقایسه عناصر متناظر دو آرایه است. به مثال زیر توجه کنیم.
a = np.array([3, 12, 9, 4, 5])
b = np.array([2, 20, 5, 1, 8])
print(a < b)
print(np.less(a, b))
خروجی کد:
[False True False False True]
[False True False False True]
دقت کنیم که ممکن است دو آرایه مورد نظر هم شکل نباشند اما به کمک خاصیت پخش (در صورت انجام) مقایسه انجام شود. به مثال زیر توجه کنیم.
a = np.array([3, 12, 9, 4, 5])
print(a > 10)
print(np.greater(a, 10))
خروجی کد:
[False True False False False]
[False True False False False]
یک مثال دیگر از اینکه دو آرایه هم شکل نباشند ببینیم.(این بار سطرهای یک آرایه دو بُعدی را با یک سطر ثابت مقایسه میکنیم).
a = np.array([[1, 10, 2],
[4, 8, 7],
[5, 9, 3],
[7, 5, 3]])
b = np.array([4, 3, 8])
print(a == b)
print()
print(np.equal(a, b))
خروجی کد:
[[False False False]
[ True False False]
[False False False]
[False False False]]
[[False False False]
[ True False False]
[False False False]
[False False False]]
توابع مخصوص آرایههای بولی
در این بخش چمد تابع مخصوص که روی آرایههای بولی اعمال میشوند را معرفی میکنیم.
فرض کنید یک آرایه 4 در 6 داریم. (4 سطر و 6 ستون) این آرایه مربوط به نمرات 6 درس 4 دانش آموز است. میخواهیم تعداد نمرات بالای 15 را به دست آوریم. برای این منظور ابتدا به کمک عملگرهای مقایسهای نمرات بزرگتر از 15 را پیدا میکنیم سپس برای شمارش آنها از توابعی چون np.count_nonzero و یا n.sum استفاده میکنیم. تابع اول تعداد عناصر غیر صفر و تابع دوم مجموع عناصر را محاسبه میکند. اما شاید سوالی پیش بیاید که در یک آرایه بولی مقادیر True و False است و 0 یا 1 نیست. در جواب باید بگوییم که این توابع مقادیر True را 1 در نظر میگیرند و مقادیر False را 0 در نظر میگیرند.
حال به کد زیر که پیادهسازی توضیح بالا است توجه کنید:
nomarat = np.array([[20, 15, 16, 10],
[13, 17, 18, 11],
[10, 12, 14, 18],
[19, 15, 17, 19]])
print(np.count_nonzero(nomarat >= 15))
print(np.sum(nomarat >= 15))
خروجی کد:
10
10
نکته: در مثال بالا میتوانستیم ابتدا نتیجه مقایسه را درون یک متغیر بریزیم و سپس از آن متغیر درون توابع np.count_nonzero و np.sum استفاده کنیم.
حال فرض کنید بخواهیم تعداد نمرات بالای 15 را در هر دانش آموز به صورت جدا بشماریم. برای این منظور باید عمل شمارش را در راستای محور 1 (axis=1) یعنی جهت چپ به راست انجام دهیم. به کد زیر توجه کنیم.
nomarat = np.array([[20, 15, 16, 10],
[13, 17, 18, 11],
[10, 12, 14, 18],
[19, 15, 17, 19]])
m = nomarat >= 15
print(np.sum(m, axis=1))
print(np.count_nonzero(m, axis=1))
خروجی کد:
[3 2 1 4]
[3 2 1 4]
اگر بخواهیم True بودن حداقل یک خانه از آرایهای را بررسی کنیم میتوانیم از تابع np.any استفاده کنیم و اگر بخواهیم بررسی کنیم که آیا تمام خانههای آرایهای True است یا نه، میتوانیم از تابع np.all استفاده کنیم.
مثلا فرض کنیم بخواهیم بررسی کنیم که آیا در بین کل نمرات نره 18 داشتهایم. برای این منظور بعد از اعمال عمگلر مقایسهای باید از تایع np.any استفاده کنیم چرا که میخواهیم بررسی کنیم که آیا فقط یک True در نتیجه وجود دارد یا نه. پس کد زیر را ببینیم.
nomarat = np.array([[20, 15, 16, 10],
[13, 17, 18, 11],
[10, 12, 14, 18],
[19, 15, 17, 19]])
print(np.any(nomarat == 18))
خروجی کد:
True
حال فرض کنید بخواهیم بررسی کنیم که آیا تمام نمرات بیشتر از 10 هستند یا نه. برای این منظور بعد از استفاده از عملگر مقایسهای مناسب باید از تابع np.all استفاده کنیم. چرا که هدف True بودن تمام عناصر آرایه حاصل از عملگر مقایسهای است. پس داریم.
nomarat = np.array([[20, 15, 16, 10],
[13, 17, 18, 11],
[10, 12, 14, 18],
[19, 15, 17, 19]])
print(np.all(nomarat >= 10))
خروجی کد:
True
توابع منطقی بولی
حتی میتوانیم عملهای منطق بولی (و منطقی، یا منطقی، نقیض منطقی) نیز روی آرایههای بولی انجام دهیم. که با استفاده از آنها میتوانیم شرطهای مختلف روی آرایههای بولی را با هم ترکیب کنیم. در جدول زیر انواع تابع منطق بولی و عملگر متناظر با آن را آوردهایم.
تابع منطق بولی | عملگر منطق بولی | عنوان | شرط درستی (True بودن) |
np.bitwise_and | & | وَیِ بیتی (عطف بیتی) | هر دو ورودی True باشند. |
np.bitwise_or | | | یای بیتی (فصل بیتی) | حداقل یک ورودی True باشد |
np.bitwise_xor | ^ | یای انحصاری بیتی | دقیقاً یک ورودی True باشد. |
np.bitwise_not | ~ | نفی بیتی | ورودی False باشد. |
فرض کنید در مثال مربوط به نمرات بخواهیم تعداد نمرات بین خود 10 تا خود 15 را به دست آوریم. برای این منظور باید دو شرط بزرگتر مساوی از 10 و کمتر مساوی از 15 را با عملگر منطقی & ترکیب کنیم. داریم.
nomarat = np.array([[20, 15, 16, 10],
[13, 17, 18, 11],
[10, 12, 14, 18],
[19, 15, 17, 19]])
print(np.sum((nomarat >= 10) & (nomarat <= 15)))
خروجی کد:
8
نکته: توجه کنیم که برای ترکیب چند شرط حتما هر شرط به صورت جدا باید درون () باشد.