<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/page/6/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</description><language>ar</language><item><title>&#x628;&#x646;&#x627;&#x621; &#x644;&#x639;&#x628;&#x629; &#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x648;&#x648;&#x62D;&#x62F;&#x629; &#x627;&#x644;&#x623;&#x644;&#x639;&#x627;&#x628; Pygame</title><link>https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-pygame-r1704/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_09/631c1e9665be2_--------PyGame.png.e937ebb861b5bd66601783654817bf53.png" /></p>
<p>
	شرحنا في <a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%86%D8%B1%D8%AF-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1703/" rel="">المقال السابق</a> كيفية بناء لعبة نرد بسيطة باستعمال <a href="https://wiki.hsoub.com/Python" rel="external">لغة بايثون</a>، وتعرفنا على وحدة السلحفاة Turtle الخاصة برسم الخطوط والأشكال البسيطة.
</p>

<p>
	وسنعرض في هذا المقال وحدة أخرى لكنها متقدمة ومتخصصة بالألعاب الرسومية كما يشير اسمها فهي تدعى PyGame، هذه الوحدة ليست مضمنة افتراضيًا في <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">بايثون</a> مثل وحدة السلحفاة، لذا عليك تنزيلها وتثبيتها بنفسك فلها مكتبةٌ خاصة.
</p>

<p>
	يمكنك مطالعة المقالات ضمن السلسلة:
</p>

<ol>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%86%D8%B1%D8%AF-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1703/" rel="">بناء لعبة نرد بسيطة بلغة بايثون</a>.
	</li>
	<li>
		بناء لعبة رسومية باستخدام بايثون ووحدة الألعاب PyGame.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%84%D8%A7%D8%B9%D8%A8-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%85%D9%83%D8%AA%D8%A8%D8%A9-pygame-r1705/" rel="">إضافة لاعب إلى اللعبة المطورة باستخدام بايثون و Pygame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D8%B4%D8%AE%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-pygame-r1706/" rel="">تحريك شخصية اللعبة باستخدام PyGame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%B4%D8%AE%D8%B5%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D8%AF%D9%88-%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%B9%D8%A8%D8%B1-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-pygame-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1707/" rel="">إضافة شخصية العدو للعبة</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D9%86%D8%B5%D8%A7%D8%AA-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-pygame-r1730/" rel="">إضافة المنصات إلى لعبة بايثون باستخدام الوحدة Pygame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%A7%D9%83%D8%A7%D8%A9-%D8%A3%D8%AB%D8%B1-%D8%A7%D9%84%D8%AC%D8%A7%D8%B0%D8%A8%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1731/" rel="">محاكاة أثر الجاذبية في لعبة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%AE%D8%A7%D8%B5%D9%8A%D8%A9-%D8%A7%D9%84%D9%82%D9%81%D8%B2-%D9%88%D8%A7%D9%84%D8%B1%D9%83%D8%B6-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1732/" rel="">إضافة خاصية القفز والركض إلى لعبة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D8%A6%D8%B2-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1733/" rel="">إضافة الجوائز إلى اللعبة المطورة بلغة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D9%86%D8%AA%D8%A7%D8%A6%D8%AC-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%B4%D8%A7%D8%B4%D8%A9-r1734/" rel="">تسجيل نتائج اللعبة المطورة بلغة بايثون وعرضها على الشاشة</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A2%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D8%B0%D9%81-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1735/" rel="">إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون</a>.
	</li>
</ol>

<p>
	يعتمد الأسلوب الحديث في تطوير بايثون على مفهوم البيئات الافتراضية، التي توفر لتطبيقك مساحة خاصة ومعزولة تتضمن كل ما يحتاج إليه، وتساعدك في إدارة المكتبات اللازمة له، ما يعني أن متطلبات التشغيل ستكون واضحة أمامك ويمكنك تحديدها بدقة لأي شخص آخر يرغب باستخدام التطبيق أو اللعبة حتى يثبتها لديه.
</p>

<p>
	تستطيع إدارة البيئة الافتراضية يدويًا لكن إدارتها وتنزيل حزمها من خلال بيئة تطوير متكاملة IDE التي تبقى الطريقة الأسهل والأكثر مرونة، وهذا ما طبقناه في حالتنا عبر بيئة PyCharm في المقال السابق المشار إليه أعلاه.
</p>

<h2>
	مكتبة Pygame
</h2>

<p>
	Pygame هي مكتبة أو وحدة بايثون، تتضمن مجموعة من الشيفرات الجاهزة الشائعة الاستخدام في مجال الألعاب، يمكنك الاستفادة منها عوضًا عن كتابتها من الصفر من جديد فهي توفر عليك إعادة اختراع العجلة كما يقال، تذكر وحدة السلحفاة التي جربّناها في مقالنا السابق وتخيل مثلًا أنك ستكتب الشيفرة البرمجية اللازمة لتنشئ القلم قبل أن ترسم به، كم ستكون العملية معقدة، وبالمثل في ألعاب الفيديو هنا تكمن فائدة Pygame.
</p>

<p>
	تحتاج اللعبة إلى عالم افتراضي أو بيئة تجري فيها الأحداث، في Pygame يمكنك إضافة ذلك بإحدى طريقتين:
</p>

<ul>
	<li>
		تعيين لون للخلفية.
	</li>
	<li>
		تعيين صورة للخلفية.
	</li>
</ul>

<p>
	وفي الحالتين لن تتفاعل شخصيات اللعبة مع الخلفية سواء أكانت صورة أو لون فهي مجرد بيئة جامدة للعبتك.
</p>

<h2>
	كيفية استعمال Pygame
</h2>

<p>
	أنشئ أولًا مشروعًا جديدًا، ومعه بطبيعة الحال مجلدًا خاصًا باللعبة لتُحفظ فيه كافة الملفات اللازمة لعملها فهذا التفصيل مهم.
</p>

<p>
	إنشاء المشروع الجديد عملية بسيطة في بيئة التطوير المتكاملة، ففي PyCharm المستخدمة في حالتنا، يكفي أن تضغط على قائمة ملف، وتختار "مشروع جديد" وتحدد بعدها اسمًا له (game_001 على سبيل المثال)، لاحظ أن مشروعك سيُحفظ ضمن مجلد خاص بمشاريع PyCharm.
</p>

<p>
	ولا تنسَ تفعيل خيار بناء البيئة الافتراضية باستخدام Virtualenv وخيار إنشاء الملف main.py مع الإبقاء على الخيارات الأخرى كما هي افتراضيًا.
</p>

<p style="text-align: center;">
	<img alt="img-01-pycharm-new-project.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="107678" data-unique="18mpin1v3" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_09/img-01-pycharm-new-project.jpg.240571c5c38423a7125bda122145e98d.jpg">
</p>

<p>
	اضغط الآن على زر إنشاء ليظهر مشروعك الجديد في نافذة PyCharm وهو يتألف من بيئة افتراضية env (تجدها في العمود اليساري) وملف نموذجي main.py.
</p>

<p>
	امسح كافة المعلومات من الملف main.py لتكتب بداخله شيفرة برنامجك، عادةً ما تبدأ ملفات بايثون بكتابة نوع الملف ومن ثم اسمك والترخيص الذي تستخدمه، استخدم ترخيصًا مفتوح المصدر لتفسح المجال أمام بقية المطورين لتطوير لعبتك وتحسينها، انظر صيغة الملف المبينة أدناه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_14" style=""><span class="com">#!/usr/bin/env python3</span><span class="pln">
</span><span class="com"># by Seth Kenlon</span><span class="pln">

</span><span class="com">## GPLv3</span><span class="pln">
</span><span class="com"># This program is free software: you can redistribute it and/or</span><span class="pln">
</span><span class="com"># modify it under the terms of the GNU General Public License as</span><span class="pln">
</span><span class="com"># published by the Free Software Foundation, either version 3 of the</span><span class="pln">
</span><span class="com"># License, or (at your option) any later version.</span><span class="pln">
</span><span class="com">#</span><span class="pln">
</span><span class="com"># This program is distributed in the hope that it will be useful, but</span><span class="pln">
</span><span class="com"># WITHOUT ANY WARRANTY; without even the implied warranty of</span><span class="pln">
</span><span class="com"># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU</span><span class="pln">
</span><span class="com"># General Public License for more details.</span><span class="pln">
</span><span class="com">#</span><span class="pln">
</span><span class="com"># You should have received a copy of the GNU General Public License</span><span class="pln">
</span><span class="com"># along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.</span></pre>

<p>
	الآن ضمن الملف نفسه، حدد الوحدات التي تحتاج لاستخدامها، وانتبه فلن تكتب إشارة # قبل التعليمات هنا، فهذه الإشارة تعني في لغة بايثون أنك تترك تعليقًا أو ملاحظةً لك أو لغيرك من المطورين.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_16" style=""><span class="kwd">import</span><span class="pln"> pygame  </span><span class="com"># ‫لاستيراد الوحدة pygame</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> sys    </span><span class="com"># تسمح لبايثون باستخدام نظام التشغيل</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> os    </span><span class="com"># لمساعدة بايثون في التعرف على نظام تشغيلك والتكيف معه</span></pre>

<p>
	ستلاحظ أن PyCharm يميز الكلمة <code>pygame</code> عن كل من <code>os</code> و <code>sys</code> ويعدّها خطأ، فهو لم يتعرف على هذه الوحدة بعد، إذ إنها غير مضمنة افتراضيًا مع بايثون كما ذكرنا في المقدمة، لذا ينبغي علينا تثبيتها قبل استخدامها.
</p>

<p>
	لتثبيتها يكفي أن تمرر مؤشر الفأرة فوق الكلمة وستظهر لك نافذة منبثقة تُطالبك بتثبيت الحزمة، انقر على رابط التثبيت وانتظر انتهاء العملية وستصبح Pygame جاهزة في بيئتك الافتراضية.
</p>

<p>
	أما لو أردت تثبيتها يدويًا بدون بيئة التطوير المتكاملة فاستخدم الأمر <code>pip</code>.
</p>

<h2>
	تقسيم شيفرة اللعبة
</h2>

<p>
	تقسيم الشيفرة البرمجية إلى مقاطع وإضافة <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r719/" rel="">التعليقات الكتلية</a> عليها هو عملية تنظيمية تفيد المبرمج في التخطيط لبرنامجه ومعرفة ما ينبغي كتابته في كل مقطع، وتسهل عليه المراجعة والتعديل فيما بعد، علمًا أن هذه التعليقات غير مرئية لبايثون أي أنه يتجاهلها ولا ينفذها وهي تظهر في برنامجك لدى قراءة الشيفرة المصدرية فقط.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_18" style=""><span class="str">'''
Variables
'''</span><span class="pln">

</span><span class="com"># ضع المتغيرات في هذا المقطع </span><span class="pln">


</span><span class="str">'''
Objects
'''</span><span class="pln">

</span><span class="com"># ضع هنا أصناف ودوال بايثون</span><span class="pln">


</span><span class="str">'''
Setup
'''</span><span class="pln">

</span><span class="com"># ضع كود التشغيل-لمرة-واحدة </span><span class="pln">


</span><span class="str">'''
Main Loop
'''</span><span class="pln">

</span><span class="com"># ضع هنا الحلقة الرئيسية للعبة </span></pre>

<p>
	لنضبط الآن قياس نافذة اللعبة، ويُراعى في ذلك اختلاف أحجام شاشات العرض بين الحواسيب والهواتف المحمولة وغيرها، لذا سنضع رقمًا وسطيًا يناسب معظم الشاشات، مع العلم بوجود طرق خاصة يتبّعها مطورو الألعاب الحديثة ليتكيف عرض اللعبة تلقائيًا مع حجم شاشة جهاز المستخدم، ولكن بصفتك في بداية الطريق يكفي أن تضع قياسًا واحدًا الآن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_20" style=""><span class="str">'''
Variables
'''</span><span class="pln">
worldx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">960</span><span class="pln">
worldy </span><span class="pun">=</span><span class="pln"> </span><span class="lit">720</span></pre>

<p>
	لننتقل للإعدادات الخاصة بمحرك Pygame، وتتمثل في ضبط معدل الأطر frame rate والساعة الداخلية internal clock (باستخدام الكلمة المفتاحية <code>init</code>).
</p>

<p>
	اكتب أولًا المتغيرات التالية في المقطع المخصص للمتغيرات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_22" style=""><span class="pln">fps   </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pln">  </span><span class="com"># معدل الأطر</span><span class="pln">
ani   </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pln">   </span><span class="com"># دورات الحركة</span></pre>

<p>
	والآن التعليمات الخاصة بالساعة الداخلية ضمن مقطع الإعدادات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_24" style=""><span class="str">'''
Setup
'''</span><span class="pln">

clock </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">time</span><span class="pun">.</span><span class="typ">Clock</span><span class="pun">()</span><span class="pln">
pygame</span><span class="pun">.</span><span class="pln">init</span><span class="pun">()</span></pre>

<h2>
	ضبط الخلفية
</h2>

<p>
	اختر صورة مناسبة للعبتك أو صممها بأحد برامج <a href="https://academy.hsoub.com/design/graphic/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%85%D9%81%D8%B5%D9%84%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%A7%D9%84%D8%AC%D8%B1%D8%A7%D9%81%D9%8A%D9%83%D9%8A-r558/" rel="">التصميم الجرافيكي</a> واحفظها باسم stage.png في المجلد images ضمن مجلد مشروعك.
</p>

<p>
	إليك بعض برامج التصميم المجانية التي يمكنك استخدامها على سبيل المثال لا الحصر:
</p>

<ul>
	<li>
		<a href="https://academy.hsoub.com/design/graphic/gimp/" rel="">جيمب</a> برنامج بسيط وسهل التعلم.
	</li>
	<li>
		<a href="https://academy.hsoub.com/design/graphic/krita/" rel="">كريتا</a> برنامج احترافي يحاكي الطلاء وأدوات الرسم الحقيقية لينتج صورًا مميزة.
	</li>
	<li>
		<a href="https://academy.hsoub.com/design/illustration/inkscape/" rel="">إنكسكيب</a> برنامج للرسوم المتجهة، توظف فيه الأشكال والخطوط ومنحنيات بيزير لرسم ما تريد.
	</li>
</ul>

<p>
	لا داعي لتكون صورة الخلفية معقدة في هذه المرحلة، إذ باستطاعتك تغييرها في أي وقت.
</p>

<p>
	والآن بعد أن جهزت الصورة اكتب التعليمات التالية ضمن مقطع الإعدادات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_26" style=""><span class="pln">world </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">display</span><span class="pun">.</span><span class="pln">set_mode</span><span class="pun">([</span><span class="pln">worldx</span><span class="pun">,</span><span class="pln">worldy</span><span class="pun">])</span><span class="pln">
backdrop </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">image</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="pln">os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">'images'</span><span class="pun">,</span><span class="str">'stage.png'</span><span class="pun">))</span><span class="pln">
backdropbox </span><span class="pun">=</span><span class="pln"> world</span><span class="pun">.</span><span class="pln">get_rect</span><span class="pun">()</span></pre>

<p>
	أما في حال رغبت بالاكتفاء بتلوين الخلفية فقط دون استخدام صورة، تكفيك هذه التعليمة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_28" style=""><span class="pln">world </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">display</span><span class="pun">.</span><span class="pln">set_mode</span><span class="pun">([</span><span class="pln">worldx</span><span class="pun">,</span><span class="pln"> worldy</span><span class="pun">])</span></pre>

<p>
	ولكن عليك أولًا تعريف الدرجات اللونية التي تفضلها للأحمر والأخضر والأزرق وفق ا<a href="https://academy.hsoub.com/design/graphic/%d9%85%d8%a7-%d9%87%d9%88-%d8%a7%d9%84%d9%86%d9%91%d8%b8%d8%a7%d9%85-%d8%a7%d9%84%d9%84%d9%88%d9%86%d9%8a-color-model-r11/" rel="">لنظام اللوني RGB</a>، وكتابتها ضمن مقطع المتغيرات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_31" style=""><span class="str">'''
Variables
'''</span><span class="pln">

BLUE  </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln">
BLACK </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">23</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23</span><span class="pun">)</span><span class="pln">
WHITE </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">254</span><span class="pun">,</span><span class="pln"> </span><span class="lit">254</span><span class="pun">,</span><span class="pln"> </span><span class="lit">254</span><span class="pun">)</span></pre>

<h2>
	اكتشاف الأخطاء
</h2>

<p>
	بيئة التطوير PyCharm مفيدة في اكتشاف مخالفات قواعد كتابة اللغة، فهي تُميّز التحذيرات باللون الأصفر والأخطاء باللون الأحمر، وتعد صارمة بعض الشيء في هذا المجال.
</p>

<p>
	التحذيرات في معظمها مخالفات بسيطة لأسلوب بايثون، وهو أمرٌ ستتعلمه وتتجاوزه مع الزمن والخبرة، لذا يعد تجاهلها آمنًا ولن يؤثر على عمل برنامجك.
</p>

<p>
	أما الأخطاء ينبغي عليك تصحيحها بالتأكيد لأنها تعطل تنفيذ البرنامج، فعلى سبيل المثال تشدد PyCharm على وجود ما يدل على إدخال سطر جديد في نهاية السطر الأخير من الكود، لذا عليك الضغط على زر Enter أو Return في لوحة المفاتيح في نهاية الكود حتى يعمل.
</p>

<h2>
	تشغيل اللعبة
</h2>

<p>
	احفظ التغييرات وشغل اللعبة، عبر الضغط على زر "تشغيل الوحدة Run Module" من قائمة تشغيل في IDLE أو الضغط على "تشغيل ملف Run file" من شريط الأدوات في PyCharm.
</p>

<p style="text-align: center;">
	<img alt="img-02-pycharm-button-run.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="107677" data-unique="i074qyfzy" style="" src="https://academy.hsoub.com/uploads/monthly_2022_09/img-02-pycharm-button-run.jpg.06fb8a38ea6279e59052480ee0b355b9.jpg">
</p>

<p>
	ويمكنك أيضًا التشغيل باستخدام نافذة الطرفية في يونيكس أو موجه سطر الأوامر في ويندوز بشرط أن تقوم بذلك وأنت في البيئة الافتراضية لبايثون.
</p>

<p>
	لكنك ستلاحظ بعد التشغيل أن لعبتك تعمل لمدة بسيطة جدًا أجزاء من الثانية فقط، وهو موضوع فقرتنا التالية.
</p>

<h2>
	حلقة التكرار
</h2>

<p>
	سيعمل البرنامج المكتوب بلغة بايثون لمرة واحدة فقط ما لم تعطه أوامر واضحة للتكرار، وبسبب سرعة معالجة الحواسيب فإن ذلك سيتم خلال أجزاء من الثانية حتى أن المستخدم لن يلحظ اللعبة، لنعالج الموضوع سنستخدم الحلقة <code>While</code> حتى تبقى اللعبة نشطة ومتاحة أمام المستخدم، وسنعيّن لهذه الحلقة متغير نسميه <code>main</code> ونعطيه قيمة ما، يضبط هذا المتغير تكرار الحلقة فهي ستتكرر باستمرار طالما أن قيمته لم تتغير، وبالمناسبة يطلق هذا النوع من الحلقات اسم الحلقة الرئيسية main loop.
</p>

<p>
	إذًا اكتب التعليمات التالية في برنامجك ضمن المقطع الخاص بالحلقة الرئيسية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_33" style=""><span class="str">'''
Main loop
'''</span><span class="pln">

</span><span class="kwd">while</span><span class="pln"> main</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> event </span><span class="kwd">in</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">event</span><span class="pun">.</span><span class="pln">get</span><span class="pun">():</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">QUIT</span><span class="pun">:</span><span class="pln">
            pygame</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
                sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">finally</span><span class="pun">:</span><span class="pln">
                main </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">KEYDOWN</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">key </span><span class="pun">==</span><span class="pln"> ord</span><span class="pun">(</span><span class="str">'q'</span><span class="pun">):</span><span class="pln">
                pygame</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
                sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">finally</span><span class="pun">:</span><span class="pln">
                main </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span></pre>

<p>
	ولا تنسَ الضغط على زر Enter أو Return في نهاية السطر الأخير، ليُضاف سطر فارغ في نهاية ملف البرنامج.
</p>

<p>
	حدّث أيضًا عالم اللعبة الافتراضي في الحلقة الرئيسية، بكتابة التعليمة التالية إن كنت تعتمد صورة للخلفية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_35" style=""><span class="pln">world</span><span class="pun">.</span><span class="pln">blit</span><span class="pun">(</span><span class="pln">backdrop</span><span class="pun">,</span><span class="pln"> backdropbox</span><span class="pun">)</span></pre>

<p>
	أو التعليمة أدناه في حال كنت تستخدم الألوان فقط للخلفية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_37" style=""><span class="pln">world</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">(</span><span class="pln">BLUE</span><span class="pun">)</span></pre>

<p>
	وأخيرًا أخبر الوحدة Pygame لتحدّث كل ما يظهر على الشاشة مع ساعتها الداخلية، وذلك وفق الأوامر الآتية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_39" style=""><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">display</span><span class="pun">.</span><span class="pln">flip</span><span class="pun">()</span><span class="pln">
 clock</span><span class="pun">.</span><span class="pln">tick</span><span class="pun">(</span><span class="pln">fps</span><span class="pun">)</span></pre>

<p>
	احفظ الملف وشغله، لتحظى بأكثر لعبة مملة رأيتها! وهذا طبيعي فلا شيء على الشاشة سوى خلفية اللعبة، لكن لا تقلق سنطوّرها تباعًا في المقالات اللاحقة، يمكنك الآن الضغط على <code>q</code> للخروج.
</p>

<h2>
	تثبيت البيئة الافتراضية لبايثون
</h2>

<p>
	تدير PyCharm المكتبات وتوفر لك متطلبات التشغيل المناسبة لبرنامجك، لكن المستخدم لن يشغل البرنامج أو اللعبة من PyCharm كما تفعل أنت، لذا يُعدّ تثبيت بيئتك الافتراضية خطوةً مهمة حتى أنها تعادل في الأهمية حفظ التعديلات على ملف البرنامج.
</p>

<p>
	استعرض قائمة الأدوات Tools واختر خيار "التزامن مع متطلبات بايثون" Sync Python Requirements، وستُحفظ عندها كافة اعتماديات المكتبات التي استخدمتها ضمن ملف نصي خاص يدعى requirements.txt.
</p>

<p>
	وفي المرة الأولى التي يعمل فيها هذا التزامن ستُطالبك PyCharm بتثبيت بعض الإضافات والاعتماديات اضغط على زر قبول، وسيتشكل عندها ملف المتطلبات الخاص بك requirements.txt ويُحفظ في مجلد مشروعك.
</p>

<p style="text-align: center;">
	<img alt="img-03-pycharm-requirements.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="107676" data-unique="gmd653gfn" style="" src="https://academy.hsoub.com/uploads/monthly_2022_09/img-03-pycharm-requirements.jpg.c7063400a6c7bf1aaa3e024826ef9e8a.jpg">
</p>

<h2>
	الكود النهائي للعبة
</h2>

<p>
	هذا ما سيبدو عليه كود اللعبة النهائي إلى الآن:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7873_41" style=""><span class="com">#!/usr/bin/env python3</span><span class="pln">
</span><span class="com"># by Seth Kenlon</span><span class="pln">

</span><span class="com"># GPLv3</span><span class="pln">
</span><span class="com"># This program is free software: you can redistribute it and/or</span><span class="pln">
</span><span class="com"># modify it under the terms of the GNU General Public License as</span><span class="pln">
</span><span class="com"># published by the Free Software Foundation, either version 3 of the</span><span class="pln">
</span><span class="com"># License, or (at your option) any later version.</span><span class="pln">
</span><span class="com">#</span><span class="pln">
</span><span class="com"># This program is distributed in the hope that it will be useful, but</span><span class="pln">
</span><span class="com"># WITHOUT ANY WARRANTY; without even the implied warranty of</span><span class="pln">
</span><span class="com"># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU</span><span class="pln">
</span><span class="com"># General Public License for more details.</span><span class="pln">
</span><span class="com">#</span><span class="pln">
</span><span class="com"># You should have received a copy of the GNU General Public License</span><span class="pln">
</span><span class="com"># along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> pygame
</span><span class="kwd">import</span><span class="pln"> sys
</span><span class="kwd">import</span><span class="pln"> os

</span><span class="str">'''
Variables
'''</span><span class="pln">

worldx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">960</span><span class="pln">
worldy </span><span class="pun">=</span><span class="pln"> </span><span class="lit">720</span><span class="pln">
fps </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pln">  </span><span class="com"># frame rate</span><span class="pln">
ani </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pln">   </span><span class="com"># animation cycles</span><span class="pln">
main </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

BLUE </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">)</span><span class="pln">
BLACK </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">23</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23</span><span class="pun">,</span><span class="pln"> </span><span class="lit">23</span><span class="pun">)</span><span class="pln">
WHITE </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">254</span><span class="pun">,</span><span class="pln"> </span><span class="lit">254</span><span class="pun">,</span><span class="pln"> </span><span class="lit">254</span><span class="pun">)</span><span class="pln">


</span><span class="str">'''
Objects
'''</span><span class="pln">

</span><span class="com"># put Python classes and functions here</span><span class="pln">


</span><span class="str">'''
Setup
'''</span><span class="pln">

clock </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">time</span><span class="pun">.</span><span class="typ">Clock</span><span class="pun">()</span><span class="pln">
pygame</span><span class="pun">.</span><span class="pln">init</span><span class="pun">()</span><span class="pln">
world </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">display</span><span class="pun">.</span><span class="pln">set_mode</span><span class="pun">([</span><span class="pln">worldx</span><span class="pun">,</span><span class="pln"> worldy</span><span class="pun">])</span><span class="pln">
backdrop </span><span class="pun">=</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">image</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="pln">os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">'images'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'stage.png'</span><span class="pun">))</span><span class="pln">
backdropbox </span><span class="pun">=</span><span class="pln"> world</span><span class="pun">.</span><span class="pln">get_rect</span><span class="pun">()</span><span class="pln">


</span><span class="str">'''
Main Loop
'''</span><span class="pln">

</span><span class="kwd">while</span><span class="pln"> main</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> event </span><span class="kwd">in</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">event</span><span class="pun">.</span><span class="pln">get</span><span class="pun">():</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">QUIT</span><span class="pun">:</span><span class="pln">
            pygame</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
                sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">finally</span><span class="pun">:</span><span class="pln">
                main </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

        </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">type </span><span class="pun">==</span><span class="pln"> pygame</span><span class="pun">.</span><span class="pln">KEYDOWN</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">key </span><span class="pun">==</span><span class="pln"> ord</span><span class="pun">(</span><span class="str">'q'</span><span class="pun">):</span><span class="pln">
                pygame</span><span class="pun">.</span><span class="pln">quit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
                sys</span><span class="pun">.</span><span class="pln">exit</span><span class="pun">()</span><span class="pln">
            </span><span class="kwd">finally</span><span class="pun">:</span><span class="pln">
                main </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
    world</span><span class="pun">.</span><span class="pln">blit</span><span class="pun">(</span><span class="pln">backdrop</span><span class="pun">,</span><span class="pln"> backdropbox</span><span class="pun">)</span><span class="pln">
    pygame</span><span class="pun">.</span><span class="pln">display</span><span class="pun">.</span><span class="pln">flip</span><span class="pun">()</span><span class="pln">
    clock</span><span class="pun">.</span><span class="pln">tick</span><span class="pun">(</span><span class="pln">fps</span><span class="pun">)</span></pre>

<h2>
	محطتنا التالية
</h2>

<p>
	سنطوّر اللعبة في مقالنا القادم ونضيف بعض الشخصيات والحركة لعالمها الفارغ، لذا يمكنك البدء بتحضير بعض الرسومات المميزة منذ الآن والمتابعة معنا.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/article/17/12/game-framework-python" rel="external nofollow">Build a game framework with Python using the Pygame module</a> لصاحبه Seth Kenlon.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%84%D8%A7%D8%B9%D8%A8-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%85%D9%83%D8%AA%D8%A8%D8%A9-pygame-r1705/" rel="">إضافة لاعب إلى لعبة مطورة باستخدام بايثون ومكتبة Pygame</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%86%D8%B1%D8%AF-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1703/" rel="">بناء لعبة نرد بسيطة بلغة بايثون</a>
	</li>
	<li>
		تعرف على أشهر <a href="https://academy.hsoub.com/programming/game-development/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8/" rel="">لغات برمجة الألعاب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%AD%D8%AC%D8%B1%D8%A9-%D9%88%D8%B1%D9%82%D8%A9-%D9%85%D9%82%D8%B5-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1701/" rel="">برمجة لعبة حجرة ورقة مقص باستخدام لغة بايثون </a>
	</li>
	<li>
		النسخة العربية الكاملة من كتاب <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1704</guid><pubDate>Mon, 12 Sep 2022 16:03:00 +0000</pubDate></item><item><title>&#x628;&#x646;&#x627;&#x621; &#x644;&#x639;&#x628;&#x629; &#x646;&#x631;&#x62F; &#x628;&#x633;&#x64A;&#x637;&#x629; &#x628;&#x644;&#x63A;&#x629; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D9%86%D8%B1%D8%AF-%D8%A8%D8%B3%D9%8A%D8%B7%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1703/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_09/631c18f533478_------.png.3ff63347d3d27aecae2d7f0e7bb95db2.png" /></p>
<p>
	لغة <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">بايثون</a> هي لغة برمجة سهلة التعلم موازنةً باللغات الأخرى مثل <a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D9%84%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-java-r599/" rel="">لغة جافا</a> ولغة C و<a href="https://academy.hsoub.com/programming/cpp/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-c-r802/" rel="">لغة ++C</a>، بالإضافة لكونها متعددة الأغراض فهي تستخدم في بناء <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8/" rel="">تطبيقات سطح المكتب</a> وألعاب الفيديو والرسومات ثلاثية الأبعاد والمواقع الإلكترونية وغيرها، زد على أنها لغة قوية برمجيًا وممتازة للتطبيقات المتقدمة، هذا كله جعلها خيارًا مثاليًا للجميع بصرف النظر عن عملهم وعمرهم وعدد سنوات خبرتهم، بالأخص للراغبين بتعلم البرمجة.
</p>

<p>
	هذا المقال جزءًا من سلسلة مقالات حول تصميم لعبة <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بلغة بايثون</a> ومكتبة Pygame وإليك كامل مقالات السلسلة:
</p>

<ol>
	<li>
		بناء لعبة نرد بسيطة بلغة بايثون.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-pygame-r1704/" rel="">بناء لعبة رسومية باستخدام بايثون ووحدة الألعاب PyGame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%84%D8%A7%D8%B9%D8%A8-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%85%D9%83%D8%AA%D8%A8%D8%A9-pygame-r1705/" rel="">إضافة لاعب إلى اللعبة المطورة باستخدام بايثون و Pygame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D8%B4%D8%AE%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-pygame-r1706/" rel="">تحريك شخصية اللعبة باستخدام PyGame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%B4%D8%AE%D8%B5%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D8%AF%D9%88-%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%B9%D8%A8%D8%B1-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-pygame-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1707/" rel="">إضافة شخصية العدو للعبة</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D9%86%D8%B5%D8%A7%D8%AA-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A9-pygame-r1730/" rel="">إضافة المنصات إلى لعبة بايثون باستخدام الوحدة Pygame</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%A7%D9%83%D8%A7%D8%A9-%D8%A3%D8%AB%D8%B1-%D8%A7%D9%84%D8%AC%D8%A7%D8%B0%D8%A8%D9%8A%D8%A9-%D9%81%D9%8A-%D9%84%D8%B9%D8%A8%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1731/" rel="">محاكاة أثر الجاذبية في لعبة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%AE%D8%A7%D8%B5%D9%8A%D8%A9-%D8%A7%D9%84%D9%82%D9%81%D8%B2-%D9%88%D8%A7%D9%84%D8%B1%D9%83%D8%B6-%D8%A5%D9%84%D9%89-%D9%84%D8%B9%D8%A8%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1732/" rel="">إضافة خاصية القفز والركض إلى لعبة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D8%A6%D8%B2-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1733/" rel="">إضافة الجوائز إلى اللعبة المطورة بلغة بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D9%86%D8%AA%D8%A7%D8%A6%D8%AC-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D8%B9%D8%B1%D8%B6%D9%87%D8%A7-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D8%B4%D8%A7%D8%B4%D8%A9-r1734/" rel="">تسجيل نتائج اللعبة المطورة بلغة بايثون وعرضها على الشاشة</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A2%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D9%82%D8%B0%D9%81-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%84%D8%B9%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D8%B1%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1735/" rel="">إضافة آليات القذف إلى اللعبة المطورة بلغة بايثون</a>.
	</li>
</ol>

<h2>
	تثبيت بايثون
</h2>

<p>
	ستحتاج إلى تثبيت بايثون الإصدار 3 لتتابع معنا خطوات العمل، لذا اتبع الخطوات المناسبة لنظام تشغيلك.
</p>

<p>
	إن كنت تستخدم <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">نظام تشغيل لينكس</a>، فستجد بايثون متوفرًا ضمنه على الأغلب، استعلم عن إصداره بالأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_14" style=""><span class="pln">python </span><span class="pun">--</span><span class="pln">version</span></pre>

<p>
	وانظر إلى الخرج فلو تبين لك أن الإصدار الموجود هو 2، أو أن بايثون غير مثبت على الإطلاق، حاول كتابة الأمر بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_16" style=""><span class="pln">python3 </span><span class="pun">--</span><span class="pln">version</span></pre>

<p>
	وإن حصلت على رسالة مفادها عدم التعرف على الأمر، فثبت بايثون 3 باستخدام مركز التطبيقات أو مدير الحزم الخاص بتوزيعة لينكس التي تعتمدها مثل <code>apt</code> في أوبونتو أو <code>dnf</code> في فيدورا.
</p>

<p>
	إن كانت توزيعتك هي فيدورا، ثبت بايثون 3 بالأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_19" style=""><span class="pln">sudo dnf install python3</span></pre>

<p>
	اتبع الخطوات نفسها في نظام تشغيل macOS لتتأكد من وجود بايثون 3 في النظام، وإن لم يكن مثبتًا، حمله من <a href="https://www.python.org/downloads/macos/" rel="external nofollow">موقع بايثون الرسمي</a> ثم ثبته، وهنا لديك هذه الطريقة فقط إذ إن macOS لا يملك مدير حزم.
</p>

<p>
	أما لو كنت تستخدم نظام تشغيل ويندوز فحمل بايثون 3 من <a href="https://www.python.org/downloads/windows/" rel="external nofollow">الموقع الرسمي</a> في البداية، ويمكنك الاستعانة بالمقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-%D9%88%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A8%D9%8A%D8%A6%D8%AA%D9%87-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%B9%D9%84%D9%89-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-10-r710/" rel="">كيفية تثبيت بايثون 3 وإعداد بيئته البرمجية على ويندوز 10</a> لهذا الغرض.
</p>

<p>
	عمومًا أيًا كان نظام التشغيل الذي تعتمده يمكنك الاستعانة بقسم <a href="https://academy.hsoub.com/programming/python/%d8%aa%d8%ab%d8%a8%d9%8a%d8%aa-%d8%a8%d8%a7%d9%8a%d8%ab%d9%88%d9%86-3-%d9%88%d8%a5%d8%b9%d8%af%d8%a7%d8%af-%d8%a8%d9%8a%d8%a6%d8%aa%d9%87%d8%a7-%d8%a7%d9%84%d8%a8%d8%b1%d9%85%d8%ac%d9%8a%d8%a9-r714/" rel="">تثبيت بايثون 3 وإعداد بيئتها البرمجية</a> من <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل لتعلم بايثون</a> المتوفر باللغة العربية على أكاديمية حسوب.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	تشغيل بيئة التطوير المتكاملة IDE
</h2>

<p>
	فعليًا كل ما تحتاج إليه لكتابة البرامج بلغة بايثون هو محرر شيفرات، ولكن استخدامك بيئة تطوير متكاملة IDE سيسهل عليك كتابة الشيفرة كثيرًا، فهذه البيئات تتضمن وظيفة محرر الشيفرات البسيط مع بعض الأدوات الإضافية الملائمة لكتابة شيفرة بايثون، ويتوفر العديد من الخيارات مفتوحة المصدر لبيئات التطوير المتكاملة، سنعرض منها IDLE 3 و PyCharm.
</p>

<h3>
	بيئة التطوير IDLE 3 الأساسية
</h3>

<p>
	هي بيئة التطوير الأساسية المضمنة مع بايثون، وتتميز هذه البيئة بتلوين أجزاء الشيفرة ما يسهل قراءتها واكتشاف الأخطاء على المبرمج، وأيضًا بإعطاء تلميحات لإكمال التعليمات البرمجية، بالإضافة إلى وجود زر تشغيل لتسهيل تشغيلها واختبارها.
</p>

<p style="text-align: center;">
	<img alt="img01-idle3.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="107672" data-unique="x5e8gu1j2" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_09/img01-idle3.jpg.9c57d2a7757ac60cf5e4e43fd6574a5b.jpg">
</p>

<p>
	لبدء استخدام هذه البيئة في لينكس ونظام macOS يكفيك كتابة الأمر <code>idle3</code> في نافذة الطرفية، أما لاستخدامها في ويندوز فابحث عن بايثون 3 في قائمة ابدأ وستجد هذه البيئة بين خياراتها، وفي حال لم تجد بايثون في قائمة ابدأ، افتح <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> بكتابة cmd في خانة البحث، ومن ثم اكتب ضمنه السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_21" style=""><span class="pln">  C</span><span class="pun">:</span><span class="pln">\Windows\py</span><span class="pun">.</span><span class="pln">exe</span></pre>

<p>
	وإن لم ينفع، أعد تثبيت بايثون وتأكد من تفعيل خيار إضافة بايثون إلى متغير البيئة PATH في أثناء عملية التثبيت.
</p>

<p>
	وإن لم ينفع ذلك أيضًا، <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D8%AF%D8%A7%D8%AE%D9%84-%D9%86%D8%B8%D8%A7%D9%85-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-%D9%81%D9%8A-%D8%A8%D9%8A%D8%A6%D8%A9-%D9%88%D9%87%D9%85%D9%8A%D8%A9-r577/" rel="">استخدم لينكس ضمن ويندوز نفسه</a> أو <a href="https://academy.hsoub.com/devops/linux/%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D9%88%D9%8A%D9%86%D8%AF%D9%88%D8%B2-r576/" rel="">بتثبيت لينكس مع ويندوز</a>، فهو في النهاية مجاني، وهذا الانتقال في البيئة لن يكلفك أكثر من حفظ ملفات بايثون خاصتك على قرص USB محمول ونقلها.
</p>

<h3>
	بيئة التطوير Pycharm النسخة المجتمعية
</h3>

<p>
	تعدّ Pycharm من أفضل بيئات التطوير مفتوحة المصدر المتوافقة مع بايثون، تتميز بتلوين الشيفرات لتسهيل قراءتها ولاكتشاف أخطاء الكتابة، وإكمال الأقواس وعلامات الاقتباس لضمان صيغ قواعدية سليمة، بالإضافة إلى ميزة ترقيم الأسطر المفيدة في أثناء تنقيح الأخطاء، وعلامات المسافة البادئة، وزر التشغيل لتسهيل اختبار الكود.
</p>

<p>
	لتستخدم هذه البيئة ثبّت النسخة المجتمعية من Pycharm، إن كان نظام تشغيلك هو لينكس فيمكنك تثبيتها باستخدام <code>flatpak</code> من منصة <a href="https://flathub.org/apps/details/com.jetbrains.PyCharm-Community" rel="external nofollow">FlatHub</a> أو تحميلها من <a href="https://www.jetbrains.com/pycharm/download/#section=linux" rel="external nofollow">الموقع الرسمي</a> و<a href="https://academy.hsoub.com/devops/linux/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D8%AB%D8%A8%D9%8A%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D9%81%D9%8A-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r634/" rel="">تثبيتها يدويًا</a>، أما لو كنت تعتمد ويندوز أو macOS فحمل نسخة البرنامج المناسبة لك من موقعه الرسمي، ومن ثم ثبتها.
</p>

<p>
	افتح البرنامج بعد التثبيت وأنشئ مشروعًا جديدًا.
</p>

<h2>
	أخبر بايثون بما تريد تنفيذه
</h2>

<p>
	الكلمات المفتاحية هي كلمات أساسية تُعرّف الدوال والأصناف وغيرها فهي إذًا الوسيلة التي تخبر بايثون بكل ما تريد تنفيذه. لتجرب إحداها، اكتب السطر التالي ضمن مشروعك الجديد الذي أنشأته على بيئة التطوير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_25" style=""><span class="kwd">print</span><span class="pun">(</span><span class="str">"Hello world."</span><span class="pun">)</span></pre>

<p>
	والآن شغله بإحدى الطريقتين:
</p>

<ul>
	<li>
		إن كنت تستخدم IDLE استعرض قائمة التشغيل وحدد الخيار "تشغيل الوحدة Run Module".
	</li>
	<li>
		أما في PyCharm انقر فوق الزر "تشغيل ملف Run file" ضمن أزرار شريط الأدوات.
	</li>
</ul>

<p style="text-align: center;">
	<img alt="img02-pycharm-button-run.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="107673" data-unique="pswunymwr" style="" src="https://academy.hsoub.com/uploads/monthly_2022_09/img02-pycharm-button-run.jpg.0e759a625830636a4cee41fccdf8a207.jpg">
</p>

<p>
	تطبع <code>print</code> -الكلمة المفتاحية التي جربناها- النص المكتوب بين علامتي اقتباس الموجود ضمن القوسين كما هو أيًا كان.
</p>

<p>
	وننوه لك أن بايثون افتراضيًا لا تستخدم إلّا الكلمات المفتاحية الأساسية مثل <code>print</code> و <code>help</code> وتلك الخاصة بالعمليات الحسابية الأساسية وما شابه، ولكن التعليمة <code>import</code> تتيح للمبرمج استيراد المزيد من الوظائف واستخدامها.
</p>

<h2>
	استخدام وحدة الرسم turtle في بايثون
</h2>

<p>
	اكتب الأسطر التالية في ملفك (بعد حذف الموجود فيه)، ومن ثم شغله:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_27" style=""><span class="kwd">import</span><span class="pln"> turtle

turtle</span><span class="pun">.</span><span class="pln">begin_fill</span><span class="pun">()</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">forward</span><span class="pun">(</span><span class="lit">100</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">left</span><span class="pun">(</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">forward</span><span class="pun">(</span><span class="lit">100</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">left</span><span class="pun">(</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">forward</span><span class="pun">(</span><span class="lit">100</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">left</span><span class="pun">(</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">forward</span><span class="pun">(</span><span class="lit">100</span><span class="pun">)</span><span class="pln">
turtle</span><span class="pun">.</span><span class="pln">end_fill</span><span class="pun">()</span></pre>

<p>
	لاحظ الأشكال التي يمكنك رسمها باستخدام مكتبة الرسم turtle والتي تدعى سلحفاة.
</p>

<p>
	لتمسح كل الموجود في مساحة الرسم، استخدم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_29" style=""><span class="pln">turtle</span><span class="pun">.</span><span class="pln">clear</span><span class="pun">()</span></pre>

<p>
	حاول تخمين معنى الدالة من اسمها الأجنبي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_31" style=""><span class="pln">turtle</span><span class="pun">.</span><span class="pln">color</span><span class="pun">(</span><span class="str">"blue"</span><span class="pun">)</span></pre>

<p>
	لابد أنك أصبت، سيصبح لون الخط أزرق بدل الأسود، فسهولة فهم التعليمات وتفسيرها هي إحدى ميزات بايثون.
</p>

<p>
	سنحاول الحصول على النتيجة نفسها بكتابة كود مختلف أكثر تعقيدًا، وذلك باستخدام الحلقة <code>while</code> التي ستوجه بايثون ليكرر عملية رسم الخط والانعطاف أربع مرات متتالية عوضًا عن إعادة كتابة السطر نفسه أربع مرات كما فعلنا في الفقرة السابقة، أما المتغير الذي سيحدد عدد مرات التكرار فيسمى العداد <code>counter</code>، ستتعلم المزيد عن المتغيرات لاحقًا، ولكن الآن اكتب الأسطر التالية ونفذها لترى كيفية تفاعل المتغير مع الحلقة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_33" style=""><span class="kwd">import</span><span class="pln"> turtle </span><span class="kwd">as</span><span class="pln"> t
</span><span class="kwd">import</span><span class="pln"> time

t</span><span class="pun">.</span><span class="pln">color</span><span class="pun">(</span><span class="str">"blue"</span><span class="pun">)</span><span class="pln">
t</span><span class="pun">.</span><span class="pln">begin_fill</span><span class="pun">()</span><span class="pln">

counter</span><span class="pun">=</span><span class="lit">0</span><span class="pln">

</span><span class="kwd">while</span><span class="pln"> counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">4</span><span class="pun">:</span><span class="pln">
    t</span><span class="pun">.</span><span class="pln">forward</span><span class="pun">(</span><span class="lit">100</span><span class="pun">)</span><span class="pln">
    t</span><span class="pun">.</span><span class="pln">left</span><span class="pun">(</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
    counter </span><span class="pun">=</span><span class="pln"> counter</span><span class="pun">+</span><span class="lit">1</span><span class="pln">

t</span><span class="pun">.</span><span class="pln">end_fill</span><span class="pun">()</span><span class="pln">
time</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span></pre>

<h2>
	تعلم بايثون عبر بناء لعبة بسيطة
</h2>

<p>
	هدفنا أساسًا <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a> عبر لغة بايثون، وتمهيدًا لاستخدامها في برمجة متقدمة تعتمد على الرسوميات، سنبدأ في هذا المقال بتعلم منطق اللعبة وكيفية تنظيم البيئة لبناء لعبة نرد بسيطة، يلعب فيها المستخدم مقابل الحاسوب ويلقي كل منهما النرد الافتراضي ليفوز صاحب القيمة الأعلى.
</p>

<h3>
	التخطيط للعبة
</h3>

<p>
	يبدأ<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/" rel=""> تطوير البرمجيات </a>عادةً بكتابة توثيق -ولو كان بسيطًا- للمتطلبات والأهداف يبين ما يفترض أن يكون عليه البرنامج، وفي لعبة النرد التي نعمل عليها سيتضمن التوثيق آلية العمل التالية.
</p>

<ol>
	<li>
		ابدأ اللعبة واضغط على زر Return أو Enter في لوحة المفاتيح لتبدأ دورة النرد.
	</li>
	<li>
		ستظهر النتيجة على الشاشة.
	</li>
	<li>
		ستُطالب بعدها بإعادة اللعب أو بالخروج.
	</li>
</ol>

<p>
	اللعبة بسيطة هذا صحيح، لكن توثيقها سيبين لك كمًّا لا بأس به من التفاصيل لتعمل عليها، مثل حاجتك لهذه المكونات.
</p>

<ul>
	<li>
		اللاعب: وهو العنصر البشري الذي سيستخدم اللعبة.
	</li>
	<li>
		AI: وترمز للذكاء الصنعي أي جهاز الحاسوب الذي يحاكي دور اللاعب الخصم، حتى يكون لديك فائز وخاسر.
	</li>
	<li>
		رقم عشوائي: الشائع في لعبة النرد استخدام نرد سداسي الأضلاع وبالتالي الرقم سيكون بين 1 و 6.
	</li>
	<li>
		عامل إجرائي: عملية رياضية بسيطة لموازنة رقمي اللاعبين ومعرفة الأكبر بينهما.
	</li>
	<li>
		رسالة فوز أو خسارة.
	</li>
	<li>
		مطالبة باتخاذ إجراء: إما اللعب مجددًا أو الخروج.
	</li>
</ul>

<h3>
	بناء الإصدار الأول من لعبة النرد
</h3>

<p>
	يتضمن الإصدار الأول من البرنامج الميزات الأساسية فقط، قلة هم من يبدأون تطوير برامجهم بميزات كاملة من أول إصدار.
</p>

<p>
	سنعرض مفهومين أساسيين قبل البدء.
</p>

<p>
	أولًا المتغير variable هو قيمة قابلة للتغيير، يستخدم لتخزين بعض المعلومات التي يحتاجها البرنامج وهو شائع الاستخدام في بايثون، فمثلًا المحرف x هو متغير في المعادلة التالية لأنه يشير إلى قيمةٍ ما أو ينوب عنها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_35" style=""><span class="pln">x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span></pre>

<p>
	وثانيًا العدد الصحيح integer فيشمل الأعداد الصحيحة الموجبة والسالبة، مثلًا: 1، ‎-1 ،14 ،10947.
</p>

<p>
	يتضمن إصدارنا الأول من اللعبة -والمسمى ألفا dice-alpha- متغيرين هما <code>player</code> و <code>ai</code>.
</p>

<p>
	لتبدأ بإنشائه، افتح مشروعًا جديدًا، وسمّه dice-alpha ثم اكتب ضمنه الكود التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_37" style=""><span class="kwd">import</span><span class="pln"> random

player </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
ai </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> player </span><span class="pun">&gt;</span><span class="pln"> ai </span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You win"</span><span class="pun">)</span><span class="pln">  </span><span class="com"># لاحظ المسافة البادئة</span><span class="pln">
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You lose"</span><span class="pun">)</span></pre>

<p>
	شغل اللعبة، ولاحظ أن وظيفتها الأساسية تعمل جيدًا، لكنها لا تبدو لعبةً متكاملة حتى الآن، فاللاعب لا يعرف نتيجة رميته ولا رمية الخصم، بالإضافة إلى أنها تنتهي مباشرةً حتى لو رغب اللاعب بالاستمرار، في الواقع يعدّ هذا شائعًا في الإصدارات الأولى من البرامج التي تسمى الإصدارات ألفا، لكنك في نهاية المطاف حللت الإجراء المهم في اللعبة وهو رمي النرد وأصبحت متيقنًا من عمله، فيمكنك استكمال بقية الميزات فيما بعد.
</p>

<h3>
	تحسين اللعبة
</h3>

<p>
	سنبني الآن الإصدار الثاني وهو الإصدار بيتا، بإضافة بعض الميزات ليصبح برنامجنا أقرب للعبة فعلًا.
</p>

<h4>
	1. إظهار تفاصيل النتيجة
</h4>

<p>
	في إصدارنا الأول أظهرنا للاعب عبارةً واحدة ليعرف إن فاز أو خسر، والآن سنطوّر الآلية ليعرف اللاعب مقدار العدد العشوائي الذي حصل عليه أي مقدار رميته للنرد وكذلك رمية الخصم.
</p>

<p>
	جرب إجراء التعديل التالي في برنامجك، ومن ثم شغله:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_39" style=""><span class="pln">player </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> player</span><span class="pun">)</span><span class="pln">

ai </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> ai</span><span class="pun">)</span></pre>

<p>
	بمجرد تشغيله ستحصل على خطأ، والسبب أن بايثون قد فهم برنامجك على أنه عملية حسابية تجمع حروف وأعداد، الحروف هي حروف العبارة التي ستُظهر النتيجة على الشاشة والعدد هو قيمة المتغير <code>player</code> أو المتغير <code>ai</code>، وهذا بالطبع غير منطقي وغير قابل للتطبيق.
</p>

<p>
	لتصحيح الخطأ سنجعل بايثون يتعامل مع قيمة المتغير بصفتها نصًا أو بالأحرى سلسلة نصية string بدلًا من معاملتها على أساس أنها عدد صحيح integer.
</p>

<p>
	نفذ إذًا التعديل التالي وأعد تشغيل اللعبة ولاحظ النتائج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_41" style=""><span class="pln">player </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">

ai </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">ai</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<h4>
	2. إضافة عامل التشويق
</h4>

<p>
	أضفنا بعض التسلية للعبة بإظهار النتائج، والآن سنضيف بعض الإثارة بإبطاء اللعبة في الأجزاء المشوقة، أما وسيلتنا لذلك فهي الدالة <code>time</code> إحدى دوال بايثون.
</p>

<p>
	نفذ التالي وتفقد النتائج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_43" style=""><span class="kwd">import</span><span class="pln"> random
</span><span class="kwd">import</span><span class="pln"> time

player </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">

ai </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer rolls...."</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
time</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer has rolled a "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> player </span><span class="pun">&gt;</span><span class="pln"> ai </span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You win"</span><span class="pun">)</span><span class="pln">   </span><span class="com"># لاحظ المسافة البادئة</span><span class="pln">
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You lose"</span><span class="pun">)</span></pre>

<h4>
	3. اكتشاف ثغرة التعادل
</h4>

<p>
	لو ثابرت على اللعب لفترةٍ جيدة، ستكتشف أن لعبتك لا تمتلك التعليمات اللازمة للتعامل مع حالة التعادل أي الحالة التي يحصل فيها اللاعب والحاسوب على القيمة نفسها، وهذه تعدّ ثغرة أو خطأ منطقي في الكود المكتوب، وحتى نعالجها سنجعل بايثون يتحقق من كون القيمتين متساويتين ويعرض عندها نتيجة التعادل.
</p>

<p>
	سنستخدم إشارة يساوي مزدوجة <code>==</code> للتحقق من تساوي القيمتين، إذ إن إشارة يساوي مفردة تعني في بايثون إسناد قيمة ما لأحد المتغيرات، أما بخصوص إدراج خيار ثالث للنتيجة، فسنعتمد على الكلمة المفتاحية <code>elif</code> (اختصار لـ else if) وهي مفيدة في الحالات التي تحتاج فيها للمطابقة مع ثلاثة احتمالات أو أكثر ضمن التعليمات الشرطية، بدلاً من مجرد احتمالين أحدهما صحيح والآخر خاطئ.
</p>

<p>
	الآن عدّل برنامجك ليغدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_45" style=""><span class="kwd">if</span><span class="pln"> player </span><span class="pun">&gt;</span><span class="pln"> ai </span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You win"</span><span class="pun">)</span><span class="pln">  </span><span class="com"># لاحظ المسافة البادئة</span><span class="pln">
</span><span class="kwd">elif</span><span class="pln"> player </span><span class="pun">==</span><span class="pln"> ai</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Tie game."</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You lose"</span><span class="pun">)</span></pre>

<p>
	وبعدها جرب اللعبة عدة مرات لتصادف حالة التعادل وتتأكد من عملها.
</p>

<p>
	يمكنك تعلم المزيد عن الدوال الشرطية في بايثون بالاطلاع على المقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D9%84%D9%8A%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B1%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r746/" rel="">كيفية كتابة التعليمات الشرطية في بايثون 3</a> ضمن دليل تعلم بايثون على أكاديمية حسوب.
</p>

<h3>
	بناء الإصدار النهائي للعبة
</h3>

<p>
	تفوق الإصدار بيتا وظيفيًا على الإصدار ألفا، وكان أكثر قربًا منه للألعاب الحقيقية، والآن سنبني إصدارنا النهائي الأكثر تكاملًا وستتعلم خلاله بناء دالتك الأولى في بايثون.
</p>

<p>
	الدالة function هي مجموعة من التعليمات البرمجية التي يتم استدعائها ضمن البرنامج بصورة كتلة واحدة متميزة، تفيد الدوال في تنظيم التطبيقات بالأخص تلك التي تتضمن عددًا كبيرًا من التعليمات البرمجية التي لا يُفترض أن تعمل في وقتٍ واحد وشروطٍ واحدة، والدوال هنا هي من تحدد ما الذي سيحدث وفي أي وقت وشرط سيحدث.
</p>

<p>
	عدّل الآن برنامجك ليصبح:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5490_7" style=""><span class="kwd">import</span><span class="pln"> random
</span><span class="kwd">import</span><span class="pln"> time

</span><span class="kwd">def</span><span class="pln"> dice</span><span class="pun">():</span><span class="pln">
    player </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You rolled "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">player</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">

    ai </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">randint</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="lit">6</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer rolls...."</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
    time</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"The computer has rolled a "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">ai</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> player </span><span class="pun">&gt;</span><span class="pln"> ai </span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You win"</span><span class="pun">)</span><span class="pln"> 
    </span><span class="kwd">elif</span><span class="pln"> player </span><span class="pun">==</span><span class="pln"> ai</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Tie game."</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"You lose"</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Quit? Y/N"</span><span class="pun">)</span><span class="pln">
    cont </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">()</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> cont </span><span class="pun">==</span><span class="pln"> </span><span class="str">"Y"</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> cont </span><span class="pun">==</span><span class="pln"> </span><span class="str">"y"</span><span class="pun">:</span><span class="pln">
        exit</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">elif</span><span class="pln"> cont </span><span class="pun">==</span><span class="pln"> </span><span class="str">"N"</span><span class="pln"> </span><span class="kwd">or</span><span class="pln"> cont </span><span class="pun">==</span><span class="pln"> </span><span class="str">"n"</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">pass</span><span class="pln">
    </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"I did not understand that. Playing again."</span><span class="pun">)</span></pre>

<p>
	في هذا الإصدار سيُسأل اللاعب إن كان يريد الخروج من اللعبة، وفي حال أجاب بنعم عبر كتابة Y أو y، فإن بايثون سينهي استدعاء الدالة وتتم مغادرة اللعبة.
</p>

<p>
	إذًا فقد بنيت دالتك الأولى المسماة نرد dice لكنها لن تعمل ما لم تستدعيها ضمن البرنامج، وهذا ما ستفعله الحلقة <code>While True</code>.
</p>

<p>
	أضف التعليمات التالية في نهاية برنامجك، وانتبه للمسافات البادئة فقد أعدنا كتابة آخر سطرين في الكود السابق عن قصد ليكون السياق واضحًا أمامك:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4552_49" style=""><span class="pun">…</span><span class="pln">
</span><span class="com"># الحلقة الأساسية</span><span class="pln">
</span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"Press return to roll your die."</span><span class="pun">)</span><span class="pln">
    roll </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">()</span><span class="pln">
    dice</span><span class="pun">()</span></pre>

<p>
	هذه الحلقة هي أول ما سيعمل في برنامجك، وتتكرر دومًا ما لم يكسرها أمرٌ من بايثون، إذ إن شرط عملها محقق دائمًا وتلقائيًا فهو True، لاحظ تسلسل عملها فهي تطالب المستخدم أولًا باتخاذ إجراء حتى يبدأ اللعب، ومن ثم تستدعي الدالة dice، ومن بعدها بناءً على إجابة المستخدم ستستمر الحلقة بالعمل أو تتوقف.
</p>

<p>
	ومن الجدير بالذكر أن الحلقات هي واحدة من أشيع الطرق المستخدمة لبدء التطبيقات فهي تضمن بقاء التطبيق متاحًا أمام المستخدم لفترة كافية تمكنه من استخدام وظائفه المختلفة.
</p>

<p>
	يمكنك تعلم المزيد عن الحلقات التكرارية بالاطلاع على المقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-while-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r509/" rel="">كيفية إنشاء حلقات تكرار while في بايثون 3</a>، والمقال <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-break-%D9%88-continue-%D9%88-pass-%D8%B9%D9%86%D8%AF-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%83%D8%B1%D8%A7%D8%B1-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r511/" rel="">كيفية استخدام تعابير break و continue و pass عند التعامل مع حلقات التكرار في بايثون 3</a> ضمن دليل تعلم بايثون على أكاديمية حسوب.
</p>

<h2>
	ختامًا
</h2>

<p>
	تعرفنا في هذا المقال على أساسيات البرمجة بلغة بايثون، وسنتعلم في مقالنا التالي كيفية بناء لعبة فيديو باستخدام الوحدة <a href="https://www.pygame.org" rel="external nofollow">PyGame</a> المتخصصة في هذا المجال.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://opensource.com/article/17/10/python-101" rel="external nofollow">Learn how to program in Python by building a simple dice game</a> لصاحبه Seth Kenlon.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D8%A8%D9%86%D8%A7%D8%A1-%D9%84%D8%B9%D8%A8%D8%A9-%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D9%88%D9%88%D8%AD%D8%AF%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-pygame-r1704/" rel="">بناء لعبة رسومية باستخدام بايثون ووحدة الألعاب Pygame</a>
	</li>
	<li>
		تعرف على أشهر <a href="https://academy.hsoub.com/programming/game-development/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8/" rel="">لغات برمجة الألعاب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%84%D8%B9%D8%A8%D8%A9-%D8%AD%D8%AC%D8%B1%D8%A9-%D9%88%D8%B1%D9%82%D8%A9-%D9%85%D9%82%D8%B5-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1701/" rel="">برمجة لعبة حجرة ورقة مقص باستخدام لغة بايثون </a>
	</li>
	<li>
		النسخة العربية الكاملة من كتاب <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1703</guid><pubDate>Tue, 06 Sep 2022 16:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62D;&#x644;&#x64A;&#x644; &#x627;&#x644;&#x633;&#x644;&#x627;&#x633;&#x644; &#x627;&#x644;&#x632;&#x645;&#x646;&#x64A;&#x629; &#x639;&#x628;&#x631; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D8%B2%D9%85%D9%86%D9%8A%D8%A9-%D8%B9%D8%A8%D8%B1-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1617/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba20535caca_-01.jpg.c37b158b6c6bd11135e79f12beae64dc.jpg" /></p>

<p>
	تُعَدّ السلسلة الزمنية time series تسلسلًا sequence من القياسات المأخوذة من نظام والمتغيرة بمرور الزمن، ومن الأمثلة الشهيرة <a href="https://www.meemapps.com/term/hockey-stick-graph" rel="external nofollow">"مخطط عصا الهوكي"</a> الذي يُظهر متوسط درجة الحرارة في العالم مع مرور الوقت، كما يمكنك الاطلاع على <a href="https://en.wikipedia.org/wiki/Hockey_stick_graph" rel="external nofollow">صفحة ويكيبيديا</a> للمزيد من المعلومات، حيث أنّ مصدر المثال الذي نناقشه في هذا المقال هو البيانات المتاحة من موقع kaggle والتي تُعطي <a href="https://www.kaggle.com/datasets/timmate/avocado-prices-2020?resource=download" rel="external nofollow">بيانات مبيعات الأفوكادو للأعوام 2015-2020 في الولايات المتحدة الأمريكية</a> وذلك لكل من نوعي الأفوكادو العادي Conventional والأفوكادو العضوي Organic.
</p>

<p>
	نأمل أن تجدوا هذا المقال مثيرًا للاهتمام. توجد الشيفرة الخاصة بهذا المقال في <a href="https://colab.research.google.com/drive/1gUN5m4Qjw8ErbMc6yUxrazgwz6ZcsaHG" rel="external nofollow">الرابط</a> على Google Colab أو يمكنك تنزيلها من المرفقات.
</p>

<h2>
	استيراد وتنظيف البيانات
</h2>

<p>
	توجد البيانات المستخدمة في موقع <a href="https://www.kaggle.com/datasets/timmate/avocado-prices-2020" rel="external nofollow">Kaggle</a> كما أرفقناها في نهاية المقال، وتُعَدّ الشيفرة التالية مسؤولةً عن قراءتها على هيئة إطار بيانات بانداز pandas DataFrame:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_7" style="">
<span class="pln">transactions </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="pln">read_csv</span><span class="pun">(</span><span class="str">'avocado.csv'</span><span class="pun">,</span><span class="pln"> parse_dates</span><span class="pun">=[</span><span class="lit">0</span><span class="pun">])</span></pre>

<p>
	حيث أنّ مهمة الوسيط <code>parse_dates</code> هي الإشارة إلى الدالة <code>read_csv</code> لتفسير القيم الموجودة في العمود رقم 0 (العمود الأول) على أساس تواريخ ومن ثم تحويلها إلى كائنات <code>datetime64</code> من نوع نمباي NumPy، كما يحتوي إطار البيانات DataFrame على سطر لكل عملية مبيع مسجلة بالإضافة إلى الأعمدة التالية:
</p>

<ul>
<li>
		<code>average_price</code>: السعر الوسطي مقدرًا بالدولار.
	</li>
	<li>
		<code>total_volume</code>: الحجم الكلي المُباع.
	</li>
	<li>
		<code>type</code>: نوع الأفوكادو: عادي أم عضوي.
	</li>
	<li>
		<code>date</code>: تاريخ عملية المبيع.
	</li>
	<li>
		<code>year</code>: عام المبيع.
	</li>
	<li>
		<code>geography</code>: الولاية والمدينة.
	</li>
</ul>
<p>
	علمًا أنّ كل عملية مبيع هي حدث في الزمن، لذا يمكننا معاملة مجموعة البيانات هذه على أساس سلسلة زمنية time series، لكن المسافة الزمنية بين الأحداث غير متساوية، إذ يتراوح عدد عمليات المبيع المسجلة بين 0 إلى عدة عشرات يوميًا، حيث تتطلب الكثير من طرق تحليل سلاسل البيانات أن تكون الأزمنة بين الأحداث متساويةً أو على الأقل فإن الأمور أكثر بساطةً إن كانت الأزمنة متساويةً، ومن أجل توضيح هذه الطرق قسَّمنا مجموعة البيانات هذه إلى مجموعات حسب الكمية ومن ثم حوَّلنا كل مجموعة إلى سلسلة تتباعد فيها الأحداث تباعدًا متساويًا عن بعضها وذلك عن طريق حساب متوسط السعر اليومي.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_9" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GroupByTypeAndDay</span><span class="pun">(</span><span class="pln">transactions</span><span class="pun">):</span><span class="pln">
    groups </span><span class="pun">=</span><span class="pln"> transactions</span><span class="pun">.</span><span class="pln">groupby</span><span class="pun">(</span><span class="str">'type'</span><span class="pun">)</span><span class="pln">
    dailies </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> group </span><span class="kwd">in</span><span class="pln"> groups</span><span class="pun">:</span><span class="pln">
        dailies</span><span class="pun">[</span><span class="pln">name</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">GroupByDay</span><span class="pun">(</span><span class="pln">group</span><span class="pun">)</span><span class="pln">       

    </span><span class="kwd">return</span><span class="pln"> dailies</span></pre>

<p>
	علمًا أن <code>groupby</code> هو تابع خاص بأُطر البيانات وهو يُعيد كائن GroupBy باسم <code>groups</code>، حيث يُستخدم الكائن <code>groups</code> في حلقة <code>for</code> ليمر مرورًا تكراريًا على أسماء المجموعات وأُطر البيانات التي تمثِّلها، وبما أنّ للنوع احتمالين هما عادي أو عضوي، فسينتج مجموعتين تحمل هذه الأسماء -أي عادي وعضوي-، حيث تمر الحلقة مرورًا تكراريًا على المجموعات وتستدعي التابع <code>GroupByDay</code> الذي يحسب متوسط السعر اليومي ويُعيد إطار بيانات جديد:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_11" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GroupByDay</span><span class="pun">(</span><span class="pln">transactions</span><span class="pun">,</span><span class="pln"> func</span><span class="pun">=</span><span class="pln">np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">):</span><span class="pln">
    grouped </span><span class="pun">=</span><span class="pln"> transactions</span><span class="pun">[[</span><span class="str">'date'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'average_price'</span><span class="pun">]].</span><span class="pln">groupby</span><span class="pun">(</span><span class="str">'date'</span><span class="pun">)</span><span class="pln">
    daily </span><span class="pun">=</span><span class="pln"> grouped</span><span class="pun">.</span><span class="pln">aggregate</span><span class="pun">(</span><span class="pln">func</span><span class="pun">)</span><span class="pln">

    daily</span><span class="pun">[</span><span class="str">'date'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">index
    start </span><span class="pun">=</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">date</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
    one_year </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">timedelta64</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Y'</span><span class="pun">)</span><span class="pln">
    daily</span><span class="pun">[</span><span class="str">'years'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">daily</span><span class="pun">.</span><span class="pln">date </span><span class="pun">-</span><span class="pln"> start</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> one_year

    </span><span class="kwd">return</span><span class="pln"> daily</span></pre>

<p>
	يُعَدّ المعامِل <code>transactions</code> إطار بيانات ويحتوي على العمودين <code>date</code> و<code>average_price</code>، لذا سنحدِّد هذين العمودين ومن ثم نجمِّعهما على أساس التاريخ، وتكون النتيجة هي <code>grouped</code> التي تُعَدّ خريطةً map تحوِّل كل تاريخ إلى إطار بيانات يحتوي على الأسعار التي أُبلِغ عنها في ذلك التاريخ المحدَّد، ويُعَدّ <code>aggregate</code> تابع تجميع <code>GroupB</code> يمر مرورًا تكراريًا على المجموعات ويطبق دالةً على كل عمود من المجموعة، وفي حالتنا هذه لا يوجد سوى عمود واحد هو <code>average_price</code> الذي يمثِّل السعر الوسطي.
</p>

<p>
	تُخزَّن البيانات في هذه الأُطر على أساس كائنات نمباي NumPy من نوع <code>datetime64</code> والتي تمثَّل على أساس أعداد صحيحة حجمها 46 بتًا -أي 64-bit integers- بالنانو ثانية، لكن سيكون من المناسب التعامل في بعض التحليلات القادمة مع وحدات قياس زمنية مألوفة أكثر بالنسبة للبشر مثل السنوات، أي يُضيف التابع <code>GroupByDay</code> عمودًا اسمه <code>date</code> عن طريق نسخ الفهرس ومن ثم يُضيف العمود <code>years</code> الذي يحتوي على عدد السنوات التي مرت منذ أول عملية مبيع وهو عدد عشري، كما يحتوي إطار البيانات الناتج على <code>average_price</code> الذي يمثِّل السعر الوسطي بالدولار والعمود <code>date</code> الذي يمثِّل التاريخ و<code>years</code>.
</p>

<h2>
	رسم المخططات
</h2>

<p>
	تكون نتيجة <code>GroupByTypeAndDay</code> خريطةً map تحوِّل كل نوع إلى إطار بيانات يحتوي على الأسعار اليومية، وإليك الشيفرة التي استخدمناها لرسم السلسلتين الزمنيتين:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_13" style="">
<span class="pln">    thinkplot</span><span class="pun">.</span><span class="typ">PrePlot</span><span class="pun">(</span><span class="pln">rows</span><span class="pun">=</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> daily</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">dailies</span><span class="pun">.</span><span class="pln">items</span><span class="pun">()):</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">SubPlot</span><span class="pun">(</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        title </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Average Price'</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="str">''</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">Config</span><span class="pun">(</span><span class="pln">ylim</span><span class="pun">=[</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">],</span><span class="pln"> title</span><span class="pun">=</span><span class="pln">title</span><span class="pun">)</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">Scatter</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">.</span><span class="pln">average_price</span><span class="pun">,</span><span class="pln"> s</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="pln">name</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> i </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">:</span><span class="pln">
            pyplot</span><span class="pun">.</span><span class="pln">xticks</span><span class="pun">(</span><span class="pln">rotation</span><span class="pun">=</span><span class="lit">30</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            thinkplot</span><span class="pun">.</span><span class="typ">Config</span><span class="pun">(</span><span class="pln">xticks</span><span class="pun">=[])</span></pre>

<p>
	تشير الدالة <code>PrePlot</code> في حال وجود الوسيط <code>rows=2</code> إلى أننا سنرسم مخططين فرعيين في السطرين، حيث تمر الحلقة مرورًا تكراريًا على أطر البيانات وتنشئ مخطط انتشار لكل إطار، ومن الشائع رسم السلاسل الزمنية مع قطع مستقيمة تصل بين النقطة والأخرى، لكن توجد في هذه الحالة العديد من نقاط البيانات والأسعار متغيرةً تغيرًا كبيرًا، لذا لن يكون من المفيد إضافة القطع المستقيمة، وبما أن التواريخ موجودة على محور x -أي المحور الأفقي-، فسنستخدِم <code>pyplot.xticks</code> للتدوير بمقدار 30 درجة، وبذلك نجعلها مقروءةً أكثر.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102166" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ad16825e_12.1.png.3b8753d93970ca9a939c48a4cdb42c21.png" rel=""><img alt="الشكل 12.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102166" data-unique="bnhcxs84i" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ad1c7e66_12.1.thumb.png.74a5ec03fefcd760ca052bda9964b95e.png" style="width: 550px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق سلسلةً زمنيةً تمثِّل متوسط السعر اليومي، وذلك بالنسبة للنوع العادي والنوع العضوي، كما يُظهر الشكل السابق النتيجة، حيث نرى صفةً واضحةً في هذه المخططات وهي وجود بعض الفجوات الزمنية ، فمن المحتمل أنّ جمع البيانات في ذلك الوقت كان متوقفًا أو أنَّ البيانات غير متوافرة، لكننا سنفكر لاحقًا في طرق للتعامل مع هذه البيانات المفقودة على أية حال.
</p>

<p>
	يبدو لنا من النظر إلى المخطط أنّ أسعار الأفوكادو وصلت لذروتها في عام 2017 ثم عاودت بالتذبذب إلا أن أسعار الأفوكادو العضوي حافظت دائماً على ارتفاعها مقارنة بالأفوكادو العادي.
</p>

<h2>
	الانحدار الخطي
</h2>

<p>
	على الرغم من وجود توابع خاصة بتحليل السلاسل الزمنية، إلا أنّ الطريقة الأبسط التي تحل الكثير من المسائل تتمثل في تطبيق أدوات ذات غرض عام مثل الانحدار الخطي، إذ تأخذ الدالة التالية إطار بيانات للأسعار اليومية وتحسب ملائمة مربعات صغرى، ومن ثم تُعيد النموذج والكائنات الناتجة من StatsModels:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_16" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">RunLinearModel</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">):</span><span class="pln">
    model </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="str">'average_price ~ years'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">daily</span><span class="pun">)</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">fit</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> model</span><span class="pun">,</span><span class="pln"> results</span></pre>

<p>
	يمكننا المرور مرورًا تكراريًا على النوعين المختلفين (العادي والعضوي) وملاءمة نموذج لكل منها:
</p>

<pre class="ipsCode">
dailies = GroupByTypeAndDay(transactions)
for name, daily in dailies.items():
    model, results = RunLinearModel(daily)
    print(results.summary()
</pre>

<p>
	إليك النتائج:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				النوع
			</th>
			<th>
				نقطة التقاطع
			</th>
			<th>
				الميل
			</th>
			<th>
				R<sup>2</sup>
</th>
		</tr></thead>
<tbody>
<tr>
<td>
				عادي
			</td>
			<td>
				1.1348
			</td>
			<td>
				0.0034
			</td>
			<td>
				0.001
			</td>
		</tr>
<tr>
<td>
				عضوي
			</td>
			<td>
				1.6690
			</td>
			<td>
				0.0183-
			</td>
			<td>
				0.044
			</td>
		</tr>
</tbody>
</table>
<p>
	تشير قيم الميل المُقدَّرة إلى انخفاض سعر الأفوكادو العضوي قليلًا (2 سنتًا) في كل سنة ضمن الفترة الزمنية التي رُصدَت الأسعار فيها؛ أما الأفوكادو العادي فقد ارتفع سعره قليلًا (أقل من 1 سنتًا في كل سنة)، علمًا أن التقديرات هذه ذات دلالة إحصائية وقيمها الاحتمالية صغيرة جدًا.
</p>

<p>
	إنّ قيمة R<sup>2</sup> هي 0.044 للأفوكادو العضوي أي أن الزمن بصفته متغير توضيحي يمثِّل 4%‎ من التباين المرصود في السعر، لكن يكون التغير في السعر أصغر والتباين في الأسعار أقل بالنسبة للأفوكادو العادي، لذا فإن قيم R<sup>2</sup> أصغر لكنها ما زالت ذات دلالة إحصائية، وترسم الشيفرة التالية الأسعار المرصودة والقيم الملاءمة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_18" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">PlotFittedValues</span><span class="pun">(</span><span class="pln">model</span><span class="pun">,</span><span class="pln"> results</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">
    years </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">exog</span><span class="pun">[:,</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
    values </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">endog
    thinkplot</span><span class="pun">.</span><span class="typ">Scatter</span><span class="pun">(</span><span class="pln">years</span><span class="pun">,</span><span class="pln"> values</span><span class="pun">,</span><span class="pln"> s</span><span class="pun">=</span><span class="lit">15</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="pln">label</span><span class="pun">)</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">years</span><span class="pun">,</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">fittedvalues</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">'model'</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	يحتوي <code>model</code> على <code>exog</code> و<code>endog</code> كما رأينا في قسم سابق في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-regression-%D9%88%D8%AF%D9%88%D8%B1%D9%87-%D9%81%D9%8A-%D9%85%D9%84%D8%A7%D8%A1%D9%85%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9-%D9%85%D8%B9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%A7%D8%AD%D8%A9-r1493/" rel="">الانحدار الإحصائي regression</a>، حيث أنهما مصفوفتا نمباي NumPy تحتويان على المتغيرات الخارجية -أي التوضيحية- والمتغيرات الداخلية -أي التابعة-.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102167" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ad49b666_12.2.png.7774e534e63e3561bb29f13b0b41b9ac.png" rel=""><img alt="الشكل 12.2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102167" data-unique="5q43njv74" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ad768d88_12.2.thumb.png.fc978a1289404dee6480c29a274fe36e.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق سلسلةً زمنيةً للأسعار اليومية، بالإضافة إلى ملاءمة مربعات صغرى خطية linear least squares fit، كما تُنشئ الدالة <code>PlotFiitedValues</code> مخططًا انتشاريًا لنقاط البيانات ورسمًا خطيًا للقيم الملائمة، ويُظهر الشكل السابق نتائج الأفوكادو العضوي، حيث يبدو أنّ النموذج يلائم البيانات ملاءمةً جيدةً ولكن الانحدار الخطي ليس الخيار الأفضل لهذه البيانات:
</p>

<ul>
<li>
		أولًا: ما من سبب يدفعنا للتوقُّع أنّ الاتجاه الذي استمر فترةً طويلةً هو خط أو دالة بسيطة، والذي يحدِّد الأسعار عمومًا هو العرض والطلب وكلاهما يختلف بمرور الزمن بطرق لا يمكن التنبؤ بها.
	</li>
	<li>
		ثانيًا: يعطي نموذج الانحدار الخطي وزنًا متساويًا لكل البيانات سواءً البيانات الحديثة أو السابقة، لكن يجب علينا إعطاء البيانات الحديثة وزنًا أكبر.
	</li>
	<li>
		أخيرًا: تقول إحدى الفرضيات حول الانحدار الخطي أنّ الرواسب residuals هي ضجيج غير مترابط، لكن غالبًا ما تكون هذه الفرضية غير صحيحة في حال تعاملنا مع سلاسل زمنية لأن القيم المتتالية مترابطة.
	</li>
</ul>
<p>
	يقدِّم القسم التالي بديلًا أفضل للتعامل مع بيانات السلاسل الزمنية.
</p>

<h2>
	المتوسطات المتحركة
</h2>

<p>
	تعتمد معظم السلاسل الزمنية على افتراض النمذجة عادةً والذي يقول أنّ السلسلة المرصودة هي ناتج جمع المكوِّنات الثلاثة التالية:
</p>

<ul>
<li>
		الاتجاه: هو دالة ملساء -أي منتظمة- تخزِّن التغييرات المستمرة.
	</li>
	<li>
		الموسمية: هو تباين دوري، وقد يتضمن دورات يومية أو أسبوعية أو شهرية أو سنوية.
	</li>
	<li>
		الضجيج: التباين العشوائي حول الاتجاه طويل الأمد.
	</li>
</ul>
<p>
	يُعَدّ الانحدار أحد طرق استخراج الاتجاه من سلسلة معينة تمامًا كما رأينا في القسم السابق، لكن يوجد بديل آخر في حال لم يكن الاتجاه دالةً بسيطةً وهو <strong>المتوسط المتحرك moving average</strong>، حيث يقسِّم المتوسط المتحرك السلسلة إلى مناطق متداخلة تُدعى <strong>نوافذ windows</strong> ومن ثم يحسب متوسط القيم في كل نافذة window.
</p>

<p>
	يُعَدّ <strong>المتوسط المتدحرج rolling mean</strong> الذي يحسب متوسط القيم في كل نافذة من أبسط أنواع المتوسطات المتحركة، فإذا كان حجم النافذة 3 مثلًا، فسيحسب المتوسط المتدحرج متوسط القيم من 0 إلى 2 ومن 1 إلى 3 ومن 2 إلى 4 وهكذا دواليك، كما يُبين المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_23" style="">
<span class="pln">df</span><span class="pun">=</span><span class="pln">pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">(</span><span class="pln">np</span><span class="pun">.</span><span class="pln">arange</span><span class="pun">(</span><span class="lit">10</span><span class="pun">))</span><span class="pln">
roll_mean </span><span class="pun">=</span><span class="pln"> df</span><span class="pun">.</span><span class="pln">rolling</span><span class="pun">(</span><span class="lit">3</span><span class="pun">).</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">roll_mean</span><span class="pun">)</span><span class="pln">

nan</span><span class="pun">,</span><span class="pln">  nan</span><span class="pun">,</span><span class="pln">   </span><span class="lit">1</span><span class="pun">,</span><span class="pln">   </span><span class="lit">2</span><span class="pun">,</span><span class="pln">   </span><span class="lit">3</span><span class="pun">,</span><span class="pln">   </span><span class="lit">4</span><span class="pun">,</span><span class="pln">   </span><span class="lit">5</span><span class="pun">,</span><span class="pln">   </span><span class="lit">6</span><span class="pun">,</span><span class="pln">   </span><span class="lit">7</span><span class="pun">,</span><span class="pln">   </span><span class="lit">8</span></pre>

<p>
	نلاحظ أنّ أول قيمتين هما <code>nan</code> -أي ليس عددًا-؛ أما القيمة التالية فهي متوسط العناصر الثلاث الأولى أي 0 و1 و2، والقيمة التالية هي متوسط 1 و2 و3، وهكذا، حيث يتعين علينا التعامل مع القيم المفقودة في البداية وقبل تطبيق <code>rolling</code> على بيانات الأفوكادو، ونلاحظ في الواقع في الفترة المرصودة وجود عدة أيام لم يُبلَّغ فيها عن أيّ عمليات مبيع لنوع معين أو أكثر من نوع -أي النوع العادي أو العضوي- من الأفوكادو.
</p>

<p>
	لم تكن هذه التواريخ موجودةً في أيّ من أُطر البيانات التي استخدمناها سابقًا، حيث كان الفهرس يتخطى الأيام التي لا تحتوي على أية بيانات، لكن بالنسبة للتحليل التالي، فنحتاج إلى تمثيل البيانات المفقودة تمثيلًا صريحًا، حيث يمكننا إنجاز ذلك عن طريق إعادة فهرسة reindexing إطار البيانات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_25" style="">
<span class="pln">dates </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="pln">date_range</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">.</span><span class="pln">index</span><span class="pun">.</span><span class="pln">min</span><span class="pun">(),</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">index</span><span class="pun">.</span><span class="pln">max</span><span class="pun">())</span><span class="pln">
reindexed </span><span class="pun">=</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">reindex</span><span class="pun">(</span><span class="pln">dates</span><span class="pun">)</span></pre>

<p>
	يحسب السطر الأول من الشيفرة السابقة مجال التواريخ الذي يتضمن تاريخ كل الأيام من بداية فترة رصد عمليات المبيع حتى اليوم الأخير؛ أما السطر الثاني فينشئ إطار بيانات جديد يحتوي على كل البيانات الموجودة في <code>daily</code> بالإضافة إلى الأسطر التي تحتوي على جميع التواريخ ذات القيمة <code>nan</code>، حيث يمكننا الآن رسم المتوسط المتجدد كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_27" style="">
<span class="pln">roll_mean </span><span class="pun">=</span><span class="pln"> reindexed</span><span class="pun">.</span><span class="pln">average_price</span><span class="pun">.</span><span class="pln">rolling</span><span class="pun">(</span><span class="lit">30</span><span class="pun">).</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">roll_mean</span><span class="pun">.</span><span class="pln">index</span><span class="pun">,</span><span class="pln"> roll_mean</span><span class="pun">,</span><span class="pln">label</span><span class="pun">=</span><span class="str">"rolling mean"</span><span class="pun">)</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">xlabel</span><span class="pun">=</span><span class="str">'date'</span><span class="pun">,</span><span class="pln"> ylabel</span><span class="pun">=</span><span class="str">'average-price'</span><span class="pun">)</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="pln">show</span><span class="pun">()</span></pre>

<p>
	حجم النافذة هنا هو 30، لذا فإن كل قيمة في <code>roll_mean</code> هي متوسط 30 قيمة من <code>reindexed.average-price</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102168" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1adc998c1_12.3.png.0ea554e5deb7e8a2242e8dab9ebdd9de.png" rel=""><img alt="الشكل 12.3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102168" data-unique="4qhx2mu8o" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ade64c9a_12.3.thumb.png.568a4b2ec7fa8cba439bf20142d8e52d.png" style="width: 750px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق الأسعار اليومية والمتوسط المتدحرج rolling mean في الجهة اليسرى والمتوسط المتحرك الموزون أسيًا exponentially-weighted moving average في الجهة اليمنى، كما يُظهر الشكل السابق الموجود في الجهة اليسرى النتيجة، حيث يبدو أن المتوسط المتدحرج قد أتقن تنظيم smoothing الضجيج واستخرج الاتجاه.
</p>

<p>
	البديل هو **المتوسط المتحرك الموزون أسيًا exponentially-weighted moving average -أو EWMA اختصارًا-، والذي يتمتع بميزتين اثنتين، حيث يمكننا استنتاج الميزة الأولى من الاسم، أي يحسب متوسطًا موزونًا يكون فيه لأحدث قيمة أعلى وزن؛ أما القيم السابقة فتنخفض قيمتها انخفاضًا أسيًا، والميزة الثانية هي أنّ تنفيذ بانداز pandas للمتوسط المتحرك الموزون أسيًا يعالِج القيم المفقودة بإتقان أكبر.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_31" style="">
<span class="pln">ewm</span><span class="pun">=</span><span class="pln">reindexed</span><span class="pun">[</span><span class="str">'average_price'</span><span class="pun">].</span><span class="pln">ewm</span><span class="pun">(</span><span class="pln">span</span><span class="pun">=</span><span class="lit">30</span><span class="pun">).</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">ewm</span><span class="pun">)</span></pre>

<p>
	يتوافق المعامِل <strong>span</strong> تقريبًا مع حجم نافذة متوسط متحرك، كما أنه يتحكم في مدى سرعة انخفاض الأوزان، لذا فهو يحدِّد عدد النقاط التي تقدِّم مساهمةً كبيرةً لكل متوسط من المتوسطات، ويُظهر الشكل السابق الموجود في الجهة اليمنى المتوسط المتحرك الموزون أسيًا EWMA الخاص بالبيانات نفسها، وهو يشبه المتوسط المتدحرج rolling mean في الحالات التي يكون كلاهما معرَّفًا، إلا أنه لا يحتوي على أي قيم مفقودة، لذا فمن السهل التعامل معه، ونلاحظ أنّ القيم تحتوي على ضجيج في بداية السلسلة الزمنية لأنها مبنية على عدد أقل من نقاط البيانات.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_33" style="">
<span class="pln">ewm</span><span class="pun">=</span><span class="pln">reindexed</span><span class="pun">[</span><span class="str">'average_price'</span><span class="pun">].</span><span class="pln">ewm</span><span class="pun">(</span><span class="pln">span</span><span class="pun">=</span><span class="lit">30</span><span class="pun">).</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">ewm</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">"ewma"</span><span class="pun">)</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">xlabel</span><span class="pun">=</span><span class="str">'date'</span><span class="pun">,</span><span class="pln"> ylabel</span><span class="pun">=</span><span class="str">'average-price'</span><span class="pun">)</span><span class="pln">
thinkplot</span><span class="pun">.</span><span class="pln">show</span><span class="pun">()</span></pre>

<h2>
	القيم المفقودة
</h2>

<p>
	ستكون الخطوة التالية بعد أن حدَّدنا توجه السلسلة الزمنية هي البحث في كل موسم على حدة، فالموسم هو فترة أسبوع أو يوم أو سنة أو …إلخ، أي ليس بالضرورة موسم كما يشير الاسم، فهو سلوك دوري، وغالبًا ما تكون السلاسل الزمنية التي تستند إلى السلوك البشري دورات يومية أو أسبوعية أو شهرية أو سنوية، لذا سنقدِّم في القسم التالي طُرقًا لاختبار المواسم لكنها لا تعمل جيدًا في حال وجود قيم مفقودة، لذا علينا حل هذه المشكلة أولًا، حيث توجد طريقة سهلة وشائعة يمكننا من خلالها ملء البيانات المفقودة وهي استخدام متوسط متحرك، حيث يزودنا تابع السلسلة <code>fillna</code> بتنفيذ ملائم جدًا لمتطلباتنا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_35" style="">
<span class="pln">reindexed</span><span class="pun">.</span><span class="pln">average_price</span><span class="pun">.</span><span class="pln">fillna</span><span class="pun">(</span><span class="pln">ewm</span><span class="pun">,</span><span class="pln"> inplace</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span></pre>

<p>
	عندما تكون قيمة <code>reindexed.average_price</code> هي <code>nan</code>، فسيستبدلها التابع <code>fillna</code> بالقيم الموافقة من <code>ewm</code>، حيث تشير الراية <code>inplace</code> إلى التابع <code>fillna</code> لكي يعدل السلسلة الحالية بدلًا من إنشاء سلسلة جديدة، وإحدى مساوئ هذه الطريقة هي أنها تقلل من قيمة الضجيج في السلسلة، لكن يمكننا حل هذه المشكلة عن طريق إضافة الرواسب التي طُبِّق عليها أخذ عينات:
</p>

<pre class="ipsCode">
resid = (reindexed.average_price - ewm).dropna()
fake_data = ewm + thinkstats2.Resample(resid, len(reindexed))
reindexed.average_price.fillna(fake_data, inplace=True)
</pre>

<p>
	يحتوي المتغير <code>resid</code> على قيم الرواسب، لكن لا يحتوي على الأيام التي يكون فيها <code>average_price</code> أي السعر الوسطي هو <code>nan</code>، كما يحتوي <code>fake_data</code> على مجموع المتوسط المتحرك وعينة عشوائية من الرواسب، وأخيرًا، يستبدل التابع <code>fillna</code> قيم <code>fake_data</code> بقيم من <code>nan</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102169" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ae258d90_12.4.png.8a2cf5f2d06b71f39ec7fc1e25ad35ca.png" rel=""><img alt="الشكل 12.4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102169" data-unique="ha903pghw" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1ae5f111d_12.4.thumb.png.2e71cb13741141bfdea4295663a81c73.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق الأسعار اليومية بعد ملء القيم ويُظهر النتيجة، حيث تشبه البيانات المملوءة القيم الفعلية من الناحية البصرية، وبما أنّ الرواسب التي تطُبِّق عليها إعادة أخذ عينات هي قيم عشوائية، فستكون النتائج مختلفةً في كل مرة، لذا سنرى لاحقًا طريقةً لوصف الخطأ الذي نتج عن القيم المفقودة.
</p>

<h2>
	الارتباط التسلسلي
</h2>

<p>
	قد تتوقع أن تجد أنماطًا متكررةً لأن الأسعار تتغيَّر يوميًا، فإذا كان السعر مرتفعًا يوم الاثنين، فقد تتوقع أن يكون مرتفعًا للأيام القليلة التالية، وإذا كان منخفضًا، فقد تتوقع أن يبقى منخفضًا، حيث يُدعى هذا النمط <strong>الارتباط التسلسلي serial correlation</strong> لأن كل قيمة مترابطة مع القيمة التالية في السلسلة، ويمكنك إزاحة السلسلة الزمنية بمقدار قدره <strong>تأخير lag</strong> من أجل حساب الارتباط التسلسلي ومن ثم حساب الارتباط بين السلسلة المزاحة والسلسلة الأصلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_39" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">SerialCorr</span><span class="pun">(</span><span class="pln">series</span><span class="pun">,</span><span class="pln"> lag</span><span class="pun">=</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
    xs </span><span class="pun">=</span><span class="pln"> series</span><span class="pun">[</span><span class="pln">lag</span><span class="pun">:]</span><span class="pln">
    ys </span><span class="pun">=</span><span class="pln"> series</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">(</span><span class="pln">lag</span><span class="pun">)[</span><span class="pln">lag</span><span class="pun">:]</span><span class="pln">
    corr </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Corr</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ys</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> corr</span></pre>

<p>
	تكون قيم التأخير الأول <code>nan</code> بعد أول إزاحة، لذا فقد استخدمنا شريحةً لإزالتها قبل حساب <code>Corr</code>، فإذا طبقنا <code>SerialCorr</code> على بيانات الأسعار الأولية بقيمة تأخير 1، فسنجد أنَّ الارتباط التسلسلي 0.26 للأفوكادو العضوي و 0.39 للأفوكادو العادي، كما نتوقَّع رؤية ارتباط تسلسلي قوي في حال كانت السلسلة الزمنية ذا اتجاه طويل الأمد، فإذا كانت الأسعار تنخفض على سبيل المثال، فسنتوقع رؤية قيم النصف الأول من السلسلة أعلى من المتوسط، وقيم النصف الثاني من السلسلة أقل من المتوسط، ومن المثير للاهتمام رؤية فيما إذا كان الارتباط مستمرًا إذا لم نأخذ الاتجاه بالحسبان، إذ يمكننا مثلًا حساب راسب المتوسط المتحرك الموزون أسيًا ومن ثم حساب ارتباطه التسلسلي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_41" style="">
<span class="pln">ewm</span><span class="pun">=</span><span class="pln">reindexed</span><span class="pun">[</span><span class="str">'average_price'</span><span class="pun">].</span><span class="pln">ewm</span><span class="pun">(</span><span class="pln">span</span><span class="pun">=</span><span class="lit">30</span><span class="pun">).</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
resid </span><span class="pun">=</span><span class="pln"> reindexed</span><span class="pun">.</span><span class="pln">average_price </span><span class="pun">-</span><span class="pln"> ewm
corr </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">SerialCorr</span><span class="pun">(</span><span class="pln">resid</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	تكون الارتباطات التسلسلية للبيانات التي أهملنا فيها الاتجاه في حال كانت قيمة التأخير 1 أي lag=1 هي 0.89 بالنسبة للأفوكادو العضوي، والقيمة 0.86 بالنسبة للأفوكادو العادي، كما تُعَدّ قيمًا كبيرة مما يشير إلى وجود ترابط تسلسلي يومي جيد، حيث سننفِّذ التحليل مرةً أخرى مع قيم تأخير مختلفة وذلك للتحقق من وجود موسمية أسبوعية أو شهرية أو سنوية، وإليك نتائج التحليل:
</p>

<table>
<thead><tr>
<th>
				التأخير
			</th>
			<th>
				العادي
			</th>
			<th>
				العضوي
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				1
			</td>
			<td>
				0.86
			</td>
			<td>
				0.89
			</td>
		</tr>
<tr>
<td>
				7
			</td>
			<td>
				0.29
			</td>
			<td>
				0.37
			</td>
		</tr>
<tr>
<td>
				30
			</td>
			<td>
				-0.18
			</td>
			<td>
				-0.33
			</td>
		</tr>
<tr>
<td>
				300
			</td>
			<td>
				0.33
			</td>
			<td>
				-0.5
			</td>
		</tr>
</tbody>
</table>
<p>
	سنُجري في القسم التالي اختبارات لنعلم ما إذا كانت هذه الارتباطات ذات دلالة إحصائية (ليست ذات دلالة إحصائية)، ولكن يمكننا مبدئيًا استنتاج أنه لا توجد أنماط موسمية كبيرة في السلسلة، فعلى الأقل لا توجد أنماط بوجود هذه التأخيرات.
</p>

<h2>
	الارتباط الذاتي
</h2>

<p>
	ستضطر إلى اختبار جميع القيم إذا اعتقدت بوجود ارتباط تسلسلي في سلسلة زمنية ما لكنك لست متأكدًا من قيم التأخير التي يجب عليك اختبارها، حيث تُعَدّ دالة الارتباط الذاتي autocorrelation function دالةً تحوِّل التأخير lag إلى ارتباط تسلسلي بتأخير مُعطى، كما يُعَدّ الارتباط الذاتي والارتباط التسلسلي وجهَين لعملة واحدة، أي أنهما يشيران إلى المفهوم ذاته لكن غالبًا يُستخدَم الارتباط الذاتي عندما تكون قيمة التأخير مختلفةً عن 1، كما تزودنا StatsModels التي استخدمناها في الانحدار الخطي في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-regression-%D9%88%D8%AF%D9%88%D8%B1%D9%87-%D9%81%D9%8A-%D9%85%D9%84%D8%A7%D8%A1%D9%85%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9-%D9%85%D8%B9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%A7%D8%AD%D8%A9-r1493/" rel="">المقال السابق</a> بدوال لتحليل السلاسل الزمنية مثل <code>acf</code> التي تحسب دالة الارتباط الذاتي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_43" style="">
<span class="pln">  </span><span class="kwd">import</span><span class="pln"> statsmodels</span><span class="pun">.</span><span class="pln">tsa</span><span class="pun">.</span><span class="pln">stattools </span><span class="kwd">as</span><span class="pln"> smtsa
  acf </span><span class="pun">=</span><span class="pln"> smtsa</span><span class="pun">.</span><span class="pln">acf</span><span class="pun">(</span><span class="pln">filled</span><span class="pun">.</span><span class="pln">resid</span><span class="pun">,</span><span class="pln"> nlags</span><span class="pun">=</span><span class="lit">365</span><span class="pun">)</span></pre>

<p>
	تحسب الدالة <code>acf</code> الارتباطات التسلسلية مع قيم تأخير بين 0 و<code>nlags</code>، وتكون النتيجة مصفوفةً من الارتباطات، فإذا حددنا الأسعار اليومية للأفوكادو العضوي واستخرجنا قيم الارتباطات في حال كانت قيم التأخير هي 1 و 7 و 30 و 365، فسيمكننا عندها التأكُّد من إنتاج الدالتين <code>acf</code> و<code>SerialCorr</code> النتائج نفسها تقريبًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_45" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> acf</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> acf</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> acf</span><span class="pun">[</span><span class="lit">7</span><span class="pun">],</span><span class="pln"> acf</span><span class="pun">[</span><span class="lit">30</span><span class="pun">],</span><span class="pln"> acf</span><span class="pun">[</span><span class="lit">365</span><span class="pun">]</span><span class="pln">
</span><span class="lit">1.000</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.859</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.286</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.176</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.000</span></pre>

<p>
	تحسب الدالة <code>acf</code> ارتباط السلسلة مع نفسها إذا كانت قيمة التأخير 0 أي lag=0، علمًا أن الارتباط في هذه الحالة هو 1 دائمًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102170" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1aea22aeb_12.5.png.1fd5b4ec4584dce4e45357c2531d9eae.png" rel=""><img alt="الشكل 12.5.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102170" data-unique="oau1hsjui" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1aebc8b28_12.5.thumb.png.0f3d5cb5d5ddfa5c6df0b7bc0188400b.png" style="width: 750px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق دالة الارتباط الذاتي للأسعار اليومية في الجهة اليسرى؛ أما في الجهة اليمنى فيوضِّح الأسعار اليومية إذا أجرينا محاكاةً لموسمية أسبوعية، حيث يُظهر الشكل السابق في الجهة اليسرى دوال الارتباط الذاتي للأفوكادو العادي والعضوي وذلك من أجل <code>nlags=40</code>، حيث تُظهر المنطقة الرمادية التباين الطبيعي الذي نتوقعه إذا لم يكن هناك أي ارتباط ذاتي فعلي، علمًا أن القيم التي تقع خارج هذا المجال هي ذات دلالة إحصائية وقيمتها الاحتمالية p-value هي أقل من 5‎%‎، وبما أن معدل الإيجابية الكاذبة هو ‎5%‎ ونحن في طور حساب 120 ارتباطًا (بمعدل 40 تأخير لكل سلسلة من السلسلتين الزمنيتين)، فسنتوقع رؤية 6 نقاط خارج هذه المنطقة وفي الواقع يوجد أكثر من 6 نقاط، لذا نستنتج أنه لا يمكننا تفسير أيّ ارتباط ذاتي في السلسلة على أنه حدث بمحض الصدفة فحسب.
</p>

<p>
	حسبنا المناطق الرمادية عن طريق إعادة أخذ عينات الرواسب (ويمكنك الاطلاع على شيفرتنا في في <a href="https://colab.research.google.com/drive/1gUN5m4Qjw8ErbMc6yUxrazgwz6ZcsaHG" rel="external nofollow">الرابط</a> وتُدعى الدالة <code>SimulateAutocorrelation</code>)، ولرؤية كيف تبدو دالة الارتباط الذاتي في حال وجود موسمية من نوع ما، ولَّدنا بيانات محاكاة وذلك عن طريق إضافة دورة أسبوعية، ففي حال كان الطلب على الأفوكادو أعلى في العطل الأسبوعية، فقد نتوقع أن يكون السعر أعلى، حيث حدَّدنا التواريخ التي تصادف يومي الجمعة والسبت ومن ثم أضفنا قيمةً عشوائيةً مُختارةً من توزيع منتظم بين 0$ و 2$ على السعر وذلك من أجل محاكاة هذا التأخير.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_48" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">AddWeeklySeasonality</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">):</span><span class="pln">
    frisat </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">daily</span><span class="pun">.</span><span class="pln">index</span><span class="pun">.</span><span class="pln">dayofweek</span><span class="pun">==</span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="pun">(</span><span class="pln">daily</span><span class="pun">.</span><span class="pln">index</span><span class="pun">.</span><span class="pln">dayofweek</span><span class="pun">==</span><span class="lit">5</span><span class="pun">)</span><span class="pln">
    fake </span><span class="pun">=</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">()</span><span class="pln">
    fake</span><span class="pun">.</span><span class="pln">average_price</span><span class="pun">[</span><span class="pln">frisat</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">uniform</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> frisat</span><span class="pun">.</span><span class="pln">sum</span><span class="pun">())</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> fake</span></pre>

<p>
	يُعَدّ <code>frisat</code> سلسلةً بوليانيةً، بحيث تكون القيمة <code>True</code> ليومي الجمعة والسبت، كما يُعَدّ <code>fake</code> إطار بيانات جديد وهو نسخة من إطار البيانات <code>daily</code> بعد أن أجرينا عليه تعديل إضافة قيم عشوائية إلى <code>average_price.frisat.sum</code> وهو العدد الكلي لأيام الجمعة والسبت التي ظهرت، أي عدد القيم التي سيتوجب علينا توليدها.
</p>

<p>
	يُظهر الشكل السابق في الجهة اليمنى دوال الارتباط الذاتي للأسعار مع موسمية محاكاة، حيث نرى كما هو متوقَّع أن الارتباطات أعلى في حال كان التأخير من مضاعفات العدد 7، كما تكون الارتباطات لنوعي الأفوكادو ليست ذات دلالة إحصائية لأن الرواسب هنا كبيرة ومن المفترض أن يكون التأخير أكبر ليكون مرئيًا في وسط كل هذا الضجيج.
</p>

<h2>
	التنبؤ
</h2>

<p>
	يمكن استخدام تحليل السلاسل الزمنية لاكتشاف أو شرح سلوك الأنظمة التي تتغير بمرور الزمن، كما يمكن استخدامها لتوليد تنبؤات، كما يمكن استخدام الانحدار الخطي الذي تناولناه في قسم الانحدار الخطي في هذا المقال لتوليد تنبؤات أيضًا، حيث يزودنا الصنف <code>RegressionResults</code> بالدالة <code>predict</code> التي تأخذ إطار بيانات يحتوي على المتغيرات التوضيحية ويُعيد تسلسلًا من التنبؤات، وإليك الشيفرة الموافقة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_50" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GenerateSimplePrediction</span><span class="pun">(</span><span class="pln">results</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">):</span><span class="pln">
    n </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">years</span><span class="pun">)</span><span class="pln">
    inter </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">ones</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln">
    d </span><span class="pun">=</span><span class="pln"> dict</span><span class="pun">(</span><span class="typ">Intercept</span><span class="pun">=</span><span class="pln">inter</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">=</span><span class="pln">years</span><span class="pun">)</span><span class="pln">
    predict_df </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln">
    predict </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">predict</span><span class="pun">(</span><span class="pln">predict_df</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> predict</span></pre>

<p>
	يُعَدّ <code>results</code> كائنًا من الصنف <code>RegressionResults</code>، ويُعَدّ <code>years</code> تسلسلًا من قيم الزمن التي نريد استنباط تنبؤاتها، كما تبني الدالة إطار بيانات وتمرره إلى الدالة <code>predict</code> وتُعيد النتيجة، فإذا كان ما نريد هو تنبؤ وحيد وأفضل ما في الإمكان فقط، فستكون مهمتنا قد انتهت هنا، لكن من المهم حساب الخطأ في معظم الأحيان، أي نريد بكلمات أخرى معرفة دقة التنبؤ المحتملة، كما يجب علينا أخذ مصادر الخطأ الثلاثة هذه بالحسبان:
</p>

<ul>
<li>
		خطأ أخذ العينات: يكون التنبؤ هنا مبنيًا على المعامِلات المُقدَّرة التي تعتمد على التبيان العشوائي في العينة، حيث نتوقَّع تغيُّر التقديرات إذا أجرينا التجربة مرةً ثانيةً.
	</li>
	<li>
		التباين العشوائي: ستتغير البيانات المرصودة قرب الاتجاه طويل الأمد حتى لو كانت المعاملات المُقدَّرة دقيقةً تمامًا، ونتوقع استمرار هذا التباين في المستقبل أيضًا.
	</li>
	<li>
		خطأ النمذجة: لدينا أدلةً تثبت أنّ الاتجاه طويل الأمد ليس خطيًا، لذا فإن التنبؤات مبنية على نموذج خطي سيفشل عاجلًا أم آجلًا.
	</li>
</ul>
<p>
	من مصادر الخطأ الأخرى التي يجب علينا أخذها بالحسبان هي الحوادث المستقبلية غير المتوقعة مثل تأثر أسعار المنتجات الزراعية بالطقس وتأثر جميع الأسعار بالقوانين والسياسات، ومن الصعب حساب أخطاء النمذجة والأخطاء المستقبلية غير المتوقعة، ومن السهل التعامل مع خطأ أخذ العينات والتباين العشوائي لذا سنبدأ بهذين النوعين.
</p>

<p>
	استخدمنا إعادة أخذ العينات كما فعلنا في قسم الرواسب في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%BA%D8%B1%D9%89-%D8%A7%D9%84%D8%AE%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1412/" rel="">المربعات الصغرى الخطية في بايثون</a> بهدف حساب خطأ أخذ العينات، وكما هي العادة فهدفنا هو استخدام عمليات الرصد الفعلية لإجراء محاكاة لما يمكن أن يحدث إذا أجرينا التجربة مرةً أخرى، حيث أنّ عمليات المحاكاة مبنية على افتراض أن المعاملات المقدَّرة صحيحة، لكن قد تكون الرواسب العشوائية مختلفةً، وإليك الدالة التي تجري عمليات المحاكاة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_52" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">SimulateResults</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">101</span><span class="pun">):</span><span class="pln">
    model</span><span class="pun">,</span><span class="pln"> results </span><span class="pun">=</span><span class="pln"> </span><span class="typ">RunLinearModel</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">)</span><span class="pln">
    fake </span><span class="pun">=</span><span class="pln"> daily</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">()</span><span class="pln">

    result_seq </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">):</span><span class="pln">
        fake</span><span class="pun">.</span><span class="pln">average_price </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">fittedvalues </span><span class="pun">+</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Resample</span><span class="pun">(</span><span class="pln">results</span><span class="pun">.</span><span class="pln">resid</span><span class="pun">)</span><span class="pln">
        _</span><span class="pun">,</span><span class="pln"> fake_results </span><span class="pun">=</span><span class="pln"> </span><span class="typ">RunLinearModel</span><span class="pun">(</span><span class="pln">fake</span><span class="pun">)</span><span class="pln">
        result_seq</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">fake_results</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> result_seq</span></pre>

<p>
	يُعَدّ <code>daily</code> إطار بيانات يحتوي على الأسعار المرصودة، و<code>iters</code> هو عدد عمليات المحاكاة التي يجب تشغيلها، كما تستخدِم الدالة <code>SimulateResults</code> الدالة <code>RunLinearModel</code> من قسم الانحدار الخطي الموجود في هذا المقال، وذلك من أجل تقدير ميل القيم المرصودة ونقطة تقاطعها، كما تولِّد الدالة مجموعة بيانات مزيفة في كل تكرار من الحلقة عن طريق إعادة أخذ عينات الرواسب وإضافتها إلى القيم الملائمة، ومن ثم تشغِّل نموذجًا خطيًا على البيانات المزيفة وتخزِّن الكائن <code>RegresssionResults</code>، في حين تكون الخطوة القادمة هنا هي استخدام النتائج التي أجرينا عليها محاكاةً من أجل توليد تنبؤات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_54" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GeneratePredictions</span><span class="pun">(</span><span class="pln">result_seq</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">,</span><span class="pln"> add_resid</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">):</span><span class="pln">
    n </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">years</span><span class="pun">)</span><span class="pln">
    d </span><span class="pun">=</span><span class="pln"> dict</span><span class="pun">(</span><span class="typ">Intercept</span><span class="pun">=</span><span class="pln">np</span><span class="pun">.</span><span class="pln">ones</span><span class="pun">(</span><span class="pln">n</span><span class="pun">),</span><span class="pln"> years</span><span class="pun">=</span><span class="pln">years</span><span class="pun">,</span><span class="pln"> years2</span><span class="pun">=</span><span class="pln">years</span><span class="pun">**</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    predict_df </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln">

    predict_seq </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> fake_results </span><span class="kwd">in</span><span class="pln"> result_seq</span><span class="pun">:</span><span class="pln">
        predict </span><span class="pun">=</span><span class="pln"> fake_results</span><span class="pun">.</span><span class="pln">predict</span><span class="pun">(</span><span class="pln">predict_df</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> add_resid</span><span class="pun">:</span><span class="pln">
            predict </span><span class="pun">+=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Resample</span><span class="pun">(</span><span class="pln">fake_results</span><span class="pun">.</span><span class="pln">resid</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">)</span><span class="pln">
        predict_seq</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">predict</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> predict_seq</span></pre>

<p>
	تأخذ الدالة <code>GeneratePredictions</code> تسلسل النتائج من الخطوة السابقة، بالإضافة إلى القيم <code>years</code> والتي هي تسلسل من القيم العشرية التي تحدِّد المجال الذي يجب علينا توليد تنبؤات له، كما تأخذ هذه الدالة الوسيط <code>add_resid</code> الذي يخبرنا فيما إذا كان يجب إضافة رواسب مُعاد أخذ عيناتها إلى التنبؤ المباشر، حيث تمر <code>GeneratePredictions</code> مرورًا تكراريًا على التسلسل <code>RegressionResults</code> وتولِّد تسلسلًا من التنبؤات.
</p>

<p>
	وأخيرًا إليك الشيفرة التي ترسم مجال الثقة 90‎%‎ للتنبؤات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3535_56" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">PlotPredictions</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">101</span><span class="pun">,</span><span class="pln"> percent</span><span class="pun">=</span><span class="lit">90</span><span class="pun">):</span><span class="pln">
    result_seq </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SimulateResults</span><span class="pun">(</span><span class="pln">daily</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="pln">iters</span><span class="pun">)</span><span class="pln">
    p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">100</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> percent</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
    percents </span><span class="pun">=</span><span class="pln"> p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">-</span><span class="pln">p

    predict_seq </span><span class="pun">=</span><span class="pln"> </span><span class="typ">GeneratePredictions</span><span class="pun">(</span><span class="pln">result_seq</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">)</span><span class="pln">
    low</span><span class="pun">,</span><span class="pln"> high </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">PercentileRows</span><span class="pun">(</span><span class="pln">predict_seq</span><span class="pun">,</span><span class="pln"> percents</span><span class="pun">)</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">FillBetween</span><span class="pun">(</span><span class="pln">years</span><span class="pun">,</span><span class="pln"> low</span><span class="pun">,</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> alpha</span><span class="pun">=</span><span class="lit">0.3</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">=</span><span class="str">'gray'</span><span class="pun">)</span><span class="pln">

    predict_seq </span><span class="pun">=</span><span class="pln"> </span><span class="typ">GeneratePredictions</span><span class="pun">(</span><span class="pln">result_seq</span><span class="pun">,</span><span class="pln"> years</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span><span class="pln">
    low</span><span class="pun">,</span><span class="pln"> high </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">PercentileRows</span><span class="pun">(</span><span class="pln">predict_seq</span><span class="pun">,</span><span class="pln"> percents</span><span class="pun">)</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">FillBetween</span><span class="pun">(</span><span class="pln">years</span><span class="pun">,</span><span class="pln"> low</span><span class="pun">,</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> alpha</span><span class="pun">=</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">=</span><span class="str">'gray'</span><span class="pun">)</span></pre>

<p>
	تستدعي الدالة <code>PlotPredictions</code> دالة <code>GeneratePredictions</code> مرتين، مرةً إذا كان <code>add_resid=True</code> ومرةً أخرى إذا كان <code>add_resid=False</code>، كما تستخدِم <code>PercentileRows</code> لتحديد المئين 95 والمئين 5 لكل سنة، وترسم أخيرًا منطقةً رماديةً بين الحدَّين.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102171" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1aef585bd_12.6.png.92d1bafcfde47087154c5ab4f69b518e.png" rel=""><img alt="الشكل 12.6.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102171" data-unique="k1k2axi9n" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1af2e6b35_12.6.thumb.png.ed18baae0bd7b9e530fcf61b1dfaddee.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يُظهر الشكل السابق النتيجة، حيث تمثِّل المنطقة الرمادية الداكنة 90‎%‎ من مجال الثقة لخطأ أخذ العينات، أي عدم اليقين بشأن الميل المقدَّر ونقطة التقاطع بسبب أخذ العينات،
</p>

<p>
	تُظهر المنطقة فاتحة اللون مجال ثقة 90‎%‎ لخطأ التنبؤ وهو نتيجة جمع التباين العشوائي مع خطأ أخذ العينات.
</p>

<p>
	تحسب هاتان المنطقتان خطأ أخذ العينات والتباين العشوائي وليس خطأ النمذجة، فمن الصعب عمومًا حساب خطأ النمذجة، لكن يمكننا في هذه الحالة معالجة مصدر خطأ واحد على الأقل وهو الأحداث الخارجية غير المتوقعة، فنموذج الانحدار مبني على افتراض أنّ النظام ثابت stationary، أي أن معامِلات النموذج لا تتغير بمرور الزمن، خاصةً الميل ونقطة التقاطع بالإضافة إلى توزيع الرواسب.
</p>

<p>
	لكن سيبدو لنا بالنظر إلى المتوسطات المتحركة في الشكل 12.5 أنّ الميل يتغير مرةً واحدةً على الأقل خلال فترة الرصد، وسيبدو تباين الرواسب في النصف الأول أكبر من تباينه في النصف الثاني، ونتيجةً لذلك نقول أنّ المعاملات تعتمد على الفترة التي نرصد فيها عمليات البيع، ولرؤية مدى تأثير هذا على التنبؤات يمكننا توسيع <code>SimulateResults</code> لاستخدام فترات الرصد لكن مع تغيير موعد بدء الرصد وانتهائه.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="102172" href="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1af92abff_12.7.png.ed4fa24b1d9d69c1b26840797e0672e8.png" rel=""><img alt="الشكل 12.7.png" class="ipsImage ipsImage_thumbnailed" data-fileid="102172" data-unique="95e64ylpg" src="https://academy.hsoub.com/uploads/monthly_2022_06/62ba1afc14d73_12.7.thumb.png.94fdf046d86eb2f1d9ebf0fa1e29d7a5.png" style="width: 600px; height: auto;"></a>
</p>

<p>
	يوضِّح الشكل السابق التنبؤات المبنية على الملاءمة الخطية ويُظهِر التباين الناتج عن فترة الرصد، كما يُظهر النتيجة من أجل الأفوكادو العادي، في حين تُظهر المنطقة الرمادية الفاتحة اللون مجال الثقة الذي يحتوي على عدم اليقين الناتج عن خطأ أخذ العينات، وكذلك يحتوي على التباين العشوائي والتباين في فترة الرصد، علمًا أنّ ميل النموذج المبني على الفترة الكلية موجب، مما يدل على أنّ الأسعار آخذة في الارتفاع، لكن تُظهر الفترة الأحدث أدلةً على انخفاض الأسعار، لذا فإن ميل النماذج المبنية على البيانات الأحدث سالب، وبالتالي تتضمن أوسع فترة تنبؤية إمكانية خفض الأسعار خلال العام المقبل.
</p>

<h2>
	لقراءة أكثر تفصيلا
</h2>

<p>
	يُعَدّ تحليل السلاسل الزمنية موضوعًا كبيرًا، ولا يتناول هذا المقال سوى جزء يسير منه، إذ يُعَدّ الانحدار الذاتي autoregression من الأدوات الهامة للتعامل مع السلاسل الزمنية، لكنا لم نذكره في هذا المقال، ويعود ذلك إلى أنه قد تبين أنه ليس مفيدًا للبيانات التي عملنا معها، لكن سيؤهلك فهمك للمواد الموجودة في هذا المقال لتعلُّم الانحدار الذاتي، كما يمكننا اقتراح مصدر يتناول موضوع تحليل السلاسل الزمنية وهو تحليل البيانات باستخدام أدوات مفتوحة المصدر، أروايلي ميديا، 2011 (Data Analysis with Open Source Tools, O’Reilly Media, 2011) للكاتب فيليب جارنيت Philipp Janert، حيث يُعَدّ المقال الذي يتحدث فيه عن تحليل السلاسل الزمنية استمرارًا لهذا المقال.
</p>

<h2>
	تمارين
</h2>

<p>
	إليك التمارين التالية لحلها والتدرب عليها، وانتبه إلى أننا تصرفنا في النص أثناء ترجمته لذا لن تكون الحلول في الملف chap12soln.py في مستودع الشيفرات <a href="https://github.com/AllenDowney/ThinkStats2" rel="external nofollow">ThinkStats2</a> على GitHub صالحة.
</p>

<h3>
	تمرين 1
</h3>

<p>
	يملك النموذج الخطي الذي استخدمناه في هذا المقال عيبًا واضحًا وهو أنه خطي، وما من سبب يدفعنا للتوقُع أن تغير الأسعار سيبقى خطيًا بمرور الوقت، حيث يمكننا إضافة مرونة إلى النموذج عن طريق إضافة مصطلح تربيعي كما فعلنا في قسم العلاقات اللاخطية في المقال الماضي.
</p>

<p>
	استخدم نموذجًا تربيعيًا لملاءمة السلسلة الزمنية للأسعار اليومية، واستخدم هذا النموذج لتوليد التنبؤات، لكن سيكون عليك في البداية كتابة نسخة من <code>RunLinearModel</code> لتستطيع تشغيل هذا النموذج التربيعي، وبعد ذلك يمكنك توليد التنبؤات عن طريق إعادة استخدام الشيفرة التي استخدمناها في هذا المقال.
</p>

<h3>
	تمرين 2
</h3>

<p>
	اكتب تعريفًا للصنف <code>SerialCorrelationTest</code> يرث الصنف <code>HypothesisTest</code> من القسم HypothesisTest الموجود في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">اختبار الفرضيات الإحصائية</a>، بحيث يأخذ سلسلةً series وتأخيرًا lag على أساس وسيطين، ومن ثم يحسب الارتباط التسلسلي للسلسلة مع التأخير المُعطى، ثم يحسب الصنف القيمة الاحتمالية p-value للارتباط المرصود.
</p>

<p>
	استخدم هذا الصنف لتعلم ما إذا كان الارتباط التسلسلي ذا دلالة إحصائية أم لا، واختبرأيضًا الرواسب للنموذج التربيعي والنموذج الخطي (إذا حللت التمرين السابق).
</p>

<h3>
	تمرين 3
</h3>

<p>
	يمكننا توسيع نموذج المتوسط المتحرك الموزون أسيًا EWMA لتوليد التنبؤات باستخدام عدة طرق، ومن أبسطها اتباع الخطوات التالية:
</p>

<ol>
<li>
		احسب المتوسط المتحرك الموزون أسيًا EWMA للسلسلة الزمنية، ومن ثم استخدِم النقطة الأخيرة على أساس نقطة تقاطع <code>inter</code>.
	</li>
	<li>
		احسب المتوسط المتحرك الموزون أسيًا EWMA للفروق بين العناصر المتتالية في السلسلة الزمنية، ومن ثم استخدِم النقطة الأخيرة على أساس ميل <code>slope</code>.
	</li>
	<li>
		احسب <code>inter+slope * dt</code> للتنبؤ بالقيم المستقبلية، حيث أنّ <code>dt</code> هو الفرق بين زمن التنبؤ وزمن آخر عملية رصد.
	</li>
</ol>
<p>
	استخدِم هذه الطريقة لتوليد تنبؤات لمدة سنة بعد آخر عملية رصد، وإليك بعض التلميحات:
</p>

<ul>
<li>
		استخدِم <code>timeseries.FillMissing</code> لملء القيم المفقودة قبل إجراء هذا التحليل لكي يكون الزمن بين العناصر المتتالية متسقًا.
	</li>
	<li>
		استخدِم <code>Series.diff</code> لحساب الفرق بين العناصر المتتالية.
	</li>
	<li>
		استخدِم <code>reindex</code> لتوسيع فهرس إطار البيانات في المستقبل.
	</li>
	<li>
		استخدِم <code>fillna</code> لوضع القيم التي تنبأت بها في إطار البيانات.
	</li>
</ul>
<p>
	ترجمة وبتصرف للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2013.html" rel="external nofollow">Chapter 12 Time series Analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<p>
	الملف المرفق <a class="ipsAttachLink" data-fileid="102173" href="https://academy.hsoub.com/applications/core/interface/file/attachment.php?id=102173" rel="">dataset.zip</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%82%D8%A7%D8%A1-%D9%84%D9%85%D8%B9%D8%B1%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%AF%D8%A9-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-%D9%84%D9%84%D8%A3%D8%B4%D9%8A%D8%A7%D8%A1-r1576/" rel="">كيفية إجراء تحليل البقاء لمعرفة المدة الافتراضية للأشياء</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-regression-%D9%88%D8%AF%D9%88%D8%B1%D9%87-%D9%81%D9%8A-%D9%85%D9%84%D8%A7%D8%A1%D9%85%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9-%D9%85%D8%B9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%A7%D8%AD%D8%A9-r1493/" rel="">الانحدار الإحصائي regression ودوره في ملاءمة النماذج المختلفة مع أنواع البيانات المتاحة</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1617</guid><pubDate>Fri, 15 Jul 2022 16:08:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; API &#x62E;&#x627;&#x631;&#x62C;&#x64A;&#x629; &#x644;&#x639;&#x631;&#x636; &#x62D;&#x627;&#x644;&#x629; &#x627;&#x644;&#x637;&#x642;&#x633;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-api-%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D9%84%D8%B9%D8%B1%D8%B6-%D8%AD%D8%A7%D9%84%D8%A9-%D8%A7%D9%84%D8%B7%D9%82%D8%B3-r1643/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62e90d8d3e31b_API.png.a47e5a4300f87b2d96252e001455111e.png" /></p>

<p>
	سنشرح في هذا الفيديو الآتي كيفية التعامل مع OpenWeatherMap <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">لغة بايثون</a>، حيث سنعمل على التواصل مع هذه الخدمة من خلال سكريبت مكتوب من خلال لغة بايثون بالأمثلة العملية.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="480" title="استخدام API خارجية لعرض حالة الطقس" width="853" src="https://www.youtube.com/embed/Wfr9dQVR02o"></iframe>
</p>

<p>
	يمكنك الاطلاع على تفاصيل إضافية حول <a href="https://academy.hsoub.com/programming/python/" rel="">لغة بايثون في شكل مقالات على أكاديمية حسوب</a>، كما يمكنك احتراف العمل بلغة بايثون وتعلم برمجة التطبيقات من خلالها عبر دورة <a href="https://academy.hsoub.com/learn/python-application-development/" rel="">تطوير التطبيقات باستخدام لغة Python</a> المقدمة من أكاديمية حسوب. يمكنك أيضًا الاستعانة خلال رحلة تعلمك وعملك لاحقًا كمبرمج في هذا المجال، <a href="https://wiki.hsoub.com/Python" rel="external">بتوثيق بايثون</a> المضاف على موسوعة حسوب.
</p>
]]></description><guid isPermaLink="false">1643</guid><pubDate>Thu, 16 Jun 2022 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x633;&#x627;&#x644;&#x64A;&#x628; &#x627;&#x644;&#x62A;&#x62D;&#x644;&#x64A;&#x644;&#x64A;&#x629; &#x644;&#x62A;&#x62D;&#x644;&#x64A;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x643;&#x634;&#x627;&#x641;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B3%D8%A7%D9%84%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D9%8A%D8%A9-%D9%84%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1577/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/62829e354517a_-01.jpg.81c709004b0f816ea20d6029d79adb95.jpg" /></p>

<p>
	ركَّزت هذه السلسلة على الأساليب الحسابية مثل المحاكاة وإعادة أخذ العينات، لكن قد يكون من الأسرع حل بعض المسائل بالاستعانة بالأساليب التحليلية، حيث سنتناول في هذا المقال بعضًا من هذه الطرق وسنشرح كيفية عملها، كما سنقدِّم اقتراحات في نهاية المقال لدمج الأساليب الحسابية والتحليلية <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81%D9%8A%D8%A9-%D9%84%D8%A5%D8%AB%D8%A8%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B8%D8%B1%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1246/" rel="">لتحليل البيانات الاستكشافية</a>.
</p>

<p>
	توجد الشيفرة الخاصة بهذا المقال في الملف <a href="https://github.com/AllenDowney/ThinkStats2/blob/master/code/normal.py" rel="external nofollow">normal.py</a>، في مستودع الشيفرات <a href="https://github.com/AllenDowney/ThinkStats2" rel="external nofollow">ThinkStats2</a> على GitHub.
</p>

<h2>
	التوزيع الطبيعي
</h2>

<p>
	دعنا نتحدث عن المسألة الموجودة في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%8A%D8%B1-estimation-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1411/" rel="">التقدير Estimation الإحصائي في بايثون</a>:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		لنفترض أنك عالم تهتم بدراسة الغوريلات في محمية للحياة البرية، وقد تجد عند وزن 9 إناث أنّ x̄=90 kg والانحراف المعياري للعينة هو S = 7.5 kg؛ فإذا استخدمت x̄ لتقدير متوسط جميع الأفراد، فما هو الخطأ المعياري للمقدار؟
	</p>
</blockquote>

<p>
	يجب أن يكون توزيع أخذ عينات x̄ معروفًا إذا أردنا الإجابة على هذا السؤال، وكما رأينا في قسم توزيع أخذ العينات في مقال التقدير Estimation الإحصائي في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون </a>المشار إليه بالأعلى، فإننا قرّبنا التوزيع عن طريق إجراء محاكاة للتجربة -أي تجربة وزن 9 إناث غوريلا- ثم حساب x̄ لكل تجربة محاكاة وتجميع توزيع التقديرات، وتكون النتيجة هنا هي تقريب لتوزيع أخذ العينات، ثم نستخدِم توزيع أخذ العينات لحساب الأخطاء المعيارية وفواصل الثقة:
</p>

<ol>
<li>
		يُعَدّ الانحراف المعياري لتوزيع أخذ العينات هو الخطأ المعياري للتقدير، ويكون في هذا المثال حوالي 2.5 كيلوغرامًا.
	</li>
	<li>
		الفاصل بين المئين 5 والمئين 95 لتوزيع أخذ العينات هو فاصل ثقة ‎90%‎ تقريبًا، وإذا أجرينا التجربة عدة مرات، فسنتوقع أن يكون التقدير في هذا الفاصل ‎90% من المرات، حيث تكون قيمة فاصل الثقة 90‎%‎ في هذا المثال هي (94, 86) كيلوغرامًا.
	</li>
</ol>
<p>
	سنجري الآن الحسابات ذاتها بأسلوب تحليلي، وسنستفيد من حقيقة أنّ أوزان إناث الغوريلا البالغات هي توزيع طبيعي تقريبًا، إذ تملك التوزيعات الطبيعية خاصتين اثنتين تجعلها قابلةً للتحليل، فهي مغلقة في التحويل الخطي والإضافة، لكنا نحتاج إلى بعض الرموز لشرح معنى هذا الكلام، فإذا كان توزيع كمية <code>X</code> طبيعيًا وكان يحوي وسيطَين هما µ وσ، فيمكننا القول:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">X</span> ∼ <span style="font-style:italic;color:red">N</span> (µ, σ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)</span>
			</td>
		</tr></tbody></table>
<p>
	حيث يشير الرمز ∼ إلى أن الكمية موزعة ويشير الرمز N إلى طبيعي normal؛ أما التحويل الخطي لـ X فهو ‎X′ = a X + b، حيث أنّ a وb هما عددان حقيقيان، وتكون عائلة من التوزيعات مغلقةً في التحويل الخطي إذا كانت X′‎ في عائلة X نفسها، ويكون للتوزيع الطبيعي هذه الخاصية إذا كان X ∼ N (µ, σ<sup>2</sup>).
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">X</span>′ ∼ <span style="font-style:italic;color:red">N</span> (<span style="font-style:italic">a</span> µ + <span style="font-style:italic">b</span>, <span style="font-style:italic">a</span></span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> σ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)  </span>
			</td>
		</tr></tbody></table>
<p>
	تُعَدّ التوزيعات الطبيعية مغلقةً في الإضافة، فإذا كانت Z = X + Y و X ∼ N (µX, σX<sup>2</sup>) وY ∼ N (µY, σY<sup>2</sup>) فيكون:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">Z</span> ∼ <span style="font-style:italic;color:red">N</span> (µ</span><sub><span style="font-style:italic;font-size:medium">X</span></sub><span style="font-size:medium"> + µ</span><sub><span style="font-style:italic;font-size:medium">Y</span></sub><span style="font-size:medium">, σ</span><sub><span style="font-style:italic;font-size:medium">X</span></sub><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> + σ</span><sub><span style="font-style:italic;font-size:medium">Y</span></sub><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)  </span>
			</td>
		</tr></tbody></table>
<p>
	تكون المعادلة التالية محققة في الحالة الخاصة عندما Z = X + X
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">Z</span> ∼ <span style="font-style:italic;color:red">N</span> (<span style="font-style:italic">n</span> µ</span><sub><span style="font-style:italic;font-size:medium">X</span></sub><span style="font-size:medium">, <span style="font-style:italic">n</span> σ</span><sub><span style="font-style:italic;font-size:medium">X</span></sub><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)  </span>
			</td>
		</tr></tbody></table>
<p>
	إذا سحبنا <code>n</code> قيمة من X وجمعناها يكون عمومًا:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">X</span> ∼ <span style="font-style:italic;color:red">N</span> (µ, σ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)</span>
			</td>
		</tr></tbody></table>
<h2>
	توزيعات أخذ العينات
</h2>

<p>
	لدينا كل ما نحتاجه لحساب توزيع أخذ عينات x̄، وتذكَّر أنه سنحسب x̄ عن طريق وزن n إناث غوريلا ونجمع القيم لنحسب الوزن الكلي ثم نقسم المجموع على n، فبفرض أنّ X توزيع أوزان الغوريلا هو توزيع طبيعي تقريبًا:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">X</span> ∼ <span style="font-style:italic;color:red">N</span> (µ, σ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">)</span>
			</td>
		</tr></tbody></table>
<p>
	يكون الوزن الكلي <code>Y</code> موزعًا إذا وزَنّا n غوريلا.
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">Y</span> ∼ <span style="font-style:italic;color:red">N</span> (<span style="font-style:italic">n</span> µ, <span style="font-style:italic">n</span> σ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium">) </span>
			</td>
		</tr></tbody></table>
<p>
	يكون Z متوسط العينة موزعًا إذا قسمنا على n وبالاستعانة بالمعادلة الثالثة.
</p>

<p>
	Z ∼ N(, 2ln)
</p>

<p>
	بالاستعانة بالمعادلة الأولى بافتراض a = 1/n.
</p>

<p>
	يكون توزيع Z هو توزيع أخذ عينات x̄، ومتوسط Z هو µ الذي يظهر أن x̄ هو تقدير غير متحيز للمقدار µ، في حين يكون تباين توزيع أخذ العينات هو σ<sup>2</sup>/n، لذا فإن الانحراف المعياري لتوزيع أخذ العينات الذي يمثل الخطأ المعياري للتقدير هو σ / √n، ويكون σ في هذا المثال هو 7.5 كيلوغرامًا وn هو 9، لذا يكون الخطأ المعياري هو 2.5 كيلوغرامًا، ونلاحظ أنّ النتيجة متسقة مع التقدير الذي نتج عن المحاكاة لكن أسرع في الحساب.
</p>

<p>
	يمكننا أيضًا استخدام توزيع أخذ العينات لحساب فواصل الثقة، حيث أنّ فاصل الثقة ‎90% لـ x̄ هو الفاصل بين المئين 9 والمئين 95 لـ Z، وبما أنّ توزيع Z توزيع طبيعي، فيمكننا حساب قيم المئين عن طريق تقييم دالة التوزيع التراكمي العكسية، كما لا يوجد شكل مغلق من دالة التوزيع التراكمي للتوزيع الطبيعي أو دالة التوزيع التراكمي العكسية، لكن توجد أساليب عددية سريعة وهي موجود على أساس تنفيذ برمجي في حزمة ساي باي SciPy كما رأينا في قسم التوزيع الطبيعي في مقال <a href="https://academy.hsoub.com/programming/python/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-modelling-distributions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1332/" rel="">نمذجة التوزيعات Modelling distributions في بايثون</a>، كما تزودنا مكتبة <code>thinkstats2</code> بدالة مغلفة تجعل دالة ساي باي SciPy سهلة الاستخدام:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_9" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">EvalNormalCdfInverse</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">=</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">norm</span><span class="pun">.</span><span class="pln">ppf</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> loc</span><span class="pun">=</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> scale</span><span class="pun">=</span><span class="pln">sigma</span><span class="pun">)</span></pre>

<p>
	يعيد المئين الموافق من توزيع طبيعي له الوسيطين <code>mu</code> و<code>sigma</code> إذا كان لدينا احتمال <code>p</code>، كما حسبنا من أجل فاصل الثقة ‎90% للمقدار x̄ المئين 5 والمئين 95 كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_11" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">EvalNormalCdfInverse</span><span class="pun">(</span><span class="lit">0.05</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">=</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">=</span><span class="lit">2.5</span><span class="pun">)</span><span class="pln">
</span><span class="lit">85.888</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">EvalNormalCdfInverse</span><span class="pun">(</span><span class="lit">0.95</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">=</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">=</span><span class="lit">2.5</span><span class="pun">)</span><span class="pln">
</span><span class="lit">94.112</span></pre>

<p>
	لذا إذا أجرينا التجربة عدة مرات، فسنتوقع أن يكون التقدير في المدى (94.1, 85.9) حوالي ‎90% من المرات، وهذا متسق مع النتائج التي حصلنا عليها عندما أجرينا محاكاة.
</p>

<h2>
	تمثيل التوزيعات الطبيعية
</h2>

<p>
	عرّفنا صنفًا يدعى <code>Normal</code> يمثِّل التوزيع الطبيعي ويرمز المعادلات الموجودة في الأقسام السابقة بهدف توضيح هذه الحسابات، أي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_13" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">,</span><span class="pln"> sigma2</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">mu </span><span class="pun">=</span><span class="pln"> mu
        self</span><span class="pun">.</span><span class="pln">sigma2 </span><span class="pun">=</span><span class="pln"> sigma2

    </span><span class="kwd">def</span><span class="pln"> __str__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">'N(%g, %g)'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">sigma2</span><span class="pun">)</span></pre>

<p>
	يمكننا استنساخ الصنف <code>Normal</code> لتمثيل توزيع أوزان الغوريلا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_15" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7.5</span><span class="pun">**</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist
N</span><span class="pun">(</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> </span><span class="lit">56.25</span><span class="pun">)</span></pre>

<p>
	يزودنا الصنف <code>Normal</code> بالدالة <code>Sum</code> التي تأخذ حجم العينة <code>n</code> وتعيد توزيع مجموع <code>n</code> قيمة باستخدام المعادلة الثالثة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_17" style="">
<span class="pln">    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="pln">n </span><span class="pun">*</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> n </span><span class="pun">*</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">sigma2</span><span class="pun">)</span></pre>

<p>
	يمكن تطبيق عمليات القسمة والضرب باستخدام المعادلة الأولى:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_19" style="">
<span class="pln">    </span><span class="kwd">def</span><span class="pln"> __mul__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> factor</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="pln">factor </span><span class="pun">*</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> factor</span><span class="pun">**</span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">sigma2</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> __div__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> divisor</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> divisor </span><span class="pun">*</span><span class="pln"> self</span></pre>

<p>
	يمكننا الآن حساب توزيع أخذ عينات المتوسط مع حجم عينة قدره 9:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_21" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist_xbar </span><span class="pun">=</span><span class="pln"> dist</span><span class="pun">.</span><span class="typ">Sum</span><span class="pun">(</span><span class="lit">9</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">9</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist_xbar</span><span class="pun">.</span><span class="pln">sigma
</span><span class="lit">2.5</span></pre>

<p>
	يكون الانحراف المعياري لتوزيع أخذ العينات هو 2.5 كيلوغرامًا كما رأينا في القسم السابق، وأخيرًا يزودنا الصنف <code>Normal</code> بالدالة <code>Percentile</code> التي تحسب فاصل الثقة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_23" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist_xbar</span><span class="pun">.</span><span class="typ">Percentile</span><span class="pun">(</span><span class="lit">5</span><span class="pun">),</span><span class="pln"> dist_xbar</span><span class="pun">.</span><span class="typ">Percentile</span><span class="pun">(</span><span class="lit">95</span><span class="pun">)</span><span class="pln">
</span><span class="lit">85.888</span><span class="pln"> </span><span class="lit">94.113</span></pre>

<p>
	هذه هي الإجابة ذاتها التي حصلنا عليها سابقًا، حيث سنستخدم الصنف <code>Normal</code> مرةً أخرى لاحقًا، لكن علينا استكشاف بعض أساليب التحليل الأخرى أولًا قبل ذلك.
</p>

<h2>
	مبرهنة النهاية المركزية
</h2>

<p>
	رأينا في الأقسام السابقة أنه إذا جمعنا القيم المأخوذة من توزيع طبيعي، فسيكون توزيع المجموع طبيعيًا، ولكن لا تتميز معظم التوزيعات الأخرى بهذه الخاصية، أي إذا جمعنا القيم المأخوذة من توزيعات أخرى، فلن يكون المجموع توزيعًا تحليليًا عادةً، لكن إذا جمعنا <code>n</code> قيمة من معظم التوزيعات، فسيتقارب توزيع المجموع إلى التوزيع الطبيعي مع زيادة <code>n</code>.
</p>

<p>
	وبتحديد أكبر، إذا كان لتوزيع القيم متوسطًا µ وانحرافًا معياريًا σ، فسيكون توزيع المجموع N(n µ, nσ<sup> 2</sup>) تقريبًا، وتكون هذه النتيجة هي مبرهنة النهاية المركزية -أو CLT اختصارًا-، إذ تُعَدّ من أفضل الأدوات للتحليل الإحصائي، لكن مع بعض التحذيرات وهي:
</p>

<ul>
<li>
		يجب أخذ القيم بصورة مستقلة، إذ لا يمكن تطبيق مبرهنة النهاية المركزية إذا كانت القيم مترابطة على الرغم من أنه نادرًا ما يمثِّل مشكلةً أثناء التطبيق العملي.
	</li>
	<li>
		يجب انتماء القيم إلى التوزيع نفسه على الرغم أنه يمكن التغاضي عن هذا الشرط إلى حد ما.
	</li>
	<li>
		يجب أخذ القيم من توزيع له متوسط وتباين محدودَين، لذا لا تنطبق معظم توزيعات باريتو Pareto على هذا الشرط.
	</li>
	<li>
		يعتمد معدل التقارب على تجانف التوزيع، إذ تتلاقى المجاميع من التوزيع الأسي إذا كانت <code>n</code> صغيرةً، في حين تتطلب مجاميع القيم المأخوذة من التوزيع اللوغاريتمي الطبيعي أحجامًا أكبر.
	</li>
</ul>
<p>
	تشرح مبرهنة النهاية المركزية انتشار التوزيعات الطبيعية في العالم الطبيعي، وتتأثر العديد من خصائص الكائنات الحية بالعوامل الوراثية والبيئية التي يكون تأثيرها مضافًا، كما تكون الخصائص التي نقيسها هي مجموع عدد كبير من التأثيرات الصغيرة، لذا يميل توزيعها إلى أن يكون طبيعيًا.
</p>

<h2>
	اختبار مبرهنة النهاية المركزية
</h2>

<p>
	سنجري بعض التجارب لنرى متى وكيف تنطبق مبرهنة النهاية المركزية، وسنجرب في البداية توزيعًا أسيًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_26" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeExpoSamples</span><span class="pun">(</span><span class="pln">beta</span><span class="pun">=</span><span class="lit">2.0</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
    samples </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> n </span><span class="kwd">in</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">]:</span><span class="pln">
        sample </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">np</span><span class="pun">.</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">exponential</span><span class="pun">(</span><span class="pln">beta</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">))</span><span class="pln">
                  </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">)]</span><span class="pln">
        samples</span><span class="pun">.</span><span class="pln">append</span><span class="pun">((</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> sample</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> samples</span></pre>

<p>
	تولِّد الدالة <code>MakeExpoExamples</code> عينات من مجاميع القيم الأسية، حيث استخدمنا مصطلح القيم الأسية على أساس اختصار لجملة القيم المأخوذة من توزيع أسي، ويكون <code>beta</code> هو وسيط التوزيع؛ أما <code>iters</code> هو عدد المجاميع التي يجب توليدها، ولتفسير هذه الدالة سنبدأ من الداخل أولًا، حيث نحصل على تسلسل من <code>n</code> قيمة أسية في كل استدعاء للدالة <code>np.normal.exponential</code> ونحسب مجموعها.
</p>

<p>
	يُعَدّ <code>sample</code> قائمةً لهذه المجاميع وبطول <code>iters</code>، ومن الصعب التمييز بين <code>n</code> و<code>iters</code>، لكن <code>n</code> هو عدد التعبيرات في كل مجموع، و<code>iters</code> هو عدد المجاميع التي نحسبها لوصف توزيع المجاميع، حيث أنّ القيمة المعادة هي قائمة من أزواج <code>(n, sample)</code>، ثم ننشئ رسمًا احتماليًا طبيعيًا لكل زوج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_28" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">NormalPlotSamples</span><span class="pun">(</span><span class="pln">samples</span><span class="pun">,</span><span class="pln"> plot</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> ylabel</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> sample </span><span class="kwd">in</span><span class="pln"> samples</span><span class="pun">:</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">SubPlot</span><span class="pun">(</span><span class="pln">plot</span><span class="pun">)</span><span class="pln">
        thinkstats2</span><span class="pun">.</span><span class="typ">NormalProbabilityPlot</span><span class="pun">(</span><span class="pln">sample</span><span class="pun">)</span><span class="pln">

        thinkplot</span><span class="pun">.</span><span class="typ">Config</span><span class="pun">(</span><span class="pln">title</span><span class="pun">=</span><span class="str">'n=%d'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> ylabel</span><span class="pun">=</span><span class="pln">ylabel</span><span class="pun">)</span><span class="pln">
        plot </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span></pre>

<p>
	تأخذ <code>NormalPlotSamples</code> قائمة الأزواج من <code>MakeExpoSamples</code> وتولِّد سطرًا من رسوم الاحتمالات الطبيعية.
</p>

<p style="text-align: center;">
	<img alt="الشكل 14.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98864" data-unique="47we9d4j0" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282915bcb66a_14.1.png.42dc720047b0fd1e65b265ea7e3b38cb.png"></p>

<p>
	يوضِّح الشكل السابق توزيع مجاميع القيم الأسية في السطر العلوي والقيم اللوغاريتمية الطبيعية في السطر السفلي، كما يُظهر الشكل السابق الموجود في الأعلى النتائج، إذ يكون توزيع المجموع أسيًا من أجل <code>n=1</code>، لذا فإن رسم الاحتمال الطبيعي ليس مستقيمًا، لكن إذا كان <code>n=10</code>، فيكون توزيع المجموع طبيعيًا تقريبًا، وإذا كان <code>n=100</code>، فلا يمكن تمييز التوزيع عندها عن الطبيعي.
</p>

<p>
	يُظهر الشكل السابق في السطر السفلي نتائجًا مشابهةً للتوزيع اللوغاريتمي الطبيعي، إذ عادةً ما تكون التوزيعات اللوغاريتمية الطبيعية أكثر تجانفًا من التوزيعات الأسية، لذا يأخذ توزيع المجاميع وقتًا أطول لكي يتقارب، وإذا كان <code>n=10</code>، يكون الرسم الاحتمالي الطبيعي أبعد ما يكون عن المستقيم، لكن إذا كان <code>n=100</code> فيكون التوزيع طبيعيًا تقربيًا.
</p>

<p style="text-align: center;">
	<img alt="الشكل 14.2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98865" data-unique="cms527opw" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282915d1c08d_14.2.png.7de4ca23d291063398deea34d27dcd60.png"></p>

<p>
	يُظهر الشكل السابق توزيعات مجاميع قيم باريتو Pareto في السطر العلوي والقيم الأسية المترابطة في السطر السفلي، حيث تُعَدّ توزيعات باريتو Pareto أكثر تجانفًا من التوزيعات اللوغاريتمية الطبيعية، وغالبًا لا يكون للعديد من توزيعات باريتو Pareto متوسطًا وتباينًا محدودَين اعتمادًا على المعامِلات، وبالتالي لا تنطبق مبرهنة النهاية المركزية على توزيع باريتو Pareto، كما يُظهر الشكل السابق في السطر العلوي توزيعات مجاميع قيم باريتو Pareto، فحتى إذا كان <code>n=100</code>، فسيكون الرسم الاحتمالي الطبيعي أبعد ما يكون عن المستقيم.
</p>

<p>
	ذكرنا أيضًا أنه لا يمكن تطبيق مبرهنة النهاية المركزية إذا كانت القيم مترابطةً، ولاختبار ذلك سنولِّد قيمًا مترابطةً من التوزيع الأسي، علمًا أنّ خطوات الخوارزمية لتوليد القيم المترابطة هي:
</p>

<ol>
<li>
		توليد القيم العادية المترابطة.
	</li>
	<li>
		استخدام دالة التوزيع التراكمي الطبيعي لجعل القيم موحدةً. 3.استخدام دالة التوزيع التراكمي العكسية الأسية لتحويل القيم الموحَّدة إلى أسية.
	</li>
</ol>
<p>
	تعيد الدالة <code>GenerateCorrelated</code> مكررًا لـ <code>n</code> قيمة طبيعية من الارتباط التسلسلي <code>rho</code> :
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_30" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GenerateCorrelated</span><span class="pun">(</span><span class="pln">rho</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">):</span><span class="pln">
    x </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">gauss</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">yield</span><span class="pln"> x

    sigma </span><span class="pun">=</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> rho</span><span class="pun">**</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">):</span><span class="pln">
        x </span><span class="pun">=</span><span class="pln"> random</span><span class="pun">.</span><span class="pln">gauss</span><span class="pun">(</span><span class="pln">x</span><span class="pun">*</span><span class="pln">rho</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">yield</span><span class="pln"> x</span></pre>

<p>
	تكون القيمة الأولى قيمةً طبيعيةً معياريةً، وتعتمد كل قيمة لاحقة على سابقتها، أي إذا كانت القيمة السابقة هي x، فيكون متوسط القيمة التالية هو<code>x * rho</code> ويكون التباين هو <code>1‎-rho**2</code>، علمًا أنّ <code>random.gauss</code> تأخذ الانحراف المعياري على أساس وسيط ثان وليس التباين، كما تأخذ الدالة <code>GenerateExpoCorrelated</code> التسلسل الناتج وتجعله أسيًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_32" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">GenerateExpoCorrelated</span><span class="pun">(</span><span class="pln">rho</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">):</span><span class="pln">
    normal </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="typ">GenerateCorrelated</span><span class="pun">(</span><span class="pln">rho</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">))</span><span class="pln">
    uniform </span><span class="pun">=</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">norm</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">(</span><span class="pln">normal</span><span class="pun">)</span><span class="pln">
    expo </span><span class="pun">=</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">expon</span><span class="pun">.</span><span class="pln">ppf</span><span class="pun">(</span><span class="pln">uniform</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> expo</span></pre>

<p>
	حيث يكون <code>normal</code> قائمةً من القيم الطبيعية المترابطة، و<code>uniform</code> تسلسلًا من القيم الموحَّدة التي تقع بين 0 و1، و<code>expo</code> تسلسلًا مترابطًا من القيم الأسية، في حين ترمز <code>ppf</code> إلى دالة نقطة النسبة المئوية percent point function التي هي اسم آخر لدالة التوزيع التراكمي المعكوسة.
</p>

<p>
	يُظهر الشكل السابق في السطر السفلي توزيعات مجاميع القيم الأسية المترابطة إذا كان <code>rho=0.9</code>، ويبطئ الترابط من معدل التقارب، لكن إذا كان <code>n=100</code>، فيكون الرسم الاحتمالي الطبيعي مستقيمًا تقريبًا، لذا على الرغم من أنّ مبرهنة النهاية المركزية لا تطبق تمامًا عندما تكون القيم مترابطة، إلا أنه نادرًا ما تشكِّل الترابطات المتوسطة مشكلةً أثناء التطبيق العملي، كما تهدف هذه التجارب إلى إظهار الطريقة التي تعمل بها مبرهنة النهاية المركزية بالإضافة إلى إظهار ماذا يحدث عندما لا تعمل، ودعونا الآن نرى كيف يمكننا استخدامها.
</p>

<h2>
	تطبيق مبرهنة النهاية المركزية
</h2>

<p>
	علينا العودة إلى المثال الموجود في قسم اختبار الفرق في المتوسطات في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">اختبار الفرضيات الإحصائية</a>، وهو اختيار الفرق الواضح في متوسط مدة الحمل للأطفال الأوائل والأطفال الآخرين، وكما رأينا فإن الفرق الواضح هو حوالي 0.078 أسبوع:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_34" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> delta </span><span class="pun">=</span><span class="pln"> firsts</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> others</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
</span><span class="lit">0.078</span></pre>

<p>
	تذكَّر منطق اختبار الفرضيات: نحسب القيمة الاحتمالية p-value وهي احتمال الفرق المرصود في ظل فرضية العدم، فإذا كان الاحتمال صغيرًا، فنستنتج أنه من غير المرجح أن يكون الفرق المرصود ناجمًا عن الصدفة فحسب، وتكون فرضية العدم في هذا المثال هي أنّ توزيع مدة الحمل هي نفسها للأطفال الأوائل ولبقية الأطفال، لذا يمكننا حساب توزيع أخذ عينات المتوسط كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_36" style="">
<span class="pln">    dist1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SamplingDistMean</span><span class="pun">(</span><span class="pln">live</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">,</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">firsts</span><span class="pun">))</span><span class="pln">
    dist2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SamplingDistMean</span><span class="pun">(</span><span class="pln">live</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">,</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">others</span><span class="pun">))</span></pre>

<p>
	علمًا أنّ توزيعي أخذ العينات مبنيان على البيانات نفسها وهي مجموعة الولادات الحية كلها، حيث تأخذ <code>SamplingDistMeans</code> تسلسلًا من القيم وحجم العينة، وتعيد كائنًا طبيعيًا يمثِّل توزيع أخذ العينات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_38" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">SamplingDistMean</span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">):</span><span class="pln">
    mean</span><span class="pun">,</span><span class="pln"> var </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(),</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">var</span><span class="pun">()</span><span class="pln">
    dist </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="pln">mean</span><span class="pun">,</span><span class="pln"> var</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> dist</span><span class="pun">.</span><span class="typ">Sum</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> n</span></pre>

<p>
	يمثِّل <code>mean</code> متوسط البيانات؛ أما <code>var</code> فهو التباين، وسننشئ تقريبًا لتوزيع البيانات بالاستعانة بتوزيع طبيعي <code>dist</code>، إذ يُعَدّ توزيع البيانات في هذا المثال لاطبيعيًا، لذا فإنّ هذا التقريب غير جيد، لكن علينا الآن حساب <code>dis.Sum(n)/n</code> وهو توزيع أخذ عينات متوسط <code>n</code> قيمة، ويكون حسب مبرهنة النهاية المركزية أنّ توزيع أخذ عينات المتوسط هو توزيع طبيعي حتى لو لم يكن توزيع البيانات طبيعيًا، ثم نحسب توزيع أخذ عينات الفرق في المتوسطات، حيث يعلم الصنف <code>Normal</code> كيفية تطبيق الطرح باستخدام المعادلة الثانية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_40" style="">
<span class="pln">    </span><span class="kwd">def</span><span class="pln"> __sub__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Normal</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">mu </span><span class="pun">-</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">mu</span><span class="pun">,</span><span class="pln">
                      self</span><span class="pun">.</span><span class="pln">sigma2 </span><span class="pun">+</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">sigma2</span><span class="pun">)</span></pre>

<p>
	لذا يمكننا حساب توزيع أخذ عينات الفرق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_42" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist </span><span class="pun">=</span><span class="pln"> dist1 </span><span class="pun">-</span><span class="pln"> dist2
N</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.0032</span><span class="pun">)</span></pre>

<p>
	يكون المتوسط هو 0، وهذا منطقي لأننا نتوقع أن يكون للعينتين من التوزيع نفسه المتوسط نفسه وسطيًا، ويكون تباين توزيع أخذ العينات هو 0.0032، كما يزودنا الصنف <code>Normal</code> بالدالة <code>Prob</code> التي تقيّم دالة التوزيع التراكمي الطبيعية، ويمكننا استخدام <code>Prob</code> لحساب احتمالية وجود فرق بحجم <code>delta</code> في ظل فرضية العدم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_44" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> dist</span><span class="pun">.</span><span class="typ">Prob</span><span class="pun">(</span><span class="pln">delta</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.084</span></pre>

<p>
	يعني هذا أنّ القيمة الاحتمالية للاختبار أحادي الجانب هو 0.84؛ أما بالنسبة للاختبار ثنائي الجانب فسنحسب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_46" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> dist</span><span class="pun">.</span><span class="typ">Prob</span><span class="pun">(-</span><span class="pln">delta</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.084</span></pre>

<p>
	ظهر لدينا النتيجة نفسها لأن التوزيع الطبيعي متناظر، ويكون مجموع الذيول هو 0.168، وهو متسق مع التقدير في قسم اختبار الفرق في المتوسطات في مقال اختبار الفرضيات الإحصائية الذي كانت قيمته 0.17.
</p>

<h2>
	اختبار الارتباط
</h2>

<p>
	استخدمنا في قسم اختبار الارتباط في مقال اختبار الفرضيات الإحصائية من هذه السلسلة والمشار إليه بالأعلى، اختبار التبديل permutation test لاختبار الارتباط بين وزن الطفل عند الولادة وعمر الأم، ووجدنا أنه ذو دلالة إحصائية والقيمة الاحتمالية هي أقل من 0.001، حيث يمكننا فعل الشيء ذاته لكن بأسلوب تحليلي مبني على نتيجة رياضية: إذا كان لدينا متغيرين موزعَين طبيعيًا وغير مترابطَين، فإذا ولدنا عينةً حجمها <code>n</code> وحسبنا ارتباط بيرسون <code>r</code> ثم حسبنا الارتباط بعد التحويل، يكون:
</p>

<table class="display" style="direction: ltr; margin: auto;"></table>
<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">t</span> = <span style="font-style:italic">r</span> </span>
			</td>
			<td class="dcell">
				<table class="cellpadding0" style="border-spacing:0"><tbody>
<tr>
<td align="right">
								<div class="vbar" style="height:2em;">
									 
								</div>
							</td>
						</tr>
<tr>
<td>
								<span style="font-size:xx-large"><span style="font-size:150%">√</span></span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<table class="cellpadding0" style="border:0;border-spacing:1;border-collapse:separate;"><tbody>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td style="text-align:center;white-space:nowrap">
								<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
												<span style="font-size:medium"><span style="font-style:italic">n</span>−2</span>
											</td>
										</tr>
<tr>
<td class="hbar">
												 
											</td>
										</tr>
<tr>
<td class="dcell" style="text-align:center">
												<span style="font-size:medium">1−<span style="font-style:italic">r</span></span><sup><span style="font-size:medium">2</span></sup>
</td>
										</tr>
</tbody></table>
</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	يُعَدّ توزيع <code>t</code> هو توزيع ستيودنت الاحتمالي Student’s t-distribution مع معامِل <code>n-2</code>، حيث يُعَدّ التوزيع <code>t</code> توزيعًا تحليليًا، ويمكن حساب دالة التوزيع التراكمي بفعالية باستخدام دوال غاما gamma، حيث يمكننا استخدام النتيجة لحساب توزيع أخذ عينات الارتباط في ظل فرضية العدم، أي إذا ولَّدنا التسلسلات غير المترابطة للقيم الطبيعية، فما هو توزيع الارتباط؟ تأخذ الدالة <code>StudentCdf</code> حجم العينة <code>n</code> ويُعيد توزيع أخذ عينات الارتباط:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_48" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">StudentCdf</span><span class="pun">(</span><span class="pln">n</span><span class="pun">):</span><span class="pln">
    ts </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">linspace</span><span class="pun">(-</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pun">)</span><span class="pln">
    ps </span><span class="pun">=</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">t</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">(</span><span class="pln">ts</span><span class="pun">,</span><span class="pln"> df</span><span class="pun">=</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    rs </span><span class="pun">=</span><span class="pln"> ts </span><span class="pun">/</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">n </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> ts</span><span class="pun">**</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">rs</span><span class="pun">,</span><span class="pln"> ps</span></pre>

<p>
	حيث أنّ <code>ts</code> هي مصفوفة نمباي NumPy لتوزيع <code>t</code> وهو الارتباط بعد التحويل، كما تحتوي <code>ps</code> على الاحتمالات الموافقة المحسوبة باستخدام دالة التوزيع التراكمي لتوزيع ستيودنت الاحتمالي وهي منفَّذة برمجيًا في حزمة ساي باي SciPy، ويمثِّل معامِل توزيع <code>t</code> (أي t-distribution) الذي يدعى <code>df</code> درجات الحرية degrees of freedom، ولن نشرح هذا المصطلح لكن يمكنك القراءة عنه في صفحة <a href="https://ar.wikipedia.org/wiki/%D8%AF%D8%B1%D8%AC%D8%A9_%D8%AD%D8%B1%D9%8A%D8%A9_(%D8%A5%D8%AD%D8%B5%D8%A7%D8%A1)" rel="external nofollow">الويكيبيديا</a>.
</p>

<p style="text-align: center;">
	<img alt="الشكل 14.3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98866" data-unique="qauie1uiy" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282915da2356_14.3.png.f64ef69e57bbcac8214df137d6a7ab43.png"></p>

<p>
	يوضِّح الشكل السابق توزيع أخذ عينات ارتباط القيم الطبيعية غير المرتبطة، ويتوجب علينا تطبيق التحويل العكسي إذا أردنا تحويل <code>ts</code> إلى معاملات الترابط <code>rs</code>:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">r</span> = <span style="font-style:italic">t</span> / </span>
			</td>
			<td class="dcell">
				<span style="font-size:x-large">√</span>
			</td>
			<td class="dcell">
				<table class="cellpadding0" style="border:0;border-spacing:1;border-collapse:separate;"><tbody>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td style="text-align:center;white-space:nowrap">
								<span style="font-size:medium"><span style="font-style:italic">n</span> − 2 + <span style="font-style:italic">t</span></span><sup><span style="font-size:medium">2</span></sup>
</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	تكون النتيجة هي توزيع أخذ عينات <code>r</code> في ظل فرضية العدم، كما يُظهر الشكل السابق هذا التوزيع إلى جانب التوزيع الذي ولَّدناه في قسم اختبار الارتباط في مقال اختبار الفرضيات الإحصائية، وذلك عن طريق تطبيق إعادة أخذ العينات، فالتوزيعان متطابقان تقريبًا، فعلى الرغم من أنّ التوزيعين الفعليين ليسا طبيعيين، إلا أنّ معامِل ارتباط بيرسون مبني على متوسطي وتبايني العينة، وبحسب مبرهنة النهاية المركزية فإنّ الإحصائيات المبنية على العزوم موزعة توزيعًا طبيعيًا حتى لو لم تكن البيانات كذلك.
</p>

<p>
	نستنتج من الشكل السابق أنّ قيمة الارتباط المرصود هو 0.07، ومن غير المرجح أن تظهر لنا هذه القيمة إذا لم تكن المتغيرات مرتبطةً، كما يمكننا حساب مدى احتمال حدوث ذلك باستخدام التوزيع التحليلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_56" style="">
<span class="pln">    t </span><span class="pun">=</span><span class="pln"> r </span><span class="pun">*</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">((</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">-</span><span class="pln">r</span><span class="pun">**</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
    p_value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">t</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">(</span><span class="pln">t</span><span class="pun">,</span><span class="pln"> df</span><span class="pun">=</span><span class="pln">n</span><span class="pun">-</span><span class="lit">2</span><span class="pun">)</span></pre>

<p>
	نحسب قيمة <code>t</code> الموافقة لـ <code>r=0.07</code> ثم نقيِّم توزيع <code>t</code> عند <code>t</code>، ونلاحظ أنّ النتيجة هي 2.9e-11، إذ يُظهر هذا المثال إحدى ميزات الأسلوب التحليلي، حيث يمكننا حساب قيم احتمالية صغيرة جدًا لكن لا يهمنا هذا الأمر في الحالات الواقعية عادةً.
</p>

<h2>
	اختبار مربع كاي
</h2>

<p>
	استخدمنا في قسم اختبارات مربع كاي الموجود في مقال اختبار الفرضيات الإحصائية إحصائيات مربع كاي لنختبر فيما إن كان حجر النرد ملتويًا، حيث تقيس إحصائية مربع كاي الانحراف الكلي الموحَّد عن القيم المتوقعة في جدول:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium">χ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium"> </span>
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:xx-large">∑</span>
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">i</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium">(<span style="font-style:italic">O</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium"> − <span style="font-style:italic">E</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium">)</span><sup><span style="font-size:medium">2</span></sup>
</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">E</span><sub><span style="font-style:italic;font-size:medium">i</span></sub>
</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	يُعَدّ توزيع أخذ العينات فيها تحليليًا في ظل فرضية العدم، وهو من أسباب شيوع استخدام إحصائية مربع كاي، وبصدفة رائعة فإنه يدعى توزيع مربع كاي، ويمكن حساب دالة التوزيع التراكمي لمربع كاي بكفاءة باستخدام دوال غاما تمامًا مثل توزيع <code>t</code>.
</p>

<p style="text-align: center;">
	<img alt="الشكل 14.4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98867" data-unique="vtvlk2ajy" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282915e32771_14.4.png.2e88eff9acced1eccc0e1c0d88d02f7c.png"></p>

<p>
	يوضِّح الشكل السابق توزيع أخذ عينات إحصائية مربع كاي للنرد العادل ذي الوجوه الستة، حيث تزودنا مكتبة ساي باي SciPy بتنفيذ برمجي لتوزيع مربع كاي الذي يمكننا استخدامه لحساب توزيع أخذ عينات إحصائية مربع كاي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_50" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">ChiSquaredCdf</span><span class="pun">(</span><span class="pln">n</span><span class="pun">):</span><span class="pln">
    xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">linspace</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">101</span><span class="pun">)</span><span class="pln">
    ps </span><span class="pun">=</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">chi2</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> df</span><span class="pun">=</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ps</span><span class="pun">)</span></pre>

<p>
	يُظهر الشكل السابق النتيجة التحليلية إلى جانب التوزيع الذي حصلنا عليه عن طريق تطبيق إعادة أخذ العينات، وهما متماثلان جدًا خاصةً من حيث شكل الذيل وهو الجزء الذي يهمنا، كما يمكننا استخدام هذا التوزيع لحساب القيمة الاحتمالية لإحصائية الاختبار <code>chi2</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9036_52" style="">
<span class="pln">    p_value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> scipy</span><span class="pun">.</span><span class="pln">stats</span><span class="pun">.</span><span class="pln">chi2</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">(</span><span class="pln">chi2</span><span class="pun">,</span><span class="pln"> df</span><span class="pun">=</span><span class="pln">n</span><span class="pun">-</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	نرى أن النتيجة هي 0.041 وهي متسقة مع النتيجة التي رأيناها في قسم اختبارات مربع كاي الموجود في مقال اختبار الفرضيات الإحصائية، حيث أن معامِل توزيع مربع كاي هو درجة الحرية أيضًا، ويكون المعامِل الصحيح في هذه الحالة هو <code>n-1</code> حيث أنّ <code>n</code> هو حجم الجدول 6، وقد يكون اختيار هذا المعامِل أمرًا صعبًا، وفي الواقع لا نستطيع التأكد من أننا أصبنا حتى نولِّد شكلًا مثل الشكل السابق لنقارن النتائج التحليلية مع نتائج إعادة أخذ العينات.
</p>

<h2>
	نقاش
</h2>

<p>
	تركِّز هذه السلسلة على الأساليب الحسابية مثل إعادة أخذ العينات والتبديل، وتملك هذه الأساليب ميزات لا تمتلكها الأساليب التحليلية مثل:
</p>

<ul>
<li>
		سهلة الفهم والشرح، إذ يُعَدّ اختبار الفرضيات على سبيل المثال من أصعب المواضيع في مجال لإحصاء، ولا يستطيع العديد من الطلاب فهم ماهية القيم الاحتمالية، لكن الطريقة التي شرحناها في مقال اختبار الفرضيات الإحصائية جعلت المفهوم أوضح والتي هي حساب إحصائية الاختبار ومحاكاة فرضية العدم.
	</li>
	<li>
		متينة ومتعددة الاستعمالات، إذ غالبًا ما تكون الأساليب التحليلية مبنيةً على افتراضات لا تنطبق على الواقع؛ أما الأساليب الحسابية فهي تتطلب افتراضات أقل ويمكن تعديلها وتوسيعها بصورة أسهل.
	</li>
	<li>
		يمكن تصحيحها، لكن غالبًا ما تكون الأساليب التحليلية أشبه بالصندوق الأسود، حيث تدخل الأعداد وتخرج الأساليب النتائج، لذا من السهل ارتكاب أخطاء خفية ومن الصعب التأكد من صحة النتائج ومن الصعب إيجاد المشكلة إذا كانت خاطئة؛ أما الأساليب الحسابية فهي قابلة للتطوير والاختبار التدريجي مما يعزِّز الثقة في النتائج.
	</li>
</ul>
<p>
	لكن هناك سلبية واحدة وهي أنّ الأساليب الحسابية بطيئة، لكن إذا أخذنا السلبيات والإيجابيات بالحسبان، فنعتقد أنّ العملية التالية هي الأفضل:
</p>

<ol>
<li>
		استخدم الأساليب الحسابية أثناء الاستكشاف، وإذا وجدت إجابةً مرضيةً وزمنًا مقبولًا للتنفيذ، يمكنك التوقف.
	</li>
	<li>
		إذا لم يكن زمن التنفيذ مقبولًا، فابحث عن حلول للتحسين.
	</li>
	<li>
		إذا كان استخدام الأسلوب التحليلي أنسب من الأسلوب الحسابي، فاستخدم الأسلوب الحسابي ليكون أساسًا للمقارنة، إذ سيوفِّر لك هذا الأمر إمكانية التحقق المتبادل في النتائج الحسابية والتحليلية.
	</li>
</ol>
<p>
	لم تتطلب معظم المسائل التي عملت عليها تجاوز الخطوة الأولى من العملية السابقة.
</p>

<h2>
	تمارين
</h2>

<p>
	يوجد حل هذه التمارين في الملف <a href="https://github.com/AllenDowney/ThinkStats2/blob/master/solutions/chap14soln.ipynb" rel="external nofollow">chap14soln.py</a> في مستودع الشيفرات <a href="https://github.com/AllenDowney/ThinkStats2" rel="external nofollow">ThinkStats2</a> على GitHub..
</p>

<h3>
	تمرين 1
</h3>

<p>
	رأينا في قسم التوزيع اللوغاريتمي الطبيعي الموجود في مقال نمذجة التوزيعات Modelling distributions في بايثون، أنّ توزيع أوزان البالغين لوغاريتمي طبيعي تقريبًا، وتتمثَّل إحدى التفسيرات في أنّ الوزن الذي يكتسبه الشخص في كل عام يتناسب مع وزنه الحالي، ويكون وزن البالغين في هذه الحالة ناتجًا عن عدد كبير من العوامل التي نطبق بينها عملية جداء:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">w</span> = <span style="font-style:italic">w</span></span><sub><span style="font-size:medium">0</span></sub><span style="font-size:medium"> <span style="font-style:italic">f</span></span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> <span style="font-style:italic">f</span></span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> … <span style="font-style:italic">f</span></span><sub><span style="font-style:italic;font-size:medium">n</span></sub><span style="font-size:medium">  </span>
			</td>
		</tr></tbody></table>
<p>
	حيث أنّ <code>w</code> هو وزن البالغ، و<code>w&lt;sub&gt;0&lt;/sub&gt;‎</code> هو وزن الطفل عند الولادة، و<code>f&lt;sub&gt;i&lt;/sub&gt;‎</code> هو عامل الوزن المكتسب في العام <code>i</code>، علمًا أنّ لوغاريتم الجداء هو جمع لوغاريتمات العوامل:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium">log<span style="font-style:italic">w</span> = log<span style="font-style:italic">w</span></span><sub><span style="font-size:medium">0</span></sub><span style="font-size:medium"> + log<span style="font-style:italic">f</span></span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> + log<span style="font-style:italic">f</span></span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> + ⋯ + log<span style="font-style:italic">f</span></span><sub><span style="font-style:italic;font-size:medium">n</span></sub><span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	يكون توزيع logw حسب مبرهنة النهاية المركزية طبيعيًا تقريبًا إذا كانت <code>n</code> كبيرةً، مما يعني أنّ توزيع w لوغاريتمي طبيعي، ويمكنك من أجل نمذجة هذه الظاهرة اختيار توزيع منطقي لـ <code>f</code> ثم توليد عينة من أوزان البالغين عن طريق اختيار قيمة عشوائية من توزيع أوزان المواليد ثم اختيار تسلسل عوامل من توزيع <code>f</code> وحساب الجداء؛ ما هي قيمة <code>n</code> التي نحتاجها للتقارب من توزيع لوغاريتمي طبيعي؟
</p>

<h3>
	تمرين 2
</h3>

<p>
	استخدمنا في هذا المقال مبرهنة النهاية المركزية لإيجاد توزيع أخذ عينات الفرق في المتوسطات <code>δ</code> في ظل فرضية العدم التي تقول أنّ العينتين مأخوذتان من البيانات نفسها، يمكننا أيضًا استخدام هذا التوزيع لإيجاد الخطأ المعياري للتقدير ولفواصل الثقة لكن لن تكون النتيجة صحيحة تمامًا، وبصورة أدق، يجب حساب توزيع أخذ العينات الخاص بـ <code>δ</code> بموجب الفرضية البديلة التي مفادها أنّ العينات مأخوذة من مجموعات مختلفة.
</p>

<p>
	أوجد هذا التوزيع واستخدمه لحساب الخطأ المعياري وفاصل الثقة ‎90% للفرق في المتوسطات.
</p>

<h3>
	تمرين 3
</h3>

<p>
	بحث القائمون على ورقة بحثية حديثة في تأثيرات التدخل الهادف إلى تخفيف النمطية بين الجنسَين فيما يخص توزيع المهام داخل المجموعات في الكليات الهندسية، حيث أجاب الطلاب والطالبات على استطلاع قبل وبعد التدخل، وكان مفاد الاستطلاع الطلب من المشاركين تقييم مساهمتهم في كل جانب من جوانب المشاريع الصفيّة على مقياس مكوَّن من 7 نقاط.
</p>

<p>
	سجّل الطلاب الذكور قبل التدخل درجات أعلى فيما يخص البرمجة في المشروع مقارنةً بالطالبات، وسجّل الرجال في المتوسط درجة 3.57 مع خطأ معياري قدره 0.28، بينما سجّلت النساء في المتوسط 1.91 مع خطأ معياري قدره 0.32.
</p>

<p>
	احسب توزيع أخذ العينات للفجوة بين الجنسين -أي الفرق في المتوسطات-، واختبر ما إذا كان التوزيع ذا دلالة إحصائية، ولا تحتاج إلى معرفة حجم العينة لتحسب توزيعات أخذ العينات لأنك تعلم الأخطاء المعيارية للمتوسطات المقدَّرة.
</p>

<p>
	أصبحت الفجوة بعد التدخل أصغر، حيث أصبح المتوسط الحسابي للرجال هو 3.44 وبخطأ معياري قدره 0.16؛ أما المتوسط الحسابي للنساء فهو 3.18 وبخطأ معياري قدره 0.16؛ احسب توزيع العينات للفجوة بين الجنسين مرةً أخرى واختبرها.
</p>

<p>
	اختبر أخيرًا التغيير في الفجوة بين الجنسين، وما هو توزيع أخذ عينات هذا التغيير؟ وهل له دلالة إحصائية؟
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2015.html" rel="external nofollow">Chapter 14 Analytics methods analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%82%D8%A7%D8%A1-%D9%84%D9%85%D8%B9%D8%B1%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%AF%D8%A9-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-%D9%84%D9%84%D8%A3%D8%B4%D9%8A%D8%A7%D8%A1-r1576/" rel="">كيفية إجراء تحليل البقاء لمعرفة المدة الافتراضية للأشياء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">العلاقات بين المتغيرات الإحصائية وكيفية تنفيذها في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1302/" rel="">التوزيعات الإحصائية في بايثون</a>
	</li>
</ul>
<style type="text/css">
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}</style>
]]></description><guid isPermaLink="false">1577</guid><pubDate>Sun, 05 Jun 2022 15:03:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629; &#x627;&#x644;&#x645;&#x62A;&#x632;&#x627;&#x645;&#x646;&#x629; &#x648;&#x641;&#x627;&#x626;&#x62F;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x628;&#x631;&#x645;&#x62C;&#x629; &#x627;&#x644;&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%88%D9%81%D8%A7%D8%A6%D8%AF%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-r1548/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/626e3165adc77_------.png.8b9946cd38792092153b8ee8535bfe43.png" /></p>
<p>
	سنغطي في هذا المقال من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">تعلم البرمجة</a> ما يلي:
</p>

<ul>
	<li>
		آلية ووقت استخدام التزامن concurrency والخيوط threads.
	</li>
	<li>
		مثال على تقسيم الحمل بين العمليات.
	</li>
	<li>
		الوصول إلى البيانات المشتركة باستخدام الخيوط.
	</li>
	<li>
		بعض الاحتمالات والمحاذير الأخرى.
	</li>
</ul>

<p>
	رأينا إضافة هذا المقال إلى شرحنا بعد تفكير طويل نظرًا لأهمية موضوعه، وسبب ترددنا في ذلك أن التزامن عمومًا -والخيوط على وجه الخصوص- موضوع يصعب فهمه، رغم مسارعة الكثيرين إليه ظنًا منهم أنه يحل مشاكلهم، لكنهم يزيدونها سوءًا لضعف فهمهم لمفاهيم التزامن والخيوط، واللتان هما أداتان قويتان عند استعمالهما في المواضع المناسبة عن فهم وإدراك، لكن نرى أن يكونا الملاذ الأخير، نلجأ إليهما بعد فشل جميع الحلول الأخرى، وقد عملنا -يقصد المؤلف نفسه- على عدة مشاريع تستخدم التزامن بصورة أو بأخرى في الأعوام الأربعين الماضية، لكننا لم نستخدمها في مشاريع شخصية من قبل، وربما يعطي هذا تصورًا عن توقيت استخدام هذه التقنية، ونوعية المشاريع التي تُستخدم فيها.
</p>

<h2 id="مفهوم-البرمجة-التزامنية-وتوقيت-استخدامها">
	مفهوم البرمجة التزامنية وتوقيت استخدامها
</h2>

<p>
	تشير البرمجة التزامنية Concurrent programming ببساطة إلى برنامج يحوي عناصر تعمل على التوازي في نفس الوقت، وهو ما نطبقه على حواسيبنا كل يوم، لكننا نترك أمر إدارة تلك البرامج لنظام التشغيل، ليجدولها لتعمل بالترتيب، ويوزع حمل التشغيل على المعالجات والنوى kernels الموجودة فيها، فمفهوم تقسيم المهمة إلى عدة أجزاء والعمل على كل جزء على حدة ليس جديدًا، وهو يشبه إنشاء فرق مختلفة للتعامل مع مشكلة كبيرة تُقسم إلى عدة أجزاء، ليعمل كل فريق على جزء منها.
</p>

<p>
	وقد اتبعنا تلك الأساليب في علم الحواسيب على مدار العقود الماضية، خاصةً في الخوادم، فمثلًا يستقبل خادم الويب طلبات ويعالجها، لكن إذا كان الموقع كبيرًا وطلباته كثيرة ومتلاحقة، وكان الخادم يُعالج كل طلب قبل بدء الطلب الذي يليه؛ فستطفح قائمة انتظار المدخلات للخادم، لذلك يقرأ الخادم الطلب من قائمة الانتظار، فإذا كان الطلب ملف html بسيطًا فيعيده، أما إن كان أعقد من هذا فيشتق الخادم عمليةً فرعيةً للتعامل معه، ويعود هو لقراءة الطلب التالي.
</p>

<p>
	وقد رأينا من قبل كيفية إنشاء عمليات من شيفرة بايثون الخاصة بنا في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%B9%D8%A8%D8%B1-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1520/" rel="">التواصل مع نظام التشغيل</a> باستخدام وحدة <code>subprocess</code>، ورأينا منظورًا مختلفًا في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1521/" rel="">التواصل بين العمليات</a>، حيث استخدمنا <code>fork</code> لإنشاء عملية فرعية تكون نسخةً من العملية الأصلية، وتعمل كلا التقنيتين بسلاسة إذا كنا نرغب في عمليات قليلة منمقالة تمامًا عن بعضها، لكنهما لا تناسبان الحالة التي نحتاج فيها إلى عمليات كثيرة، كما في حالة معالجة خادم الويب لآلاف الطلبات كل دقيقة، وإحدى المشاكل التي قد تحدث عند استخدامهما لحالة الطلبات الكثيرة هو ما ينتج عن بطء إنشاء العملية الجديدة -بمقياس سرعة الحواسيب-، كما تكلف كل عملية الكثير من الأحمال الزائدة من استهلاك الذاكرة وغيرها، وهنا يبرز دور الخيوط therads، وهي تشبه العمليات الدقيقة التي تعمل داخل العملية الأصل، فتتشارك جميعها نفس مساحة الذاكرة ونفس الشيفرة، وتأتي تسمية الخيوط من فكرة أن الشيفرة الخاصة بنا تعمل من الأعلى إلى الأسفل -مع قفزات وحلقات تكرارية في طريقها-، فهي مثل بكرة من القطن أمسكنا طرف خيطها وتركناها تسقط، فكل خيط جديد يشبَّه بكرة قطن جديدة نسقطها بالتوازي مع الخيط الأول، وكل خيط له مسار تنفيذ خاص به في نفس الشيفرة.
</p>

<p>
	لقد أشرنا أن التزامنية تتعلق بشكل ما بالأداء، خاصةً في حالة الأحمال الكبيرة من البيانات، والحق أن هذا هو السبب الوحيد لاستخدامها، فإذا واجهنا مشكلة عند زيادة البيانات ونكون قد بذلنا كل ما نستطيع من تعديلات على الأداء الأساسي -لعملية تبادل أو وحدة بيانات واحدة-، فحينئذ قد نحتاج إلى إعادة التفكير في شأن تقسيم الأحمال.
</p>

<h2 id="اختيار-أسلوب-التزامن">
	اختيار أسلوب التزامن
</h2>

<p>
	بعد أن أثبتنا فائدة التزامن في تحسين الأداء في حالة الأحمال الكبير، كيف نقرر التقنية التي سنستخدمها لمعالجة مهمة ما؟ أيهما أفضل لمهمتنا العمليات المتعددة أم الخيوط المتعددة؟ والجواب أننا ننظر في عدة عوامل، حيث:
</p>

<ul>
	<li>
		نستخدم الخيوط إذا كان في المهمة انتظار لنشاط الإدخال والإخراج، مثل رسائل الشبكة أو نتائج استعلامات قواعد البيانات.
	</li>
	<li>
		نستخدم الخيوط إذا تطلبت المهمة مشاركة البيانات بين عدة "خيوط"، لكن يجب تذكر قفل lock البيانات المشارَكة أثناء التحديثات.
	</li>
	<li>
		نستخدم العمليات الفرعية إذا كان في المهمة معالجة خالصة للبيانات data crunching، وقد صممت الوحدة <code>multiprocessing</code> في بايثون خصيصًا لهذه الحالة.
	</li>
	<li>
		إذا كانت المهمة الفرعية تعمل لوقت طويل، أو لا تُستدعى إلا لوقت قصير، فننشئ عملية خادم طويلة التشغيل long running server process، كما فعلنا في مقال التواصل بين العمليات، لنرسل البيانات إليها عند الحاجة.
	</li>
</ul>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p data-gramm="false">
		تواجه بايثون مشكلةً كبيرةً في منظورها للخيوط تنطوي على آليةً داخليةً معقدةً، تسمى قفل المفسر العام Global Interpreter Lock أو GIL اختصارًا، يتمثل تأثير هذا القفل في منع خيوط العمليات الحسابية من العمل بالكفاءة التي نريدها، إلى الحد الذي دفع البعض إلى القول بعدم استخدام الخيوط في بايثون بالكلية، غير أن في مبالغةً كبيرةً، إذ إن الخيوط طريقة ممتازة للتعامل مع كل ما يجب قفله لعمليات الإدخال والإخراج، وهذا يعني "كل شيء" تقريبًا، والمهام الحسابية الخالصة فقط هي التي تواجه مشاكل، وليست المشاكل فيها بذلك السوء كما سنرى في المثال أدناه، وسنستخدم وحدة <code>multiprocessing</code> عندما نواجه تلك المشاكل، وهي مماثلة للخيوط في استخدامها، وستنفذ نفس المهمة لكن مع استخدام العمليات بدلًا من الخيوط، وهو الخيار الذي يستهلك الموارد كما ذكرنا في بداية المقال.
	</p>
</blockquote>

<h2 id="البدء-بالبرمجة-المتزامنة">
	البدء بالبرمجة المتزامنة
</h2>

<p>
	بما أننا قررنا استخدام التزامن فلننظر في الشيفرة المطلوبة لها، لدينا وحدتان في المكتبة القياسية تتعلقان بالخيوط، هما <code>Thread</code> و<code>threading</code>، والوحدة الأخيرة هي وحدة عالية المستوى بُنيت فوق وحدة <code>Thread</code>، ولا نحتاج إلا إلى النظر فيها، أما في حالة العمليات المتعددة فنستخدم وحدة <code>multiprocessing</code> التي تعمل تقريبًا بنفس طريقة <code>threading</code>.
</p>

<h3 id="تحديد-المشكلة">
	تحديد المشكلة
</h3>

<p>
	لننظر في مشروع بسيط نريد فيه حساب مضروب أول N عدد من مجموعة ما من الأعداد، فنكتب دالةً لحساب المضروب ثم نضعها في حلقة تكرارية ونخزن النتائج، وستبدو كما يلي:
</p>

<pre class="ipsCode">import time, sys

factorials = []

def fact(n):
    if n &lt; 2: return 1

    result = 1.0
    for x in range(2,n+1):
        result *= x
    return result

def do_it(f,lo,hi):
    global factorials
    for n in range(lo,hi):
        factorials.append(f(n))

if __name__ == "__main__":
    hi = int(sys.argv[1]) + 1
    start = time.time()
    
    do_it(fact, 1, hi)    
    print('Time for %s: %s' % (hi, time.time() - start))
</pre>

<p>
	نلاحظ أننا أنشأنا دالة <code>do_it</code> التي تغلف الحلقة التكرارية الخارجية واستدعاءات المضروب، وهي ليست ضروريةً لكننا سنرى نفعها لاحقًا عندما ننظر في التزامن، وقد جعلنا عدد مرات التكرار وسيطًا في سطر الأوامر، وأضفنا مؤقتًا لإظهار الزمن الذي تستغرقه.
</p>

<p>
	احفظ ذلك في ملف باسم no_threads.py وشغله لأول 100 عدد كما يلي:
</p>

<pre class="ipsCode">$ python3 no_threads.py 100
</pre>

<p>
	إذا شغلنا هذا الأمر فسنجد أن الوقت المستغرق أقل من ثانية، نظرًا لسرعة الحواسيب هذه الأيام، لكن جرب ذلك مع أول ألف عدد مثلًا وانظر الوقت المستغرق، وقد جربناها لكل من أول 100 عدد، وأول 1000، وأول 10000، وخرجنا بالنتائج التالية:
</p>

<pre class="ipsCode">$ python3 no_threads.py 100
Time for 100: 0.0006973743438720703
$ python3 no_threads.py 1000
Time for 1000: 0.06539225578308105
$ python3 no_threads.py 10000
Time for 10000: 6.800917863845825
</pre>

<p>
	نلاحظ أن السرعة جيدة إلى أن نصل إلى الألف عدد، لكنها تسوء فجأةً مع زيادة العدد عن ذلك، ونريد تحسين هذا الأداء لتلك الأعداد الكبيرة، وهنا يأتي دور التزامن.
</p>

<h3 id="إضافة-التزامن-إلى-العملية">
	إضافة التزامن إلى العملية
</h3>

<p>
	باتباع الإرشادات التي شرحناها أعلاه، سنختار <code>multiprocessing</code> بدلًا من <code>threading</code> لهذه المهمة، ونريد أن نعرف مقدار العدد المُدخَل أولًا، فإذا كان أكثر من 1000، فسنقسم العملية إلى عدة فروع، وسيكون ذلك أبطأ في الأعداد الكبيرة، لذلك بدلًا من تقسيم البيانات بالتساوي بين العمليات سنقسمها عند 75% -عشوائيًا-.
</p>

<p>
	ستكون الشيفرة كما يلي:
</p>

<pre class="ipsCode">import time, sys
import multiprocessing

factorials = []

def fact(n):
    if n &lt; 2: return 1

    result = 1.0
    for x in range(2,n+1):
        result *= x
    return result

def do_it(f,lo,hi):
    global factorials
    for n in range(lo,hi):
        factorials.append(f(n))

if __name__ == "__main__":
    hi = int(sys.argv[1]) + 1

    start = time.time()
    if hi &gt; 1000:
        hi_1 = int(hi * 0.75)
        p1 = multiprocessing.Process(target=do_it, args=(fact,1,hi_1))
        p2 = multiprocessing.Process(target=do_it, args=(fact,hi_1,hi+1))
        p1.start()
        p2.start()
        p1.join()
        p2.join()
    else:
        do_it(fact, 1, hi)
        
    print('Time for %s: %s' % (hi, time.time() - start))
</pre>

<p>
	نلاحظ أننا أنشأنا كائنيْ <code>Process</code>، ومررنا الدالة التي نريد تشغيلها -وهي <code>do_it</code>- إضافةً إلى الوسائط اللازمة في صف tuple، ثم استدعينا <code>start</code> لتشغيل العمليات، أخيرًا استخدمنا التابع <code>join</code> لجعل البرنامج الرئيسي ينتظر انتهاء العمليات.
</p>

<pre class="ipsCode">$ python3 processes.py 100
Time for 100: 0.0006771087646484375
$ python3 processes.py 1000
Time for 1000: 0.06577491760253906
$ python3 processes.py 10000
Time for 10000: 3.690302610397339
</pre>

<p>
	نلاحظ أن الوقت المستغرق للعملية الكبيرة قد انخفض إلى النصف تقريبًا، لكن ما نفذناه لم يكن بسيطًا أو مباشرًا، فلم نقسم البيانات بالتساوي، فإذا جربت تقسيمها عند 50% فستجد أن الوقت المستغرق زاد مرةً أخرى.
</p>

<p>
	يبدو أن التزامن يعطينا تحسنًا كبيرًا في الأداء، لكن في الواقع لدينا خلل كبير في برنامجنا، فإذا طبعنا <code>factorials</code> -وهي قائمة النتائج- فسنجدها فارغةً!
</p>

<p>
	والسبب في هذا أن كل عملية فرعية هي نسخة من العملية الأصل، وبالتالي لها نسختها الخاصة من قائمة <code>factorials</code>، والتي نفقد بياناتها عند انتهاء العملية، لذلك نحتاج إلى طريقة لتمرير البيانات إلى العملية الأصل، مما يعني الكتابة إلى ذاكرة مشتركة -انظر وحدة <code>mmap</code>- أو إلى قاعدة بيانات أو إلى ملف.
</p>

<p>
	نستخدم في تلك الحالات كلها عمليات الإدخال والإخراج، لذا فلم لا نستخدم الخيوط بما أنها تتشارك الذاكرة! فنكون قد أنشأنا نسختنا الخاصة من معضلة <a href="https://ar.wikipedia.org/wiki/%D9%83%D8%A7%D8%AA%D8%B4-22_(%D9%85%D9%86%D8%B7%D9%82)" rel="external nofollow">Catch-22</a>)!
</p>

<p>
	لكن لحسن الحظ نادرًا ما يكون لدينا عمليات نحتاج إلى تقسيمها بالتوازي دون أن يكون فيها عمليات إدخال وإخراج، لذا فإن الخيوط هنا حل يمكن تنفيذه، وسننظر في مثال "مفتعل" إلى حد ما لمجرد موازنة هيكل الشيفرة مع مثال تعدد العمليات السابق.
</p>

<h2 id="استخدام-الوحدة-threading">
	استخدام الوحدة threading
</h2>

<p>
	سننشئ في هذا المثال خيطًا مساعِدًا لتحديث المتغير <code>theTime</code> في كل ثانية، وسيتكرر البرنامج الرئيسي بلا نهاية ليطبع المتغير في كل مرة يتغير فيها، ولضمان تشغيل الخيوط بسلاسة سنضيف تأخيرًا زمنيًا باستخدام <code>time.sleep</code>، التي يعاملها المفسر على أنها عملية إدخال/إخراج.
</p>

<pre class="ipsCode">import time, threading

theTime = 0

def getTime():
    global theTime
    while True:
        theTime = time.time()
        time.sleep(1)

def main():
    global theTime
    current = theTime

    thrd = threading.Thread(target=getTime)
    thrd.start()

    while True:
        if current != theTime:
            current = theTime
            print(time.asctime(time.localtime(current)))
        time.sleep(0.01)

if __name == "__main__":
   main()
</pre>

<p>
	لقد أنشأنا في المثال أعلاه دالةً تغلف مهمة الخيوط، لضمان وجود عملية <code>time.sleep</code> فيها -أو أي دالة I/O أخرى-، ثم أنشأنا خيطًا نسند فيه دالتنا على أنها الهدف، كما فعلنا في العمليات أعلاه، ونبدأ تشغيل الخيط في الخلفية ثم ننتقل إلى الحلقة الأساسية التي تتحقق من المتغير في كل جزء من مئة من الثانية.
</p>

<p>
	عند الحاجة إلى إنهاء البرنامج نكتب <code>Ctrl+C</code> أو نستخدم مدير المهام في نظام التشغيل لإنهاء هذه العملية.
</p>

<p>
	إذا شغلنا الشيفرة السابقة فستكون النتيجة شبيهةً بما يلي:
</p>

<pre class="ipsCode">$ python3 clock.py 
Sat Dec 30 11:30:33 2017
Sat Dec 30 11:30:34 2017
Sat Dec 30 11:30:35 2017
Sat Dec 30 11:30:36 2017
...
</pre>

<p>
	كان من الممكن تحقيق ذلك بسهولة دون استخدام الخيوط، لكنها تعطينا فكرةً عما تبدو عليه أبسط شيفرة تحوي خيوطًا، فإذا وازنّاها مع شيفرة العمليات المتعددة فسنجد أنهما متطابقتان تقريبًا في بنيتيهما، والفرق بينهما أن شيفرة الخيوط تستطيع تحديث متغير <code>getTime</code> العام الذي تستطيع الدالة الرئيسية الموجودة في العملية الأصل أن تراه، نستطيع الآن أن نعود ونستبدل العمليات بالخيوط في المثال السابق ونوازن بين النتائج، وسنجد أن نسخة الخيوط لم تقدم مزيةً زمنيةً على نسخة العمليات.
</p>

<p>
	سنكتفي بهذا القدر من شرح التزامن لأنه موضوع معقد فيه الكثير من الفخاخ الدقيقة، وننصح بعدم استخدامه إلا عند الضرورة، وحتى في تلك الضرورة فالشرح الموجود هنا يمثل نقطة بداية فقط، يمكن الانتقال بعدها إلى شروح أكثر تفصيلًا وعمقًا.
</p>

<p>
	للمزيد من المعلومات، ننصحك بالإطلاع على دورة <a href="https://academy.hsoub.com/learn/python-application-development/" rel="">تطوير التطبيقات باستخدام لغة Python</a> التي تشرح الكثير من المفاهيم الأساسية لبناء التطبيقات في بايثون.
</p>

<h2 id="خاتمة">
	خاتمة
</h2>

<p>
	نأمل في نهاية هذا المقال أن تكون تعلمت ما يلي:
</p>

<ul>
	<li>
		يُستخدم التزامن لتحسين الأداء.
	</li>
	<li>
		يُنفَّذ التزامن بتشغيل عمليات أو خيوط إضافية.
	</li>
	<li>
		تعمل العمليات بشكل مستقل عن بعضها، وتتواصل مع العملية الأصل باستخدام الأنابيب pipes.
	</li>
	<li>
		تعمل الخيوط افتراضيًا داخل العملية الأصل، وتتشارك الموارد -مثل الذاكرة وغيرها- فيما بينها.
	</li>
	<li>
		يجب إقفال الموارد المشتركة قبل تغيير الحالة لتجنب حدوث تعارضات.
	</li>
	<li>
		توجد طرق أخرى للتزامن، كما في وحدة <code>asyncio</code> في بايثون التي تستخدم منظورًا مختلفًا.
	</li>
</ul>

<p>
	ترجمة -بتصرف- <a href="http://www.alan-g.me.uk/l2p2/tutthread.htm" rel="external nofollow">للمقال الثاني والثلاثين: Concurrent Programming</a> من كتاب Learn To Program لصاحبه Alan Gauld.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%81%D9%8A-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%84%D8%A7%D8%B3%D9%83-%D9%86%D9%85%D9%88%D8%B0%D8%AC%D8%A7-r1547/" rel="">استخدام أطر العمل في برمجة تطبيقات الويب: فلاسك نموذجا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B9%D9%85%D9%84%D8%A7%D8%A1-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1523/" rel="">برمجة عملاء ويب باستخدام بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1524/" rel="">كيفية كتابة تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1522/" rel="">كيفية التعامل مع الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%81%D9%8A-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%84%D8%A7%D8%B3%D9%83-%D9%86%D9%85%D9%88%D8%B0%D8%AC%D9%8B%D8%A7-r1547/" rel="">استخدام أطر العمل في برمجة تطبيقات الويب: فلاسك نموذجًا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1548</guid><pubDate>Tue, 24 May 2022 08:04:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x625;&#x62C;&#x631;&#x627;&#x621; &#x62A;&#x62D;&#x644;&#x64A;&#x644; &#x627;&#x644;&#x628;&#x642;&#x627;&#x621; &#x644;&#x645;&#x639;&#x631;&#x641;&#x629; &#x627;&#x644;&#x645;&#x62F;&#x629; &#x627;&#x644;&#x627;&#x641;&#x62A;&#x631;&#x627;&#x636;&#x64A;&#x629; &#x644;&#x644;&#x623;&#x634;&#x64A;&#x627;&#x621;</title><link>https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A5%D8%AC%D8%B1%D8%A7%D8%A1-%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%82%D8%A7%D8%A1-%D9%84%D9%85%D8%B9%D8%B1%D9%81%D8%A9-%D8%A7%D9%84%D9%85%D8%AF%D8%A9-%D8%A7%D9%84%D8%A7%D9%81%D8%AA%D8%B1%D8%A7%D8%B6%D9%8A%D8%A9-%D9%84%D9%84%D8%A3%D8%B4%D9%8A%D8%A7%D8%A1-r1576/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_05/6282842948d5f_-01.jpg.e7e78ab0a59032d3fb7524e41df43144.jpg" /></p>

<p>
	يُعَدّ تحليل البقاء survival analysis أحد طرق وصف مدة بقاء شيء ما، حيث يستخدَم لدراسة عمر الإنسان غالبًا، ولكنه ينطبق أيضًا على بقاء الأجهزة الميكانيكية والإلكترونية، أو قد يدل على الفترات الزمنية التي تسبق حدثًا ما.
</p>

<p>
	فلربما قد رأيت سابقًا مصطلح "معدل البقاء على قيد الحياة لمدة 5 سنوات" إذا شُخِّص أحد معارفك بمرض خطير، وهو احتمال بقاء المريض على قيد الحياة لمدة 5 سنوات بعد التشخيص، علمًا أنّ هذا التقدير والإحصاءات ذات الصلة هي نتيجة لتحليل البقاء.
</p>

<p>
	توجد الشيفرة الخاصة بهذا المقال في الملف <a href="https://github.com/AllenDowney/ThinkStats2/blob/master/code/survival.py" rel="external nofollow">survival.py</a>، في مستودع الشيفرات <a href="https://github.com/AllenDowney/ThinkStats2" rel="external nofollow">ThinkStats2</a> على GitHub.
</p>

<h2>
	منحنيات البقاء
</h2>

<p>
	يُعَدّ منحني البقاء survival curve الذي يرمز له بـ <code>S(t) </code>المفهوم الأساسي في تحليل البقاء، كما يُعَدّ دالةً تحوِّل المدة <code>t</code> إلى احتمال البقاء أطول من <code>t</code>، ويُعَدّ حساب منحني البقاء سهلًا إذا علمت توزيع المدة أو مدة الحياة، حيث يمكن حساب المنحني عندها عن طريق حساب مكمل <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D9%83%D9%85%D9%8A-cumulative-distribution-functions-r1331/" rel="">دالة التوزيع التراكمي</a> كما يلي:
</p>

<p style="text-align: center;">
	S(t)=1-CDF(t)
</p>

<p>
	حيث يكون CDF(t) هو احتمال أن تكون مدة البقاء على قيد الحياة أقل أو تساوي <code>t</code>، ونعلم مثلًا في مجموعة بيانات المسح الوطني لنمو الأسرة مدة حالات الحمل التامة التي بلغ عددها 1189 حالة، حيث يمكننا قراءة هذه البيانات وحساب دالة التوزيع التراكمي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_14" style="">
<span class="pln">    preg </span><span class="pun">=</span><span class="pln"> nsfg</span><span class="pun">.</span><span class="typ">ReadFemPreg</span><span class="pun">()</span><span class="pln">
    complete </span><span class="pun">=</span><span class="pln"> preg</span><span class="pun">.</span><span class="pln">query</span><span class="pun">(</span><span class="str">'outcome in [1, 3, 4]'</span><span class="pun">).</span><span class="pln">prglngth
    cdf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">complete</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">'cdf'</span><span class="pun">)</span></pre>

<p>
	يدل رمز الخرج 1 على ولادة حية، ويدل رمز الخرج 3 على ولادة جنين ميت، في حين يدل رمز الخرج 4 على حالة إجهاض لا إرادية -أي غير متعمدة من قبل الأم-، كما استبعدنا حالات الإجهاض المتعمدة وحالات الحمل خارج الرحم وحالات الحمل التي كانت مستمرة أثناء مقابلة المستجيبة وذلك لأغراض هذا التحليل، كما يأخذ تابع إطار البيانات <code>query</code> تعبيرًا بوليانيًا ويقيّمه لكل سطر، ومن ثم يحدِّد الأسطر التي ينتج عنها قيمة True.
</p>

<p style="text-align: center;">
	<img alt="الشكل 13.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98856" data-unique="g655iif73" src="https://academy.hsoub.com/uploads/monthly_2022_05/62828420ed694_13.1.png.3865f18cac0ab5487650c3a1ab2cd476.png"></p>

<p>
	يوضِّح الشكل السابق دالة التوزيع التراكمي ومنحني البقاء لمدة الحمل في الأعلى؛ أما في الأسفل فيوضِّح منحني الخطر hazard curve، حيث عرّفنا كائنًا يغلِّف صنف <code>Cdf</code> وينفذ الواجهة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_10" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">SurvivalFunction</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> cdf</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">cdf </span><span class="pun">=</span><span class="pln"> cdf
        self</span><span class="pun">.</span><span class="pln">label </span><span class="pun">=</span><span class="pln"> label </span><span class="kwd">or</span><span class="pln"> cdf</span><span class="pun">.</span><span class="pln">label

    </span><span class="lit">@property</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> ts</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">.</span><span class="pln">xs

    </span><span class="lit">@property</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> ss</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">.</span><span class="pln">ps</span></pre>

<p>
	يزودنا الصنف <code>SurvivalFunction</code> بخاصيتين اثنتين هما <code>ts</code> وهي تسلسل مدد الحياة و<code>ss</code> التي هي منحني البقاء، إذ تُعَدّ الخاصية في <a href="https://wiki.hsoub.com/Python" rel="external">لغة بايثون</a> تابعًا يمكن استدعاؤه كما لو أنه متغير، كما يمكننا استنتاج الصنف <code>SurvivalFunction</code> عن طريق تمرير دالة التوزيع التراكمي لمدة الحياة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_12" style="">
<span class="pln">    sf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SurvivalFunction</span><span class="pun">(</span><span class="pln">cdf</span><span class="pun">)</span></pre>

<p>
	كما يزودنا الصنف <code>SurvivalFunction</code> بالدالتين <code>__getitem__</code> و<code>Prob</code> اللتين تقيّمان منحني البقاء.
</p>

<pre class="ipsCode">
# class SurvivalFunction

    def __getitem__(self, t):
        return self.Prob(t)

    def Prob(self, t):
        return 1 - self.cdf.Prob(t)
</pre>

<p>
	يُعَدّ <code>sf[13]</code> على سبيل المثال نسبة حالات الحمل التي تجاوزت الثلث الأول من الحمل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_16" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> sf</span><span class="pun">[</span><span class="lit">13</span><span class="pun">]</span><span class="pln">
</span><span class="lit">0.86022</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> cdf</span><span class="pun">[</span><span class="lit">13</span><span class="pun">]</span><span class="pln">
</span><span class="lit">0.13978</span></pre>

<p>
	نرى أنّ 86‎%‎ من حالات الحمل تتجاوز الثلث الأول من الحمل؛ أما النسبة المتبقية 14‎‎%‎ فهي لا تتجاوز هذه المدة، كما يزودنا الصنف <code>SurvivalFunction</code> بالدالة <code>Render</code> التي ترسم <code>sf</code> باستخدام الدوال الموجودة في المكتبة <code>thinkplot</code>:
</p>

<pre class="ipsCode">
    thinkplot.Plot(sf)
</pre>

<p>
	يُظهر الشكل السابق الموجود في الأعلى النتيجة، حيث يكون المنحني مسطحًا تقريبًا بين الأسبوعين 13 و26، مما يدل على أن عدد قليل من حالات الحمل تنتهي في الثلث الثاني من الحمل، ويكون المنحني أكثر حدةً عند حوالي 39 أسبوعًا وهي أكثر فترات الحمل شيوعًا.
</p>

<h2>
	دالة الخطر
</h2>

<p>
	يمكننا اشتقاق دالة الخطر hazard function من منحني البقاء، حيث تُعَدّ دالة الخطر لمدة الحمل دالةً تحوِّل الزمن <code>t</code> إلى نسبة حالات الحمل التي تستمر حتى المدة <code>t</code> ومن ثم تنتهي عند <code>t</code>، ونقول بصورة أدق:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell" style="text-align: center;">
				<span style="font-size:medium">λ(<span style="font-style:italic">t</span>) = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium"><span style="font-style:italic">S</span>(<span style="font-style:italic">t</span>) − <span style="font-style:italic">S</span>(<span style="font-style:italic">t</span>+1)</span>
							</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium"><span style="font-style:italic">S</span>(<span style="font-style:italic">t</span>)</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	يُعَدّ البسط نسبة مدة الحياة التي تنتهي عند <code>t</code> وهي تمثِّل أيضًا دالة الكثافة الاحتمالية عند t أي <code>PMF(t)</code>، كما يزودنا الصنف <code>SurvivalFunction</code> بالدالة <code>MakeHazard</code> التي تحسب دالة الخطر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_18" style="">
<span class="com"># class SurvivalFunction</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeHazard</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">
        ss </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">ss
        lams </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> t </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">ts</span><span class="pun">[:-</span><span class="lit">1</span><span class="pun">]):</span><span class="pln">
            hazard </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ss</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> ss</span><span class="pun">[</span><span class="pln">i</span><span class="pun">+</span><span class="lit">1</span><span class="pun">])</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> ss</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span><span class="pln">
            lams</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> hazard

        </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">HazardFunction</span><span class="pun">(</span><span class="pln">lams</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="pln">label</span><span class="pun">)</span></pre>

<p>
	حيث يُعَدّ الكائن <code>HazardFuntion</code> مغلِفًا لسلسلة بانداز:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_20" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">HazardFunction</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> d</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">series </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">Series</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">label </span><span class="pun">=</span><span class="pln"> label</span></pre>

<p>
	قد يكون <code>d</code> قاموسًا أو أيّ نوع آخر قادر على استنساخ سلسلة تتضمن سلسلةً أخرى، في حين يكون <code>label</code> سلسلةً نصيةً مستخدَمةً لتحديد HazardFunction عند رسمه، كما يزودنا <code>HazardFunction</code> بالدالة <code>__getitem__</code>، وبالتالي يمكننا تقييمه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_22" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> hf </span><span class="pun">=</span><span class="pln"> sf</span><span class="pun">.</span><span class="typ">MakeHazard</span><span class="pun">()</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> hf</span><span class="pun">[</span><span class="lit">39</span><span class="pun">]</span><span class="pln">
</span><span class="lit">0.49689</span></pre>

<p>
	لذا تنتهي حوالي ‎50% من بين جميع حالات الحمل التي تستمر حتى الأسبوع 39 في الأسبوع 39.
</p>

<p>
	يُظهر الشكل السابق الموجود في الأسفل دالة الخطر hazard function لمدة الحمل، كما نرى أنّ دالة الخطر بعد الأسبوع 42 تصبح غير منتظمة لأنها مبنية على عدد صغير من الحالات، لكن بخلاف ذلك يكون شكل المنحني كما هو متوقع، بحيث يبلغ ذروته عند الأسبوع 30 تقريبًا ويصبح في الثلث الأول أعلى من الثلث الثاني، كما تُعَدّ دالة الخطر مفيدةً لوحدها، لكنها أيضًا أداةً مهمةً لتقدير منحنيات البقاء كما سنرى في القسم التالي.
</p>

<h2>
	استنتاج منحنيات البقاء
</h2>

<p>
	إذا علمت دالة التوزيع التراكمي CDF، فمن السهل حساب دالة البقاء ودالة الخطر، لكن من الصعب في كثير من المواقف الواقعية قياس توزيع مدة الحياة مباشرةً ويجب علينا استنتاجها، فلنفترض مثلًا أنك تراقب مجموعةً من المرضى لترى المدة التي بقوا فيها على قيد الحياة بعد التشخيص، وبما أنّ التشخيص لا يكون في اليوم نفسه لكل المرضى، فسيعيش بعض المرضى فترةً أطول من غيرهم في أي فترة من الزمن، وبالطبع نعلم مدة بقاء المرضى الذين تُوفوا، إلا أننا لا نعلم مدة بقاء المرضى الذين لا زالوا على قيد الحياة وإنما لدينا حدًا أدنى لمدة البقاء.
</p>

<p>
	يمكننا حساب منحني البقاء إذا انتظرنا وفاة جميع المرضى، لكننا لن نستطيع الانتظار مدةً طويلةً إذا كنا بصدد تقييم فعالية دواء جديد، لذا نحتاج إلى تقدير منحنيات البقاء باستخدام معلومات غير مكتملة، وبالانتقال إلى مثال مُبهج، استخدمنا بيانات المسح الوطني لنمو الأسرة لحساب مدة بقاء المستجيبين بدون أول حالة زواج، أي المدة التي تسبق أول حالة زواج، بالطبع فإن المستجيبين هم من النساء كون الأسئلة تخص حالات الحمل، كما يتراوح مدى عمر المستجيبات بين 14 و 44 سنة، وبالتالي تزودنا مجموعة البيانات بلمحة عن النساء في مراحل مختلفة من حياتهن.
</p>

<p>
	تتضمن مجموعة البيانات بالنسبة للنساء المتزوجات تاريخ أول زواج بالإضافة إلى عمر المرأة عندها؛ أما بالنسبة لغير المتزوجات فنحن نعلم عمر المستجيبة أثناء المسح لكننا لا نعلم متى ستتزوج أو أنها ستتزوج حتى، ونظرًا لأننا نعلم عمر أول حالة زواج لبعض النساء، فسيبدو لنا مغريًا استبعاد بقية النساء وحساب دالة التوزيع التراكمي للبيانات المعلومة، ولكنها فكرة سيئة لأن النتيجة ستكون في هذه الحالة مضللةً جدًا لسببين اثنين هما:
</p>

<ol>
<li>
		سينتج عن هذا مبالغة في تمثيل النساء الأكبر عمرًا، لأنه من المرجح أن تكون هذه الفئة متزوجة أثناء إجراء المسح.
	</li>
	<li>
		سينتج مبالغة في تمثيل النساء المتزوجات.
	</li>
</ol>
<p>
	سيؤدي هذا التحليل في الواقع إلى استنتاج مفاده أنّ جميع النساء يتزوجن، وهذا الأمر غير صحيح وضوحًا.
</p>

<h2>
	تقدير كابلان ماير
</h2>

<p>
	ليس من المفضل في هذا المثال تضمين حالات النساء غير المتزوجات وإنما هو أمر ضروري، وهو ما يقودنا إلى إحدى <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1282/" rel="">الخوارزميات</a> الأساسية في تحليل البقاء والتي هي تقدير كابلان ماير Kaplan-Meier estimation.
</p>

<p>
	تستند الفكرة العامة على استخدام البيانات لتقدير دالة الخطر ومن ثم تحويل دالة الخطر إلى منحني البقاء، وإذا أردنا تقدير تابع الخطر، فيمكننا من أجل كل عمر الأخذ في الحسبان: (1) عدد النساء اللواتي تزوجن في هذا العمر و(2) عدد النساء "المعرضات لخطر" الزواج، وهذا يتضمن النساء اللواتي لم يتزوجن من قبل، وإليك الشيفرة الموافقة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_24" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">EstimateHazardFunction</span><span class="pun">(</span><span class="pln">complete</span><span class="pun">,</span><span class="pln"> ongoing</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="str">''</span><span class="pun">):</span><span class="pln">

    hist_complete </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Counter</span><span class="pun">(</span><span class="pln">complete</span><span class="pun">)</span><span class="pln">
    hist_ongoing </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Counter</span><span class="pun">(</span><span class="pln">ongoing</span><span class="pun">)</span><span class="pln">

    ts </span><span class="pun">=</span><span class="pln"> list</span><span class="pun">(</span><span class="pln">hist_complete </span><span class="pun">|</span><span class="pln"> hist_ongoing</span><span class="pun">)</span><span class="pln">
    ts</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">()</span><span class="pln">

    at_risk </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">complete</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">ongoing</span><span class="pun">)</span><span class="pln">

    lams </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">Series</span><span class="pun">(</span><span class="pln">index</span><span class="pun">=</span><span class="pln">ts</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> t </span><span class="kwd">in</span><span class="pln"> ts</span><span class="pun">:</span><span class="pln">
        ended </span><span class="pun">=</span><span class="pln"> hist_complete</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln">
        censored </span><span class="pun">=</span><span class="pln"> hist_ongoing</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln">

        lams</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> ended </span><span class="pun">/</span><span class="pln"> at_risk
        at_risk </span><span class="pun">-=</span><span class="pln"> ended </span><span class="pun">+</span><span class="pln"> censored

    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">HazardFunction</span><span class="pun">(</span><span class="pln">lams</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">=</span><span class="pln">label</span><span class="pun">)</span></pre>

<p>
	تُعَدّ <code>complete</code> أنها الحالات الكاملة التي رُصِدَت، وتكون في مثالنا هذا أعمار المستجيبات عندما تزوجن، في حين تُعَدّ <code>ongoing</code> أنها الحالات غير الكاملة وهي أعمار النساء غير المتزوجات في المسح.
</p>

<p>
	نحسب بدايةً <code>hist_complete</code>، وهو دالة عدادة Counter تحوِّل العمر إلى عدد النساء المتزوجات في هذا العمر، كما نحسب <code>hist_ongoing</code> هو دالة عدادة <code>Counter</code> تحوِّل العمر إلى عدد النساء غير المتزوجات اللواتي قوبِلن في ذلك العمر؛ أما <code>ts</code> فهو اجتماع الأعمار التي تزوجت فيها المستجيبات والأعمار التي قوبلت فيها النساء غير المتزوجات مرتبًا ترتيبًا تصاعديًا، كما تتتبّع <code>at_risk</code> عدد المستجيبات المعرضات للخطر في كل عمر وهو العدد الكلي للمستجيبات، وتُخزن النتيجة في سلسلة <code>Series</code> بانداز Pandas والتي تحول كل عمر إلى دالة الخطر المقدَّرة في ذلك العمر.
</p>

<p>
	نتعامل في كل مرور على الحلقة مع عمر واحد <code>t</code> ونحسب عدد الأحداث التي تنتهي عند <code>t</code> -أي عدد المستجيبات المتزوجات عند هذا العمر- وعدد الأحداث التي أوقِفت عند <code>t</code> -أي عدد النساء اللواتي قوبِلن عند <code>t</code> ولكن تواريخ زواجهن المستقبلية موقفة censored- ويشير مصطلح "أوقف" إلى أنّ البيانات غير متاحة بسبب عملية جمع البيانات، كما تُعَدّ دالة الخطر المُقدَّرة بأنها نسبة الحالات المعرَّضة للخطر والتي تنتهي عند <code>t</code>، ونطرح في نهاية الحلقة من <code>at_risk</code> عدد الحالات التي انتهت أو أوقفت عند t، ثم نمرِّر في النهاية <code>lams</code> إلى الباني <code>HazardFunction</code> ونُعيد النتيجة.
</p>

<h2>
	منحني الزواج
</h2>

<p>
	علينا تنظيف البيانات وتحويلها إذا أردنا اختبار هذه الدالة، علمًا أنّ المتغيرات التي نحتاجها من المسح الوطني لنمو الأسرة هي:
</p>

<ul>
<li>
		<code>cmbirth</code>: يوم ميلاد كل مستجيبة وهو معلوم في كل الحالات.
	</li>
	<li>
		<code>cmintvw</code>: تاريخ مقابلة كل مستجيبة وهو معلوم في كل الحالات.
	</li>
	<li>
		<code>cmmarrhx</code>: تاريخ أول حالة زواج للمستجيبة إذا كانت متزوجةً وكان التاريخ معلومًا.
	</li>
	<li>
		<code>evrmarry</code>: قيمة هذا المتغير 1 إذا كانت المستجيبة قد تزوجت قبل تاريخ المقابلة و0 بخلاف ذلك.
	</li>
</ul>
<p>
	حيث أن المتغيرات الثلاثة الأولى مرمَّزة بنظام أشهر القرن وهو العدد الصحيح للأشهر منذ شهر 12 من عام 1899، أي يكون شهر القرن 1 هو شهر 1 من عام 1900، وسنقرأ في البداية ملف المستجيبات ونستبدل قيم <code>cmmarrhx</code> غير الصالحة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_26" style="">
<span class="pln">    resp </span><span class="pun">=</span><span class="pln"> chap01soln</span><span class="pun">.</span><span class="typ">ReadFemResp</span><span class="pun">()</span><span class="pln">
    resp</span><span class="pun">.</span><span class="pln">cmmarrhx</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">([</span><span class="lit">9997</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9998</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9999</span><span class="pun">],</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">nan</span><span class="pun">,</span><span class="pln"> inplace</span><span class="pun">=</span><span class="kwd">True</span></pre>

<p>
	ثم نحسب عمر كل مستجيبة عند الزواج وعند مقابلتها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_28" style="">
<span class="pln">    resp</span><span class="pun">[</span><span class="str">'agemarry'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">resp</span><span class="pun">.</span><span class="pln">cmmarrhx </span><span class="pun">-</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">cmbirth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">12.0</span><span class="pln">
    resp</span><span class="pun">[</span><span class="str">'age'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">resp</span><span class="pun">.</span><span class="pln">cmintvw </span><span class="pun">-</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">cmbirth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">12.0</span></pre>

<p>
	ثم نستخرِج <code>complete</code> وهو عمر النساء المتزوجات عند زواجهن واللاتي لم تزلن متزوجات، و<code>ongoing</code> وهو عمر النساء اللاتي لا يحققن ما سبق أثناء المقابلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_32" style="">
<span class="pln">    complete </span><span class="pun">=</span><span class="pln"> resp</span><span class="pun">[</span><span class="pln">resp</span><span class="pun">.</span><span class="pln">evrmarry</span><span class="pun">==</span><span class="lit">1</span><span class="pun">].</span><span class="pln">agemarry
    ongoing </span><span class="pun">=</span><span class="pln"> resp</span><span class="pun">[</span><span class="pln">resp</span><span class="pun">.</span><span class="pln">evrmarry</span><span class="pun">==</span><span class="lit">0</span><span class="pun">].</span><span class="pln">age</span></pre>

<p>
	سنحسب أخيرًا دالة الخطر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_30" style="">
<span class="pln">    hf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">EstimateHazardFunction</span><span class="pun">(</span><span class="pln">complete</span><span class="pun">,</span><span class="pln"> ongoing</span><span class="pun">)</span></pre>

<p>
	يُظهر الشكل 13.2 (الموجود في الجهة العليا) دالة الخطر المُقدَّرة، وهي منخفضة في فترة المراهقة ومرتفعة في العشرينات من العمر وتنخفض في الثلاثينات وتعود لترتفع في الأربعينات، لكن هذا ناتج عملية التقدير، حيث سينتج عن زواج عدد صغير من النساء خطرًا مقدَّرًا كبيرًا مع نقصان المستجيبات لمعرضات للخطر، لكن سيخفف منحني البقاء من هذا الضجيج.
</p>

<h2>
	تقدير منحني البقاء
</h2>

<p>
	يمكننا تقدير منحني البقاء عند حصولنا على دالة الخطر، حيث أنّ فرصة البقاء بعد الوقت <code>t</code> هو فرصة البقاء لكل الأوقات بدءًا من بداية الرصد حتى <code>t</code>، وهو ناتج الجداء التراكمي لدالة الخطر المكملة:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium">[1−λ(0)] [1−λ(1)] … [1−λ(<span style="font-style:italic">t</span>)] </span>
			</td>
		</tr></tbody></table>
<p>
	يزودنا الصنف <code>HazardFunction</code> بالدالة <code>MakeSurvival</code> التي تحسب هذا الجداء:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_36" style="">
<span class="com"># class HazardFunction:</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeSurvival</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        ts </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">series</span><span class="pun">.</span><span class="pln">index
        ss </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">series</span><span class="pun">).</span><span class="pln">cumprod</span><span class="pun">()</span><span class="pln">
        cdf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">ts</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">-</span><span class="pln">ss</span><span class="pun">)</span><span class="pln">
        sf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SurvivalFunction</span><span class="pun">(</span><span class="pln">cdf</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> sf</span></pre>

<p>
	حيث أنّ <code>ts</code> هو تسلسل الأوقات التي قُدِّرت فيها دالة الخطر، و<code>ss</code> هو ناتج الجداء التراكمي لدالة الخطر المكملة، وبالتالي فهو منحني البقاء، كما يتوجب علينا حساب مكمل <code>ss</code> ومن ثم إنشاء Cdf واستنساخ كائن SurvivalFunction وذلك بسبب الطريقة التي يُنفَّذ بها <code>SurvivalFunction</code>.
</p>

<p style="text-align: center;">
	<img alt="الشكل 13.2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98857" data-unique="oauxq84kh" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282842185520_13.2.png.b2a5384f38c4a294933f8ce52d39435b.png"></p>

<p>
	يوضَّح الشكل السابق الموجود في الأعلى دالة الخطر لعمر أول زواج؛ أما الذي في الأسفل فيوضَّح منحني البقاء، كما يُظهر الشكل السابق الموجود في الأسفل النتيجة، حيث يكون منحني البقاء أكثر حدةً بين العمرين 25 و35، وهو المدى الذي تتزوج فيه معظم النساء؛ أما بين العمرين 35 و45 فيكون المنحني مسطحًا تقريبًا، مما يشير إلى أنه من غير المرجح زواج النساء اللواتي لم يتزوجن قبل سن الخامس والثلاثين.
</p>

<p>
	كان منحني ما مثل المنحني السابق أساس مقال شهير ظهر في مجلة عام 1986، فقد نُشر في مجلة نيوزويك Newsweek أن احتمال موت امرأة غير متزوجة عمرها 40 على يد قاتل أكبر من احتمال زواجها، وانتشرت هذه الإحصائيات انتشارًا واسعًا وأصبحت جزءًا من الثقافة الشعبية، لكنها كانت خاطئةً لأنها بُنيَت على تحليل خاطئ، واتضح أنهم على خطأ بسبب التغيرات الثقافية التي كانت جاريةً حينها واستمرت بعدها، لذا فقد نشرت مجلة نيوزويك Newsweek مقالًا آخرًا اعترفوا فيه بأنهم كانوا مخطئين، ونرى أنه من الأفضل قراءة المزيد عن هذا المقال والإحصائية المبنية عليه وردود الفعل لأنه سيذكِّرك بالالتزام الأخلاقي الذي يحتم عليك إجراء التحليل بعناية وتفسير النتائج بحيادية وعرضها على الجمهور بدقة وصدق.
</p>

<h2>
	فواصل الثقة
</h2>

<p>
	ينتج عن تحليل كابلان-ماير تقديرًا واحدًا لمنحني البقاء ولكنه مهم لحساب عدم اليقين الناتج عن التقدير، كما توجد ثلاثة مصادر محتملة للخطأ على أساس العادة وهي خطأ القياس measurement error وخطأ أخذ العينات sampling error وخطأ النمذجة modeling error، حيث يُعَدّ خطأ القياس في هذا المثال صغيرًا غالبًا، أي يعلم الأشخاص التاريخ الصحيح لولادتهم وإن كانوا قد تزوجوا بالإضافة إلى تاريخ الزواج، ويفترض أنهم قدَّموا هذه المعلومات بدقة، وإليك الشيفرة التي تحسب خطأ أخذ العينات عن طريق تطبيق إعادة أخذ العينات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_38" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">ResampleSurvival</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">101</span><span class="pun">):</span><span class="pln">
    low</span><span class="pun">,</span><span class="pln"> high </span><span class="pun">=</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">agemarry</span><span class="pun">.</span><span class="pln">min</span><span class="pun">(),</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">agemarry</span><span class="pun">.</span><span class="pln">max</span><span class="pun">()</span><span class="pln">
    ts </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">arange</span><span class="pun">(</span><span class="pln">low</span><span class="pun">,</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">/</span><span class="lit">12.0</span><span class="pun">)</span><span class="pln">

    ss_seq </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">):</span><span class="pln">
        sample </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">ResampleRowsWeighted</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">)</span><span class="pln">
        hf</span><span class="pun">,</span><span class="pln"> sf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">EstimateSurvival</span><span class="pun">(</span><span class="pln">sample</span><span class="pun">)</span><span class="pln">
        ss_seq</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">sf</span><span class="pun">.</span><span class="typ">Probs</span><span class="pun">(</span><span class="pln">ts</span><span class="pun">))</span><span class="pln">

    low</span><span class="pun">,</span><span class="pln"> high </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">PercentileRows</span><span class="pun">(</span><span class="pln">ss_seq</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">95</span><span class="pun">])</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">FillBetween</span><span class="pun">(</span><span class="pln">ts</span><span class="pun">,</span><span class="pln"> low</span><span class="pun">,</span><span class="pln"> high</span><span class="pun">)</span></pre>

<p>
	تأخذ الدالة <code>ResampleSurvival</code> الوسيطَين <code>resp</code> وهو إطار بيانات المستجيبين و<code>iters</code> وهو عدد المرات التي يجب فيها إعادة أخذ العينات، ومن ثم تحسب <code>ts</code> وهو تسلسل الأعمار وهنا سنقيِّم منحني البقاء، كما تقوم الدالة <code>ResampleSurvival</code> بالخطوات التالية ضمن الحلقة:
</p>

<ul>
<li>
		تعيد أخذ عينات المستجيبين باستخدام <code>ResampleRowsWeighted</code>، ورأينا هذا في قسم إعادة أخذ العينات مع الأوزان في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%BA%D8%B1%D9%89-%D8%A7%D9%84%D8%AE%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1412/" rel="">المربعات الصغرى الخطية في بايثون</a>.
	</li>
	<li>
		تستدعي <code>EstimateSurvival</code> التي تستخدِم العملية الموجودة في الأقسام السابقة بهدف تقدير منحني البقاء ومنحني الخطر.
	</li>
	<li>
		ثم تقيِّم منحني البقاء في كل عمر في <code>ts</code>.
	</li>
</ul>
<p>
	يُعَدّ <code>ss_seq</code> تسلسل منحنيات البقاء المقدَّرة، كما تأخذ الدالة <code>PercentileRows</code> هذا التسلسل وتحسب المئين الخامس والمئين الخامس والتسعين وتعيد فاصل الثقة 90‎%‎ الخاص بمنحني البقاء.
</p>

<p style="text-align: center;">
	<img alt="الشكل 13.3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98858" data-unique="2np0eolq9" src="https://academy.hsoub.com/uploads/monthly_2022_05/628284221acfe_13.3.png.846696e405c579cb095750e15c12945c.png"></p>

<p>
	يوضِّح الشكل السابق منحني البقاء للعمر عند أول زواج الممثَّل بالخط الداكن وفاصل الثقة ‎90% المبني على إعادة أخذ العينات مع الأوزان والممثَّل بالخط الرمادي، كما يظهر الشكل السابق النتيجة ومنحني البقاء الذي قدَّرناه في القسم السابق، ويأخذ فاصل الثقة أوزان أخذ العينات بالحسبان على عكس المنحني المقدَّر الذي لا يضع الأوزان في حسبانه، علمًا أنَّ التناقض بينهما يشير إلى التأثير الكبير لأوزان أخذ العينات على التقدير وعلينا أخذ ذلك في الحسبان.
</p>

<h2>
	تأثيرات الفوج
</h2>

<p>
	يُعَدّ اعتماد الأجزاء المختلفة للمنحني المقدَّر على عدة مجموعات بأنه أحد تحديات تحليل البقاء، حيث أنّ جزء المنحني عند الوقت <code>t</code> مبني على المستجيبات اللاتي كان عمرهن <code>t</code> على الأقل أثناء المقابلة، لذا يحتوي الجزء الموجود في اليسار على بيانات جميع من شارك في المسح، في حين يحتوي الجزء الموجود في اليمين على المستجيبات الأكبر سنًا.
</p>

<p>
	إذا لم تكن صفات المستجيبات ذات الصلة متغيرةً بمرور الوقت، فلا توجد مشكلة بالطبع، لكن يبدو في هذه الحالة أنّ أنماط الزواج متغيرة بالنسبة للنساء المولودات في أجيال مختلفة، كما يمكننا البحث في هذا التأثير عن طريق تصنيف المستجيبات إلى مجموعات بحسب عقد الميلاد، علمًا أنّ المجموعات المشابهة لهذه أي المعرفة بتاريخ ميلاد أو حدث مشابه تدعى الأفواج cohorts، في حين تدعى الفروق بين المجموعات تأثيرات الفوج cohort effects.
</p>

<p>
	جمعنا بيانات الدورة السادسة من 2002 المستخدَمة في هذه السلسلة وبيانات الدورة السابعة من 2006-2010 المستخدَمة في قسم التكرار في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">اختبار الفرضيات الإحصائية</a> وبيانات الدورة الخامسة من 1995، حيث تحوي مجموعة البيانات كلها 30769 مستجيبةً، وذلك من أجل البحث في تأثيرات الفوج في بيانات الزواج في المسح الوطني لنمو الأسرة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_40" style="">
<span class="pln">    resp5 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ReadFemResp1995</span><span class="pun">()</span><span class="pln">
    resp6 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ReadFemResp2002</span><span class="pun">()</span><span class="pln">
    resp7 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">ReadFemResp2010</span><span class="pun">()</span><span class="pln">
    resps </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">resp5</span><span class="pun">,</span><span class="pln"> resp6</span><span class="pun">,</span><span class="pln"> resp7</span><span class="pun">]</span></pre>

<p>
	استخدمنا <code>cmbirth</code> من أجل كل إطار بيانات <code>resp</code> لحساب عقد ولادة كل مستجيبة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_42" style="">
<span class="pln">    month0 </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="pln">to_datetime</span><span class="pun">(</span><span class="str">'1899-12-15'</span><span class="pun">)</span><span class="pln">
    dates </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">month0 </span><span class="pun">+</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DateOffset</span><span class="pun">(</span><span class="pln">months</span><span class="pun">=</span><span class="pln">cm</span><span class="pun">)</span><span class="pln"> 
             </span><span class="kwd">for</span><span class="pln"> cm </span><span class="kwd">in</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">cmbirth</span><span class="pun">]</span><span class="pln">
    resp</span><span class="pun">[</span><span class="str">'decade'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pandas</span><span class="pun">.</span><span class="typ">DatetimeIndex</span><span class="pun">(</span><span class="pln">dates</span><span class="pun">).</span><span class="pln">year </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1900</span><span class="pun">)</span><span class="pln"> </span><span class="pun">//</span><span class="pln"> </span><span class="lit">10</span></pre>

<p>
	حيث أن المتغير <code>cmbirth</code> مرمَّز ليدل على العدد الصحيح للأشهر التي مضت منذ شهر 12 من عام 1899، فتمثِّل <code>month0</code> هذا التاريخ على أساس كائن ختم زمني <code>Timestamp</code>، كما نستنسخ <code>DateOffset</code> من أجل كل تاريخ ميلاد والذي يحتوي على أشهر القرن ونضيفه إلى <code>month0</code> لتكون النتيجة تسلسلًا من الأختام الزمنية TimeStamps التي يجري تحوَّل إلى النوع <code>DateTimeIndex</code>، ونستخرج أخيرًا <code>year</code> الذي يمثِّل السنة ونحسب <code>decades</code> الذي يمثِّل العقد.
</p>

<p>
	أعدنا أخذ العينات وصّنفنا المستجيبات حسب عقد الميلاد ورسمنا منحني البقاء وذلك لكي نحرص على أخذ أوزان أخذ العينات بالحسبان وإظهار التباين الناتج عن خطأ أخذ العينات أيضًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_44" style="">
<span class="pln">    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">):</span><span class="pln">
        samples </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">ResampleRowsWeighted</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">)</span><span class="pln"> 
                   </span><span class="kwd">for</span><span class="pln"> resp </span><span class="kwd">in</span><span class="pln"> resps</span><span class="pun">]</span><span class="pln">
        sample </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">samples</span><span class="pun">,</span><span class="pln"> ignore_index</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span><span class="pln">
        groups </span><span class="pun">=</span><span class="pln"> sample</span><span class="pun">.</span><span class="pln">groupby</span><span class="pun">(</span><span class="str">'decade'</span><span class="pun">)</span><span class="pln">

        </span><span class="typ">EstimateSurvivalByDecade</span><span class="pun">(</span><span class="pln">groups</span><span class="pun">,</span><span class="pln"> alpha</span><span class="pun">=</span><span class="lit">0.2</span><span class="pun">)</span></pre>

<p>
	تستخدِم بيانات الدورات الثلاثة للمسح الوطني لنمو الأسرة أوزانًا مختلفةً، لذا أعدنا أخذ عينات كل منها على حدة ثم استخدمنا <code>concat</code> لدمجها لتصبح إطار بيانات واحد، علمًا أنّ المعامِل <code>ignore_index</code> يشير إلى <code>concat</code> لكي لا يطابق المستجيبين حسب الفهرس وإنما ينشئ فهرسًا جديدًا من 0 إلى 30768، كما ترسم الدالة <code>EstimateSurvivalByDecade</code> منحنيات بقاء كل فوج.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_46" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">EstimateSurvivalByDecade</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> group </span><span class="kwd">in</span><span class="pln"> groups</span><span class="pun">:</span><span class="pln">
        hf</span><span class="pun">,</span><span class="pln"> sf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">EstimateSurvival</span><span class="pun">(</span><span class="pln">group</span><span class="pun">)</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">sf</span><span class="pun">)</span></pre>

<p style="text-align: center;">
	<img alt="الشكل 13.4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98859" data-unique="20b5grvxa" src="https://academy.hsoub.com/uploads/monthly_2022_05/628284229f6d7_13.4.png.7d949a50cb86e6c77310ddb5cec5cd26.png"></p>

<p>
	يوضِّح الشكل السابق منحنيات بقاء المستجيبات اللواتي ولدن ضمن عقود مختلفة، كما يظهر الشكل السابق النتائج ونرى عدة أنماط فيه وهي:
</p>

<ul>
<li>
		النساء اللواتي ولدن في الخمسينيات هم أكثر من تزوج في عمر صغير، وكذلك فإن أفراد الفوج التالي تزوجن في عمر متأخر أكثر، والفوج التالي بعد الفوج السابق وهكذا، وبقوا على الأقل حتى عمر الثلاثين تقريبًا.
	</li>
	<li>
		تملك النساء اللواتي ولدن في ستينيات القرن الماضي نمطًا غريبًا، إذ تزوجت النساء هنا في عمر 25 بمعدل أبطأ من الفوج السابق، ولكن بعد عمر 25 أصبح معدل الزواج أسرع، لكن النساء في هذا الفوج تجاوزت فوج الخمسينيات عند عمر 32، واتضح أنَّ احتمال زواج النساء في عمر 44 هو المرجَّح، لكن النساء اللواتي ولدن في الستينيات بلغن عمر 25 بين عامَي 1985 و1995، ومن المغري اعتقاد أنّ المقال الذي ذكرناه منذ قليل قد تسبب في ازدياد حالات الزواج، لكنه تفسير رديء للغاية، ومع ذلك فإنه من المحتمل أن يكون المقال وردّ الفعل عليه مؤشرَين على حالة مزاجية أثرت على سلوك هذا الفوج.
	</li>
	<li>
		يملك فوج السبعينات نمطًا مشابهًا، حيث أن النساء هنا أقل احتمالًا لأن يتزوجن قبل عمر 25 إذا وازناه مع الأفواج السابقة، لكن هذا لهذا الفوج احتمالات مشابهة للأفواج السابقة فيما يخص الزواج عند عمر 35.
	</li>
	<li>
		احتمال أن زواج فوج الثمانينيات قبل 25 هو أقل من الفوج السابق، ولكن ما يحدث بعد ذلك هو غير واضح، وإذا أردنا بيانات أكثر، فعلينا الانتظار حتى الدورة التالية من المسح الوطني لنمو الأسرة.
	</li>
</ul>
<p>
	يمكننا توليد بعض التنبؤات ريثما تصلنا البيانات.
</p>

<h2>
	الاستقراء الخارجي Extrapolation
</h2>

<p>
	ينتهي منحني البقاء لفوج السبعينات عند عمر 38 تقريبًا؛ أما فوج الثمانينات فينتهي منحني البقاء الخاص به عند سن 28، وبالطبع فإن بيانات فوج التسعينات نادرة جدًا، كما يمكننا استقراء هذه المنحنيات خارجيًا عن طريق استعارة بيانات من الفوج السابق، حيث يزودنا الصنف HazardFunction بالتابع <code>Extend</code> الذي ينسخ الذيل من <code>HazardFunction</code> أطول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_48" style="">
<span class="com"># class HazardFunction</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">Extend</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> other</span><span class="pun">):</span><span class="pln">
        last </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">series</span><span class="pun">.</span><span class="pln">index</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
        more </span><span class="pun">=</span><span class="pln"> other</span><span class="pun">.</span><span class="pln">series</span><span class="pun">[</span><span class="pln">other</span><span class="pun">.</span><span class="pln">series</span><span class="pun">.</span><span class="pln">index </span><span class="pun">&gt;</span><span class="pln"> last</span><span class="pun">]</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">series </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">([</span><span class="pln">self</span><span class="pun">.</span><span class="pln">series</span><span class="pun">,</span><span class="pln"> more</span><span class="pun">])</span></pre>

<p>
	يحتوي <code>HazardFunction</code> على سلسلة تحوِّل الوقت <code>t</code> إلى <code>λ(t)</code>، ويجد التابع <code>Extend</code> المتغير <code>last</code> وهو الفهرس الأخير في <code>self.series</code>،ثم يختار قيم من <code>other</code> التي تأتي بعد <code>last</code> ويضيفها إلى نهاية <code>self.series</code>، ويمكننا الآن توسيع HazardFunction الخاص بكل فوج وذلك بالاستعانة بقيم من الفوج السابق:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_50" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">PlotPredictionsByDecade</span><span class="pun">(</span><span class="pln">groups</span><span class="pun">):</span><span class="pln">
    hfs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> group </span><span class="kwd">in</span><span class="pln"> groups</span><span class="pun">:</span><span class="pln">
        hf</span><span class="pun">,</span><span class="pln"> sf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">EstimateSurvival</span><span class="pun">(</span><span class="pln">group</span><span class="pun">)</span><span class="pln">
        hfs</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">hf</span><span class="pun">)</span><span class="pln">

    thinkplot</span><span class="pun">.</span><span class="typ">PrePlot</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">hfs</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> hf </span><span class="kwd">in</span><span class="pln"> enumerate</span><span class="pun">(</span><span class="pln">hfs</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> i </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span><span class="pln">
            hf</span><span class="pun">.</span><span class="typ">Extend</span><span class="pun">(</span><span class="pln">hfs</span><span class="pun">[</span><span class="pln">i</span><span class="pun">-</span><span class="lit">1</span><span class="pun">])</span><span class="pln">
        sf </span><span class="pun">=</span><span class="pln"> hf</span><span class="pun">.</span><span class="typ">MakeSurvival</span><span class="pun">()</span><span class="pln">
        thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">sf</span><span class="pun">)</span></pre>

<p>
	حيث أن <code>groups</code> هو كائن GroupBy فيه معلومات المستجيبات مصنفة إلى مجموعات حسب عقد الولادة، كما تحسب الحلقة الأولى HazardFunction كل مجموعة، في حين توسِّع الحلقة الثانية كل HazardFunction بقيم من الفوج السابق له والذي قد يحتوي على قيم من المجموعة التي تسبقه أيضًا (أي قد يحتوي فوج الخمسينات على قيم من فوج الأربعينات وقد يحتوي فوج الأربعينات على قيم من فوج الثلاثينات وهكذا)، ثم تحوِّل كل HazardFunction إلى SurvivalFunction وترسمه.
</p>

<p style="text-align: center;">
	<img alt="الشكل 13.5.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98860" data-unique="35qfefwze" src="https://academy.hsoub.com/uploads/monthly_2022_05/62828424297e2_13.5.png.cdcb74e940602140f565df4ac32452cf.png"></p>

<p>
	يوضِّح الشكل السابق منحنيات البقاء الخاصة بالمستجيبات اللواتي ولدن خلال عقود مختلفة، مع تنبؤات للأفواج اللاحقة، كما يُظهر الشكل السابق النتائج، وقد حذفنا فوج الخمسينات لجعل التنبؤات أكثر وضوحًا، وتقترح هذه النتائج أنه بحلول السن الأربعين ستتقارب الأفواج الأحدث مع فوج الستينيات وستمثل المستجيبات المتزوجات نسبةً تقل عن %20 من المجموع الكلي.
</p>

<h2>
	العمر المتبقي المتوقع
</h2>

<p>
	إذا كان لدينا منحني بقاء، فيمكننا حساب العمر المتبقي المتوقع على أساس دالة للعمر الحالي، أي إذا كان لدينا مثلًا منحني البقاء لطول الحمل من القسم الأول من هذا المقال، فيمكننا حساب الوقت المتوقع حتى حدوث المخاض والولادة، حيث تتمثل الخطوة الأولى في استخراج <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%83%D8%AB%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1335/" rel="">دالة الكثافة الاحتمالية</a> PMF للأعمار، كما يزودنا الصنف <code>SurvivalFunction</code> بالتابع الذي يقوم بالمطلوب:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_52" style="">
<span class="com"># class SurvivalFunction</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakePmf</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> filler</span><span class="pun">=</span><span class="kwd">None</span><span class="pun">):</span><span class="pln">
        pmf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Pmf</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> val</span><span class="pun">,</span><span class="pln"> prob </span><span class="kwd">in</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">.</span><span class="typ">Items</span><span class="pun">():</span><span class="pln">
            pmf</span><span class="pun">.</span><span class="typ">Set</span><span class="pun">(</span><span class="pln">val</span><span class="pun">,</span><span class="pln"> prob</span><span class="pun">)</span><span class="pln">

        cutoff </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">cdf</span><span class="pun">.</span><span class="pln">ps</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">]</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> filler </span><span class="kwd">is</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> </span><span class="kwd">None</span><span class="pun">:</span><span class="pln">
            pmf</span><span class="pun">[</span><span class="pln">filler</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">-</span><span class="pln">cutoff

        </span><span class="kwd">return</span><span class="pln"> pmf</span></pre>

<p>
	تذكر أنّ <code>SurvivalFunction</code> تحتوي على <code>Cdf</code> للعمر، وتنسخ الحلقة القيم والاحتمالات من <code>Cdf</code> إلى <code>Pmf</code>، كما تُعَدّ <code>cutoff</code> هي أعلى احتمال في Cdf، وهي 1 إذا كان <code>Cdf</code> كاملًا وأقل من 1 بخلاف ذلك، وإذا كان <code>Cdf</code> غير كامل، فسنُدخل القيمة المزوَّدة إلى <code>filter</code> لنكملها، لكن يُعَدّ <code>Cdf</code> لمدة الحمل كاملًا، لذا لا داع للقلق حول هذا الأمر؛ أما الخطوة التالية هنا فهي حساب العمر المتبقي المتوقع، حيث يعني المتوقع هنا المتوسط الحسابي، كما يزودنا <code>SurvivalFunction</code> بدالة تقوم بهذا أيضًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_54" style="">
<span class="com"># class SurvivalFunction</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RemainingLifetime</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> filler</span><span class="pun">=</span><span class="kwd">None</span><span class="pun">,</span><span class="pln"> func</span><span class="pun">=</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">Pmf</span><span class="pun">.</span><span class="typ">Mean</span><span class="pun">):</span><span class="pln">
        pmf </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">MakePmf</span><span class="pun">(</span><span class="pln">filler</span><span class="pun">=</span><span class="pln">filler</span><span class="pun">)</span><span class="pln">
        d </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> t </span><span class="kwd">in</span><span class="pln"> sorted</span><span class="pun">(</span><span class="pln">pmf</span><span class="pun">.</span><span class="typ">Values</span><span class="pun">())[:-</span><span class="lit">1</span><span class="pun">]:</span><span class="pln">
            pmf</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
            pmf</span><span class="pun">.</span><span class="typ">Normalize</span><span class="pun">()</span><span class="pln">
            d</span><span class="pun">[</span><span class="pln">t</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> func</span><span class="pun">(</span><span class="pln">pmf</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> t

        </span><span class="kwd">return</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">Series</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span></pre>

<p>
	تأخذ <code>RemainingLifetime</code> الوسيط <code>filter</code>الذي يمرَّر إلى <code>MakePmf</code> و<code>func</code> وهو الدالة المستخدَمة لتلخيص توزيع العمر المتبقي؛ أما <code>pmf</code> فهي Pmf الأعمار المتبقية المستخرَجة من SurvivalFunction، و<code>d</code> هو قاموس يحتوي على النتائج وهو تحويل من العمر الحالي <code>t</code> إلى العمر المتبقي المتوقع.
</p>

<p>
	تمر الحلقة مرورًا تكراريًا على القيم في Pmf، وهي تحسب التوزيع الشرطي للأعمار المتبقية لكل قيمة من <code>t</code>، باعتبار أنّ العمر المتبقي يتجاوز <code>t</code>، فإنها تنجز هذه المهمة عن طريق إزالة القيم من <code>Pmf</code> الواحدة تلو الأخرى ومن ثم إعادة توحيد renoramlizing القيم المتبقية، كما تستخدِم بعدها <code>func</code> لتلخيص التوزيع الشرطي وفي هذا المثال النتيجة هي مدة الحمل المتوسطة باعتبار أنّ المدة تتجاوز <code>t</code>، كما نحصل على متوسط مدة الحمل المتبقية عن طريق طرح <code>t</code>.
</p>

<p style="text-align: center;">
	<img alt="الشكل 13.6.png" class="ipsImage ipsImage_thumbnailed" data-fileid="98855" data-unique="v5wpr9va7" src="https://academy.hsoub.com/uploads/monthly_2022_05/6282842062e8e_13.6.png.c642a6e6fe1be3ef2d64dacb7a8d9618.png"></p>

<p>
	يوضِّح الشكل السابق مدة الحمل المتوقعة المتبقية في الجهة اليسرى؛ أما في الجهة اليمنى فيوضِّح السنوات حتى أول زواج، كما يُظهر الشكل السابق في الجهة اليسرى طول الحمل المتبقي المتوقع على أساس دالة للمدة الحالية، أي المدة المتبقية المتوقعة في الأسبوع 0 مثلًا هي حوالي 34 أسبوع، وهي أقل من طول الحمل الكامل -أي 39 أسبوع- لأن حالات الإجهاض التي حصلت في الثلث الأول قد خفضت من المتوسط.
</p>

<p>
	ينخفض المنحني ببطء في الثلث الأول، وتكون المدة المتبقية المتوقعة بعد 13 أسبوع قد انخفضت 9 أسابيع لتصبح 25 أسبوع، بعد ذلك ينخفض بسرعة أكبر، وذلك بمعدل انخفاض أسبوع كامل في كل أسبوع جديد، في حين ينخفض المنحني حوالي أسبوع أو أسبوعين في الفترة ما بين الأسبوع 37 والأسبوع 42، وتكون المدة المتبقية المتوقعة في هذه الفترة ثابتةً، أي لا تصبح الوجهة أقرب مع مرور الأسابيع، وتدعى العمليات التي تحمل هذه الخاصية بعمليات بلا ذاكرة memoryless لأن ليس للماضي تأثير على التنبؤات، علمًا أنّ هذا السلوك هو الأساس الرياضي لجملة الممرضات الشهيرة التي تثير الغضب: اقترب موعد الولادة ومتوقع أن تلدي في أيّ يوم الآن.
</p>

<p>
	يُظهر الشكل السابق في الجهة اليمنى الوقت الوسيط المتبقي حتى أول زواج على أساس دالة للعمر، حيث يكون الوسيط هو 14 عامًا بالنسبة لفتاة عمرها 11 عامًا، ويقل المنحني حتى عمر 22 حينما يصبح الوقت المتبقي الوسيط هو حوالي 7 سنوات، ويزداد مرةً أخرى بعدها وبحلول العمر 30 يعود إلى ما كان عليه أي 14 عامًا، ويمكننا استنادًا إلى هذه البيانات استنتاج أنّ للنساء صغيرات السن أعمارًا متبقيةً متناقصةً، وتدعى المكونات الميكانيكية المرتبطة بهذه الخاصية NBUE وهي اختصار لمِن المتوقع أن يكون الجديد أفضل من المستخدَم new better than used in expectation، أي من المتوقع بقاء الجزء الجديد فترةً أطول.
</p>

<p>
	تملك النساء اللواتي تجاوزت أعمارهن 22 سنة وقتًا متبقيًا متزايدًا حتى أول زواج، وتدعى المكونات الميكانيكية المرتبطة بهذه الخاصية UBNE وهي اختصار لمِن المتوقع أن يكون المستخدَم أفضل من الجديد used better than new in expectation، أي من المتوقع أن يبقى الجزء المستخدَم فترةً أطول، إذ يُعَدّ الأطفال حديثو الولادة مثلًا ومرضى السرطان هم UBNE أيضًا لأن العمر المتوقع لديهم يزيد كلما طالت مدة حياتهم، فقد حسبنا الوسيط median في هذا المثال بدلًا من المتوسط mean لأن Cdf غير كامل، ويتوقع منحني البقاء أن نسبة ‎20% من المستجيبات لن يتزوجن قبل سن 44، وبما أن سن الزواج الأول لهؤلاء النساء غير معلوم وقد يكون غير موجود، فلن نتمكن من حساب المتوسط.
</p>

<p>
	استبدلنا القيم غير المعلومة هنا بالقيمة <code>np.inf</code> وهي قيمة خاصة تمثِّل اللانهاية، أي أنها تجعل متوسط اللانهاية لكل الاعمار، لكن يبقى الوسيط محدَّدًا تمامًا طالما أنّ أكثر من 50‎%‎ من الأعمار المتبقية نهائية، وهذا صحيح حتى عمر الثلاثين؛ أما بعدها فمن الصعب تحديد عمر متبقي متوقع له، وإليك الشيفرة التي تحسب وترسم هذه الدوال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6331_56" style="">
<span class="pln">    rem_life1 </span><span class="pun">=</span><span class="pln"> sf1</span><span class="pun">.</span><span class="typ">RemainingLifetime</span><span class="pun">()</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">rem_life1</span><span class="pun">)</span><span class="pln">

    func </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pln"> pmf</span><span class="pun">:</span><span class="pln"> pmf</span><span class="pun">.</span><span class="typ">Percentile</span><span class="pun">(</span><span class="lit">50</span><span class="pun">)</span><span class="pln">
    rem_life2 </span><span class="pun">=</span><span class="pln"> sf2</span><span class="pun">.</span><span class="typ">RemainingLifetime</span><span class="pun">(</span><span class="pln">filler</span><span class="pun">=</span><span class="pln">np</span><span class="pun">.</span><span class="pln">inf</span><span class="pun">,</span><span class="pln"> func</span><span class="pun">=</span><span class="pln">func</span><span class="pun">)</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">Plot</span><span class="pun">(</span><span class="pln">rem_life2</span><span class="pun">)</span></pre>

<p>
	حيث أن <code>sf1</code> هو منحني البقاء لطول الحمل، ويمكن في هذه الحالة استخدام القيم الافتراضية للدالة <code>RemainingLifetime</code>؛ أما <code>sf2</code> فهو منحني البقاء للعمر عند أول زواج، و<code>func</code> هو دالة تأخذ Pmf وتحسب وسيطها -أي المئين رقم 50-.
</p>

<h2>
	تمارين
</h2>

<p>
	يوجد الحل الخاص بهذا التمرين في <a href="https://github.com/AllenDowney/ThinkStats2/blob/master/solutions/chap13soln.ipynb" rel="external nofollow">chap13soln.py</a> في مستودع الشيفرات <a href="https://github.com/AllenDowney/ThinkStats2" rel="external nofollow">ThinkStats2</a> على GitHub (وسائر ملفات التمارين).
</p>

<h3>
	تمرين 1
</h3>

<p>
	يحتوي المتغير <code>cmdivorcx</code> في الدورتين السادسة والسابعة من المسح الوطني لنمو الأسرة على تاريخ طلاق المستجيبين من أول حالة زواج، وهي مرمَّزة بطريقة أشهر القرن.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: يوفِّر نظام أشهر القرن طريقةً سهلةً للعمل مع البيانات المرمزة حسب الشهر والسنة، وهي الطريقة الأساسية ترمَّز بها التواريخ في المسوح السكانية والصحية.
	</p>
</blockquote>

<p>
	احسب مدة حالات الزواج التي انتهت بطلاق ومدة حالات الزواج المستمرة حتى الآن، وقدِّر منحني الخطر ومنحني البقاء لمدة الزواج، ثم استخدِم طريقة إعادة أخذ العينات resampling لمراعاة أوزان أخذ العينات، ثم وضِّح خطأ أخذ العينات بصريًا عن طريق رسم البيانات الناتجة عن عدة مرات أخذ عينات، علمًا أنه من الأفضل أن تفكِّر في تقسيم المستجيبين إلى مجموعات حسب عقد الولادة وربما حسب العمر عند أول حالة زواج.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://greenteapress.com/thinkstats2/html/thinkstats2014.html" rel="external nofollow">Chapter 13 Survival analysis analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-regression-%D9%88%D8%AF%D9%88%D8%B1%D9%87-%D9%81%D9%8A-%D9%85%D9%84%D8%A7%D8%A1%D9%85%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9-%D9%85%D8%B9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%A7%D8%AD%D8%A9-r1493/" rel="">الانحدار الإحصائي regression ودوره في ملاءمة النماذج المختلفة مع أنواع البيانات المتاحة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-modelling-distributions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1332/" rel="">نمذجة التوزيعات Modelling distributions في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81%D9%8A%D8%A9-%D9%84%D8%A5%D8%AB%D8%A8%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B8%D8%B1%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1246/" rel="">تحليل البيانات الاستكشافية لإثبات النظريات الإحصائية</a>
	</li>
</ul>
<style type="text/css">
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}</style>
]]></description><guid isPermaLink="false">1576</guid><pubDate>Tue, 17 May 2022 07:08:02 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x645;&#x644;&#x641;&#x627;&#x62A; CSV</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%85%D9%84%D9%81%D8%A7%D8%AA-csv-r1644/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_08/62e912a883148_CSV.png.2ea485a94d08a68225d8f685d8d0a3a0.png" /></p>

<p>
	في هذا الفيديو نشرح كيفية التعامل مع  ملفات csv في لغة بايثون Python، بالإضافة إلى معالجة وتحلّيل البيانات ثم عرضها وتصديرها باستخدام مكتبة pandas الشهيرة في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">لغة بايثون Python</a>.
</p>

<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="480" src="https://www.youtube.com/embed/bKflBXozZ9c" title="التعامل مع ملفات CSV" width="853"></iframe>
</p>

<p>
	يمكنك التعرف على تفاصيل أكثر حول لغة بايثون وكيفية العمل بها من خلال <a href="https://academy.hsoub.com/programming/python/" rel="">قسم بايثون على أكاديمية حسوب</a>، كما يمكنك احتراف العمل بلغة بايثون وتعلم برمجة التطبيقات من خلالها عبر دورة <a href="https://academy.hsoub.com/learn/python-application-development/" rel="">تطوير التطبيقات باستخدام لغة Python</a> المقدمة من أكاديمية حسوب. يمكنك أيضًا الاستعانة خلال رحلة تعلمك وعملك لاحقًا كمبرمج في هذا المجال، <a href="https://wiki.hsoub.com/Python" rel="external">بتوثيق بايثون</a> المضاف على موسوعة حسوب.
</p>
]]></description><guid isPermaLink="false">1644</guid><pubDate>Sun, 15 May 2022 15:00:00 +0000</pubDate></item><item><title>&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x643;&#x62A;&#x627;&#x628;&#x629; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1524/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/624838bb411ec_--.png.e78218a87821543ca6644b132b5ee630.png" /></p>
<p>
	سنغطي في هذا المقال من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">تعلم البرمجة</a>:
</p>

<ul>
	<li>
		تاريخ موجز <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">لخوادم الويب</a>.
	</li>
	<li>
		أساسيات CGI: واجهة البوابة المشتركة.
	</li>
</ul>

<h2>
	مقدمة في برمجة خوادم الويب
</h2>

<p>
	في بدايات الإنترنت كنا نطلب صفحات HTML من خوادم الويب فتعطينا صفحات ثابتةً أو ساكنةً، ليس للمستخدمين أي يد في تعديل محتواها أو التفاعل معها، باستثناء بعض عمليات باتش batch processes التي استخدمتها بعض المواقع لإنشاء صفحات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML </a>من مصادر البيانات، لضمان تحديث معلومات تلك الصفحات، وفي هذه الحالة أيضًا لم يكن للمستخدمين دور في ما يظهر أمامهم على صفحة الويب.
</p>

<p>
	ثم صرنا ندمج البرامج في خوادم الويب لتوليد الصفحات ديناميكيًا كلما طلبها المستخدم، وسميت الآلية المستخدمة في ذلك واجهة البوابة المشتركة Common Gateway Interface أو CGI اختصارًا، ولا يزال أثر تلك التقنية باقيًا في مواقع الويب التي نتصفحها حاليًا.
</p>

<p>
	ومع استغلال المبرمجين إمكانيات واجهة CGI -مع وسم <code>&lt;form&gt;</code> في HTML- صارت المواقع التي يبنونها أكثر تطورًا وتعقيدًا، وصار من الممكن بناء مواقع التسوق الإلكتروني، والألعاب، والدعم الفني، وغيرها من الخدمات التي صارت اليوم بدهيات.
</p>

<p>
	تُبنى واجهة CGI على مفهوم بسيط، هو أن بيانات الإدخال تُرسَل مثل جزء من رسالة http GET أو http POST، ويشير الرابط إلى مجلد خاص في خادم الويب، الذي يعرف أن عليه تنفيذ المورد resource بدلًا من إعادته، وهذا المورد هو برنامج يرسل الخرج الخاص به إلى مجرى الخرج القياسي stdout في صفحة HTML، فيقرأ الخادم مجرى الخرج القياسي، ويوجهه إلى العميل الذي طلب الصفحة.
</p>

<p>
	سندرس الآن جزءًا من تلك المرحلة المبكرة في تطور خوادم الويب، بأن ننشئ خادم ويب بسيط على حاسوبنا المحلي، ليعطينا صفحة HTML ثابتةً، ثم نوسّع الصفحة لتشمل استمارةً تلتقط اسم المستخدم وترسله إلى الخادم، ونختم بإنشاء برنامج CGI يقرأ اسم المستخدم ذاك، ويعيد رسالة ترحيب مخصصةً باسمه من الخادم.
</p>

<h2>
	إنشاء صفحة ترحيب باستخدام واجهة CGI
</h2>

<p>
	توفر <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">بايثون</a> وحدة خادم ويب بسيطةً نستطيع استخدامها للاختبار والتطوير، قبل نقل الشيفرة إلى منصة استضافة ويب حقيقية في <a href="https://academy.hsoub.com/certificates/comptia/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%88%D8%A7%D8%B3%D9%8A%D8%A8-%D9%85%D8%B5%D8%B7%D9%84%D8%AD%D8%A7%D8%AA-%D9%88%D9%81%D9%87%D9%85-%D8%B7%D8%A8%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-r65/" rel="">شبكة</a> ما أو على الإنترنت، وسنبدأ بالنظر في كيفية تشغيلها لإرسال صفحات ويب ساكنة static web pages.
</p>

<h3>
	صفحة ويب بسيطة
</h3>

<p>
	أول ما علينا فعله هو إنشاء صفحة الويب التي نريد إرسالها، ولن نشرح كيفية كتابة شيفرة HTML بالتفصيل هنا، وإنما سنشرح ما يكفي لتشغيل الأمثلة التي نكتبها، وستبدو صفحة "Hello World" بسيطةً كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1705_15" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html&gt;</span><span class="pln">
   </span><span class="tag">&lt;head&gt;</span><span class="pln">
     </span><span class="tag">&lt;title&gt;</span><span class="pln">Hello world web page</span><span class="tag">&lt;/title&gt;</span><span class="pln">
   </span><span class="tag">&lt;/head&gt;</span><span class="pln">
   </span><span class="tag">&lt;body&gt;</span><span class="pln">
       </span><span class="tag">&lt;h1&gt;</span><span class="pln">Hello World</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
   </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	تتكون <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> من وسوم، وهي أسماء محددة مسبقًا في توصيف اللغة نفسها، تحيط بها علامتا <code>&lt;</code> و<code>&gt;</code>، وتأتي أغلب الوسوم في أزواج يكون وسم الإغلاق فيها مسبوقًا بشرطة مائلة <code>/</code>، كما قد تحتوي الوسوم على بيانات تُعرف بالخاصيات attributes، غير أن المثال أعلاه ليس فيه أي منها لأننا بدأنا به بسيطًا، لكننا سنراها لاحقًا.
</p>

<p>
	تجدر الإشارة هنا إلى أن بعض العناصر إجبارية في HTML، فيجب أن تحتوي كل صفحة HTML عليها على الأقل لتكون صالحةً، رغم أن أغلب المتصفحات الحالية ستعرض الصفحة حتى لو لم تكن هذه العناصر موجودةً، وتلك العناصر هي:
</p>

<ol>
	<li>
		التصريح doctype: يشير إلى أن هذا المستند هو مستند HTML، وقد كان هذا التصريح طويلًا ومفصلًا سابقًا، لكن -ومنذ صارت <a href="https://academy.hsoub.com/programming/html/html5/%D8%AE%D9%85%D8%B3%D8%A9-%D8%A3%D8%B4%D9%8A%D8%A7%D8%A1-%D8%B9%D9%84%D9%8A%D9%83-%D9%85%D8%B9%D8%B1%D9%81%D8%AA%D9%87%D8%A7-%D8%B9%D9%86-html5-r324/" rel="">HTML5</a> قياسيةً ومعتمدةً- نكتفي بكتابة <code>html</code> في ذلك التصريح. لاحظ أن تنسيق الوسم في doctype مختلف عن وسوم HTML العادية، إذ توجد علامة تعجب مباشرةً بعد علامة <code>‎&lt;‎</code>.
	</li>
	<li>
		الوسم html: نستخدم زوجًا من الوسم <code>&lt;html&gt;</code> لتحديد بداية متن المستند نفسه ثم وسم إغلاق له، ويشكل هذا الوسم جذر المستند، وتمثَّل صفحات HTML في ذاكرة المتصفح في هيكل شجري، تكون قمته الوسم <code>&lt;html&gt;</code>. وتعدِّل شيفرة جافاسكربت التي في المتصفح ذلك الهيكل، لذا تؤثر طريقة كتابة HTML كثيرًا في كيفية كتابة شيفرة جافاسكربت، وكذلك في قراءة عملاء الويب web clients -مثل تلك التي كتبناها في <a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B9%D9%85%D9%84%D8%A7%D8%A1-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1523/" rel="">المقال السابق</a>- لتلك الصفحة.
	</li>
	<li>
		الوسم head: يحدد الوسم <code>&lt;head&gt;</code> جزءًا من الصفحة يحوي معلومات عن الصفحة نفسها، لكن المتصفح لا يعرضها، فمثلًا تعرَّف فيه بيانات أوراق الأنماط المتتالية أو التنسيقات الموروثة <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a>، وقد يُستورد ملف CSS عند وجود عدة أنماط في الصفحة، كما تستورد ملفات جافاسكربت المستخدَمة في الصفحة. نلاحظ أننا أزحنا الوسوم في المثال أعلاه، لكن ذلك لا يؤثر على تنفيذ الشيفرة، وهو لتحسين قراءة الشيفرة وإظهار الهيكل الشجري المتشعب للمستند لا أكثر، ولا فرق بالنسبة للمتصفح سواء أزيحت الشيفرة أم لا، وقسم <code>head</code> غير إلزامي عند كتابة مستندات HTML نظريًا، لكن وجوده يرجع إلى حاجة أغلب المتصفحات الحديثة للمعلومات التي فيه.
	</li>
	<li>
		الوسم title: يحتوي هذا الوسم على عنوان الصفحة الذي سيعرضه المتصفح، وهو الجزء الوحيد المعروض من قسم الترويسة، رغم تأثير الأنماط وجافاسكربت في المظهر العام لمحتوى الصفحة الرئيسية.
	</li>
	<li>
		الوسم body: يغلف هذا الوسم المحتوى الأساسي للصفحة، والذي نراه في نافذة المتصفح.
	</li>
</ol>

<p>
	هذه هي العناصر الأساسية لصفحة الويب، وقد استخدمنا الوسم <code>&lt;h1&gt;</code> الذي يحتوي رسالة "Hello World"، وهو جزء من مجموعة عناصر <code>h</code> -التي تشير إلى كلمة ترويسة header- وتُرقَّم من 1 إلى 6 لتوضح ترتيب عناوين الفقرات، ويصغر حجم الخط المستخدم ومظهره كلما اقتربنا من العنوان السادس، رغم إمكانية تعديل ذلك باستخدام <a href="https://academy.hsoub.com/programming/css/%d8%aa%d8%b9%d8%b1%d9%91%d9%81-%d8%b9%d9%84%d9%89-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-css-r70/" rel="">CSS</a>.
</p>

<p>
	نحفظ شيفرة HTML في مستند باسم index.htm في مجلد باسم hello، ونتحقق من عمله بتحميله في متصفح ويب، فإذا عرض المتصفح الصفحة كما نريدها فنكون قد نجحنا في هذه المهمة البسيطة، وننتقل الآن إلى توفيره من خلال خادم الويب الخاص بنا.
</p>

<h3>
	تشغيل خادم الويب
</h3>

<p>
	يوجد خادم الويب الخاص <a href="https://wiki.hsoub.com/Python" rel="external">ببايثون</a> في وحدة <code>http.server</code>، ويمكن تشغيله دون أي تعديلات من خلال سطر أوامر نظام التشغيل، بتغيير المجلد العامل إلى مجلد hello الذي أنشأناه قبل قليل، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_1705_19" style=""><span class="pln">$ python </span><span class="pun">-</span><span class="pln">m http</span><span class="pun">.</span><span class="pln">server </span><span class="pun">--</span><span class="pln">cgi </span><span class="lit">8000</span></pre>

<p>
	يحدد الخيار <code>‎-m</code> في الشيفرة أعلاه الوحدة التي يجب أن تشغلها بايثون، أما الراية <code>‎--cgi</code> فتفعّل عمليات واجهة CGI التي سنحتاج إليها لاحقًا، ويشير العدد 8000 إلى منفذ الشبكة الذي ستستخدمه.
</p>

<h3>
	تحميل صفحة الويب
</h3>

<p>
	نستطيع الآن الوصول إلى الخادم من خلال تحميل الرابط التالي في شريط عنوان المتصفح:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_1705_21" style=""><span class="pln">http</span><span class="pun">://</span><span class="pln">localhost</span><span class="pun">:</span><span class="lit">8000</span></pre>

<p>
	إذا كان الخادم يعمل فستحمَّل تلك الصفحة وتُعرض كما فعلنا من قبل، إلا أنها الآن تُرسَل من قبل الخادم، كما يتضح من شريط العنوان في المتصفح، وبهذا نكون قد أرسلنا أول صفحة ويب لنا!
</p>

<p>
	والسبب الذي يجعل هذه العملية ناجحةً دون تحديد اسم الملف هو أن خوادم الويب تبحث عن ملف باسم <code>index.htm</code> تلقائيًا، فإذا كان موجودًا ولم نحدّد ملفًا مسبقًا فسيُعرض <code>index.htm</code>. كما يمكن إعداد الخادم ليبحث عن ملفات افتراضية أخرى، مثل <code>index.html</code> أو <code>index.php</code>.
</p>

<h2>
	استخدام واجهة CGI لعرض رسالة ترحيب بالمستخدم
</h2>

<p>
	نريد الآن أن نوسع صفحة الويب السابقة لتلتقط اسم المستخدم وترسله إلى الخادم، الذي يرد بإرسال رسالة ترحيب مخصصة باسم المستخدم.
</p>

<h3>
	إنشاء استمارة HTML
</h3>

<p>
	الخطوة الأولى لذلك هي تعديل ملف HTML ليتضمن حقل إدخال لاسم المستخدم، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_1705_23" style=""><span class="pun">&lt;!</span><span class="pln">doctype html</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">html</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">head</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">title</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pln"> user page</span><span class="pun">&lt;/</span><span class="pln">title</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">head</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="typ">Hello</span><span class="pun">,</span><span class="pln"> welcome to our website</span><span class="pun">!&lt;/</span><span class="pln">h1</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">form name</span><span class="pun">=</span><span class="str">"hello"</span><span class="pln"> 
          method</span><span class="pun">=</span><span class="str">"get"</span><span class="pln"> 
          action</span><span class="pun">=</span><span class="str">"http://localhost:8000/cgi-bin/sayhello.py"</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Please</span><span class="pln"> tell us your name</span><span class="pun">:&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"text"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"username"</span><span class="pln"> name</span><span class="pun">=</span><span class="str">"username"</span><span class="pln"> 
             size</span><span class="pun">=</span><span class="str">"30"</span><span class="pln"> required autofocus</span><span class="pun">/&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">input type</span><span class="pun">=</span><span class="str">"submit"</span><span class="pln"> value</span><span class="pun">=</span><span class="str">"Submit"</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">form</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="pln">body</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">html</span><span class="pun">&gt;</span></pre>

<p>
	نلاحظ هنا أن عناصر <code>&lt;form&gt;</code> تحتوي على خاصيات في وسومها، حيث تشكل الخاصية <code>name</code> جزءًا من البيانات المرسَلة إلى الخادم مع القيمة التي في صندوق الإدخال النصي، وتخبر الخاصية <code>method</code> الخاصة بوسم <code>form</code> المتصفح نوع رسالة http التي يجب إرسالها -وهي GET في هذه الحالة-، كما تخبره الخاصية <code>action</code> بالمكان الذي يجب أن يرسلها إليه.
</p>

<p>
	تخبر خاصيتا العنصر <code>input</code> الأخيرتان المتصفح ألا يرسل الاستمارة إلا إذا احتوى الحقل النصي على قيمة، وأن يضع المؤشر في ذلك الحقل ليكون مستعدًا لاستقبال الإدخال، كما تخبر القيمة <code>submit</code> الموجودة في نوع الإدخال <code>type</code> المتصفح أن يعرض هذا العنصر مثل زر تظهر عليه الكلمة الموجودة في قيمة الخاصية <code>value</code> -والتي هي <code>submit</code> أيضًا-، وسيرسل ذلك الزر الاستمارة عند الضغط عليه إلى الخاصية <code>action</code> الخاصة بالاستمارة.
</p>

<p>
	نحفظ هذا في نفس المجلد كما فعلنا من قبل، لكن نستدعي hello.htm في هذه المرة، ونتحقق من صحة عملها بكتابة ما يلي في شريط العنوان:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_1705_29" style=""><span class="pln">http</span><span class="pun">://</span><span class="pln">localhost</span><span class="pun">:</span><span class="lit">8000</span><span class="pun">/</span><span class="pln">hello</span><span class="pun">.</span><span class="pln">htm</span></pre>

<p>
	ينبغي أن نرى رسالةً فيها حقل إدخال نصي يطلب منا إدخال الاسم مع زر تحته، إلا أنه لن يحدث شيء عند ضغطنا على الزر لأننا لم نكتب أي شيفرة تعالج تلك النقرة على الخادم، لذا نحتاج إلى إنشاء برنامج CGI يُستدعى عند إرسال الاستمارة إلى الخادم بالضغط على الزر.
</p>

<h3>
	كتابة شيفرة CGI
</h3>

<p>
	يشبه برنامج CGI برامج <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a> التي كتبناها من قبل، باستثناء أمرين هما:
</p>

<ul>
	<li>
		برنامج CGI يستورد وحدة CGI.
	</li>
	<li>
		يوجد في مجلد باسم cgi-bin، في جذر خادم الويب، ويكون هذا البرنامج تنفيذيًا executable، لنحقق الأمر الثاني ننشئ مجلد <code>cgi-bin</code> داخل مجلد <code>html</code>، ثم ننشئ ملفًا داخل ذلك المجلد الجديد ونسميه sayhello.py يحتوي على الشيفرة التالية:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_1705_11" style=""><span class="com">#!/usr/bin/env python3</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> cgi

</span><span class="com"># استخرج حقول البيانات</span><span class="pln">
data </span><span class="pun">=</span><span class="pln"> cgi</span><span class="pun">.</span><span class="typ">FieldStorage</span><span class="pun">()</span><span class="pln">
username </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">[</span><span class="str">"username"</span><span class="pun">].</span><span class="pln">value 

</span><span class="com"># \n\n الإجبارية مع اللاحقة http أرسل ترويسة:</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"ContentType: text/html\n\n"</span><span class="pun">)</span><span class="pln"> 

</span><span class="com"># HTML والآن، أرسل محتوى</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"&lt;!doctype html&gt;\n&lt;html&gt;&lt;head&gt;"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">"&lt;title&gt;Hello %s&lt;/title&gt;&lt;/head&gt;"</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> username</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="str">'''
&lt;body&gt;
   &lt;h1&gt;Welcome %s&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;'''</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> username</span><span class="pun">)</span></pre>

<p>
	تنفَّذ العمليات الخاصة بالبيانات داخل استدعاء <code>FieldStorage</code>، حيث تجمع وحدة <code>cgi</code> جميع البيانات من طلب <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">http</a>، وتضعها في قاموس لنستطيع الوصول إليها بسهولة باستخدام مفاتيح نصية هي خاصيات <code>name</code> من الاستمارة الخاصة بنا.
</p>

<p>
	نلاحظ كيف تمكننا علامات الاقتباس الثلاثية الخاصة ببايثون من هيكلة الخرج في HTML بصورة يمكن قراءتها -انظر قسم head و body.
</p>

<p>
	بعد أن نحفظ الصفحة، نتأكد من تغيير الصلاحيات ليكون الملف قابلًا للتنفيذ من جميع المستخدمين، فإذا أعدنا تحميل صفحة hello.htm، وملأنا الاستمارة فسنحصل على رسالة ترحيب من الخادم عند الضغط على الزر.
</p>

<p>
	تصلح هذه التقنية لمثل هذه التطبيقات البسيطة، وتمتاز بشفافية الخطوات، ومن ثم سهولة تصحيحها، غير أنه مع زيادة حجم المواقع وتعقيد البيانات لا تكون واجهة CGI مناسبةً للعمل معها، وهنا يأتي دور أطر عمل الويب frameworks، والتي سننظر فيها في المقال التالي.
</p>

<h2>
	خاتمة
</h2>

<p>
	نأمل في نهاية هذا المقال أن تكون تعلمت ما يلي:
</p>

<ul>
	<li>
		تُنشأ الطلبات إلى خوادم الويب باستخدام طلبات http GET.
	</li>
	<li>
		يرد خادم الويب بمستند HTML.
	</li>
	<li>
		تُمرَّر البيانات في رابط الطلب أو متغيرات البيئة.
	</li>
	<li>
		تعالج وحدة CGI البروتوكول الأساسي.
	</li>
	<li>
		تُجلب بيانات الاستمارة من خلال قاموس <code>FieldStorage</code>.
	</li>
	<li>
		واجهة CGI مناسبة لاستمارات الويب البسيطة، مثل شاشات تسجيل الدخول، إلا أنها لا تناسب التطبيقات الكبيرة.
	</li>
</ul>

<p>
	ترجمة -بتصرف- <a href="http://www.alan-g.me.uk/l2p2/tutwebcgi.htm" rel="external nofollow">للفصل الثلاثين: Writing Web Applications</a> من كتاب Learn To Program لصاحبه Alan Gauld.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%B7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%81%D9%8A-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%81%D9%84%D8%A7%D8%B3%D9%83-%D9%86%D9%85%D9%88%D8%B0%D8%AC%D8%A7-r1547/" rel="">استخدام أطر العمل في برمجة تطبيقات الويب: فلاسك نموذجا</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B9%D9%85%D9%84%D8%A7%D8%A1-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1523/" rel="">برمجة عملاء ويب باستخدام بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب في ريآكت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81-%D8%AA%D9%83%D8%AA%D8%A8-%D8%A3%D9%88%D9%84-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%84%D9%83-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r715/" rel="">كيف تكتب أول برنامج لك في بايثون 3</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1524</guid><pubDate>Sat, 30 Apr 2022 15:01:00 +0000</pubDate></item><item><title>&#x628;&#x631;&#x645;&#x62C;&#x629; &#x639;&#x645;&#x644;&#x627;&#x621; &#x648;&#x64A;&#x628; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%B9%D9%85%D9%84%D8%A7%D8%A1-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1523/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/624837890f766_---Web-Client-Programming.png.8a760d7c7c43cb225ba43bcb550ca86a.png" /></p>
<p>
	سنغطي في هذا المقال من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">تعلم البرمجة</a> ما يلي:
</p>

<ul>
	<li>
		إرسال الطلبات إلى <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">خادم الويب</a>.
	</li>
	<li>
		إرسال البيانات إلى الخادم.
	</li>
	<li>
		معالجة HTML بواسطة html.parser.
	</li>
	<li>
		الخيارات الأخرى.
	</li>
</ul>

<h2>
	برمجة عملاء الويب
</h2>

<p>
	يمكن وصف برمجة عملاء الويب في تعريفين رئيسيين هما:
</p>

<ol>
	<li>
		استخدام جافاسكربت لتعديل محتوى صفحة الويب داخل المتصفح، بتغيير ألوان العناصر وتحريك اللوحات حولها، وإظهار العناصر أو إخفائها، وجلب أجزاء من البيانات الأولية من الخادم، مثل حالة البحث الحي live search مثلًا. وهي ليست معقدةً، لكنها تتطلب معرفةً عميقةً بكل من <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="https://wiki.hsoub.com/CSS" rel="external">CSS</a>، ومن ثم فهي خارج نطاق شرحنا، فإذا أردت معرفة المزيد عن مثل هذا النوع من برمجة عملاء الويب فانظر سلسلة <a href="https://academy.hsoub.com/tags/%D9%85%D8%AF%D8%AE%D9%84%20%D8%A5%D9%84%D9%89%20html5/" rel="">مدخل إلى html5</a> مثلًا في أكاديمية حسوب، وكتاب <a href="https://academy.hsoub.com/files/13-%D9%86%D8%AD%D9%88-%D9%81%D9%87%D9%85-%D8%A7%D9%94%D8%B9%D9%85%D9%82-%D9%84%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-html5/" rel="">نحو فهم أعمق لتقنيات HTML5</a>، كما توجد عدة مكتبات وأطر عمل لجافاسكربت يجب البحث فيها وتعلمها، لعل أشهرها jQuery، والتي يمكن القراءة عنها في <a href="https://wiki.hsoub.com/jQuery" rel="external">توثيق jQuery</a> في موسوعة حسوب، وغيرها من السلاسل والمواد العلمية في الأكاديمية والموسوعة.
	</li>
	<li>
		إنشاء برنامج يعمل على حاسوب ويتصل بخادم ويب متظاهرًا بأنه برنامج متصفح ويب، حيث يجلب هذا البرنامج بعض البيانات للتحليل، وهو ما يسمى بالبوت bot -اختصار robot-، أو يتبع بعض الروابط من موقع لآخر بحثًا عن بيانات تتعلق بموضوع ما، وهو ما يسمى بعناكب الويب web spyders أو زاحفات الوب، وسندرس بعض هذه الأنواع من برامج عملاء الويب في هذا المقال.
	</li>
</ol>

<h2>
	التعلم بالتطبيق
</h2>

<p>
	سنخالف في هذا المقال النهج الذي كنا نتبعه في شرحنا، إذ سنستخدم مثالًا عمليًا لتوضيح كيفية كتابة برنامج عميل ويب، وسنبدأ ببساطة في البداية، ثم نضيف المزايا لإبراز بعض المشاكل المعقدة بالتدريج، لكنه سيظل برنامجًا بسيطًا للغاية عند تمامه، لكن القصد منه أن يعطيك فكرةً عما يمكن تنفيذه بمثل تلك البرامج.
</p>

<p>
	مهمة هذا التدريب هي بناء برنامج يولد صفحة ويب جديدةً، تحتوي على تجميعة من جميع محتويات قائمة "سنغطي في هذا المقال (What will we cover?‎)" ونحتاج إلى تنفيذ بضعة أمور لبناء ذلك البرنامج:
</p>

<ol>
	<li>
		قراءة محتوى HTML لملف الفهرس.
	</li>
	<li>
		تحليل محتويات الصفحة، واستخراج جميع الروابط الموجودة في الجزء الأيسر إلى قائمة.
	</li>
	<li>
		جلب محتوى HTML الخاص بكل رابط من روابط تلك القائمة.
	</li>
	<li>
		استخراج قائمة النقاط الموجودة في قسم "What will we cover" إلى قائمة جديدة.
	</li>
	<li>
		توليد صفحة HTML باستخدام البيانات التي جمعناها.
	</li>
</ol>

<h3>
	جلب المحتوى
</h3>

<p>
	رأينا سابقًا كيفية جلب صفحة HTML بسيطة من خادم باستخدام الوحدة <code>urllib.request</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_13" style=""><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="str">'http://www.alan-g.me.uk/l2p2/index.htm'</span><span class="pun">)</span><span class="pln">
page </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">()</span></pre>

<p>
	لدينا الآن سلسلة نصية تحتوي على شيفرة HTML الخاصة بالصفحة الرئيسية، أو صفحة المستوى الأعلى للنسخة الأجنبية من الفصول، وسنلاحظ صعوبة قراءة الخرج، لهذا نستخدم خيار عرض المصدر view source الموجود في المتصفح لننظر في شيفرة HTML، لنرى أن جدول المحتويات موجود في زوج من وسوم <code>nav</code> -اختصار للتنقل navigation- وأن كل قائمة من قوائم الفصول الموجودة في كل قسم تُجمع في قائمة غير مرتبة unordered list، تحمل الوسم <code>ul</code>، ونميز كل عنصر فيها بوجود وسم <code>li</code> فيه، وتمثل جميع عناصر القائمة روابط تشعبيةً إلى ملفات الفصول، لهذا نجدها محاطةً بوسم الرابط التشعبي <code>&lt;a&gt;</code>.
</p>

<p>
	مهمتنا التالية استخراج جميع عناصر <code>&lt;a&gt;</code> داخل لوحة <code>nav</code> من الصفحة.
</p>

<h3>
	استخراج محتوى الوسوم
</h3>

<p>
	ذكرنا في المقدمة أننا نستطيع استخدام عمليات البحث النصية البسيطة للعثور على الوسوم وغيرها في صفحات الويب، لكن يفضل استخدام محلل HTML مناسب، لذا سنستخدم الصنف <code>HTMLParser</code> الموجود في وحدة المكتبة القياسية <code>html.parser</code>، وهو محلل لغوي مجرَّد abstract، مدفوع بالأحداث أو حدَثي event-driven، ويجب أن نقسمه إلى أصناف فرعية subclasses لتوفير المزايا التي نريدها، مع ملاحظة أنه يستدعي تابعين، الأول هو <code>handle_starttag()‎</code> عند كل وسم HTML افتتاحي، والثاني هو <code>handle_endtag()‎</code> عند كل وسم إغلاق، ويجب أن نوفر النسخ الخاصة بنا من تلك التوابع لتنفذ الإجراءات المناسبة عند العثور على الوسوم التي نريدها، ونريد في حالتنا هذه أن نجد كل الروابط الموجودة في لوحة <code>nav</code>، لذا يجب تعيين راية flag نسميها <code>in_nav</code> في كل مرة نعثر فيها على وسم <code>nav</code>، فإذا وجدنا وسم <code>a</code> وكانت الراية <code>True</code> فسنحفظ خاصية <code>href</code> في قائمة، حيث تُمرَّر الخاصيات إلى التابع في صف tuple من ثنائيات المفتاح/القيمة، ونريد أخيرًا التقاط وسم الإغلاق <code>‎/nav</code>، وإعادة تعيين الراية إلى <code>False</code>، لضمان أننا لا نجمع أي روابط من خارج لوحة المحتويات، وعليه سنعِدّ المحلل اللغوي، ونتأكد من قدرته على تعرّف الوسوم الثلاثة المطلوبة باستخدام تعليمات الطباعة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_15" style=""><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
</span><span class="kwd">import</span><span class="pln"> html</span><span class="pun">.</span><span class="pln">parser

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">(</span><span class="pln">html</span><span class="pun">.</span><span class="pln">parser</span><span class="pun">.</span><span class="typ">HTMLParser</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">links </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">  </span><span class="com">#list to store the links</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># the flag</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_starttag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> attributes</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"We found a &lt;nav&gt; tag"</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'a'</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"We found an &lt;a&gt; tag"</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_endtag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">print</span><span class="pun">(</span><span class="str">"We found a &lt;/nav&gt; tag"</span><span class="pun">)</span><span class="pln">

site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="str">'http://www.alan-g.me.uk/l2p2/index.htm'</span><span class="pun">)</span><span class="pln">
page </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># convert bytes to str</span><span class="pln">

parser </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">()</span><span class="pln">
parser</span><span class="pun">.</span><span class="pln">feed</span><span class="pun">(</span><span class="pln">page</span><span class="pun">)</span></pre>

<p>
	عند تشغيل الشيفرة السابقة ستجلب الصفحة، وتحللها، ثم تطبع الرسائل الموافقة لكل وسم تجده، وهي بهذا تثبت أن المحلل يعمل بكفاءة، وأننا بحاجة إلى راية <code>is_nav</code> لأن لدينا رابطًا خارج لوحة <code>nav</code>، نستطيع الآن أن نغير تعليمات الطباعة للشيفرة الحقيقية التي نريدها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_17" style=""><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
</span><span class="kwd">import</span><span class="pln"> html</span><span class="pun">.</span><span class="pln">parser

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">(</span><span class="pln">html</span><span class="pun">.</span><span class="pln">parser</span><span class="pun">.</span><span class="typ">HTMLParser</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">links </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">  </span><span class="com"># قائمة لتخزين الروابط</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># الراية</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_starttag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> attributes</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'a'</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">is_nav</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> key</span><span class="pun">,</span><span class="pln">val </span><span class="kwd">in</span><span class="pln"> attributes</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">"href"</span><span class="pun">:</span><span class="pln">
                    self</span><span class="pun">.</span><span class="pln">links</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">val</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_endtag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="str">'http://www.alan-g.me.uk/l2p2/index.htm'</span><span class="pun">)</span><span class="pln">
page </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># حوِّل سلسلة البايت إلى سلسلة نصية</span><span class="pln">

parser </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">()</span><span class="pln">
parser</span><span class="pun">.</span><span class="pln">feed</span><span class="pun">(</span><span class="pln">page</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">parser</span><span class="pun">.</span><span class="pln">links</span><span class="pun">)</span></pre>

<p>
	نلاحظ هنا أن التغييرات التي أجريناها كانت في التابعين <code>handle_starttag()‎</code> و<code>handle_endtag()‎</code>، إضافةً إلى السطر الأخير في الشيفرة <code>print(parser.links)‎</code>، ويجب أن تصبح النتيجة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_19" style=""><span class="pun">[</span><span class="str">'tutintro.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutneeds.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutwhat.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutstart.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutseq1.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutdata.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutseq2.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutloops.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutstyle.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutinput.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutbranch.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutfunc.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutfiles.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tuttext.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tuterrors.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutname.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutregex.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutclass.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutevent.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutgui.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutrecur.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutfctnl.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutcase.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutpractice.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutdbms.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutos.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutipc.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutsocket.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutweb.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutwebc.htm'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'tutwebcgi.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutflask.htm'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'tutrefs.htm'</span><span class="pun">]</span></pre>

<h3>
	استخراج النقاط من الفصول
</h3>

<p>
	بعد أن حصلنا على قائمة الفصول نريد إنشاء دالة تستطيع استخراج النقاط التي في أول كل صفحة، لذا سنستخدم خاصية View Source الموجودة في المتصفح مرةً أخرى، إذ نفحص شيفرة HTML الخاصة بإطار الفصل، فنرى أننا ننظر في مجموعة من عناصر القائمة الموجودة داخل وسم <code>div</code>، مع تعيين الخاصية <code>class</code> على القيمة <code>"todo"</code>، وهذا يشبه تقريبًا ما فعلناه عند بحثنا عن الروابط، وسننشئ الآن دالةً تأخذ سلسلة HTML وتعيد قائمةً من سلاسل <code>li</code>، وسنضيف اسم الملف في الشيفرة ونثبته ليكون <code>tutstart.htm</code>.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_21" style=""><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
</span><span class="kwd">import</span><span class="pln"> html</span><span class="pun">.</span><span class="pln">parser

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BulletParser</span><span class="pun">(</span><span class="pln">html</span><span class="pun">.</span><span class="pln">parser</span><span class="pun">.</span><span class="typ">HTMLParser</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">bullets </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_starttag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> attributes</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'div'</span><span class="pun">:</span><span class="pln">
           </span><span class="kwd">for</span><span class="pln"> key</span><span class="pun">,</span><span class="pln"> val </span><span class="kwd">in</span><span class="pln"> attributes</span><span class="pun">:</span><span class="pln">
               </span><span class="kwd">if</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'class'</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> val </span><span class="pun">==</span><span class="pln"> </span><span class="str">'todo'</span><span class="pun">:</span><span class="pln">
                   self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'li'</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">in_todo</span><span class="pun">:</span><span class="pln">
               self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_data</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">is_bullet</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">bullets</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># أعد تعيين الراية</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_endtag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'div'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

topic_url </span><span class="pun">=</span><span class="pln"> </span><span class="str">"http://www.alan-g.me.uk/l2p2/tutstart.htm"</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> get_bullets</span><span class="pun">(</span><span class="pln">aTopic</span><span class="pun">):</span><span class="pln">
    site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="pln">aTopic</span><span class="pun">)</span><span class="pln">
    topic </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln">
    topic_parser </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BulletParser</span><span class="pun">()</span><span class="pln">
    topic_parser</span><span class="pun">.</span><span class="pln">feed</span><span class="pun">(</span><span class="pln">topic</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> topic_parser</span><span class="pun">.</span><span class="pln">bullets

</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> get_bullets</span><span class="pun">(</span><span class="pln">topic_url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	لاحظ أن لدينا تابع معالجة حدث إضافي سنعيد تعريفه، وهو <code>handle_data()‎</code>، لأننا نريد استخراج البيانات الموجودة في وسوم <code>&lt;li&gt;</code> بدلًا من الوسم نفسه أو خصائصه، وفيما عدا ذلك تشابه هذه الشيفرة المثال السابق، حيث نعين الراية <code>in_todo</code> لتوضيح متى نكون داخل صندوق المحتويات، وكذلك الراية <code>is_bullet</code> التي تشير إلى أننا وجدنا عنصر قائمة داخل الصندوق، ثم نعيد تعيين راية <code>is_bullet</code> بمجرد قراءتنا للبيانات، ونعيد تعيين <code>is_todo</code> عندما نترك الصندوق، أي عند <code>‎&lt;/div&gt;‎</code>.
</p>

<p>
	يمكن دمج البرنامجين معًا، بإضافة الصنف والدالة الجديدين إلى الملف السابق، ويتبقى أن نكتب حلقة <code>for</code> للتكرار على الروابط من المحلل الأول وإرسالها إلى دالة <code>get_bullets()‎</code>، ثم تُجمع النتائج في قاموس عام global dictionary مرتب وفق الفصول، كما سننظمها أكثر من خلال إنشاء دالة <code>get_topics()‎</code> التي تشبه <code>get_bullets()‎</code>، يمكن أن نضيف شيئًا لمعالجة الأخطاء هنا، ونحوله إلى تنسيق وحدة في نفس الوقت، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_23" style=""><span class="kwd">import</span><span class="pln"> urllib
</span><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
</span><span class="kwd">import</span><span class="pln"> html</span><span class="pun">.</span><span class="pln">parser

</span><span class="com">###### Link handling code  ####</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">(</span><span class="pln">html</span><span class="pun">.</span><span class="pln">parser</span><span class="pun">.</span><span class="typ">HTMLParser</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">links </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">  </span><span class="com">#list to store the links</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># the flag</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_starttag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> attributes</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'a'</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">is_nav</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> key</span><span class="pun">,</span><span class="pln">val </span><span class="kwd">in</span><span class="pln"> attributes</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">if</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">"href"</span><span class="pun">:</span><span class="pln">
                    self</span><span class="pun">.</span><span class="pln">links</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">val</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_endtag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'nav'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_nav </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> get_topics</span><span class="pun">(</span><span class="pln">aSite</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
        site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="pln">aSite</span><span class="pun">)</span><span class="pln">
        page </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln"> </span><span class="com"># convert bytestring to str</span><span class="pln">
        link_parser </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LinkParser</span><span class="pun">()</span><span class="pln">
        link_parser</span><span class="pun">.</span><span class="pln">feed</span><span class="pun">(</span><span class="pln">page</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> link_parser</span><span class="pun">.</span><span class="pln">links
    </span><span class="kwd">except</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">error</span><span class="pun">.</span><span class="typ">HTTPError</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">


</span><span class="com">##### Bullet handling code</span><span class="pln">

</span><span class="kwd">class</span><span class="pln"> </span><span class="typ">BulletParser</span><span class="pun">(</span><span class="pln">html</span><span class="pun">.</span><span class="pln">parser</span><span class="pun">.</span><span class="typ">HTMLParser</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        super</span><span class="pun">().</span><span class="pln">__init__</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">bullets </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_starttag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">,</span><span class="pln"> attributes</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'div'</span><span class="pun">:</span><span class="pln">
           </span><span class="kwd">for</span><span class="pln"> key</span><span class="pun">,</span><span class="pln"> val </span><span class="kwd">in</span><span class="pln"> attributes</span><span class="pun">:</span><span class="pln">
               </span><span class="kwd">if</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'class'</span><span class="pln"> </span><span class="kwd">and</span><span class="pln"> val </span><span class="pun">==</span><span class="pln"> </span><span class="str">'todo'</span><span class="pun">:</span><span class="pln">
                   self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">
        </span><span class="kwd">elif</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'li'</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">in_todo</span><span class="pun">:</span><span class="pln">
               self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_data</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">is_bullet</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">bullets</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">is_bullet </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">  </span><span class="com"># reset the flag</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> handle_endtag</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> name </span><span class="pun">==</span><span class="pln"> </span><span class="str">'div'</span><span class="pun">:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">in_todo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> get_bullets</span><span class="pun">(</span><span class="pln">aTopic</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
        site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="pln">aTopic</span><span class="pun">)</span><span class="pln">
        topic </span><span class="pun">=</span><span class="pln"> site</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln">
        topic_parser </span><span class="pun">=</span><span class="pln"> </span><span class="typ">BulletParser</span><span class="pun">()</span><span class="pln">
        topic_parser</span><span class="pun">.</span><span class="pln">feed</span><span class="pun">(</span><span class="pln">topic</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> topic_parser</span><span class="pun">.</span><span class="pln">bullets
    </span><span class="kwd">except</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">error</span><span class="pun">.</span><span class="typ">HTTPError</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">

</span><span class="com">#### driver code  ####</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    summary </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    site_root </span><span class="pun">=</span><span class="pln"> </span><span class="str">"http://www.alan-g.me.uk/l2p2/"</span><span class="pln">

    the_topics </span><span class="pun">=</span><span class="pln"> get_topics</span><span class="pun">(</span><span class="pln">site_root</span><span class="pun">+</span><span class="str">'index.htm'</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> topic </span><span class="kwd">in</span><span class="pln"> the_topics</span><span class="pun">:</span><span class="pln">
        topic_url </span><span class="pun">=</span><span class="pln"> site_root </span><span class="pun">+</span><span class="pln"> topic
        summary</span><span class="pun">[</span><span class="pln">topic</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> get_bullets</span><span class="pun">(</span><span class="pln">topic_url</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">summary</span><span class="pun">[</span><span class="str">'tutdata.htm'</span><span class="pun">])</span></pre>

<p>
	لا زال بإمكاننا إجراء المزيد من التنظيم والترتيب، من خلال أخذ اثنين من أصناف المحلل والدوال المرتبطة بهما إلى وحدات منفصلة، لكن نترك هذا تدريبًا لك، لذا أنشئ الوحدتين linkparser.py و bulletparser.py، واستوردهما إلى الشيفرة المتبقية التي تستطيع حفظها باسم topic_summary.py، ويجب أن يبدو الملف الأخير أشبه بما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_25" style=""><span class="kwd">import</span><span class="pln"> linkparser </span><span class="kwd">as</span><span class="pln"> LP
</span><span class="kwd">import</span><span class="pln"> bulletparser </span><span class="kwd">as</span><span class="pln"> BP

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    summary </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    site_root </span><span class="pun">=</span><span class="pln"> </span><span class="str">"http://www.alan-g.me.uk/l2p2/"</span><span class="pln">

    the_topics </span><span class="pun">=</span><span class="pln"> LP</span><span class="pun">.</span><span class="pln">get_topics</span><span class="pun">(</span><span class="pln">site_root</span><span class="pun">+</span><span class="str">'index.htm'</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> topic </span><span class="kwd">in</span><span class="pln"> the_topics</span><span class="pun">:</span><span class="pln">
        topic_url </span><span class="pun">=</span><span class="pln"> site_root </span><span class="pun">+</span><span class="pln"> topic
        summary</span><span class="pun">[</span><span class="pln">topic</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BP</span><span class="pun">.</span><span class="pln">get_bullets</span><span class="pun">(</span><span class="pln">topic_url</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">summary</span><span class="pun">[</span><span class="str">'tutdata.htm'</span><span class="pun">])</span></pre>

<h4>
	إنشاء صفحة الملخص
</h4>

<p>
	بقي لمشروعنا مهمة واحدة، وهي أخذ البيانات التي جمعناها وإنشاء صفحة ويب لعرضها فيها، وهي أكثر من مجرد إنشاء ملف بسيط ومعالجة نصوص، مثلما فعلنا في مثال القائمة الذي شرحناها في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات في البرمجة</a>، فالتعقيد الوحيد هنا هو الحاجة إلى استخدام وسوم HTML لتنسيق البيانات، والذي ننفذه بكتابة بعض ترويسات HTML الثابتة، ثم إنشاء حلقة تتكرر على بيانات الفصول لتطبع قائمةً من الفصول والنقاط bulletins، ثم نختم بوسم تذييل HTML ثابت أيضًا، ونغلق الملف، ثم نفتح الملف في المتصفح لرؤية الصفحة النهائية، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_27" style=""><span class="kwd">import</span><span class="pln"> linkparser </span><span class="kwd">as</span><span class="pln"> LP
</span><span class="kwd">import</span><span class="pln"> bulletparser </span><span class="kwd">as</span><span class="pln"> BP
</span><span class="kwd">import</span><span class="pln"> time

</span><span class="kwd">def</span><span class="pln"> create_summary</span><span class="pun">(</span><span class="pln">filename</span><span class="pun">,</span><span class="pln">data</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">filename</span><span class="pun">,</span><span class="pln"> </span><span class="str">'w'</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> outf</span><span class="pun">:</span><span class="pln">
        </span><span class="com"># اكتب الترويسة</span><span class="pln">
        outf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'''&lt;!Doctype htm&gt;
&lt;html&gt;&lt;body&gt;
&lt;h1&gt;Summary of tutor topics&lt;/h1&gt;
&lt;dl&gt;'''</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># اكتب اسم كل فصل...</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> topic </span><span class="kwd">in</span><span class="pln"> data</span><span class="pun">:</span><span class="pln">
            outf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'&lt;dt&gt;%s&lt;/dt&gt;'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> topic</span><span class="pun">)</span><span class="pln">
            </span><span class="com"># ...and its bullets</span><span class="pln">
            </span><span class="kwd">for</span><span class="pln"> bullet </span><span class="kwd">in</span><span class="pln"> data</span><span class="pun">[</span><span class="pln">topic</span><span class="pun">]:</span><span class="pln">
                outf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">"&lt;dd&gt;%s&lt;/dd&gt;"</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> bullet</span><span class="pun">)</span><span class="pln">

        </span><span class="com"># اكتب التذييل</span><span class="pln">
        outf</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'''
&lt;/dl&gt;
&lt;/body&gt;&lt;/html&gt;'''</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> __name__ </span><span class="pun">==</span><span class="pln"> </span><span class="str">"__main__"</span><span class="pun">:</span><span class="pln">
    summary </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span class="pln">
    site_root </span><span class="pun">=</span><span class="pln"> </span><span class="str">"http://www.alan-g.me.uk/l2p2/"</span><span class="pln">
    summary_file </span><span class="pun">=</span><span class="pln"> </span><span class="str">'./topic_summary.htm'</span><span class="pln">

    the_topics </span><span class="pun">=</span><span class="pln"> LP</span><span class="pun">.</span><span class="pln">get_topics</span><span class="pun">(</span><span class="pln">site_root</span><span class="pun">+</span><span class="str">'index.htm'</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> topic </span><span class="kwd">in</span><span class="pln"> the_topics</span><span class="pun">:</span><span class="pln">
        topic_url </span><span class="pun">=</span><span class="pln"> site_root </span><span class="pun">+</span><span class="pln"> topic
        summary</span><span class="pun">[</span><span class="pln">topic</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> BP</span><span class="pun">.</span><span class="pln">get_bullets</span><span class="pun">(</span><span class="pln">topic_url</span><span class="pun">)</span><span class="pln">
        time</span><span class="pun">.</span><span class="pln">sleep</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="com"># DOS تعطيل رؤية الخادم له على أنه هجمة</span><span class="pln">

    create_summary</span><span class="pun">(</span><span class="pln">summary_file</span><span class="pun">,</span><span class="pln"> summary</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'OK'</span><span class="pun">)</span></pre>

<p>
	يجب أن نحصل عند تشغيل الشيفرة على ملف اسمه topic_summary.htm نستطيع فتحه في المتصفح، ويجب أن يكون مثل <a href="http://www.alan-g.me.uk/l2p2/topic_summary.htm" rel="external nofollow">هذه الصفحة</a>.
</p>

<h3>
	إرسال البيانات في الطلب
</h3>

<p>
	نحتاج أحيانًا إلى تنفيذ أمور أكثر من مجرد جلب ملف ثابت من الخادم، فقد نرغب في تسجيل الدخول، ونحتاج إلى إرسال اسم المستخدم وكلمة المرور أو بيانات اعتماد أخرى، أو قد نستخدم وسيلة بحث لجلب بعض الروابط، وتوجد طريقتان لإرسال البيانات إلى الخادم وفقًا لنوع رسالة HTTP المتوقعة.
</p>

<p>
	رأينا من قبل أن HTTP تحتوي على العديد من الرسائل التي يمكن إرسالها، وقلنا إن أكثر تلك الرسائل استخدامًا هي GET وPOST، حيث ترسل GET جميع بياناتها في سطر العنوان، ونستطيع رؤية ذلك في المتصفح في صورة المحارف الغريبة التي تحتوي على الكثير من علامات الاستفهام وإشارات التساوي، فهي قيم البيانات التي في طلب GET.
</p>

<h4>
	إرسال سلسلة بحث إلى GitHub
</h4>

<p>
	تنقسم برمجة عملاء الويب web clients عمليًا إلى جزء علمي وفق قواعد معروفة مسبقًا ونتوقع نتائجها، وجزء يأتي بالتجربة والخطأ، ويمكن تعلم الكثير عن موقع ويب بزيارته من خلال متصفح ويب، وإلقاء نظرة متفحصة في كل من شريط العنوان ومصدر الصفحات، فإذا زرنا مستودع الشيفرات مفتوحة المصدر في GitHub مثلًا، وبحثنا عن كلمة "python" فسنرى أن عنوان الصفحة المعادة يكون أشبه بما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_29" style=""><span class="pln">https</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">search</span><span class="pun">?</span><span class="pln">utf8</span><span class="pun">=%</span><span class="pln">E2</span><span class="pun">%</span><span class="lit">9C</span><span class="pun">%</span><span class="lit">93</span><span class="pun">&amp;</span><span class="pln">q</span><span class="pun">=</span><span class="pln">python</span><span class="pun">&amp;</span><span class="pln">type</span><span class="pun">=</span></pre>

<p>
	يشير الجزء <code>utf8=%E2%9C%93</code> إلى محرف يونيكود -هو <code>'</code>- الذي يخبرنا أن البحث يستخدم UTF-8، ونستطيع تجاهل هذا التفصيل وكذلك <code>type=‎</code> الفارغة في النهاية، ونرسل ما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_32" style=""><span class="pln">http</span><span class="pun">://</span><span class="pln">github</span><span class="pun">.</span><span class="pln">com</span><span class="pun">/</span><span class="pln">search</span><span class="pun">?</span><span class="pln">q</span><span class="pun">=</span><span class="pln">python</span></pre>

<p>
	وسنرى أنها تعطينا نفس النتيجة.
</p>

<p>
	نستطيع اكتشاف نوع هيكل HTML المُعاد من خلال عرض مصدر الصفحة، وخصائص العنصر element properties باستخدام أدوات الفحص الخاصة بالمتصفح inspection tools، ونستطيع إجراء البحث النصي لشاشة مصدر الصفحة في حالة GitHub ونحصل منه على أول نتيجة -والتي كانت geekcomputers/Python عند بحثنا-، والتي بدت كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_34" style=""><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"repo-list-item d-flex flex-justify-start py-4 public source"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"col-8 pr-3"</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">h3</span><span class="pun">&gt;</span><span class="pln">
      </span><span class="pun">&lt;</span><span class="pln">a href</span><span class="pun">=</span><span class="str">"/geekcomputers/Python"</span><span class="pln"> </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"v-align-middle"</span><span class="pun">&gt;</span><span class="pln">geekcomputers</span><span class="pun">/&lt;</span><span class="pln">em</span><span class="pun">&gt;</span><span class="typ">Python</span><span class="pun">&lt;/</span><span class="pln">em</span><span class="pun">&gt;&lt;/</span><span class="pln">a</span><span class="pun">&gt;</span><span class="pln">
    </span><span class="pun">&lt;/</span><span class="pln">h3</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">...</span></pre>

<p>
	نرى هنا كيفية استخراج الروابط، وصعوبته أكثر مما في مثال topic_summary.htm السابق.
</p>

<p>
	لا شك أن المشاكل لا حصر لها، وسنجد للمواقع آليات تمنع كل شيء -عدا المتصفحات- من الوصول إلى بياناتها، أو تستخدم تقنيات <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%B3%D8%B1%D9%8A%D8%B9-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-javascript-r550/" rel="">جافاسكربت</a> متطورةً لعرض الصفحة، ولن تفلح تقنيات تحليل HTML البسيطة معها، لكن بنظرةً متفحصةً في تلك المواقع سنرى أنها توفر <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهة برمجة تطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> يمكن استخدامها بديلًا، وهي أفضل من أدوات تحليل HTML، وقد يطلب الموقع مالًا لقاء رخصة استخدامها إن كان تجاريًا لأنها الطريقة التي يمول بها الموقع نفسه.
</p>

<p>
	وتوجد عدة مشاكل أخرى لا يتسع لها شرحنا، لكن نشير إلى أهمها والتي ينبغي البحث فيها، مثل معالجة محاولات تسجيل الدخول، واستخدام ملفات تعريف الارتباط cookies، ومعالجة اتصالات https المشفرة، فيمكن تنفيذ كل ذلك بقليل من الجهد، غير أنها خارج سياق شرحنا كما ذكرنا، ويمكن استخراج أغلب المعلومات من صفحة HTML باستخدام المحلل بدمج تباديل مختلفة من تلك التقنيات، إلا أننا قد نحصل على شيفرة HTML غير متقنة الكتابة أو التنسيق، وعندها سنحتاج إلى كتابة شيفرة خاصة لتصحيح الشيفرة، بل قد نضطر إلى كتابتها إلى ملف نصي واستخدام متحقق HTML خارجي -مثل <a href="http://www.html-tidy.org/" rel="external nofollow">HTMLtidy</a>- لتصحيحها، إذا كان التشوه الحاصل فيها أكثر مما يمكن تعديله يدويًا، وذلك قبل أن نحللها، أو يمكن استخدام حزمة من طرف ثالث -مثل <a href="https://www.crummy.com/software/BeautifulSoup/bs4/doc/" rel="external nofollow">Beautiful Soup</a>- التي تستطيع التعامل مع أغلب المشاكل الشائعة في HTML.
</p>

<h3>
	اكتشاف رموز الخطأ
</h3>

<p>
	يعيد الخادم أحيانًا أخطاءً بدلًا من صفحات الويب التي نتوقعها، ويجب أن نتمكن من التقاط تلك الأخطاء وقراءتها، والتي يعرضها المتصفح في صورة خطأ "Page Not Found" المشهور، أو صورة عبارات ألطف قليلًا، لكن إذا كنا نحن الذين نجلب البيانات بأنفسنا من الخادم فسنجد أن الخطأ يأتي في صورة رمز خطأ في ترويسة http، التي يحولها <code>urllib.request</code> إلى استثناء <code>urllib.error.HTTPError</code>، وبما أننا نعرف كيفية التقاط الاستثناءات باستخدام بنية <code>try/except</code> العادية، فنستطيع التقاط تلك الأخطاء بسهولة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_182_36" style=""><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">request </span><span class="kwd">as</span><span class="pln"> url
</span><span class="kwd">import</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">error
</span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
   site </span><span class="pun">=</span><span class="pln"> url</span><span class="pun">.</span><span class="pln">urlopen</span><span class="pun">(</span><span class="str">"http://some.nonexistent.address/foo.html"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">except</span><span class="pln"> urllib</span><span class="pun">.</span><span class="pln">error</span><span class="pun">.</span><span class="typ">HTTPError</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">print</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">code</span></pre>

<p>
	تأتي القيمة الموجودة في <code>urllib.error.HTTPError.code</code> من أول سطر من رد <a href="https://academy.hsoub.com/programming/general/%d9%85%d8%af%d8%ae%d9%84-%d8%a5%d9%84%d9%89-http-r73/" rel="">HTTP</a> الخاص بخادم الويب، مباشرةً قبل بداية الترويسات، مثل "HTTP/1.1 200 OK" أو "HTTP/1.1 404 Not Found"، ويتكون من رقم الخطأ، ويمكن الاطلاع على أشهر رموز الخطأ التي يعيدها خادم الويب في <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" rel="external nofollow">هذه الصفحة من موقع w3</a>، وما يهمنا هي الأخطاء التي تبدأ بالرقم 4 أو 5، وأغلب تلك الأخطاء ستكون:
</p>

<ul>
	<li>
		401: غير مصرح له Unauthorized.
	</li>
	<li>
		404: الصفحة غير موجودة Page not found.
	</li>
	<li>
		407: يُطلب توثيق الوكيل Proxy Authentication Required.
	</li>
	<li>
		500: خطأ داخلي في الخادم Internal Server Error.
	</li>
	<li>
		503: الخدمة غير متاحة Service Unavailable.
	</li>
	<li>
		504: انتهت المهلة الزمنية للبوابة Gateway Timeout.
	</li>
</ul>

<p>
	قد يكون المطلوب في بعض تلك الأخطاء مثل -503 و504- مجرد إعادة المحاولة بعد بضعة دقائق، أما في الخطأ 407 فقد نحتاج إلى مزيد من العمل للوصول إلى صفحة الويب، ولمزيد من التفاصيل حول هذه الرموز انظر مقال <a href="https://academy.hsoub.com/programming/general/%D8%B1%D9%85%D9%88%D8%B2-%D8%A7%D9%84%D8%A5%D8%AC%D8%A7%D8%A8%D8%A9-%D9%81%D9%8A-http-r75/" rel="">رموز الإجابة في HTTP</a>.
</p>

<p>
	لننظر الآن في الجانب الآخر، فما الذي يحدث عند وصول طلباتنا إلى خادم الويب؟ وكيف ننشئ خادم ويب خاص بنا؟ هذا هو موضوع المقال التالي.
</p>

<h2>
	خاتمة
</h2>

<p>
	في نهاية هذا المقال نأمل أن تكون تعلمت ما يلي:
</p>

<ul>
	<li>
		تُنشأ طلبات إلى خوادم الويب باستخدام طلبات http GET.
	</li>
	<li>
		يرد خادم الويب بمستند HTML.
	</li>
	<li>
		نحتاج إلى تحليل HTML لاستخراج البيانات المطلوبة.
	</li>
	<li>
		توفر بايثون عدة وحدات للتحليل، وأبسطها وحدة <code>html.parser</code>.
	</li>
	<li>
		توفر وحدات من طرف ثالث، مثل BeautifulSoup، وسائل تحليل أسهل وأفضل.
	</li>
</ul>

<p>
	ترجمة -بتصرف- <a href="http://www.alan-g.me.uk/l2p2/tutwebc.htm" rel="external nofollow">للفصل التاسع والعشرين: Web Client Programming</a> من كتاب Learn To Program لصاحبه Alan Gauld.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1524/" rel="">كيفية كتابة تطبيقات الويب</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1522/" rel="">كيفية التعامل مع الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/" rel="">المدخل الشامل لتعلم علوم الحاسوب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">دليلك الشامل إلى تعلم البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1523</guid><pubDate>Thu, 21 Apr 2022 15:01:00 +0000</pubDate></item><item><title>&#x645;&#x631;&#x628;&#x639;&#x627;&#x62A; &#x627;&#x644;&#x627;&#x62E;&#x62A;&#x64A;&#x627;&#x631; &#x648;&#x623;&#x632;&#x631;&#x627;&#x631; &#x627;&#x644;&#x627;&#x646;&#x62A;&#x642;&#x627;&#x621; &#x648;&#x627;&#x644;&#x642;&#x648;&#x627;&#x626;&#x645; &#x641;&#x64A; &#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/62419826724e0_----------.png.3a58b26bcd12de75d9d6b2833941bc34.png" /></p>

<p>
	تحدثنا في هذه السلسلة عن <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">عناصر الواجهة الرسومية Widgets</a> ثم بدأنا بشرح أهم العناصر فيها وهي <a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry</a> ثم انتقلنا إلى <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox</a> ونستكمل سلسلتنا التعليمية في هذا المقال لواجهات المستخدم الرسومية بعنصرين مهمين في الواجهات لعرض خيارات مختلفة للمستخدم هما مربعات الاختيار Checkbutton و أزرار الانتقاء Radio Button، إذ سنستعرض طريقة إنشائها وخصائصها، ثم ننتقل بعد ذلك إلى الحديث عن القوائم Menus وخصائصها.
</p>

<p>
	هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter.</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون.
	</li>
</ul>
<h2>
	مربعات الاختيار Checkbutton
</h2>

<p>
	تُستخدم مربعات الاختيار لعرض مجموعة من الخيارات للمستخدم يستطيع أن يختار أكثر من خيار واحد من بينها عن طريق تحديد المربع الماثل أمام كل خيار، ونستطيع استعادة قيمة الخيار الذي تم تحديده وكذلك ربطه بدالة محددة لتقوم بعمل معين عندها.
</p>

<h3>
	إنشاء مربعات الاختيار داخل النافذة
</h3>

<p>
	يتم اختيار اسم الكائن المطلوب ليمثل مربعات الاختيار ويستخدم الصنف <code>Checkbutton</code> من مكتبة Tkinter، ويعطى الصنف أولًا المكان الذي نريد وضع المربع فيه وهو النافذة الرئيسية، ومن ثم نص الخيار المراد عرضه أمام مربع الاختيار باستخدام الخاصية <code>text</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_9" style="">
<span class="typ">Button1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Checkbutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">)</span></pre>

<p>
	تكون قيمة المربع عند عدم اختياره صفر افتراضيا وقيمة 1 عند الاختيار وإذا أردنا تغيير ذلك نستخدم الخاصية <code>onvalue</code> ونعطيها القيمة المطلوبة عند اختيار المربع والخاصية <code>offvalue</code> ونعطيها القيمة عند عدم الاختيار، ويوضح المثال التالي المقصود:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_11" style="">
<span class="typ">Button1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Checkbutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">,</span><span class="pln">
                    onvalue </span><span class="pun">=</span><span class="pln"> </span><span class="str">"True"</span><span class="pun">,</span><span class="pln">offvalue </span><span class="pun">=</span><span class="pln"> </span><span class="str">"False"</span><span class="pun">)</span><span class="pln"> </span></pre>

<p>
	ونضيف عدة خصائص مع مربع الاختيار منها <code>command</code> ونربط بها الدالة التي نريد نداءها عند تغير حالة المربع من مُختار إلى غير مختار، ونستطيع الحصول على قيمة الاختيار عند ربط الخاصية <code>variable</code> بمتغير فإذا كانت القيمة رقمية يكون من نوع <code>IntVar</code> وإذا كانت غير ذلك من نوع <code>StringVar</code>، وتكون شفرة مربع الاختيار كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_13" style="">
<span class="typ">Checkbutton1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">IntVar</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Button1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Checkbutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">,</span><span class="pln">
                    onvalue </span><span class="pun">=</span><span class="lit">2</span><span class="pln"> </span><span class="pun">,</span><span class="pln">offvalue </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
                    variable </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Checkbutton1</span><span class="pln"> </span><span class="pun">,</span><span class="pln">
                      command</span><span class="pun">=</span><span class="pln">get_choice</span><span class="pun">)</span></pre>

<p>
	حيث أن <code>get_choice</code> هي الدالة التي سيتم ندائها عند تغير الحالة، وفي النهاية نضيفها للدالة الرئيسية باستخدام الدالة <code>pack</code>.
</p>

<h3>
	خصائص مربعات الاختيار
</h3>

<p>
	نستطيع كما في كل عناصر واجهات المستخدم الرسومية أن نعدل خصائصها، مثل تغيير لون النص ولون الخلفية ونوع الخط وشكل المؤشر بنفس الطريقة السابقة وبنفس الخصائص المستخدمة، فنستطيع مثلًا التعديل على حالة المربع باستخدام الدالة <code>select</code> لاختياره ونلغي عنه الاختيار باستخدام الدالة <code>deselect</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_15" style="">
<span class="typ">Button1</span><span class="pun">.</span><span class="pln">select</span><span class="pun">()</span><span class="pln">
</span><span class="pun">أو</span><span class="pln">
</span><span class="typ">Button1</span><span class="pun">.</span><span class="pln">deselect</span><span class="pun">()</span></pre>

<h2>
	أزرار الانتقاء Radio Button
</h2>

<p>
	أزرار الانتقاء شبيهة جدًا بمربعات الاختيار فهي تعرض للمستخدم عدة خيارات ولكن هذه المره عليه أن يختار أو ينتقي واحدًا منها فقط.
</p>

<h3>
	إنشاء أزرار الانتقاء داخل النافذة
</h3>

<p>
	يتم اختيار اسم الكائن المطلوب ليمثل مربعات الاختيار ويستخدم الصنف <code>Radiobutton</code> من مكتبة Tkinter، ويعطى الصنف أولًا المكان الذي نريد وضع المربع فيه وهو النافذة الرئيسية، ومن ثم نص الخيار المراد عرضه أمام زر الانتقاء باستخدام الخاصية <code>text</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_18" style="">
<span class="typ">Button1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Radiobutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">)</span></pre>

<p>
	شفرة إنشاء الأزرار شبيهة جدًا بمربعات الاختيار، وتختلف عنها بأن خاصية <code>variable</code> يجب أن تحتوي على متغير واحد تشترك فيه جميع الخيارات قد يكون من نوع <code>IntVar</code> إذا كانت قيم الخيارات رقمية أو نوع <code>StringVar</code> إذا كانت غير ذلك، كذلك نستخدم الخاصية <code>value</code> لنعطي كل خيار قيمة مختلفة عن الآخر.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_20" style="">
<span class="typ">Button1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Radiobutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">,</span><span class="pln">
                      variable </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Var1</span><span class="pun">,</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	في هذا المثال يجب على جميع أزرار الانتقاء ان يكون لها نفس هذه القيمة <code>variable = Var1</code>، ونستخدم الخاصية <code>command</code> لنربطها بالدالة التي ستُسدعى في كل مرة يتم فيها تغيير الاختيار من بين خيارات أزرار الانتقاء المطروحة.
</p>

<h3>
	خصائص أزرار الانتقاء
</h3>

<p>
	نستطيع كما في كل عناصر واجهات المستخدم الرسومية أن نعدل خصائصها كتغيير لون النص ولون الخلفية ونوع الخط وشكل المؤشر بنفس الطريقة السابقة وبنفس الخصائص المستخدمة، وكما في مربعات الاختيار نستطيع استخدام الدالة <code>select</code> والدالة <code>deselect</code> مع كل عنصر، سنعرض في المثال التالي للمستخدم مجموعة أزرار انتقاء ليختار منها واحدا وتعرض حاوية عنوان اسم الخيار الذي اختاره المستخدم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_22" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"أزرار الانتقاء "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">Var1</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">def</span><span class="pln"> sel</span><span class="pun">():</span><span class="pln">
   selection </span><span class="pun">=</span><span class="pln"> </span><span class="str">"لقد قمت باختيار  "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="typ">Var1</span><span class="pun">.</span><span class="pln">get</span><span class="pun">())</span><span class="pln">
   label</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">text </span><span class="pun">=</span><span class="pln"> selection</span><span class="pun">)</span><span class="pln">

B1</span><span class="pun">=</span><span class="typ">Radiobutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الأول "</span><span class="pun">,</span><span class="pln">variable </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Var1</span><span class="pun">,</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الأول"</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">sel</span><span class="pun">)</span><span class="pln">
B2</span><span class="pun">=</span><span class="typ">Radiobutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الثاني "</span><span class="pun">,</span><span class="pln">variable </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Var1</span><span class="pun">,</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الثاني"</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">sel</span><span class="pun">)</span><span class="pln">
B3</span><span class="pun">=</span><span class="typ">Radiobutton</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الخيار الثالث "</span><span class="pun">,</span><span class="pln">variable </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Var1</span><span class="pun">,</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"الثالث"</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">sel</span><span class="pun">)</span><span class="pln">

B1</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">
B2</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">
B3</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

label </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span><span class="pln">

label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	نحصل على النتيجة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94902" href="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI5_Radiobutton.jpg.ca11e2a3ed466d756b95f33869fcf036.jpg" rel=""><img alt="001_GUI5_Radiobutton.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94902" data-unique="c2br6v391" src="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI5_Radiobutton.jpg.ca11e2a3ed466d756b95f33869fcf036.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	أزرار الانتقاء
</p>

<h2>
	القوائم Menus
</h2>

<p>
	قد يحتوي البرنامج الذي صمم باستخدام واجهات المستخدم الرسومية الكثير من الوظائف والخصائص، فتبرز الحاجة إلى مزيد من الترتيب والتنظيم في عرضها، ومن العناصر المساعدة على فعل ذلك القوائم <code>Menus</code>، فهي تضفي المزيد من الترتيب على مظهر النافذة حيث تقوم بتجميع العمليات المتشابهة وإدراج تصنيفات لها.
</p>

<p>
	تدعم مكتبة Tkinter القوائم وتعرضها بمظهر مشابه لأشكال القوائم في نظام التشغيل الذي يعمل عليه البرنامج، ويمكن عدّ قائمة Tkinter على أنها مجموعة من عدة عناصر واجهة المستخدم الأخرى المدمجة في عنصر واحد، لذلك تعتبر عملية إنشائها أكثر صعوبة من بقية عناصر واجهة المستخدم، ولكن الجانب الإيجابي هو أن استخدام القائمة بشكل صحيح في التطبيق يحسن الوظائف بشكل كبير ويجعلها تبدو احترافية.
</p>

<h3>
	إنشاء القائمة داخل النافذة
</h3>

<p>
	يتطلب إنشاء القائمة عدة خطوات:
</p>

<p>
	الخطوة الأولى،إنشاء كائن ليمثل القائمة الرئيسية ويستخدم المصنف <code>Menu</code> من مكتبة Tkinter، ويعطى الصنف المكان الذي نريد وضع الإطار فيه وهو النافذة الرئيسية على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_27" style="">
<span class="pln">main_menu</span><span class="pun">=</span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span></pre>

<p>
	الخطوة الثانية، نحتاج إلى ربط القائمة الرئيسية main_menu بالنافذة الرئيسية main_window باستخدام الخاصية <code>menu</code> وبما أننا نقوم بتعديل خصائص النافذة بعد عملية إنشائها، فسنحتاج إلى استخدام الدالة <code>config</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_32" style="">
<span class="pln">main_window</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">menu</span><span class="pun">=</span><span class="pln">main_menu</span><span class="pun">)</span></pre>

<p>
	الخطوة الثالثة، نحتاج إلى إضافة القوائم الفرعية التي ستنسدل من القائمة الرئيسية مثل قائمة ملف، تعديل، المظهر والخيارات ونحوها، ونحتاج لفعل ذلك إلى إنشاء كائن من الصنف <code>Menu</code> ويعطى المكان الذي نريد وضعه فيه وهو القائمة الرئيسية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_30" style="">
<span class="pln">file_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_menu</span><span class="pun">)</span></pre>

<p>
	بعد ذلك نقوم بربطه مع القائمة الرئيسية باستخدام الدالة <code>add_cascade</code> وتعطى رقعة التعريف <code>label</code> التي يكتب عنوان هذه القائمة فيه، واسم القائمة <code>file_menu</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_34" style="">
<span class="pln"> main_menu</span><span class="pun">.</span><span class="pln">add_cascade</span><span class="pun">(</span><span class="pln">
    label</span><span class="pun">=</span><span class="str">"ملف"</span><span class="pun">,</span><span class="pln">
    menu</span><span class="pun">=</span><span class="pln">file_menu</span><span class="pun">)</span><span class="pln">  </span></pre>

<p>
	ستكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_36" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" القوائم "</span><span class="pun">)</span><span class="pln"> 

main_menu</span><span class="pun">=</span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">menu</span><span class="pun">=</span><span class="pln">main_menu</span><span class="pun">)</span><span class="pln">
file_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_menu</span><span class="pun">)</span><span class="pln">

main_menu</span><span class="pun">.</span><span class="pln">add_cascade</span><span class="pun">(</span><span class="pln">
    label</span><span class="pun">=</span><span class="str">"ملف"</span><span class="pun">,</span><span class="pln">
    menu</span><span class="pun">=</span><span class="pln">file_menu
</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على النافذة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94903" href="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI5_menu.jpg.2ca14d8b282a1dd4cc7b5fe01d16c9fa.jpg" rel=""><img alt="002_GUI5_menu.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94903" data-unique="mzb7l9eps" src="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI5_menu.jpg.2ca14d8b282a1dd4cc7b5fe01d16c9fa.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بقائمة
</p>

<p>
	في هذه النافذة قائمة ملف فارغة، نحتاج إلى إضافة عناصر في هذه القائمة، لعمل هذا نستخدم الدالة <code>add_command</code>، وتعطى حاوية عنوان لاسم عنصر القائمة باستخدام الخاصية label وكذلك تعطى الأمر الذي يجب تنفيذه عند النقر على عنصر القائمة باستخدام الخاصية <code>command</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_39" style="">
<span class="pln">file_menu</span><span class="pun">.</span><span class="pln">add_command</span><span class="pun">(</span><span class="pln">label</span><span class="pun">=’</span><span class="pln">name</span><span class="pun">’</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=………..)</span></pre>

<p>
	سنضيف على المثال السابق عنصر للقائمة ملف تحت اسم خروج، وتغلق النافذة الرئيسية عند النقر عليه ونستخدم للإغلاق الأمر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_42" style="">
<span class="pln">main_window</span><span class="pun">.</span><span class="pln">destroy</span></pre>

<p>
	وتكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_44" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" القوائم "</span><span class="pun">)</span><span class="pln"> 

main_menu</span><span class="pun">=</span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">menu</span><span class="pun">=</span><span class="pln">main_menu</span><span class="pun">)</span><span class="pln">
file_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_menu</span><span class="pun">)</span><span class="pln">

main_menu</span><span class="pun">.</span><span class="pln">add_cascade</span><span class="pun">(</span><span class="pln">
    label</span><span class="pun">=</span><span class="str">"ملف"</span><span class="pun">,</span><span class="pln">
    menu</span><span class="pun">=</span><span class="pln">file_menu</span><span class="pun">)</span><span class="pln">
file_menu</span><span class="pun">.</span><span class="pln">add_command</span><span class="pun">(</span><span class="pln">label</span><span class="pun">=</span><span class="str">'خروج'</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">main_window</span><span class="pun">.</span><span class="pln">destroy</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	نحصل عند تشغيل الشفرة على النافذة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94904" href="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI5_active_menu.jpg.5438e55382ed56de072ba38c17f56c7f.jpg" rel=""><img alt="003_GUI5_active_menu.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94904" data-unique="sjhydd45q" src="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI5_active_menu.jpg.5438e55382ed56de072ba38c17f56c7f.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بقائمة تحوي خيارات
</p>

<p>
	بإمكاننا عمل قائمة فرعية لتكون مدرجة داخل قائمة أخرى مثلًا إذا أردنا إضافة قائمة تصدير داخل القائمة ملف فعلينا إنشاء كائن من الصنف <code>Menu</code> ومن ثم وضعه على القائمة ملف باستخدام الدالة <code>add_cascade</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_47" style="">
<span class="pln">export_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">file_menu</span><span class="pun">)</span><span class="pln">
file_menu</span><span class="pun">.</span><span class="pln">add_cascade</span><span class="pun">(</span><span class="pln">
    label</span><span class="pun">=</span><span class="str">"تصدير"</span><span class="pun">,</span><span class="pln">
    menu</span><span class="pun">=</span><span class="pln">export_menu</span><span class="pun">)</span></pre>

<h3>
	خصائص القوائم
</h3>

<p>
	يمكننا تغيير الكثير من خصائص القوائم كتغيير لون النص <code>fg</code> و لون الخلفية <code>bg</code> و نوع الخط <code>font</code> و شكل المؤشر <code>cursor</code>، كذلك يمكننا التحكم بحالتها باستخدام الخاصية <code>state</code> حيث أنه افتراضيًا يكون فعالًا <code>normal</code> ويمكننا تغييره إلى غير فعال <code>disable</code>.
</p>

<p>
	تضع Tkinter افتراضيًا خطًا متقطعًا في بداية القائمة بإمكاننا إخفاءه باستخدام الخاصية <code>tearoff</code> وإعطائها قيمة <code>False</code> أثناء إنشاء القائمة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_49" style="">
<span class="pln">file_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_menu</span><span class="pun">,</span><span class="pln">tearoff</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">)</span></pre>

<p>
	عندما تحتوي القائمة على الكثير من العناصر، فمن الجيد تجميع المتشابه منها وإدراجها متسلسة ووضع فاصل بين مجموعة وأخرى، ونستخدم لعمل ذلك الدالة <code>add_separator()‎</code> مع اسم القائمة على النحو التالي:
</p>

<pre class="ipsCode">
file_menu.add_separator()
</pre>

<h2>
	تطبيق الآلة الحاسبة (الجزء الخامس)
</h2>

<p>
	قمنا في الدروس السابقة برسم نافذة الآلة مع وضع إطارين عليها واحد للمدخلات والآخر لأزرار الأرقام والعمليات، وقمنا بربط الأزرار بالدوال المطلوبة لتنفيذ العمليات الحسابية، وتوصلنا للشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94905" href="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI5_calculater.jpg.860674f3df03928e1c013c7955cda4e5.jpg" rel=""><img alt="004_GUI5_calculater.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94905" data-unique="zogq1y1d8" src="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI5_calculater.thumb.jpg.7d85f1f52b1d4e09e8a03676cc2d3e17.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة الرابعة
</p>

<p>
	سنطبق ما تعلمناه عن القوائم و نضيف لتطبيق الآلة قائمة رئيسة نسميها الخيارات، بتنفيذ نفس الخطوات السابقة، فنبدأ بإنشاء كائن ليمثل القائمة الرئيسية نسمية main_menu، بعدها نربط الكائن بنافذتنا الرئيسية mycalculator.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_54" style="">
<span class="pln">main_menu</span><span class="pun">=</span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">)</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">menu</span><span class="pun">=</span><span class="pln">main_menu</span><span class="pun">)</span></pre>

<p>
	بعد ذلك نضيف قائمتنا الوحيدة نسميها option_menu ونعنونها بخيارات ونضيفها على القائمة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_56" style="">
<span class="pln">option_menu </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln">main_menu</span><span class="pun">)</span><span class="pln">

main_menu</span><span class="pun">.</span><span class="pln">add_cascade</span><span class="pun">(</span><span class="pln">
    label</span><span class="pun">=</span><span class="str">"الخيارات"</span><span class="pun">,</span><span class="pln">
    menu</span><span class="pun">=</span><span class="pln">option_menu</span><span class="pun">)</span></pre>

<p>
	نضيف في قائمة الخيارات خيارين، الأول بعنوان (حول التطبيق) يعرض مربع رسائل من نوع <code>showinfo</code> تعرض معلومات عن الآلة، ونضيف دالة <code>about_MSG</code> تعرض مربع الرسائل ونربطها باستخدام الأمر <code>command</code> مع عنصر القائمة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_58" style="">
<span class="kwd">def</span><span class="pln"> about_MSG</span><span class="pun">():</span><span class="pln">
     showinfo</span><span class="pun">(</span><span class="pln">title</span><span class="pun">=</span><span class="str">'حول التطبيق'</span><span class="pun">,</span><span class="pln">
     message</span><span class="pun">=</span><span class="str">'تم تصميم هذه الآلة خلال سلسلة دروس \n'</span><span class="pun">+</span><span class="pln">
 </span><span class="str">'تعلم استخدام واجهات المستخدم الرسومية \n'</span><span class="pun">+</span><span class="pln">
</span><span class="str">'في بايثون باستخدام '</span><span class="pun">+</span><span class="str">'\nTkinter'</span><span class="pun">)</span><span class="pln">

option_menu</span><span class="pun">.</span><span class="pln">add_command</span><span class="pun">(</span><span class="pln">label</span><span class="pun">=</span><span class="str">'حول التطبيق'</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">about_MSG</span><span class="pun">)</span></pre>

<p>
	يغلق الخيار الثاني بعنوان "خروج" النافذة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6619_60" style="">
<span class="pln">option_menu</span><span class="pun">.</span><span class="pln">add_command</span><span class="pun">(</span><span class="pln">label</span><span class="pun">=</span><span class="str">'خروج'</span><span class="pun">,</span><span class="pln">command</span><span class="pun">=</span><span class="pln">mycalculator</span><span class="pun">.</span><span class="pln">destroy</span><span class="pun">)</span></pre>

<p>
	وتظهر الآلة بشكلها النهائي كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94906" href="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI5_calculator5.jpg.ce673c8dc715954638ff3e2caf4be5bf.jpg" rel=""><img alt="005_GUI5_calculator5.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94906" data-unique="ut78afe3i" src="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI5_calculator5.thumb.jpg.8f952472226a800879f6eaac8fea17c1.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة بقوائم
</p>

<p style="text-align: center;">
	<img alt="006_GUI5_infomessage5.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94907" data-unique="vl82k6lkq" src="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI5_infomessage5.jpg.648cc80011a7864f24a811ca7f4830fb.jpg" style="width: 300px; height: auto;"></p>

<p style="text-align: center;">
	مربع رسائل حول التطبيق
</p>

<p>
	وبهذا نكون قد أنهينا تطبيق الآلة الحاسبة كاملًا.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://coderslegacy.com/python/python-gui/python-tkinter-check-button/" rel="external nofollow">Python Tkinter Check Button</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-checkbox/" rel="external nofollow">Tkinter Checkbox</a>.
	</li>
	<li>
		<a href="https://www.studytonight.com/tkinter/python-tkinter-checkbutton-widget" rel="external nofollow">Python Tkinter Checkbutton Widget</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-radio-button/" rel="external nofollow">Tkinter Radio Button</a>.
	</li>
	<li>
		<a href="https://coderslegacy.com/python/python-gui/python-tkinter-radio-button/" rel="external nofollow">Python Tkinter Radio Button</a>.
	</li>
	<li>
		<a href="https://www.studytonight.com/tkinter/python-tkinter-radiobutton-widget" rel="external nofollow">Python Tkinter Radiobutton Widget</a>.
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_radiobutton.htm" rel="external nofollow">Python - Tkinter Radiobutton</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-menu/" rel="external nofollow">Tkinter Menu</a>.
	</li>
	<li>
		<a href="https://coderslegacy.com/python/python-gui/python-tkinter-menu/" rel="external nofollow">Python Tkinter Menu</a>.
	</li>
</ul>
<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
	<li>
		النسخة الكاملة من كتاب <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1511</guid><pubDate>Tue, 12 Apr 2022 15:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x632;&#x631;&#x627;&#x631; Buttons  &#x648;&#x645;&#x631;&#x628;&#x639;&#x627;&#x62A; &#x627;&#x644;&#x631;&#x633;&#x627;&#x626;&#x644; messagebox &#x641;&#x64A; &#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/624193d42c826_-Buttons----messagebox------.png.a1cd005d33d07a58fc778f3ea7df2396.png" /></p>

<p>
	تحدثنا في <a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">المقال السابق</a> عن حاوية العنوان label و صناديق المدخلات Entry والتي هي جزء من <a href="http://%D9%85%D8%AF%D8%AE%D9%84%20%D8%A5%D9%84%D9%89%20%D8%B9%D9%86%D8%A7%D8%B5%D8%B1%20%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA%20%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%20%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9%20Widgets%20%D9%81%D9%8A%20%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86" rel="external nofollow">عناصر الواجهة الرسومية Widgets في مكتبة TKinter</a>، وسنواصل الحديث في هذا المقال عن مجموعة العناصر التي يمكننا إضافتها للنافذة الرئيسية في واجهات المستخدم الرسومية وهي الأزرار Buttons ومربعات الرسائل messagebox بقليل من التفصيل.
</p>

<p>
	هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter.</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
</ul>
<h2>
	الأزرار Buttons
</h2>

<p>
	تعد الأزرار واحدة من أهم العناصر لواجهات المستخدم الرسومية، وهي عبارة عن مربعات قابلة للنقر عليها، حيث أن الغرض الرئيسي منها تفاعل الواجهة الرسومية مع نقر المستخدم على الأزرار لتنفيذ أمر معين مربوط بها.
</p>

<h3>
	إنشاء الأزرار داخل النافذة
</h3>

<p>
	نختار اسم المتغير المطلوب ليمثل الأزرار ويُستخدم الصنف Button من مكتبة Tkinter لذلك، ويعطى أولًا المكان الذي نريد وضع الزرار فيه وهو النافذة الرئيسية، ومن ثم النص المراد عرضه داخله ليوضح الهدف منه أو العمل الذي سيقوم بتنفيذه عند النقر عليه باستخدام الخاصية text على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_6" style="">
<span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"النص"</span><span class="pun">)</span></pre>

<p>
	تنشئ الشيفرة التالية زرارًا يعرض كلمة "موافق" ومن ثم تضعه على النافذة الرئيسية باستخدام الدالة <code>pack</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_8" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'موافق'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	نحصل بتشغيل الشيفرة على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94869" href="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI_button.jpg.be93dc140d527cb131cb024a11e88905.jpg" rel=""><img alt="001_GUI_button.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94869" data-unique="q36v4rtrd" src="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI_button.jpg.be93dc140d527cb131cb024a11e88905.jpg" style="width: 300px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بزر
</p>

<h3>
	خصائص الأزرار
</h3>

<p>
	يمكننا تغيير الكثير من خصائص الأزرار كتغيير لون النص <code>fg</code>، ولون الخلفية <code>bg</code>، ونوع الخط <code>font</code>، ولون الأزرار أثناء النقر عليها <code>activebackground</code>، ولون النص أثناء النقر عليه <code>activeforeground</code>، وسمك الحدود <code>bd</code>، بالإضافة إلى ذلك تسمح الخاصية <code>relief</code> بتعديل نوع الحدود حيث يمكن إعطائها قيم مختلفة مثل :
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_11" style="">
<span class="pln">raised</span><span class="pun">,</span><span class="pln"> flat</span><span class="pun">,</span><span class="pln"> ridge</span><span class="pun">,</span><span class="pln"> groove </span><span class="kwd">and</span><span class="pln"> sunken</span><span class="pun">.</span></pre>

<p>
	كذلك يمكننا التحكم بحالة الزر باستخدام الخاصية <code>state</code> حيث يكون فعالًا <code>normal</code> افتراضيًا ويمكننا تغييره إلى غير فعال <code>disable</code>، ويمكننا عمل ذلك إما أثناء إنشاء الزر أو في خطوة لاحقة مستقلة باستخدام الدالة <code>config</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_13" style="">
<span class="typ">First_button</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">state</span><span class="pun">=</span><span class="str">'disable'</span><span class="pun">)</span></pre>

<p>
	كما يمكننا التعديل على الخصائص باستخدام مبدأ القواميس <code>dictionary</code> حيث نقوم بمعاملة العنصر كقاموس ونستخدم اسم الخاصية المراد وضع قيمة لها كمفتاح للقاموس ونضع القيمة المرغوبة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_9" style="">
<span class="typ">First_button</span><span class="pun">[</span><span class="str">'state'</span><span class="pun">]=</span><span class="str">'disable'</span></pre>

<p>
	سنقوم برسم زر بالخصائص التالية لون النص <code>fg</code> أبيض، ولون الخلفية <code>bg</code> أخضر، وبخط من نوع <code>Verdana</code> بحجم 14 ويكون النص "موافق" تحته خط، بحدود ثخينة <code>bd</code> بمقدار 5 ونوع حدود <code>relief</code> بقيمة <code>groove</code> ونحيطه بمساحة حدود داخلية وخارجية على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_15" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'موافق'</span><span class="pun">,</span><span class="pln">
                    font </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Verdana 14 underline"</span><span class="pun">,</span><span class="pln">
                    fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"green"</span><span class="pun">,</span><span class="pln">
                    bd</span><span class="pun">=</span><span class="lit">5</span><span class="pln"> </span><span class="pun">,</span><span class="pln">relief </span><span class="pun">=</span><span class="str">'groove'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">30</span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تشغيل الشفرة سنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94870" href="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI_colored_button.jpg.30307dd36209b53e35d477a80f84baba.jpg" rel=""><img alt="002_GUI_colored_button.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94870" data-unique="5vzd9reky" src="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI_colored_button.jpg.30307dd36209b53e35d477a80f84baba.jpg" style="width: 300px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بزر ملون
</p>

<h3>
	استجابة الأزرار
</h3>

<p>
	عند النقر على زر موافق في النافذة السابقة لا يحصل شيء، وذلك لأننا لم نقم بتحديد العمل المطلوب تنفيذه عند النقر على الزرار، ونحتاج لضبط ذلك أن نكتب دالة نضع بداخلها الشفرة المراد تشغيلها عند النقر على الزرار ونربطها معه باستخدام الخاصية <code>command</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_12" style="">
<span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"النص"</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> command</span><span class="pun">=</span><span class="pln">function_name</span><span class="pun">)</span></pre>

<p>
	حيث تمثل <code>function_name</code> اسم الدالة التي سنكتبها، وسنعيد كتابة الشفرة السابقة ونضع استجابة للزر بحيث تظهر لنا رسالة ترحيبية عند النقر عليه في حاوية العنوان، لذا سنقوم بتعريف دالة نسميها <code>CallBack</code> ونضع فيها شفرة إظهار رسالة التعريف على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_23" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> callback</span><span class="pun">():</span><span class="pln">
     mylabeltext</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="str">" مرحباً باستجابة الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'موافق'</span><span class="pun">,</span><span class="pln">
                    font </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Verdana 14 underline"</span><span class="pun">,</span><span class="pln">
                    fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"green"</span><span class="pun">,</span><span class="pln">
                    bd</span><span class="pun">=</span><span class="lit">5</span><span class="pln"> </span><span class="pun">,</span><span class="pln">relief </span><span class="pun">=</span><span class="str">'groove'</span><span class="pun">,</span><span class="pln">
                    command</span><span class="pun">=</span><span class="pln">callback</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">30</span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">

mylabeltext</span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
result_Label </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mylabeltext </span><span class="pun">)</span><span class="pln">
result_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على النتيجة التالية عند تشغيل الشفرة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94871" href="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI_functional_button.jpg.0de10c95a4e793a159b467c4ff027296.jpg" rel=""><img alt="003_GUI_functional_button.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94871" data-unique="j3c9x9vxb" src="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI_functional_button.thumb.jpg.f7964bd314d2da2c7e951fbc96ac2d08.jpg" style="width: 650px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بزر يستجيب للنقر
</p>

<h3>
	إدراج صورة داخل زر
</h3>

<p>
	بإمكاننا استبدال النص الذي يُعرض بالزرار بصورة قد تكون أكثر تعبيرًا من الكلمات لفعل ذلك نحتاج إلى تعريف كائن من الصنف <code>PhotoImage</code> وسنقوم بتسميته <code>buttonphoto</code>، باستخدام الخاصية <code>file</code> إذ سنعطيه مسار الصورة (مكان تواجدها في الجهاز) على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_29" style="">
<span class="pln">buttonphoto </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PhotoImage</span><span class="pun">(</span><span class="pln">file</span><span class="pun">=</span><span class="str">'Photo\path'</span><span class="pun">)</span></pre>

<p>
	ومن ثم نستخدم الخاصية <code>image</code> أثناء تعريف الزر ونسند لها قيمة الكائن الذي أنشأناه ستكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8498_31" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> callback</span><span class="pun">():</span><span class="pln">
     mylabeltext</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="str">" مرحباً باستجابة الأزرار "</span><span class="pun">)</span><span class="pln">

buttonphoto </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PhotoImage</span><span class="pun">(</span><span class="pln">file</span><span class="pun">=</span><span class="str">'Arrow.png'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">image</span><span class="pun">=</span><span class="pln">buttonphoto</span><span class="pun">,</span><span class="pln">
                     bd</span><span class="pun">=</span><span class="lit">5</span><span class="pun">,</span><span class="pln">command</span><span class="pun">=</span><span class="pln">callback</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">30</span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">

mylabeltext</span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
result_Label </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mylabeltext </span><span class="pun">)</span><span class="pln">
result_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على النتيجة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94872" href="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI_img_button.jpg.f0e38a19634c452d03f7056aef9ad137.jpg" rel=""><img alt="004_GUI_img_button.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94872" data-unique="w3w7nubsd" src="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI_img_button.jpg.f0e38a19634c452d03f7056aef9ad137.jpg" style="width: 300px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بزرار على شكل صورة
</p>

<p>
	استخدمنا في المثال السابق الصورة المسماة Arrow.png وكانت موجودة في نفس مسار وجود ملف الشفرة، لذلك وضعنا اسمها مباشرة، ولو كانت في مكان مختلف لقمنا بتحديد مكان وجودها، للأسف الكائن <code>PhotoImage</code> يدعم فقط صيغ محدودة وهي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_16" style="">
<span class="pln">GIF</span><span class="pun">,</span><span class="pln"> PGM</span><span class="pun">,</span><span class="pln"> PPM</span><span class="pun">,</span><span class="pln"> PNG</span></pre>

<p>
	ولو أردنا أن نعرض نصًا وصورة في نفس الزرار يجب علينا أن نستعين بالخاصية <code>compound</code> والتي تحدد مكان ظهور الصورة بالنسبة للنص وتأخذ قيمة من الاحتمالات التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_18" style="">
<span class="str">'top'</span><span class="pun">,</span><span class="str">'bottom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'left'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'right'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'center'</span><span class="pun">,</span><span class="str">'none'</span></pre>

<p>
	يعرض السطر التالي الصورة فوق النص:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_20" style="">
<span class="pln">buttonphoto </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PhotoImage</span><span class="pun">(</span><span class="pln">file</span><span class="pun">=</span><span class="str">'Arrow.png'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">image</span><span class="pun">=</span><span class="pln">buttonphoto </span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'موافق'</span><span class="pun">,</span><span class="pln">
        bd</span><span class="pun">=</span><span class="lit">5</span><span class="pln"> </span><span class="pun">,</span><span class="pln">command</span><span class="pun">=</span><span class="pln">callback </span><span class="pun">,</span><span class="pln">compound</span><span class="pun">=</span><span class="str">'top'</span><span class="pun">)</span></pre>

<p>
	وتكون النافذة بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94873" href="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI_img_txt_button.jpg.fd48b9b65e8bfdeea83007ee34eb3e0f.jpg" rel=""><img alt="005_GUI_img_txt_button.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94873" data-unique="ir5pofxb1" src="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI_img_txt_button.jpg.fd48b9b65e8bfdeea83007ee34eb3e0f.jpg" style="width: 300px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بزرار صورة ونص
</p>

<h2>
	مربع الرسائل messagebox
</h2>

<p>
	يحتاج البرنامج عند <a href="https://academy.hsoub.com/learn/front-end-web-development/" rel="">تطوير تطبيقات واجهات المستخدم الرسومية</a> بالغالب لإعلام المستخدم بسير الأحداث، وإخباره برسائل الخطأ أو الرسائل التحذيرية ونحوها، على سبيل المثال عند نقر المستخدم على زر الحفظ، من الجيد إظهار رسالة تبين حالة العملية من نجاح أو فشل. أو عند حدوث خطأ ما وعدم تمكن البرنامج من إتمام مهمة معينة يُفترض إظهار رسالة تبين الخطأ ونوعه للمستخدم، لنتمكن من عمل هذا سنستعين بالوحدة messagebox من مكتبة Tkinter.
</p>

<h3>
	استدعاء مربع الرسائل
</h3>

<p>
	تأتي الوحدة <code>messagebox</code> مدمجةً مع مكتبة Tkinter لذلك لا حاجة لأي تثبيت إضافي له، فقط يضاف أمر استدعاء في بداية الشفرة لنتمكن من استخدامها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_25" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> messagebox</span></pre>

<h3>
	مربعات الرسائل الخبرية
</h3>

<p>
	النوع الأول من أنواع مربعات الرسائل هي الخبرية والتي تظهر رسالة للمستخدم مع زر واحد فقط تغلق النافذة بالضغط عليه. وسنتعرف إلى الدوال التالية التي تظهر رسالة خبرية للمستخدم:
</p>

<ul>
<li>
		<code>showinfo</code>: تظهر مربع رسالة على شكل معلومة خبرية، لإخبار المستخدم بانتهاء تنفيذ أمر معين مثلًا
	</li>
	<li>
		<code>showerror</code>: تظهر مربع رسالة على شكل رسالة خطأ، لإخبار المستخدم بحدوث خطأ أثناء تنفيذ أمر معين مثلًا.
	</li>
	<li>
		<code>showwarrning</code>: تظهر مربع رسالة على شكل رسالة تحذيرية، لإخبار المستخدم بانتهاء تنفيذ أمر معين لكن بوجود أمر لم يتم بالشكل المطلوب مثلًا.
	</li>
</ul>
<p>
	تأخذ جميع هذه الأنواع من المستخدم أمرين: أولًا عنوان مربع الرسالة وثانيًا نص الرسالة
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_27" style="">
<span class="pln">showinfo</span><span class="pun">(</span><span class="pln">title</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln">
showerror</span><span class="pun">(</span><span class="pln">title</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span><span class="pln">
showwarrning</span><span class="pun">(</span><span class="pln">title</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">)</span></pre>

<p>
	عندما نرغب باستخدام واحدة من مربعات الرسائل السابقة نقوم باستدعائها مستخدمين اسم الوحدة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_29" style="">
<span class="pln">messagebox</span><span class="pun">.</span><span class="pln">showerror</span><span class="pun">(</span><span class="pln"> title</span><span class="pun">=</span><span class="str">'Error'</span><span class="pun">,</span><span class="pln"> message</span><span class="pun">=</span><span class="str">'This is an error message.'</span><span class="pun">))</span></pre>

<p>
	يمكننا للكتابة بطريقة مختصرة تعديل سطر الاستدعاء منذ البداية ليكون على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_31" style="">
<span class="kwd">from</span><span class="pln"> tkinter</span><span class="pun">.</span><span class="pln">messagebox </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span></pre>

<p>
	ثم يمكننا استدعاء مربع الرسالة المطلوب باستخدام اسمه مباشرة دون الحاجة لوضع اسم الوحدة قبله.
</p>

<p>
	سنقوم بعمل نافذة تحتوي ثلاث أزرار يقوم كل واحد منها بإظهار نوع من مربعات الرسائل عند النقر عليه، من خلال الشفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_33" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
</span><span class="kwd">from</span><span class="pln"> tkinter</span><span class="pun">.</span><span class="pln">messagebox </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الأزرار "</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> error_message</span><span class="pun">():</span><span class="pln">
     showerror</span><span class="pun">(</span><span class="pln"> title</span><span class="pun">=</span><span class="str">'خطأ'</span><span class="pun">,</span><span class="pln">message</span><span class="pun">=</span><span class="str">'هذه رسالة خطأ'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> info_message</span><span class="pun">():</span><span class="pln">
     showinfo</span><span class="pun">(</span><span class="pln">title</span><span class="pun">=</span><span class="str">'معلومة'</span><span class="pun">,</span><span class="pln">message</span><span class="pun">=</span><span class="str">'هذه رسالة معلومات'</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> warning_message</span><span class="pun">():</span><span class="pln">
     showwarning</span><span class="pun">(</span><span class="pln">title</span><span class="pun">=</span><span class="str">'تحذير'</span><span class="pun">,</span><span class="pln">message</span><span class="pun">=</span><span class="str">'هذه رسالة تحذيرية'</span><span class="pun">)</span><span class="pln">

</span><span class="typ">Error_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'إظهار مربع رسالة خطأ  '</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln">error_message</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Error_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">

</span><span class="typ">Info_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'إظهار مربع رسالة إخبارية'</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln">info_message</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Info_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">

warning_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'إظهار مربع رسالة تحذيرية'</span><span class="pun">,</span><span class="pln">command</span><span class="pun">=</span><span class="pln">warning_message</span><span class="pun">)</span><span class="pln">
warning_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على النافذة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94874" href="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI_window_withbuttons.jpg.dfef2e45f0167d34faf275c860337811.jpg" rel=""><img alt="006_GUI_window_withbuttons.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94874" data-unique="a7mfeb3bn" src="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI_window_withbuttons.jpg.dfef2e45f0167d34faf275c860337811.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بأزرار لعرض مربعات الرسائل
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94875" href="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI_messagebox.jpg.66cb958c8294a073d0abf82041336d0e.jpg" rel=""><img alt="007_GUI_messagebox.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94875" data-unique="pin2p4h03" src="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI_messagebox.thumb.jpg.218d74e1d0cd44e312161dfd3e9b4347.jpg" style="width: 600px; height: auto;"></a>
</p>

<p style="text-align: center;">
	مربعات الرسائل
</p>

<h3>
	مربعات الرسائل الحوارية
</h3>

<p>
	النوع الثاني من أنواع مربعات الرسائل هي الحوارية والتي تُظهر للمستخدم رسالة وتنتظر منه استجابة وتأتي على عدة أشكال:
</p>

<ul>
<li>
		<code>askquestion</code>: تظهر له خيار إما نعم أو لا Yes/No وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة Yes/No.
	</li>
	<li>
		<code>askyesno</code>: تظهر له خيار إما نعم أو لا Yes/No وترجع الدالة قيمة الزرار الذي تم النقر عليه بصيغة True/False.
	</li>
	<li>
		<code>askyesnocancel</code>: تظهر له خيار إما نعم أو لا أو إلغاء Yes/No/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False/None على التوالي.
	</li>
	<li>
		<code>askokcancel</code>: تظهر له خيار إما نعم أو إلغاء OK/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False.
	</li>
	<li>
		<code>askretrycancel</code>: تظهر له خيار إما إعادة المحاولة أو إلغاء retry/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False.
	</li>
</ul>
<h2>
	تطبيق الآلة الحاسبة (الجزء الرابع)
</h2>

<p>
	بعد أن تعرفنا على عنصر الأزرار نستطيع الآن إكمال تطبيقنا ونضعها بداخل الإطار السفلي للآلة، نذكّر بالشكل التالي التخيلي للآلة الذي بدأنا منه فكرة ترتيب العناصر:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94876" href="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI_calculater.jpg.91796f110bccf5c3924330a7e55fbfc0.jpg" rel=""><img alt="008_GUI_calculater.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94876" data-unique="c9uu09r0e" src="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI_calculater.thumb.jpg.5d491dabf841787850c378ce4a2aa781.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة
</p>

<p>
	رسمنا في الدروس السابقة الإطار العلوي وبداخله حاوية العنوان وصندوق المدخلات، وكذلك رسمنا الإطار السفلي ووصلنا إلى شكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94877" href="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI4_calculator3.jpg.a68e7317529c5309ab25866b58afd5b7.jpg" rel=""><img alt="009_GUI4_calculator3.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94877" data-unique="wezwu4y5o" src="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI4_calculator3.thumb.jpg.435f0cf430bcb519043d686b83f2543a.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة الثانية
</p>

<p>
	سنبدأ الآن بوضع الأزرار من الشكل التخيلي نرى أن هناك شكل شبيه بالشبكة بأربعة أعمدة وخمسة صفوف، كما يوجد نوعين من الأزرار واحدة للأرقام، وواحدة للعمليات الحسابية، وتشترك جميع أزرار الأرقام بخصائص متشابهة فهي بطول 3 وعرض 10 ولون خلفية من درجات الأزرق مشابه لما استخدمناه في حاوية العنوان ولون نص أبيض، وأزرار العمليات تشابهها في الطول والعرض وتختلف عنها في لون الخلفية فهو رمادي و لون الخط أسود ويشتركون جميعًا بتغير شكل المؤشر عند المرور عليها ليصبح شكله كاليد.
</p>

<p>
	بدلًا من أن نعيد كتابة الخصائص وتكرارها لكل زرار نستطيع إنشاء قاموس يحتوي على كل خاصية وقيمتها المطلوبة للأزرار المتشابهه ونستخدمه وقت الإنشاء، فنضع قاموسًا لأزرار العمليات سنسميه <code>operators</code> وقاموسًا للأرقام سنسميه <code>numbers</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_43" style="">
<span class="pln">numbers</span><span class="pun">={</span><span class="str">'fg'</span><span class="pun">:</span><span class="str">"white"</span><span class="pun">,</span><span class="str">'width'</span><span class="pun">:</span><span class="lit">10</span><span class="pun">,</span><span class="str">'height'</span><span class="pun">:</span><span class="lit">3</span><span class="pun">,</span><span class="str">'bd'</span><span class="pun">:</span><span class="lit">2</span><span class="pun">,</span><span class="str">'bg'</span><span class="pun">:</span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln">
</span><span class="str">'cursor'</span><span class="pun">:</span><span class="str">"hand2"</span><span class="pun">}</span><span class="pln">
operators</span><span class="pun">={</span><span class="str">'fg'</span><span class="pun">:</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">,</span><span class="str">"width"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="str">"height"</span><span class="pun">:</span><span class="lit">3</span><span class="pun">,</span><span class="str">"bd"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bg"</span><span class="pun">:</span><span class="str">"#eee"</span><span class="pun">,</span><span class="str">"cursor"</span><span class="pun">:</span><span class="str">"hand2"</span><span class="pun">}</span></pre>

<p>
	طبعًا لإنشاء الأزرار، سبق وذكرنا خلال الدرس أننا نحتاج لتكوين كائن من الصنف <code>Button</code> ونحدد خصائصه عند إنشائه، لذا سننشئ على سبيل المثال الزر رقم 7 فتكون الشفرة باستخدام القاموس على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_49" style="">
<span class="pln">seven </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"7"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">)</span></pre>

<p>
	وسنراعي عند وضع الأزرار على الشبكة ترتيب أماكنها، فالزر رقم سبعة هنا سيكون في الصف الثاني الذي يحمل الرقم 1 (لأن الترتيب يبدأ من صفر) والعمود الأول الذي يحمل الرقم 0، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_47" style="">
<span class="pln">seven</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span></pre>

<p>
	وهكذا نقوم بوضع جميع الأزرار على الشبكة، من خلال الشكل نرى الزر الذي يحمل الحرف (C) وهو لمحو شاشة الإدخال والزر الذي يحمل الرقم 0 مختلفين قليلًا في الأبعاد عن بقية الأزرار الأخرى، فالرمز (C) يحجز مساحة ثلاثة أزرار والرقم صفر يحجز مساحة زرين، فنحتاج أن نكتب خصائصهما بشكل كامل بشكل منفصل عن البقية لأن فيهما اختلافًا عنهم في خاصية العرض، على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_51" style="">
<span class="pln">clear </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"C"</span><span class="pun">,</span><span class="pln"> fg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">,</span><span class="pln"> width </span><span class="pun">=</span><span class="pln"> </span><span class="lit">32</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
               bd </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> bg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"#eee"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hand2"</span><span class="pun">(</span><span class="pln"> 
zero </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pun">,</span><span class="pln"> fg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">,</span><span class="pln">
              width </span><span class="pun">=</span><span class="pln"> </span><span class="lit">21</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> bd </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> bg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hand2"</span><span class="pun">)</span></pre>

<p>
	وعند وضعهم على الشبكة نحتاج لاستخدام خاصية <code>columnspan</code> والتي تحدد كم عدد الأعمدة التي سيشغلها هذا الزرار فتكون قيمتها عند الرمز (C) القيمة 3 وقيمتها عند الصفر 2 بالشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_55" style="">
<span class="pln">clear</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> columnspan </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
zero</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> columnspan </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span></pre>

<p>
	يتبقى لنا الآن كتابة شفرات الدوال التي تتعامل مع الأزرار عند النقر عليها، فنحتاج لثلاثة دوال الأولى لتمسح محتوى صندوق الإدخال عند النقر على الرمز (C) وسنعطيها اسم bt_clear والثانية لتضيف محتوى الأزرار (الرقم أو العملية) على مربع الإدخال سنسميها <code>btn_click</code>، ونربط كل زرار بالدالة المناسبة باستخدام الأمر <code>command</code> وسنأخذ مثالًا على الزر رقم 4:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_57" style="">
<span class="pln">four </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"4"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">4</span><span class="pun">))</span></pre>

<p>
	ونكمل على هذا المنوال مع بقية الأزرار.
</p>

<p>
	أما الدالة الثالثة مرتبطة بالنقر على علامة المساواة = سنسميها <code>bt_equal</code> وهي تقوم بتنفيذ العملية الحسابية التي أدخلها المستخدم وتعرض النتيجة في مربع الإدخال، وسنضيف لها جزءًا بسيطًا في حال كانت العملية المدخلة خاطئة، فنستخدم مربعات الرسائل لنُظهر رسالة خطأ عند عدم القدرة على تنفيذ العملية المدخلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_59" style="">
<span class="pln">showerror</span><span class="pun">(</span><span class="pln"> title</span><span class="pun">=</span><span class="str">'خطأ'</span><span class="pun">,</span><span class="pln">message</span><span class="pun">=</span><span class="str">'العملية الحسابية غير صحيحة'</span><span class="pun">)</span></pre>

<p>
	الكود بالكامل سيكون على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3018_61" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
</span><span class="kwd">from</span><span class="pln"> tkinter</span><span class="pun">.</span><span class="pln">messagebox </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">


</span><span class="kwd">from</span><span class="pln"> ctypes </span><span class="kwd">import</span><span class="pln"> windll
windll</span><span class="pun">.</span><span class="pln">shcore</span><span class="pun">.</span><span class="typ">SetProcessDpiAwareness</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln">


mycalculator </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"آلة حاسبة بسيطة"</span><span class="pun">)</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="str">"575x740"</span><span class="pun">)</span><span class="pln">  
mycalculator</span><span class="pun">.</span><span class="pln">resizable</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> 


input_frame </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"المدخلات "</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">550</span><span class="pun">)</span><span class="pln">
input_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln">TOP</span><span class="pun">)</span><span class="pln">

btns_frame </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">" لوحة الأرقام والعمليات "</span><span class="pun">,</span><span class="pln">
                        bg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">550</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
btns_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">


expression </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pln">
input_text </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> bt_clear</span><span class="pun">():</span><span class="pln"> 
    </span><span class="kwd">global</span><span class="pln"> expression 
    expression </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pln"> 
    input_text</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="str">""</span><span class="pun">)</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="pln">item</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">global</span><span class="pln"> expression
    expression </span><span class="pun">=</span><span class="pln"> expression </span><span class="pun">+</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">item</span><span class="pun">)</span><span class="pln">
    input_text</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">)</span><span class="pln">


</span><span class="kwd">def</span><span class="pln"> bt_equal</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
          </span><span class="kwd">global</span><span class="pln"> expression
          result </span><span class="pun">=</span><span class="pln"> str</span><span class="pun">(</span><span class="pln">eval</span><span class="pun">(</span><span class="pln">expression</span><span class="pun">))</span><span class="pln">
          input_text</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="pln">result</span><span class="pun">)</span><span class="pln">
          expression </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pln">
    </span><span class="kwd">except</span><span class="pln"> </span><span class="typ">Exception</span><span class="pln"> </span><span class="pun">:</span><span class="pln">
           showerror</span><span class="pun">(</span><span class="pln"> title</span><span class="pun">=</span><span class="str">'خطأ'</span><span class="pun">,</span><span class="pln">message</span><span class="pun">=</span><span class="str">'العملية الحسابية غير صحيحة'</span><span class="pun">)</span><span class="pln">



input_label</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"أدخل المعادلة الحسابية "</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln">fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
input_label</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
input_field </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">=</span><span class="lit">18</span><span class="pun">,</span><span class="pln">
                    font</span><span class="pun">=(</span><span class="str">'arial'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bold'</span><span class="pun">),</span><span class="pln">textvariable</span><span class="pun">=</span><span class="pln">input_text</span><span class="pun">)</span><span class="pln">
input_field</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">10</span><span class="pun">)</span><span class="pln">

numbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="str">'fg'</span><span class="pun">:</span><span class="str">"white"</span><span class="pun">,</span><span class="str">'width'</span><span class="pun">:</span><span class="lit">10</span><span class="pun">,</span><span class="str">'height'</span><span class="pun">:</span><span class="lit">3</span><span class="pun">,</span><span class="str">'bd'</span><span class="pun">:</span><span class="lit">2</span><span class="pun">,</span><span class="str">'bg'</span><span class="pun">:</span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="str">'cursor'</span><span class="pun">:</span><span class="str">"hand2"</span><span class="pun">}</span><span class="pln">
operators</span><span class="pun">={</span><span class="str">'fg'</span><span class="pun">:</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">,</span><span class="str">"width"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10</span><span class="pun">,</span><span class="str">"height"</span><span class="pun">:</span><span class="lit">3</span><span class="pun">,</span><span class="str">"bd"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bg"</span><span class="pun">:</span><span class="str">"#eee"</span><span class="pun">,</span><span class="str">"cursor"</span><span class="pun">:</span><span class="str">"hand2"</span><span class="pun">}</span><span class="pln">


</span><span class="com">#السطر الأول</span><span class="pln">

clear </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"C"</span><span class="pun">,</span><span class="pln">fg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">,</span><span class="pln"> width </span><span class="pun">=</span><span class="pln"> </span><span class="lit">32</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
               bd </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> bg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"#eee"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hand2"</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln">bt_clear</span><span class="pun">)</span><span class="pln">
clear</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln">columnspan </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">
divide </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">" / "</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">operators</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="str">"/"</span><span class="pun">))</span><span class="pln">
divide</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">

</span><span class="com">###السطر الثاني</span><span class="pln">

seven </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"7"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">7</span><span class="pun">))</span><span class="pln">
seven</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

eight </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"8"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">8</span><span class="pun">))</span><span class="pln">
eight</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">

nine </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"9"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">9</span><span class="pun">))</span><span class="pln">
nine</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,)</span><span class="pln">

multiply </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">" * "</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">operators</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="str">"*"</span><span class="pun">))</span><span class="pln">
multiply</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">

</span><span class="com">#السطر الثالث</span><span class="pln">

four </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"4"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">4</span><span class="pun">))</span><span class="pln">
four</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

five </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"5"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">5</span><span class="pun">))</span><span class="pln">
five</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">

six </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"6"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">6</span><span class="pun">))</span><span class="pln">
six</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">

minus </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"-"</span><span class="pun">,**</span><span class="pln">operators</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="str">"-"</span><span class="pun">))</span><span class="pln">
minus</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">

</span><span class="com">#السطر الرابع</span><span class="pln">
one </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"1"</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln"> command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">1</span><span class="pun">))</span><span class="pln">
one</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln">

two </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"2"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">numbers </span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
two</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">

three </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"3"</span><span class="pun">,</span><span class="pln">  </span><span class="pun">**</span><span class="pln">numbers </span><span class="pun">,</span><span class="pln"> command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">3</span><span class="pun">))</span><span class="pln">
three</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">

plus </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"+"</span><span class="pun">,**</span><span class="pln">operators</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="str">"+"</span><span class="pun">))</span><span class="pln">
plus</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">

</span><span class="com">#السطر الخامس</span><span class="pln">
zero </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pun">,</span><span class="pln"> fg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">,</span><span class="pln">
              width </span><span class="pun">=</span><span class="pln"> </span><span class="lit">21</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> bd </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> bg </span><span class="pun">=</span><span class="pln"> </span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="pln"> </span><span class="str">"hand2"</span><span class="pun">,</span><span class="pln">
              command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="lit">0</span><span class="pun">))</span><span class="pln">
zero</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> columnspan </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">

point </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"."</span><span class="pun">,**</span><span class="pln">numbers</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> btn_click</span><span class="pun">(</span><span class="str">"."</span><span class="pun">))</span><span class="pln">
point</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">

equals </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Button</span><span class="pun">(</span><span class="pln">btns_frame</span><span class="pun">,</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> </span><span class="str">"="</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">operators</span><span class="pun">,</span><span class="pln">command </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">lambda</span><span class="pun">:</span><span class="pln"> bt_equal</span><span class="pun">())</span><span class="pln">
equals</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln">

mycalculator</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وبهذا نكون شبه أكملنا تطبيق الآلة، وسنضيف لها قائمة رئيسية لعرض معلومات عنها، وهذا ما سنتعرف عليه في <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">الدرس القادم</a> من هذه السلسلة.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://www.tutorialspoint.com/python/tk_button.htm" rel="external nofollow">Python - Tkinter Button</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-button/" rel="external nofollow">Tkinter Button</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-messagebox/" rel="external nofollow">Tkinter messagebox</a>.
	</li>
	<li>
		<a href="https://coderslegacy.com/python/tkinter-messagebox/" rel="external nofollow">Python Tkinter messagebox – Displaying Prompts</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-askyesno/" rel="external nofollow">Tkinter askyesno</a>.
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_grid.htm" rel="external nofollow">Python - Tkinter grid() Method</a>.
	</li>
</ul>
<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>
	</li>
	<li>
		النسخة الكاملة من كتاب<a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel=""> البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1510</guid><pubDate>Thu, 07 Apr 2022 15:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x648;&#x627;&#x635;&#x644; &#x645;&#x639; &#x646;&#x638;&#x627;&#x645; &#x627;&#x644;&#x62A;&#x634;&#x63A;&#x64A;&#x644; &#x639;&#x628;&#x631; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D9%85%D8%B9-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%B9%D8%A8%D8%B1-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1520/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_04/62480f712185a_--.png.b0735ad168f5fed0c8b95b5a1521ef34.png" /></p>
<p>
	سننظر في هذا المقال من سلسلة <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B9%D9%84%D9%85%20%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">تعلم البرمجة</a> في دور نظام التشغيل وكيفية الوصول إليه من بايثون، وسنشرح فيه:
</p>

<ul>
	<li>
		دور نظام التشغيل ووظيفته.
	</li>
	<li>
		وصول <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> إلى نظام التشغيل.
	</li>
	<li>
		العمل مع الملفات والمجلدات.
	</li>
	<li>
		التعامل مع العمليات.
	</li>
	<li>
		معرفة البيانات الخاصة بالمستخدمين.
	</li>
</ul>

<h2>
	التعرف على نظام التشغيل
</h2>

<p>
	يدرك أغلب مستخدمي الحواسيب أن حواسيبهم تعمل بنظم تشغيل، سواء كانت ويندوز أو <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%A7-%D9%87%D9%88-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3%D8%9F-r451/" rel="">لينكس</a> أو ماك أو غير ذلك، لكنهم قد يجهلون الدور الذي تقوم به تلك النظم على وجه الدقة، خاصةً أن أغلب نظم التشغيل التجارية تأتي مع برامج كثيرة ليست جزءًا من نظام التشغيل، مثل برامج عرض الصور، وتصفح الويب، ومحررات النصوص، لكن الجهة المصدرة للنظام تحزمها معه لأن المستخدم لن يستطيع الاستفادة من نظام التشغيل وحده.
</p>

<h3>
	مبدأ طبقات الكعكة
</h3>

<p>
	إذا أردنا معرفة حقيقة نظام التشغيل فيجب أن ننظر أولًا في الطريقة التي بُنيت بها الحواسيب، والتي يمكن النظر إليها على أنها كعكة متعددة الطبقات multi-layer cake، حيث يكون عتاد الحاسوب والقطع الإلكترونية المختلفة فيه الطبقة الدنيا، بما في ذلك وحدة المعالجة المركزية CPU، والقرص الصلب، والذاكرة، ونظام الإدخال والإخراج الذي يحوي مداخل USB، وفتحات بطاقات الذاكرة، ووصلات الشبكة وغيرها.
</p>

<p>
	أما الطبقة التي فوقها فهي BIOS، أو نظام الإدخال والإخراج البسيط Basic Input Output System، وهي أول طبقة برمجية في تكوين الحاسوب، والمسؤولة عن إقلاع الحاسوب وتوفير أبسط واجهة ممكنة للعتاد، إذ تسمح بنقل رؤوس القرص الصلب من مسار لآخر، ومن قطاع لآخر داخل المسار الواحد، وبقراءة أو كتابة البايتات المنفردة في المخازن المؤقتة للبيانات data buffers المتصلة بكل منفذ، ومع هذا لا تعرف BIOS نفسها أي معلومات عن الملفات أو المجلدات أو أي من المفاهيم العليا الأخرى التي نتعامل معها نحن المستخدمون، وإنما تعرف كيفية التعامل مع الأجهزة الإلكترونية البسيطة التي يتكون منها الحاسوب، بل قد لا تتعامل معها مباشرةً، وإنما من خلال برمجيات يثبتها مزود العتاد نفسه في أماكن حساسة داخل BIOS، وهذا شائع في بطاقات الرسوم graphics cards على وجه الخصوص، إذ تثبت الشركات المصنعة لتلك البطاقات روابط إلى تعريفات الرسوم الخاصة بها في مكان معياري داخل شيفرة BIOS، لتستدعي BIOS واجهةً متفقًا عليها، مع توفير الشركة لبرنامجها الخاص بها، وتُخزَّن BIOS عادةً في نوع خاص من رقاقات الذاكرة ذات طبيعة شبه دائمة، أي أنها لا تحذَف عند قطع التيار الكهربي، لكن يمكن إعادة كتابتها عند الحاجة لتحديث BIOS.
</p>

<p>
	أما الطبقة التالية لطبقة BIOS فهي طبقة نظام التشغيل، ويختلف هيكل هذه الطبقة كثيرًا وفقًا لكل نظام تشغيل، غير أنها تتكون في الغالب من نواة kernel، أو مجموعة أساسية من الخدمات مع تعريفات للعتاد device drivers، وقد توضع تعريفات العتاد داخل النواة، أو تكون وحدات تحمّلها النواة عند الحاجة، وهذا شبيه بتحميل البرامج لوحدات بايثون عند الحاجة، أما وظيفة هذه الطبقة فهي الترجمة من العتاد منخفض المستوى إلى البُنى المنطقية التي نستطيع فهمها واستخدامها، مثل الملفات والمجلدات، ومن المهم هنا ملاحظة أن نفس العتاد والـ BIOS يستطيعان تشغيل عدة نظم تشغيل مختلفة معًا، فمن السهل إعداد النظام والقرص الصلب بحيث يمكن تثبيت عدة نظم تشغيل على نفس الحاسوب، ويختار المستخدم بينها عند الإقلاع.
</p>

<p>
	ننتقل إلى الطبقة قبل الأخيرة في هذه الكعكة، وهي <a href="https://academy.hsoub.com/devops/linux/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%AF%D9%81%D8%A9-shell-scripts-r252/" rel="">الصدفة Shell</a> أو بيئة المستخدم، وهي واجهة رسومية في أغلب أنظمة التشغيل الحديثة، رغم توفر واجهة سطرية لها بشكل أو بآخر.
</p>

<p>
	أما الطبقة الأخيرة، فهي طبقة برمجيات المستخدم، التي تتكون من حزمة من البرامج التي يثبتها المستخدم أو تأتي مثبتةً مع النظام، وفيها متصفحات الويب، وبرامج البريد، ومعالجات النصوص وغيرها، كما تحتوي على أدوات برمجية مثل بايثون.
</p>

<p>
	لندرس مثالًا نفهم من خلاله التفاعل بين هذه الطبقات، حيث سنرى ما الذي يحدث عند فتح ملف في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a> وقراءته.
</p>

<ul>
	<li>
		نستخدم صدفة نظام التشغيل لبدء بايثون، وتحميل ملف سكربت أيضًا.
	</li>
	<li>
		تستدعي شيفرة بايثون الدالة <code>open()‎</code> الخاصة ببايثون.
	</li>
	<li>
		تستدعي بايثون داخل هذه الدالة دالة نظام تشغيل لفتح نفس الملف.
	</li>
	<li>
		يبحث نظام التشغيل عن بعض البيانات الداخلية، ويترجم اسم الملف إلى مجموعة من المسارات والقطاعات على القرص الصلب. -ويكون قد عرف أي قرص يستخدم بداهةً!-، ويحدد ذلك الملف على أنه ملف مفتوح، ويمنع تعديلات المستخدمين الآخرين عليه إن دعت الحاجة إلى ذلك.
	</li>
	<li>
		يستدعي برنامج بايثون <code>file.read()‎</code>.
	</li>
	<li>
		تستدعي بايثون دالة القراءة الخاصة بنظام التشغيل.
	</li>
	<li>
		يستدعي نظام التشغيل عدة دوال من BIOS لوضع رؤوس القراءة في القرص الصلب عند المواضع المناسبة.
	</li>
	<li>
		يخبر نظام التشغيل BIOS أن تقرأ أعداد البايتات المناسبة من ذلك الموضع.
	</li>
	<li>
		يكرر نظام التشغيل عملية الموضعة والقراءة تلك حتى الانتهاء من قراءة جميع البيانات المطلوبة للملف.
	</li>
	<li>
		تعيد دالة النظام البيانات إلى بايثون.
	</li>
	<li>
		تعالج بايثون البيانات، وتعرض النتائج للمستخدم من خلال دوال أخرى لنظام التشغيل والبيوس BIOS.
	</li>
</ul>

<p>
	قد يبدو هذا معقدًا، ولن نقول إنه ليس كذلك، لكن هذا التعقيد نفسه هو سبب وجود نظم التشغيل، لتوفر على المستخدمين والمبرمجين تلك المهام، فما علينا نحن المبرمجون إلا استدعاء <code>open()‎</code> و<code>read()‎</code>.
</p>

<h3>
	التحكم في العمليات
</h3>

<p>
	يوفر نظام التشغيل إمكانية بدء البرامج وتشغيلها، إضافةً إلى ما ذكرناه سابقًا من التحكم في الوصول إلى العتاد، كما يوفر الآليات اللازمة لإدارة تلك البرامج التي تعمل بالتوازي -أي في نفس الوقت– على الحاسوب، لأن عدد المعالجات الموجود على اللوحة الأم أقل بكثير من البرامج التي ستعمل على نظام التشغيل، لذا تنفذ نظم التشغيل عمليةً تسمى التقسيم الزمني time slicing، وهي إعطاء كل برنامج من البرامج العاملة حاليًا على الحاسوب حصةً صغيرةً من المعالج؛ ولفترة قصيرة من الزمن، قبل أن تنقل تلك الحصة بسرعة إلى عملية أخرى أو برنامج آخر، فتعطي ذلك الإحساس بأن جميع البرامج تعمل في نفس الوقت، وتختلف كفاءة نظم التشغيل في تنفيذ هذه العملية، فلم تكن الإصدارات الأولى من ويندوز وماك MacOS قادرةً على إجرائها وإدارتها إلا بمساعدة البرامج نفسها، فإذا فشل أحد البرامج في توفير نقطة توقف مناسبة فإن الحاسوب كله يتوقف عن الاستجابة، فيما يعرف بالتعليق Hanging.
</p>

<p>
	وتستخدم أغلب نظم التشغيل الحديثة نظامًا يسمى تعدد المهام الوقائي pre-emptive multi-tasking، حيث يقاطع نظام التشغيل البرامج بغض النظر عما تفعله تلك البرامج عند المقاطعة، ويعطي البرنامج التالي الوصول إلى المعالج تلقائيًا، وتُستخدم عدة خوارزميات هنا لتحسين كفاءة تلك العملية وفقًا لنظام التشغيل نفسه، مثل التبديل الحلَقي round robin، أو الأحدث استخدامًا most recently used، أو الأقدم استخدامًا least recently used، ولا تهمنا هذه <a href="https://academy.hsoub.com/programming/advanced/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%88%D8%A7%D8%B1%D8%B2%D9%85%D9%8A%D8%A7%D8%AA-r1282/" rel="">الخوارزميات</a> نحن المبرمجون، ولا بأس إن افترضنا أن تلك البرامج المتعددة تعمل بالتوازي.
</p>

<p>
	تأتي أغلب الحواسيب الحديثة بمعالجات متعددة قد تكون رقاقات متعددةً من السليكون، أو عدة نوى -عدة معالجات منفردة- على رقاقة واحدة، ووظيفة نظام التشغيل هنا أن يخصص العمليات الحالية إلى المعالجات والأنوية ليضمن الاستخدام الأمثل، ونعيد التذكير هنا أن ذلك لا يهمنا، ونترك هذه المهمة <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">لنظام التشغيل</a> نفسه.
</p>

<h3>
	صلاحيات المستخدمين والأمان
</h3>

<p>
	تسمح أغلب نظم التشغيل الحديثة بوجود عدة مستخدمين على نفس الحاسوب، لكل واحد منهم ملفاته الخاصة وإعدادات سطح مكتبه وغير ذلك، وتزيد بعض نظم التشغيل على ذلك بأن تسمح بولوج عدة مستخدمين إلى نفس النظام في نفس الوقت، فيما يعرف باسم الجلسات المتعددة multi-session، لكن تعدد المستخدمين هذا تأتي معه مشكلة الأمان، فمن المهم ألا يستطيع سعيد رؤية بيانات محمد، وكذلك العكس، إلا إذا أعطى محمد صلاحية الوصول إلى ملفاته إلى سعيد، ويكون نظام التشغيل مسؤولًا عن ضمان حماية ملفات كل مستخدم، عن طريق منع الوصول إليها إلا من الأشخاص المصرح لهم فقط.
</p>

<h2>
	استخدام نظام التشغيل في البرامج
</h2>

<p>
	قد نتساءل عن جدوى هذا الحديث عن أنظمة التشغيل لنا نحن المبرمجون، بما أن وظيفة ذلك النظام أن يجنبنا كل تلك التفاصيل التي ذكرناها، من التعامل مع العتاد وتنظيم عمليات تشغيل البرامج.
</p>

<p>
	والإجابة على ذلك أننا قد نحتاج أحيانًا إلى التعامل مع ذلك العتاد، ولا تستطيع الدوال البرمجية المعتادة تنفيذ ما نريده، أو ربما نحتاج إلى تشغيل برنامج آخر من داخل برنامجنا مثلًا، بل ربما نحتاج -من داخل برنامجنا- إلى التحكم في الحاسوب بنفس الطريقة التي يتحكم بها المستخدم فيه، وحل ذلك كله أن يكون لنا صلاحية الوصول إلى طبقة نظام التشغيل نفسه.
</p>

<p>
	وتوفر <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r211/" rel="">بايثون</a> عددًا من الوحدات التي يمكن استخدامها للتفاعل مع نظام التشغيل، لعل أهمها وحدة <code>os</code>، التي توفر واجهةً مشتركةً لأي نظام تشغيل، من خلال تحميل وحدات منخفضة المستوى lower level، وستتصرف أنظمة التشغيل تصرفًا مختلفًا وفقًا للطريقة التي تنفذ بها تلك الدوال داخليًا، ولا ينتج عن هذا السلوك مشاكل غالبًا، لكن إذا واجهنا سلوكًا غريبًا من وحدة <code>os</code> فنرجع إلى التوثيق لنرى إن كان ثمة قيود على نظام التشغيل الخاص الذي نستخدمه.
</p>

<p>
	أما وحدات النظام الأخرى التي سندرسها فهي الوحدة <code>shutil</code> التي توفر للمبرمجين تحكمًا في الملفات والمجلدات على مستوى المستخدم، والوحدتان <code>os.path</code> و<code>glob</code> اللتان توفران أدوات للتنقل في نظام ملفات الحاسوب، وهذا هو الجزء الذي سننظر فيه أولًا.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			انتبه: ذكرنا أن نظم التشغيل مختلفة عن بعضها، لذا فإن الأمثلة التي سنذكرها أدناه لن تعمل على ويندوز في الغالب، وستحتاج إلى البحث عن أوامر CMD المكافئة لها وأماكنها، واستبدالها بأوامر يونكس التي سنكتبها هنا، أما مستخدمو <a href="https://academy.hsoub.com/devops/linux/%d8%a3%d9%84%d9%81-%d8%a8%d8%a7%d8%a1-%d8%a3%d8%b3%d8%a7%d8%b3%d9%8a%d8%a7%d8%aa-%d8%a7%d9%84%d8%aa%d8%b9%d8%a7%d9%85%d9%84-%d9%85%d8%b9-%d9%84%d9%8a%d9%86%d9%83%d8%b3-r61/" rel="">لينكس</a> أو MacOS X فلن تكون لديهم تلك المشكلة لأن كلا النظامين يونكس، مادامت البرامج المطلوبة موجودةً ومثبتةً، لأن بعض أنظمة يونكس لا تأتي بتلك البرامج افتراضيًا.
		</p>
	</div>
</blockquote>

<h2>
	معالجة الملفات
</h2>

<p>
	شرحنا كيفية التعامل مع الملفات في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات في البرمجة</a> فما الذي نريده من نظام التشغيل طالما نستطيع التعامل معها؟
</p>

<p>
	والجواب أن ما ذكرناه في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات في البرمجة</a> هو أننا نستطيع إنشاء الملفات وتعديلها، لكننا لم نذكر حذفها لأننا لم نكن نستطيع ذلك بتوابع الملفات العادية، أما هنا فنستطيع استخدام نظام التشغيل في حذف الملفات، كما أن <code>open()‎</code> ستكون كافيةً إذا علمنا مكان الملف، لكن كيف نجده إذا لم نعرف مكانه، بل ماذا لو أردنا معالجة مجموعات من الملفات؟ مثل ملفات الصور في مجلد ما، وكذلك التحكم الدقيق في القراءة من الملفات، حيث كانت التوابع القياسية التي شرحناها من قبل تقرأ سطرًا واحدًا فقط أو الملف كله، فماذا لو أردنا قراءة بضعة بايتات فقط؟ إن دوال نظام التشغيل هي الحل لتلك الحالات كلها.
</p>

<h3>
	إيجاد الملفات
</h3>

<p>
	أول وحدة سندرسها هي الوحدة <code>glob</code> التي نستخدمها للعثور على الملفات، والتي تجلب قوائم بأسماء الملفات، وتعود تسميتها إلى نظام يونكس، حيث استُخدم المصطلح لوصف عملية تحديد مجموعات من الملفات باستخدام محارف بدل wildcards، أما الاسم نفسه فيعود تاريخه إلى أمر قديم في نظم التشغيل استُخدم لإجراء عملية التوسع، وهو اختصار global، والوحدة سهلة الاستخدام، فبعد استيرادها نجد دالةً وحيدةً هي <code>glob()‎</code>، ونمرر إليها نمطًا pattern لتطابقه، فتعيد قائمةً بأسماء الملفات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_21" style=""><span class="kwd">import</span><span class="pln"> glob
files </span><span class="pun">=</span><span class="pln"> glob</span><span class="pun">.</span><span class="pln">glob</span><span class="pun">(</span><span class="str">"*.txt"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> files </span><span class="pun">)</span></pre>

<p>
	سنحصل على قائمة من الملفات النصية في المجلد الحالي، وهذا يطرح سؤالًا حول كيفية معرفة المجلد الحالي الذي نحن فيه، وهل نستطيع تغييره أم لا؟ ولا شك أننا نستطيع معرفة المجلد وتغييره باستخدام وحدة <code>os</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_23" style=""><span class="kwd">import</span><span class="pln"> os
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getcwd</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln">  </span><span class="com">#cwd=current working directory</span><span class="pln">
os</span><span class="pun">.</span><span class="pln">chdir</span><span class="pun">(</span><span class="str">"/usr/local"</span><span class="pun">)</span><span class="pln"> </span><span class="com">#chdir=change working directory</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getcwd</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="com">#عرض المجلدات الجديدة</span></pre>

<p>
	تعلمنا البحث عن ملف في المجلد الحالي، وكيف ننتقل من ذلك المجلد إلى أي مجلد نريد، لكن لا زال البحث عن ملف ما عمليةً متعبةً، ولتسهيلها سنستخدم الدالة <code>os.walk()‎</code>، فللعثور على ملف في مكان ما من نقطة بداية نستخدام <code>os.walk</code>، كما سننشئ دالة <code>findfile</code> التي نستطيع استخدامها في برامجنا، لكن سننشئ أولًا بيئة اختبار تتكون من هرمية مجلدات تحت مجلد الجذر <code>Root</code>، وسنضع بعض الملفات في كل مجلد، وسيكون الملف الذي نبحث عنه في أحدها، وقد سميناه traget.txt، ويمكن رؤية هذه الهرمية في لقطة الشاشة التالية:
</p>

<p style="text-align: center;">
	<img alt="tree.png" class="ipsImage ipsImage_thumbnailed" data-fileid="97641" data-unique="66eba96xd" style="" src="https://academy.hsoub.com/uploads/monthly_2022_05/tree.png.6941b5e335a3681da93a332926ab2ef7.png">
</p>

<p>
	تأخذ دالة <code>os.walk</code> معاملاً هو نقطة البداية، وتعيد مولدًا generator هو أشبه بقائمة وهمية تبني نفسها عند الحاجة، ويتكون من صفوف tuples فيها ثلاثة عناصر، تسمى أحيانًا بالثلاثي triplet أو 3-tuple، وهذه العناصر هي: الجذر، قائمة المجلدات في الجذر الحالي، قائمة الملفات الحالية في مجلد الجذر.
</p>

<p>
	فإذا نظرنا إلى الهرمية التي أنشأناها، فسنتوقع أن يبدو صف tuple الأول كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_25" style=""><span class="pun">(</span><span class="pln"> </span><span class="str">'Root'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'D1'</span><span class="pun">,</span><span class="str">'D2'</span><span class="pun">,</span><span class="str">'D3'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="str">'FB.txt'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	نتحقق بسهولة من ذلك بكتابة حلقة <code>for</code> في المحث التفاعلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_27" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> t </span><span class="kwd">in</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">walk</span><span class="pun">(</span><span class="str">'Root'</span><span class="pun">):</span><span class="pln"> 
</span><span class="pun">...</span><span class="pln">    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> t </span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FB.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root/D1'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'D1-1'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FC.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root/D1/D1-1'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FF.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root/D2'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FD.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root/D3'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'D3-1'</span><span class="pun">],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'FE.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">(</span><span class="str">'Root/D3/D3-1'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[],</span><span class="pln"> </span><span class="pun">[</span><span class="str">'target.txt'</span><span class="pun">])</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span></pre>

<p>
	يوضح هذا المسارَ الذي أخذته <code>os.walk</code>، كما يوضح كيف يمكن العثور على ملف، وإنشاء مساره الكامل بالبحث في عنصر <code>files</code> من الصفوف التي تعيدها <code>os.walk</code>، ودمج الاسم بمجرد العثور عليه مع قيمة <code>root</code> للصف الذي يحويه.
</p>

<p>
	وإذا كتبنا دالتنا بحيث تستخدم التعابير النمطية وتعيد قائمةً، فنستطيع إنشاء دالة تكون أقوى من <code>glob.glob</code> البسيطة التي رأيناها من قبل، لكنها ستكون أبطأ أيضًا، لنجربها وننظر كيف تبدو:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_29" style=""><span class="com"># على دالة واحدة findfile.py تحتوي وحدة </span><span class="pln">
</span><span class="com"># os.walk() المبنية على استخدام دالة ،find_file() هي دالة </span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> os</span><span class="pun">,</span><span class="pln">re

</span><span class="kwd">def</span><span class="pln"> find_file</span><span class="pun">(</span><span class="pln">filepattern</span><span class="pun">,</span><span class="pln"> base </span><span class="pun">=</span><span class="pln"> </span><span class="str">'.'</span><span class="pun">):</span><span class="pln">
    regex </span><span class="pun">=</span><span class="pln"> re</span><span class="pun">.</span><span class="pln">compile</span><span class="pun">(</span><span class="pln">filepattern</span><span class="pun">)</span><span class="pln">
    matches </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> root</span><span class="pun">,</span><span class="pln">dirs</span><span class="pun">,</span><span class="pln">files </span><span class="kwd">in</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">walk</span><span class="pun">(</span><span class="pln">base</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">for</span><span class="pln"> f </span><span class="kwd">in</span><span class="pln"> files</span><span class="pun">:</span><span class="pln">
          </span><span class="kwd">if</span><span class="pln"> regex</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">f</span><span class="pun">):</span><span class="pln">
             matches</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">root </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> f</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> matches </span></pre>

<p>
	احفظ ذلك في ملف اسمه findfile.py، ثم لنختبر المحث التفاعلي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_31" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> findfile
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> findfile</span><span class="pun">.</span><span class="pln">find_file</span><span class="pun">(</span><span class="str">'t.*'</span><span class="pun">,</span><span class="str">'Root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'Root/D3/D3-1/target.txt'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> findfile</span><span class="pun">.</span><span class="pln">find_file</span><span class="pun">(</span><span class="str">'F.*'</span><span class="pun">,</span><span class="str">'Root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'Root/FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/FB.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D1/FC.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D1/D1-1/FF.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D2
/FD.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D3/FE.txt'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> findfile</span><span class="pun">.</span><span class="pln">find_file</span><span class="pun">(</span><span class="str">'.*\.txt'</span><span class="pun">,</span><span class="str">'Root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'Root/FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/FB.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D1/FC.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D1/D1-1/FF.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D2
/FD.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D3/FE.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Root/D3/D3-1/target.txt'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> findfile</span><span class="pun">.</span><span class="pln">find_file</span><span class="pun">(</span><span class="str">'D.*'</span><span class="pun">,</span><span class="str">'Root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[]</span></pre>

<p>
	لاحظ أن الوسيط الأول في <code>find_file()‎</code> هو <code>‎'t.*'‎</code>، وتذكر أنه تعبير نمطي وليس محرف بدل لاسم ملف؛ مثل الذي استخدمناه مع <code>glob</code>، لذا فإنه يشير إلى <code>t</code> متبوعةً بعدد من المحارف يساوي صفرًا أو أكثر، وليس ملفًا فقط مثل t.txt.
</p>

<p>
	يظهِر الخرج أن برنامجنا يعمل، ونلاحظ في المثال الأخير أنه يعمل فقط في حالة الملفات لأن أسماء المجلدات تكون في قوائم <code>dirs</code> التي لا نستطيع التحقق منها. جرب إضافة دالة جديدة إلى وحدة <code>findfile</code> سمِّها <code>find_dir()‎</code>، للبحث عن المجلدات التي تطابق تعبيرًا نمطيًا ما، ثم ادمجهما معًا لإنشاء دالة ثالثة هي <code>find_all()‎</code> تبحث في كل من الملفات والمجلدات.
</p>

<h3>
	نقل الملفات ونسخها وحذفها
</h3>

<p>
	تحدثنا في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات</a> عن كيفية نسخ ملف من خلال قراءته ثم كتابته إلى موقع جديد، إلا أنه يمكن استخدام نظام التشغيل لينفذ هذا العمل بدلًا منا بتعليمة واحدة، ونستخدم في بايثون وحدة <code>shutil</code> لمثل هذه المهام، وهي تحتوي على عدة دوال مفيدة، سندرس منها ما يلي:
</p>

<h4>
	الدالة copy(src, dst)‎
</h4>

<p>
	تنسخ هذه الدالة الملف المصدر <code>src</code> إلى الملف أو المجلد الوجهة <code>dst</code>، فإذا كانت الوجهة مجلدًا فسيُنشأ ملف له نفس اسم الملف المصدر، أو يكتب فوقه overwritten في المجلد المحدد، وتُنسخ بتات الصلاحية permission bits كذلك، وتعطى أسماء المصدر <code>src</code> والوجهة <code>dst</code> في صورة سلاسل نصية.
</p>

<h4>
	الدالة move(src, dst)‎
</h4>

<p>
	تنقل هذه الدالة الملف أو المجلد تعاوديًا recursively إلى موقع جديد، فإذا كانت الوجهة على نظام الملفات الحالي فستعاد تسمية الملف المصدر <code>src</code>، وإلا فستنسخ الدالة الملف <code>src</code> إلى الوجهة <code>dst</code>، ثم تحذف المصدر <code>src</code>.
</p>

<p>
	كما سندرس أيضًا الدوال التالية من وحدة <code>os</code>:
</p>

<h4>
	الدالة remove(path)‎
</h4>

<p>
	تحذف هذه الدالة مسار الملف، فإذا كان المسار <code>path</code> مجلدًا فيُرفع <code>OSError</code>، ولحذف المجلد انظر في <code>rmdir()‎</code>.
</p>

<h4>
	rename(src, dst)‎
</h4>

<p>
	تعيد هذه الدالة تسمية الملف أو المجلد المصدر <code>src</code> ليكون اسم الملف الوجهة <code>dst</code>، فإذا كانت الوجهة مجلدًا <code>dst</code> فسيُرفع خطأ <code>OSError</code>.
</p>

<p>
	وأبسط طريقة لنرى بها هذه الدوال عمليًا هي تجربتها في المحث التفاعلي باستخدام بنية المجلد/الملف التي أنشأناها لمثال <code>os.walk</code> السابق:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_33" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> os
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> shutil </span><span class="kwd">as</span><span class="pln"> sh
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> glob </span><span class="kwd">as</span><span class="pln"> g
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">chdir</span><span class="pun">(</span><span class="str">'Root'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FB.txt'</span><span class="pun">]</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sh</span><span class="pun">.</span><span class="pln">copy</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'CA.txt'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'CA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FB.txt'</span><span class="pun">]</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> sh</span><span class="pun">.</span><span class="pln">move</span><span class="pun">(</span><span class="str">'FB.txt'</span><span class="pun">,</span><span class="str">'CB.txt'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'CA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'CB.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FA.txt'</span><span class="pun">]</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'CA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'CB.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">]</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> f </span><span class="kwd">in</span><span class="pln"> g</span><span class="pun">.</span><span class="pln">glob</span><span class="pun">(</span><span class="str">'*.txt'</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">    newname </span><span class="pun">=</span><span class="pln"> f</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">'C'</span><span class="pun">,</span><span class="str">'F'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">    os</span><span class="pun">.</span><span class="pln">rename</span><span class="pun">(</span><span class="pln">f</span><span class="pun">,</span><span class="pln">newname</span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'D1'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D2'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'D3'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'FB.txt'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span></pre>

<p>
	في الأمثلة السابقة نقلنا الملفات ونسخناها وحذفنا الملف الأصلي، ثم أعدنا التسمية لاستعادة المجلد إلى حالته الأولى، ويستطيع المستخدم تنفيذ كل تلك عمليات في سطر الأوامر أو في مدير الملفات، لكننا نفذناها هنا باستخدام بايثون.
</p>

<p>
	لاحظ استخدام حلقة <code>for</code> لتنفيذ التغييرات المتعددة، وقد كان باستطاعتنا إضافة جميع أشكال التحققات والقواعد داخل الحلقة، للسماح بإنشاء بعض الأدوات القوية للتعديل في الملفات، وبكتابة تلك الشيفرة في سكربت يمكننا تنفيذ تلك التغييرات بالقدر الذي نشاء بمجرد تشغيل السكربت.
</p>

<h3>
	اختبار خصائص الملفات
</h3>

<p>
	نحتاج عند التعامل مع الملفات إلى معرفة خصائصها، فمثلًا عندما نقرأ قائمة مجلد من <code>glob</code>، سنرغب في معرفة هل ما نريده ملف أم مجلد، وكذلك معرفة آخر مرة عدِّل عليه فيها، أو مراقبة ذلك لنرى إن كان يُعدَّل بانتظام، مما يدل على أن مستخدمًا غيرنا -أو حتى برنامجًا آخر- له وصول إلى الملف، أو ربما نريد مراقبة حجم الملف، لنرى إن كان يزيد مثلًا، ونستطيع فعل كل تلك الأمور من برامجنا باستغلال مزايا نظام التشغيل، ولكن يجب أن نرى أولًا كيف نتحقق من نوع الملف الذي نتعامل معه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_35" style=""><span class="kwd">import</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path </span><span class="kwd">as</span><span class="pln"> p
</span><span class="kwd">import</span><span class="pln"> glob
</span><span class="kwd">for</span><span class="pln"> item </span><span class="kwd">in</span><span class="pln"> glob</span><span class="pun">.</span><span class="pln">glob</span><span class="pun">(</span><span class="str">'*'</span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">if</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">isfile</span><span class="pun">(</span><span class="pln">item</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> item</span><span class="pun">,</span><span class="pln"> </span><span class="str">' is a file'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">elif</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">isdir</span><span class="pun">(</span><span class="pln">item</span><span class="pun">):</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> item</span><span class="pun">,</span><span class="pln"> </span><span class="str">' is a directory'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
   </span><span class="kwd">else</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> item</span><span class="pun">,</span><span class="pln"> </span><span class="str">' is of unknown type'</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	لاحظ أن دوال الاختبار توجد في وحدة <a href="http://docs.python.org/lib/module-os.path.html" rel="external nofollow"><code>os.path</code></a>، وأن هناك عدة اختبارات أخرى متاحة، ويمكن الرجوع إليها والقراءة عنها في توثيق وحدة <code>os.path</code>.
</p>

<p>
	سننظر الآن في ميزة عمر الملف، إذ توجد عدة تواريخ مهمة في حياة كل الملف، أولها تاريخ إنشائه، ثم تاريخ آخر تعديل عليه، وأخيرًا تاريخ آخر وصول إليه، وقد لا توفر بعض أنظمة التشغيل جميع تلك التواريخ، لكن أغلبها توفر تواريخ الإنشاء والتعديل، ويمكن الوصول إليهما في بايثون من خلال وحدة <code>os.path</code> باستخدام الدالتين <code>ctime()‎</code> و<code>mtime()‎</code> على الترتيب، وسننظر في بعض الملفات الموجودة في بنية <code>Root</code> لدينا، وقد أنشئت جميعها بنفس الوقت تقريبًا، مع اختلاف طفيف لملفات المستوى الأعلى، لأننا أجرينا تعديلات عليها في مثالنا السابق باستخدام <code>rename()‎</code>.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_37" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> time </span><span class="kwd">as</span><span class="pln"> t
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> r</span><span class="pun">,</span><span class="pln">d</span><span class="pun">,</span><span class="pln">files </span><span class="kwd">in</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">walk</span><span class="pun">(</span><span class="str">'.'</span><span class="pun">):</span><span class="pln">
</span><span class="pun">...</span><span class="pln">   </span><span class="kwd">for</span><span class="pln"> f </span><span class="kwd">in</span><span class="pln"> files</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> f</span><span class="pun">,</span><span class="str">' created: %s:%s:%s'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> t</span><span class="pun">.</span><span class="pln">localtime</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">getctime</span><span class="pun">(</span><span class="pln">r</span><span class="pun">+</span><span class="str">'/'</span><span class="pun">+</span><span class="pln">f</span><span class="pun">))[</span><span class="lit">3</span><span class="pun">:</span><span class="lit">6</span><span class="pun">]</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">...</span><span class="pln">     </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> f</span><span class="pun">,</span><span class="str">' modified: %s:%s:%s'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> t</span><span class="pun">.</span><span class="pln">localtime</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">getmtime</span><span class="pun">(</span><span class="pln">r</span><span class="pun">+</span><span class="str">'/'</span><span class="pun">+</span><span class="pln">f</span><span class="pun">))[</span><span class="lit">3</span><span class="pun">:</span><span class="lit">6</span><span class="pun">])</span><span class="pln">
</span><span class="pun">...</span><span class="pln">
FA</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">42</span><span class="pun">:</span><span class="lit">11</span><span class="pln">
FA</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">36</span><span class="pun">:</span><span class="lit">27</span><span class="pln">
FB</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">13</span><span class="pun">:</span><span class="lit">42</span><span class="pun">:</span><span class="lit">11</span><span class="pln">
FB</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
FC</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">46</span><span class="pln">
FC</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
FF</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">34</span><span class="pun">:</span><span class="lit">3</span><span class="pln">
FF</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
FD</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">33</span><span class="pun">:</span><span class="lit">12</span><span class="pln">
FD</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
FE</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">33</span><span class="pun">:</span><span class="lit">53</span><span class="pln">
FE</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
target</span><span class="pun">.</span><span class="pln">txt  created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">34</span><span class="pun">:</span><span class="lit">28</span><span class="pln">
target</span><span class="pun">.</span><span class="pln">txt  modified</span><span class="pun">:</span><span class="pln"> </span><span class="lit">17</span><span class="pun">:</span><span class="lit">32</span><span class="pun">:</span><span class="lit">5</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span></pre>

<p>
	لاحظ النتيجة الغريبة لملف <code>FA</code> حيث يظهر أنه عُدّل قبل إنشائه! لأننا أنشأناه نسخةً من الملف الأصلي ثم حذفنا الأصل، وأعدنا تسمية النسخة بنفس اسم الملف الأصلي، فرأى نظام التشغيل أن إعادة التسمية لم تغير شيئًا من المحتوى فلم يغير تاريخ التعديل -وهو وقت عملية النسخ- لكنه رأى عملية إعادة التسمية على أنها توقيت إنشاء اسم الملف الحالي، فلابد أن هذا ملف <code>FA.txt</code> جديد بما أننا حذفنا الملف الأصلي!
</p>

<p>
	يختلف أمر الملف <code>FB.txt</code> قليلًا، إذ لم يتغير المحتوى لأننا نقلنا الملف بدلًا من نسخه، لذا تاريخ آخر تعديل هو نفسه تاريخ الإنشاء الأصلي -انظر الملفات الأخرى-، لكن مرةً أخرى يرى نظام التشغيل عملية إعادة التسمية على أنها إنتاج لملف <code>FB.txt</code> جديد، فتظهر هنا قيمة الوقت الأخرى.
</p>

<p>
	في نظام التشغيل دالة تستطيع إعادة أغلب المعلومات التي نحتاجها عن ملف في صف tuple واحد، وهي دالة <code>stat()‎</code>، ولها عدة صور، إلا أننا لن ندرس إلا النسخة الموجودة في وحدة <code>os</code>، حيث تعيد <code>os.stat()‎</code> صف tuple يحتوي على ما يلي:
</p>

<ul>
	<li>
		st_mode: بتات الحماية.
	</li>
	<li>
		st_ino: رقم مؤشر الفهرسة inode.
	</li>
	<li>
		st_dev: جهاز.
	</li>
	<li>
		st_nlink: عدد الروابط الصلبة hard links.
	</li>
	<li>
		st_uid: معرِّف المستخدم المالك.
	</li>
	<li>
		st_gid: معرِّف المجموعة للمالك.
	</li>
	<li>
		st_size: حجم الملف بالبايت.
	</li>
	<li>
		st_atime: وقت آخر وصول للملف.
	</li>
	<li>
		st_mtime: وقت آخر تعديل للمحتوى.
	</li>
	<li>
		st_ctime: وقت الإنشاء، لكن وفقًا للمنصة.
	</li>
</ul>

<p>
	لاحظ أنه قد توجد بعض الحقول الإضافية أحيانًا وفقًا لما يدعمه نظام التشغيل نفسه، فتحقق من توثيق المنصة لديك. لننظر المثال التالي المطبق على ملف المستوى الأعلى <code>FA.txt</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_39" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> fmtString </span><span class="pun">=</span><span class="pln"> </span><span class="str">"protection: %s\nsize: %s\naccessed: %s\ncreated: %s"</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> stats </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">stat</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> fmtString </span><span class="pun">%</span><span class="pln"> stats</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln">stats</span><span class="pun">[</span><span class="lit">6</span><span class="pun">],</span><span class="pln">stats</span><span class="pun">[</span><span class="lit">7</span><span class="pun">],</span><span class="pln">stats</span><span class="pun">[</span><span class="lit">9</span><span class="pun">]</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
protection</span><span class="pun">:</span><span class="pln"> </span><span class="lit">33279</span><span class="pln">
size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
access</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1132407387</span><span class="pln">
created</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1132407731</span></pre>

<p>
	نحتاج لفك ترميز جميع القيم لنفهمها، باستثناء الحجم الذي ما هو إلا عدد البايتات في الملف، وسنرى كيفية العمل مع كل منها لاحقًا، أما العلامات الزمنية timestamps فهي سهلة لأن الأرقام فيها تمثل عدد الثواني لكل دورة، وقد شرحنا ذلك من قبل، ونستطيع استخدام دوال وحدة <code>time</code>، مثلما فعلنا في <code>localtime()‎</code> أعلاه لتحويلها إلى بنية بيانات ذات فائدة لنا أو إلى سلسلة نصية.
</p>

<p>
	وتحتاج صيغة الحماية protection لفك ترميزها أولًا، باستخدام بعض القيم الخاصة الموجودة في وحدة <code>stat</code>، إلا أننا نحتاج إلى استخدام بعض العوامل operators الأخرى، والتي تعرف بالعوامل الثنائية bitwise operators.
</p>

<h4>
	العوامل الثنائية والرايات
</h4>

<p>
	تحتوي وحدة <code>stat</code> على مجموعة من الثوابت مسبقة التعريف، مثل المتغيرات ذات القيم التي لا تتغير، وتسمح تلك الثوابت بفك ترميز بيانات الصلاحيات باستخدام العوامل الثنائية، وهذه العوامل هي نفسها العوامل البوليانية المنطقية التي استخدمناها من قبل مثل <code>and</code> و<code>or</code> و<code>not</code>، إضافةً إلى عامل جديد هو <code>xor</code>، لكن الفرق أن هذه تعمل على البتات الثنائية المنفردة من البيانات، بدلًا من القيمة الكلية.
</p>

<p>
	يمكن العثور على القيم الموجودة في <code>stat</code> بالنظر إلى المتغيرات المعرَّفة مثل قيم ثنائية، وتوفر بايثون خيار صيغة ثنائية مضمنة فيها هي <code>bin</code>، والتي تبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_41" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">22</span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b10110'</span></pre>

<p>
	يمثل الجزء <code>0b</code> في بداية السلسلة النصية أسلوب بايثون في إخبارنا أن هذا عدد ثنائي، ونستطيع تحويل السلسلة النصية الثنائية إلى عدد عشري باستخدام دالة <code>int</code>، من خلال توفير وسيط ثانٍ قيمته 2، لأن قاعدة العدد الثنائي هي 2 في الاصطلاح الرياضي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_43" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> int</span><span class="pun">(</span><span class="str">'0b10110'</span><span class="pun">,</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="lit">22</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> int</span><span class="pun">(</span><span class="str">'10110'</span><span class="pun">,</span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="lit">22</span></pre>

<p>
	لاحظ أن <code>0b</code> في بداية السلسلة النصية اختيارية، لأن الوسيط الثاني <code>2</code> يخبر بايثون بوجوب معاملة السلسلة النصية على أنها ثنائية، وهذا مفيد إذا كنا نقرأ السلسلة من المستخدم مثلًا بدلًا من استخدام خرج <code>bin</code>.
</p>

<h5>
	العوامل الثنائية Bitwise Operators
</h5>

<p>
	سننظر الآن في كيفية عمل العوامل الثنائية باستخدام دالة <code>bin</code> لعرض قيم الدخل والخرج، ولننظر أولًا في تأثير <code>and</code> الثنائية التي رمزها <code>&amp;</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_45" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b101'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b001'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b010'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">5</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b001'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">5</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b000'</span></pre>

<p>
	سنراجع تلك النتائج لنرى ما يحدث، تذكر أن <code>and</code> المنطقية تتحقق -أي تكون true- إذا وفقط إذا تحققت كلا القيمتين، أي كانتا <code>true</code>، وبالمثل فإن <code>&amp;</code> الثنائية تكون true -قيمتها 1- إذا تحقق البتّان الموافقان -كان لهما القيمة 1-، وإذا نظرنا إلى <code>‎5 &amp; 1</code> فسنجد أن البت الذي في أقصى اليمين يتحقق في كل من 5 و1، أي تكون قيمتها 1، وبناءً عليه فإن البت الذي في أقصى اليمين لـ <code>‎5 &amp; 1</code> سيكون 1 أيضًا، أما في حالة <code>‎5 &amp; 2</code> فلا توجد حالة تكون فيها لبتين اثنين القيمة 1 في كل من 5 و2، وعليه تكون النتيجة كلها أصفارًا بالنسبة لـ <code>‎5 &amp; 2</code>.
</p>

<p>
	يقود هذا السلوك إلى ميزة خاصة لعمليات <code>&amp;</code> الثنائية، فمن خلال "جمع" قيمة ثنائية مع عدد يحتوي على رقم ثنائي وحيد مضبوط على القيمة 1، نستطيع معرفة إن كانت قيمة البت الموافقة في القيمة التي نختبرها هي 1 أيضًا أم لا، فإن كانت كذلك فسنحصل على نتيجة غير صفرية، وهي الموافقة للقيمة <code>True</code> في الاصطلاح البولياني، لنستعرض الآن مثالًا نفترض فيه أننا نريد التحقق من كون البت الثاني في عدد معيَّنًا أم لا، ونحن نعلم مما سبق أن القيمة التي فيها بت واحد في الموضع الثاني -إذا بدأنا العد من اليمين- هي 2:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_47" style=""><span class="pln">BIT2 </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
</span><span class="kwd">for</span><span class="pln"> n </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">10</span><span class="pun">):</span><span class="pln">   </span><span class="kwd">if</span><span class="pln"> n </span><span class="pun">&amp;</span><span class="pln"> BIT2</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> n</span><span class="pun">,</span><span class="str">' = '</span><span class="pun">,</span><span class="pln">bin</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	ستكون النتيجة أن البت الثانية معينة -أي set- في كل من 2 و3 و6 و7.
</p>

<p>
	نستطيع تنفيذ أمور مشابهة لذلك باستخدام <code>or</code> الثنائية، والتي نستخدم لها الرمز <code>|</code>، وكذلك <code>not</code> الثنائية التي لها الرمز <code>~</code>، رغم أن هذه الأخيرة قد يكون لها بعض النتائج الغريبة المتعلقة بكيفية تخزين الحواسيب للأعداد السالبة، جرب استخدام دالة <code>bin()‎</code> مع هذه العوامل لعرض دخل وخرج البتّات لترى سلوكها، وتذكر أن توازن بين القيم بتًا بتًا.
</p>

<p>
	أما العامل الثنائي الأخير فهو or الحصرية أو <code>xor</code>، التي لها الرمز <code>^</code>، وتتحقق -أي تكون true- إن تحققت قيمة واحدة فقط من قيمتي الاختبار، لكنها لا تتحقق عند تحقق كلا القيمتين، وسنحصل على نتائج مثيرة للاهتمام وفقًا لهذه القاعدة، فمثلًا إذا طبقنا هذا العامل على أي عدد مع نفسه فسنحصل على صفر، أما إذا طبقناه على أي عدد مع مفتاح فسنحصل على نتيجة، وإذا طبقنا هذا العامل مرةً ثانيةً على النتيجة والمفتاح فسنحصل على القيمة الأصلية، وهذا السلوك مفيد جدًا في علم التشفير. لننظر في بعض الأمثلة قبل أن نعود إلى وحدة <code>stat</code> ونبحث في قيم الصلاحيات.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_49" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">5</span><span class="pln"> </span><span class="pun">^</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b111'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="lit">5</span><span class="pun">^</span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b000'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">((</span><span class="lit">5</span><span class="pun">^</span><span class="lit">2</span><span class="pun">)^</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b101'</span></pre>

<p>
	لاحظ أن النتيجة في الحالة الأخيرة هي سلسلة ثنائية للعدد 5، أي أننا إذا طبقنا العامل <code>xor</code> مرتين فسنحصل على القيمة الأصلية.
</p>

<h5>
	الرايات Flags
</h5>

<p>
	يسمى المتغير المستخدَم لتخزين قيمة بوليانية رايةً flag، لأن الراية يمكن رفعها أو خفضها، وعندما يكون لدينا قيم رايات كثيرة تتعلق بوحدة واحدة؛ فمن الشائع استخدام عدد واحد لتخزين الفئة المجمَّعة للرايات، باستخدام بتات بيانات منفردة لتمثيل كل راية منفردة، ويمكن جلب قيم الرايات فيما بعد باستخدام العوامل الثنائية التي شرحناها أعلاه، مدمجةً مع قيمة فك ترميز تعرف باسم القناع mask، مما يسمح لنا باستخراج البتات التي نريدها تحديدًا، فعلى سبيل المثال، كانت القيمة <code>BIT2</code> أعلاه قناعًا لاستخراج البت الثاني، وتشكل وحدة <code>stat</code> مجموعةً من الأقنعة مسبقة التعريف لاختبار رايات الصلاحيات التي تعيدها الدالة <code>os.stat()‎</code>.
</p>

<h3>
	استخدام ثوابت stat مع العوامل الثنائية
</h3>

<p>
	سننظر الآن إلى بعض قيم <code>stat</code> مثل أعداد ثنائية، ونرى إن كنا نستطيع معرفة كيفية استخدامها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_51" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> stat
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> dir</span><span class="pun">(</span><span class="pln">stat</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="str">'ST_ATIME'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_CTIME'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_DEV'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_GID'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_INO'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_MODE'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_MTIME'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'ST_NLINK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_SIZE'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'ST_UID'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ENFMT'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IEXEC'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFBLK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFCHR'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'S_IFDIR'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFIFO'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFLNK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFMT'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFREG'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IFSOCK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IMODE'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'S_IREAD'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IRGRP'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IROTH'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IRUSR'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IRWXG'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IRWXO'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IRWXU'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'S_ISBLK'</span><span class="pun">,</span><span class="str">'S_ISCHR'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISDIR'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISFIFO'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISGID'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISLNK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISREG'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'S_ISSOCK'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISUID'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_ISVTX'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IWGRP'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IWOTH'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IWRITE'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IWUSR'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'S_IXGRP'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IXOTH'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'S_IXUSR'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'__builtins__'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'__doc__'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'__file__'</span><span class="pun">,</span><span class="pln"> 
</span><span class="str">'__name__'</span><span class="pun">]</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="pln">stat</span><span class="pun">.</span><span class="pln">S_IREAD</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b100000000'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="pln">stat</span><span class="pun">.</span><span class="pln">S_IWRITE</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b010000000'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> bin</span><span class="pun">(</span><span class="pln">stat</span><span class="pun">.</span><span class="pln">S_IEXEC</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="str">'0b001000000'</span></pre>

<p>
	الملاحظة الأولى هنا هي تعريفنا للكثير من الثوابت، والثانية أن القيم الثلاثة التي طبعناها هي القيم التي تحدد إمكانية قراءة الملف أو كتابته أو تنفيذه على الترتيب، ولاحظ أن لكل قيمة مجموعة بتات واحدة، كما في قيمة <code>BIT2</code> في الأمثلة السابقة، لذا نستطيع استخدام عامل <code>and</code> الثنائي لإيجاد صلاحيات ملفنا، بعد استدعاء الدالة <code>os.stat()‎</code>، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_53" style=""><span class="kwd">import</span><span class="pln"> os</span><span class="pun">,</span><span class="pln"> stat
permission </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">stat</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">)[</span><span class="lit">0</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> permission </span><span class="pun">&amp;</span><span class="pln"> stat</span><span class="pun">.</span><span class="pln">S_IREAD</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> </span><span class="str">'The file is readable'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> permission </span><span class="pun">&amp;</span><span class="pln"> stat</span><span class="pun">.</span><span class="pln">S_IWRITE</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> </span><span class="str">'The file is writeable'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> permission </span><span class="pun">&amp;</span><span class="pln"> stat</span><span class="pun">.</span><span class="pln">S_IEXEC</span><span class="pun">:</span><span class="pln">
   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> </span><span class="str">'The file is executable'</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	هذه هي الصلاحيات التي نريدها غالبًا، ويمكنك الرجوع إلى توثيق وحدة <code>stat</code> للمزيد من الصلاحيات، والتحقق من فهمها بتجربتها في محث بايثون.
</p>

<p>
	كما توجد دالة <code>access()‎</code> المساعِدة والموجودة في وحدة <code>os</code>، والتي تسمح لنا بالتحقق من أغلب صلاحيات الوصول الشائعة، غير أن منظور القناع البِتي bitmask الموصوف أعلاه يغطي خيارات أكثر، لذا يمكن استخدامه في المواضع التي لا يصلح استخدام <code>access()‎</code> فيها.
</p>

<h3>
	تغيير صلاحيات الملفات
</h3>

<p>
	بعد أن عرفنا صلاحيات ملف ما، نستطيع استخدام وحدة <code>os</code> لتغييرها إلى ما يناسبنا، وتستخدم بايثون اصطلاحات يونكس لتغييرها، حيث يكون لكل ملف مجموعة من ثلاث رايات هي (قراءة read، كتابة write، تنفيذ execute)، لكل تصنيف من تصنيفات المستخدمين الثلاثة (المالك owner، المجموعة المالكة group، العالم world أي بقية المستخدمين)، لذا سيكون لدينا 9 رايات لكل ملف، وتمثَّل تلك الرايات بتسعة بتّات، تشكل البتات اليمنى في راية الصلاحيات التي تعيدها <code>os.stat</code>.
</p>

<p>
	تمثَّل مجموعات الصلاحيات تلك في التوثيق بسلسلة من تسعة محارف، تتكون من ثلاث مجموعات من المحارف <code>rwx</code>، مع شرطةٍ بدل المحرف إذا لم تكن الصلاحية مضبوطةً أو معيَّنةً set، وبناءً على ذلك ستعني السلسلة النصية "rwxr-xr--‎" أن للمستخدم صلاحية القراءة والكتابة والتنفيذ rwx، وللمجموعة صلاحية القراءة والتنفيذ r-x، وللنوع الثالث الذي هو العالم صلاحية القراءة فقط r--‎، وإذا أردنا تغيير الصلاحيات فسنعين البتات وفقًا لما نريد التغيير إليه، لذلك سنستخدم الدالة <code>chmod()‎</code> في وحدة <code>os</code>، حيث تأخذ وسيطًا هو الرقم المكون من 9 بتات، فعلى سبيل المثال يمثل الرقم 0b111101100 الصلاحيات <code>rwxr-xr--‎</code>، ونستخدم ذلك لضبط الوصول لملف ما كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_55" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">chmod</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="lit">0b111101100</span><span class="pun">)</span></pre>

<p>
	وإذا كانت لديك خبرة سابقة بالأعداد الثُمانية octal فستعرف أن كل رقم ثُماني يمثل ثلاثة بتات ثنائية، وعلى ذلك نستطيع التعبير عن الصلاحيات بسهولة في ثلاثة أرقام ثُمانية، ويربط الجدول التالي القيم الثُمانية بنظائرها المكافئة لها من الأرقام الثنائية وصلاحيات <code>rwx</code>:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
	<thead>
		<tr>
			<th>
				الثُماني
			</th>
			<th>
				الثنائي
			</th>
			<th>
				"rwx"
			</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>
				0
			</td>
			<td>
				0b000
			</td>
			<td>
				"---"
			</td>
		</tr>
		<tr>
			<td>
				1
			</td>
			<td>
				0b001
			</td>
			<td>
				"‎--x"
			</td>
		</tr>
		<tr>
			<td>
				2
			</td>
			<td>
				0b010
			</td>
			<td>
				"‎-w-"
			</td>
		</tr>
		<tr>
			<td>
				3
			</td>
			<td>
				0b011
			</td>
			<td>
				"‎-wx"
			</td>
		</tr>
		<tr>
			<td>
				4
			</td>
			<td>
				0b100
			</td>
			<td>
				"r--‎"
			</td>
		</tr>
		<tr>
			<td>
				5
			</td>
			<td>
				0b101
			</td>
			<td>
				"r-x"
			</td>
		</tr>
		<tr>
			<td>
				6
			</td>
			<td>
				0b110
			</td>
			<td>
				"rw-‎"
			</td>
		</tr>
		<tr>
			<td>
				7
			</td>
			<td>
				0b111
			</td>
			<td>
				"rwx"
			</td>
		</tr>
	</tbody>
</table>

<p>
	لن يجد مستخدمو يونكس مشكلةً في التعبير عن الصلاحيات بهذه الطريقة، ويمكن استخدام ذلك في بايثون أيضًا لجعل استدعاء <code>chmod</code> يبدو كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_57" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="com"># يجب استخدام صفر في البداية ليُعامل على أنه ثُماني</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">chmod</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="lit">0754</span><span class="pun">)</span><span class="pln"> </span></pre>

<p>
	ينفذ المثالان أعلاه نفس الأمر، حيث يعيّنان صلاحيات المالكين لتكون قراءةً وكتابةً وتنفيذًا، ويعيّنان صلاحيات المجموعة لتكون قراءةً وتنفيذًا فقط، وصلاحيات العالم لتكون قراءةً فقط، وتجدر الإشارة إلى أن النسخة الثُمانية أسهل في الكتابة إذا أردت التعود على التحويلات بين الثُمانية والثنائية.
</p>

<h2>
	المسارات والملفات والمجلدات
</h2>

<p>
	من الشائع عند تطوير برنامج ما أن تكون ملفات البيانات في نفس المجلد الذي فيه ملفات البرنامج، ليسهل العثور على الملفات عند الحاجة، أما إذا كان استخدام البرنامج عامًا فلا يمكن افتراض أن الملفات ستكون في مواضع معروفة، لذا قد نحتاج إلى البحث عنها باستخدام <code>glob</code> أو <code>os.walk</code> اللتين شرحناهما أعلاه، فإذا وجدنا الملف الذي نريده وأردنا فتحه أو فحص خصائصه؛ فسنحتاج إلى تعيين المسار الكامل له، أما إذا كان لدينا اسم المسار الكامل فقد نفككه لاستخراج اسم الملف أو المجلد فقط، لوضعه في متغير مثلًا، وتوفر <code>os.path</code> إمكانية تنفيذ مثل تلك المهام.
</p>

<p>
	يُنظر إلى أسماء الملفات في بايثون على أنها تتكون من عدة أجزاء، فهناك حرف اختياري يمثل القرص الذي يوجد عليه الملف، رغم أن هذا قد لا يكون في جميع أنظمة التشغيل، إذ ليس لأنظمة التشغيل غير ويندوز هذا المفهوم الذي يضع اسم القرص الصلب جزءًا من اسم الملف، ثم يأتي تسلسل من أسماء المجلدات تفصل بينها بعض المحارف المحددة، ففي بايثون مثلًا نستخدم <code>/</code>، لكن قد يكون لبعض أنظمة التشغيل محارفها الخاصة، وأخيرًا لدينا اسم الملف أو الاسم القاعدي basename، والذي يحوي صورةً من صور امتدادات الملفات extension:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_59" style=""><span class="pln">F</span><span class="pun">:/</span><span class="pln">PROJECTS</span><span class="pun">/</span><span class="pln">PYTHON</span><span class="pun">/</span><span class="typ">Root</span><span class="pun">/</span><span class="pln">FA</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	يخبرنا المسار أعلاه أن الملف المسمى <code>FA.txt</code> موجود في مجلد الجذر <code>Root</code>، الذي يوجد بدوره في مجلد <code>PYTHON</code> تحت مجلد <code>PROJECTS</code>، في مجلد المستوى الأعلى للقرص <code>F:‎</code>، ويحمل الملف امتداد الملفات النصية <code>‎.txt</code>.
</p>

<p>
	نستطيع الآن استخراج الاسم القاعدي base name، والامتداد extension، وتسلسل المجلدات من المسار الكامل، باستخدام الدوال الموجودة في وحدة <code>os.path</code>، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_61" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> pth </span><span class="pun">=</span><span class="pln"> F</span><span class="pun">:/</span><span class="pln">PROJECTS</span><span class="pun">/</span><span class="pln">PYTHON</span><span class="pun">/</span><span class="typ">Root</span><span class="pun">/</span><span class="pln">FA</span><span class="pun">.</span><span class="pln">txt
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> stem</span><span class="pun">,</span><span class="pln"> aFile </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="pln">pth</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> </span><span class="str">'stem : '</span><span class="pun">,</span><span class="pln">stem</span><span class="pun">,</span><span class="pln"> </span><span class="str">'\nfile : '</span><span class="pun">,</span><span class="pln">aFile </span><span class="pun">)</span><span class="pln">
stem </span><span class="pun">:</span><span class="pln">  F</span><span class="pun">:/</span><span class="pln">PROJECTS</span><span class="pun">/</span><span class="pln">PYTHON</span><span class="pun">/</span><span class="typ">Root</span><span class="pln">  
file </span><span class="pun">:</span><span class="pln">  FA</span><span class="pun">.</span><span class="pln">txt

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="com"># this only works on OS with drive concept, like Windows</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">splitdrive</span><span class="pun">(</span><span class="pln">pth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="str">'F:'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/PROJECTS/PYTHON/Root/FA.txt'</span><span class="pun">)</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">dirname</span><span class="pun">(</span><span class="pln">pth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
F</span><span class="pun">:/</span><span class="pln">PROJECTS</span><span class="pun">/</span><span class="pln">PYTHON</span><span class="pun">/</span><span class="typ">Root</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">basename</span><span class="pun">(</span><span class="pln">pth</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
FA</span><span class="pun">.</span><span class="pln">txt

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">splitext</span><span class="pun">(</span><span class="pln">aFile</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">(</span><span class="str">'FA'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'.txt'</span><span class="pun">)</span></pre>

<p>
	وندمجها معًا بالشكل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_63" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">stem</span><span class="pun">,</span><span class="pln">aFile</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
F</span><span class="pun">:/</span><span class="pln">PROJECTS</span><span class="pun">/</span><span class="pln">PYTHON</span><span class="pun">/</span><span class="typ">Root</span><span class="pln">\\FA</span><span class="pun">.</span><span class="pln">txt</span></pre>

<p>
	يجب ملاحظة أن <code>os.path.join</code> تستخدم محرف الفصل الرسمي للنظام، فإذا أردنا بناء مسار يصلح للعمل على نظم تشغيل مختلفة فيجب استخدام <code>os.path.join</code> بدلًا من كتابة المسار في برنامجنا، كما يمكن تحديد أي عدد نرغب فيه من عناصر المسار في قائمة الوسطاء، فيمكن كتابة المثال أعلاه بالشكل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_65" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">"F:\\"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"PROJECTS"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"PYTHON"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Root"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"FA.txt"</span><span class="pun">))</span><span class="pln">
F</span><span class="pun">:</span><span class="pln">\\PROJECTS\\PYTHON\\</span><span class="typ">Root</span><span class="pln">\\FA</span><span class="pun">.</span><span class="pln">txt</span></pre>

<h3>
	واصفات الملفات وكائنات الملفات
</h3>

<p>
	تستخدم بعض الوحدات من عائلة <code>os</code> آليةً مختلفةً قليلًا للوصول إلى الملفات، وهو ما يُعرف باسم واصف الملفات file descriptor، وهو مرتبط بشدة بمفهوم الملف في نظم التشغيل بدلًا من كائن الملف file object الذي استخدمناه حتى الآن، ومزيته أننا نستطيع الوصول إلى حزمة من عمليات الملفات منخفضة المستوى low-level operations، والتي تمكننا من الحصول على تحكم أكبر في الملفات وبياناتها، ويمكن إنشاء واصف ملف من كائن ملف والعكس، لكن من الأفضل ألا نخلط بين وضعي العمليات داخل دالة واحدة أو برنامج واحد، فإما أن نستخدم واصفات الملفات أو كائنات الملفات.
</p>

<p>
	تشمل دوال واصفات الملفات جميع عمليات الملفات المعتادة، مثل فتح open وقراءة read وكتابة write وإغلاق close، أما البرامج منخفضة المستوى فتكون صعبة الاستخدام، إضافةً إلى احتمال حدوث أخطاء عند استخدامها، لهذا يجب استخدام الوصول منخفض المستوى عند الضرورة فقط، أما في أغلب الحالات فيكفي استخدام كائنات الملف القياسية.
</p>

<p>
	لكن ما هي الحالات التي قد نحتاج فيها إلى وصول منخفض المستوى إلى الملفات؟
</p>

<p>
	تستخدم العمليات القياسية مبدأً يعرف باسم التخزين المؤقت للدخل/الخرج أو buffered IO، حيث تُحفظ البيانات في مناطق تخزين تسمى بالمخازن المؤقتة buffers أثناء عمليات القراءة والكتابة، وقد تتسبب تلك المخازن في مشاكل عند الوصول إلى عتاد غريب أو إجراء عمليات ذات توقيت حرج time critical، ففي مثل تلك الحالات يكون الحل هو العمليات منخفضة المستوى، لكن إذا لم تكن متأكدًا من سبب استخدامها فلا تفعل، لكن مع هذا العيب الكبير في العمليات منخفضة المستوى، فإننا نقول إن استخدامها ليس بتلك الصعوبة التي تظهر من سياق الحديث، وإنما قصدنا أن هناك بعض المشاكل التي يجب تجنبها، لنبدأ بإجراء مهمة بسيطة، مثل فتح ملف نصي، وكتابة بعض البيانات فيه، ثم إغلاقه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_67" style=""><span class="pln">fname </span><span class="pun">=</span><span class="pln"> </span><span class="str">'F:/PROJECTS/PYTHON/Root/FL.txt'</span><span class="pln">
mode </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">O_CREAT </span><span class="pun">|</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">O_WRONLY </span><span class="com"># create and write</span><span class="pln">
access </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0777</span><span class="pln"> </span><span class="com"># read/write/execute for all</span><span class="pln">
data </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Test text to check that it worked'</span><span class="pln">
fd </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln"> mode</span><span class="pun">,</span><span class="pln"> access</span><span class="pun">)</span><span class="pln"> </span><span class="com"># NB. os version not the builtin!</span><span class="pln">
length </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">fd</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> length </span><span class="pun">!=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">data</span><span class="pun">):</span><span class="pln">
   </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> </span><span class="str">'Amount of data written doesn'</span><span class="pln">t match the data</span><span class="pun">!</span><span class="str">' )
os.close(fd)</span></pre>

<p>
	بالمثل نستطيع قراءة البيانات من الملف:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_69" style=""><span class="pln">mode </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">O_RDONLY </span><span class="com"># read only</span><span class="pln">
fd </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="pln">fname</span><span class="pun">,</span><span class="pln">mode</span><span class="pun">)</span><span class="pln"> </span><span class="com"># no access needed this time</span><span class="pln">
result </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">read</span><span class="pun">(</span><span class="pln">fd</span><span class="pun">,</span><span class="pln"> length</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> result </span><span class="pun">)</span><span class="pln">
os</span><span class="pun">.</span><span class="pln">close</span><span class="pun">(</span><span class="pln">fd</span><span class="pun">)</span></pre>

<p>
	نلاحظ عدة أمور هي:
</p>

<ul>
	<li>
		الطريقة التي نعين بها نوع وصول الملف أعقد هنا، ويجب أن نستخدم عامل <code>or</code> الثنائي لدمج جميع الرايات المطلوبة كما وفرتها وحدة <code>os</code>.
	</li>
	<li>
		نستطيع توفير مستوى وصول غير المستوى الافتراضي، وهذا أمر تمتاز به عن توابع كائن الملف القياسية.
	</li>
	<li>
		العدد الثُماني -لأنه يبدأ بصفر- هو نفسه العدد المذكور في قسم "تغيير صلاحيات الملفات" السابق.
	</li>
	<li>
		عند قراءة البيانات يجب أن نمرر طول البيانات المقروءة، باستخدام <code>read</code> القياسية، لكن هذا إجباري بالنسبة للعمليات منخفضة المستوى.
	</li>
</ul>

<p>
	أما البيانات المقروءة والمكتوبة فعلًا فهي من النوع سلسلة بايتات bytestring، وهذا لن يكون مشكلةً عند التعامل مع سلاسل المحارف، لأننا نستطيع استخدام تابع <code>decode</code> الخاص باليونيكود لتحويل البايتات إلى محارف، لكن تظهر المشكلة عند التعامل مع أنواع بيانات أخرى، إذ يجب استخدام وحدة <code>struct</code> كما شرحنا في مقال <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1334/" rel="">التعامل مع الملفات</a>.
</p>

<h2>
	معالجة العمليات
</h2>

<p>
	يُعد تنفيذ البرامج أو تشغيلها من أكثر المهام التي ننفذها عن استخدام نظام التشغيل، ونفعل ذلك غالبًا من خلال واجهة رسومية أو صدفة سطر أوامر، كما يمكن أيضًا أن نبدأ البرامج من داخل برامج أخرى، فقد لا نحتاج أحيانًا إلى أكثر من بدء برنامج والسماح له بالعمل حتى نهايته، أما في أحيان أخرى فقد نحتاج إلى توفير بيانات الدخل أو قراءة الخرج إلى برنامجنا، ويُعرف هذا اصطلاحًا باسم التواصل البيني للعمليات أو التواصل بين العمليات inter-proces communication، أو IPC اختصارًا، وسنشرح هذا المفهوم بالتفصيل في المقال التالي.
</p>

<h3>
	تعريف العملية
</h3>

<p>
	العملية Process في الاصطلاح الحاسوبي هي ما يطلق عليه المستخدمون "تشغيل البرنامج"، فإذا كان لدينا ملف تنفيذي على الحاسوب ونريد تنفيذه؛ فسيبدأ التشغيل داخل مساحة الذاكرة الخاصة به، وقد يكون من الممكن بدء أكثر من نسخة من نفس الملف التنفيذي، ويشغل كل منها مساحةً خاصةً به من الذاكرة، ويدير بياناته الخاصة به، ويطلَق اسم العملية على كل واحد من تلك البرامج التنفيذية مع بيئة التشغيل الخاصة به.
</p>

<p>
	نستطيع أن نرى العمليات الحالية على الحاسوب باستخدام الأدوات التي يوفرها نظام التشغيل، فإذا كنت على ويندوز فاضغط على Ctrl+Alt+Del لتشغيل برنامج Task Manager، وانظر في التبويب المسمى Processes، وسترى قوائم طويلةً من العمليات الحالية التي قد تتعرف على بعضها من البرامج الخاصة بك، أما بقية العمليات فتكون خدمات بدأها نظام ويندوز نفسه، كما قد تلاحظ أن بعض التطبيقات تبدأ عدة عمليات، مثل قواعد البيانات العلائقية relational databases وخوادم الويب، وتوضح الصورة التالية مثالًا لبرنامج Task Manager:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="95231" href="https://academy.hsoub.com/uploads/monthly_2022_04/TaskManager.png.e913068d31ec8d5438408f3acefc465b.png" rel="" data-fileext="png"><img alt="TaskManager.png" class="ipsImage ipsImage_thumbnailed" data-fileid="95231" data-unique="q200gp8b0" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_04/TaskManager.png.e913068d31ec8d5438408f3acefc465b.png"></a>
</p>

<p>
	أما على لينكس أو نظام MacOS فيُستخدم الأمر <code>ps</code> لعرض العمليات أو المهام الحالية، أو <code>top</code> الذي يعطي عرضًا حيًا لتلك العمليات، وسيبدو أمر <code>ps</code> كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="95230" href="https://academy.hsoub.com/uploads/monthly_2022_04/ps-linux.png.7999303b00a21bd486a20cf9e13baa09.png" rel="" data-fileext="png"><img alt="ps-linux.png" class="ipsImage ipsImage_thumbnailed" data-fileid="95230" data-unique="40axnuhlj" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_04/ps-linux.thumb.png.e55cd9781ba284dfd16508e5ac6fa42c.png"></a>
</p>

<h3>
	تشغيل برنامج خارجي: الدالة os.system()‎
</h3>

<p>
	نعلم الآن الفرق بين البرنامج والعملية، وسننظر في كيفية تنفيذ برنامج من بايثون، وتوجد الكثير من الطرق لذلك -لأسباب تاريخية-، لكننا سنكتفي باثنتين فقط، والأولى منهما بسيطة الاستخدام للغاية، لكنها محدودة الإمكانيات، أما الثانية فهي المنظور الذي يُنصح به هذه الأيام، وهي أكثر قوةً ومرونةً.
</p>

<p>
	الطريقة الأولى السهلة والقديمة هي استخدام دالة <code>system()‎</code> من وحدة <code>os</code>، وهذا ينفذ سلسلة أمر command string، ويعيد شيفرة خطأ تعكس انتهاء الأمر على النحو الصحيح أو لا، ولا توجد طريقة للوصول إلى الخرج الفعلي للبرنامج المستدعى ولا لتزويد العملية الحالية بالدخل المناسب، لذا تناسب <code>system()‎</code> تنفيذ البرامج التي لا تحتاج إلى متابعة، مثل إخلاء شاشة الطرفية، حيث لا نحتاج أن نعرف هل نُفّذ الأمر بنجاح أم لا، ولا أن نتفاعل مع الأمر بمجرد بدئه، ويمكن رؤية مثال على ذلك في يونكس:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_71" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> os
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> errorcode </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">system</span><span class="pun">(</span><span class="str">"clear"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> errorcode </span><span class="pun">)</span><span class="pln">
</span><span class="lit">0</span></pre>

<p>
	ويختلف الأمر قليلًا في أنظمة التشغيل المبنية على ويندوز:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_73" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> errorcode </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">system</span><span class="pun">(</span><span class="str">"CLS"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> errorcode </span><span class="pun">)</span><span class="pln">
</span><span class="lit">0</span></pre>

<p>
	غير أن النتيجة في الحالتين يجب أن تكون إخلاء النافذة الطرفية، ويجب أن يكون رمز الخطأ errorcode هو الصفر إشارةً إلى نجاح التنفيذ، وقد لا يكون هذا مفيدًا؛ لكننا نستطيع استخدام <code>system</code> في سكربتاتنا حين نرغب في عرض الخرج الطبيعي للأمر، أو عندما لا يهمنا إلا نجاح الأمر أو فشله، فمثلًا نستطيع أن نطلب من المستخدم أن يعدل إعدادات ملف من خلال بدء جلسة محرر editor، ثم يغلق جلسة التحرير عند انتهائه، ويعود التحكم إلى برنامجنا الذي يستطيع عندئذ أن يقرأ الملف المعدَّل.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_76" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> filename </span><span class="pun">=</span><span class="pln"> </span><span class="str">'xxyyzz.config'</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> errorcode </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">system</span><span class="pun">(</span><span class="str">'nano %s'</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> filename</span><span class="pun">)</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="kwd">not</span><span class="pln"> errorcode</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">    </span><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">filename</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> config</span><span class="pun">:</span><span class="pln">
</span><span class="pun">...</span><span class="pln">       </span><span class="com"># للعملية config هنا ملف </span></pre>

<p>
	يُظهر المثال بعض التقنيات المفيدة، حيث يُظهر طريقةً لوصف استدعاءات النظام باستخدام تنسيق السلاسل النصية، كما يُظهر تفسير رمز الخطأ لتحديد نتيجة العملية.
</p>

<p>
	إذا نجحت عملية التعديل فسيكون رمز الخطأ صفرًا، حتى لو لم يحدث أي تعديل على الملف، لكن إذا فشلت -كما في حالة عدم العثور على المحرر مثلًا- فسنحصل على رمز خطأ قيمته غير الصفر.
</p>

<p>
	ومع أن <code>system</code> سهلة الاستخدام إلا أنها ليست مرنةً، ولا توجد طريقة مباشرة لتوصيل أي بيانات إلى برامجنا، ونستطيع محاكاة ذلك بالتقاط الخرج إلى ملف نصي مؤقت، ثم فتح ذلك الملف ومعالجته مثلما اعتدنا، لكن ثمة طريقة أفضل لتحقيق نفس النتيجة وهي استخدام وحدة <code>subprocess</code>.
</p>

<h3>
	إدارة العمليات باستخدام الوحدة subprocess
</h3>

<p>
	بعد العديد من المحاولات الفاشلة لتحسين التحكم في العمليات، حدث تطور كبير عند إدخال الوحدة <code>subprocess</code> إلى الإصدار 2.4 من بايثون، وقد جاءت تلك الوحدة خصيصًا لاستبدال جميع الآليات القديمة، ويمكن رؤية العديد من الأمثلة على كيفية استخدامها في توثيقها، أما هنا فسنغطي أغلب استخدامها العام، وتوجد دوال سهلة ومريحة إذا رغبت في محاكاة وظيفة <code>os.system</code> أو ما شابهها.
</p>

<p>
	بُنت الوحدة <code>subprocess</code> على صنف يسمى <code>Popen</code>، لاحظ الحرف الأول بالحرف الكبير، ويمكن استخدامه لإنشاء نسخة من أمر ما، لكن توثيق هذا الصنف قد يسبب الرهبة للبعض لأن باني <code>Popen</code> يحتوي على الكثير من المعامِلات الرائعة، لكن الجيد في الأمر أن لجميعها تقريبًا قيمًا افتراضية، ويمكن تجاهلها في الحالات بالغة البساطة، وبناءً على ذلك لتشغيل أمر من أوامر نظام التشغيل من داخل سكربت ما، لا نحتاج إلا إلى فعل ما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_78" style=""><span class="kwd">import</span><span class="pln"> subprocess
ps </span><span class="pun">=</span><span class="pln"> subprocess</span><span class="pun">.</span><span class="typ">Popen</span><span class="pun">([</span><span class="str">'ps'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'-ef'</span><span class="pun">],</span><span class="pln"> shell</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">)</span></pre>

<p>
	الوسيط الأول قائمة من السلاسل النصية، التي تمثل الأمرَ وجميع وسطائه، ففي المثال أعلاه ننفذ أمر <code>ps -ef</code>.
</p>

<p>
	يمنع الوسيط الثاني <code>shell=False</code> تمرير الأمر من خلال برامج الصدفة الخاصة بالمستخدمين -مثل Bash-، مما قد ينتج عنه مشاكل أمنية، بسبب الأسماء البديلة aliases المخصصة للأوامر مثلًا، فيجب أن نستخدم <code>shell=False</code> كلما أمكن ذلك، لكن من الضروري أحيانًا أن نفسر الأمر بواسطة الصدفة، كما في حالة تمرير محارف بدل wild-cards مثل "*.jpg" إلى تطبيق معالجة للصور، فالصدفة هي التي توسع محرف البدل إلى قائمة من أسماء الملفات فعلًا.
</p>

<p>
	خزّنا نسخة Popen الناتجة في متغير سميناه باسم الأمر الذي ننفذه، وهذا ليس ضروريًا لكنه سلوك حسن إذا كنا نشغّل عدة عمليات مساعِدة في نفس الوقت.
</p>

<p>
	توجد دالة أخرى اسمها <code>call</code> يمكن استخدامها بدلًا من <code>os.sytem</code> في المثال أعلاه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_80" style=""><span class="pln">subprocess</span><span class="pun">.</span><span class="pln">call</span><span class="pun">([</span><span class="str">'ps'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'-ef'</span><span class="pun">],</span><span class="pln"> shell</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">)</span></pre>

<p>
	تكاد دالة <code>call</code> أن تطابق استخدام صنف <code>Popen</code> الذي شرحناه من قبل، غير أنه ليس لها تلك الخيارات المتاحة في <code>Popen</code>، ولا تنشئ نسخًا، لذا فهي أوفر في استهلاك موارد النظام، لكن لها نفس عيوب <code>os.system</code>.
</p>

<p>
	لوحدة <code>subprocess</code> التي نستخدمها هنا مزية عن الدوال القديمة، وهي أنها ترفع استثناءً <code>OSError</code> إذا لم يوجد الأمر المطلوب، أما الدوال الأخرى فكانت ستتركنا دون أي إشارة على وجود أخطاء.
</p>

<h3>
	التواصل مع العمليات باستخدام Popen
</h3>

<p>
	من الممكن تنفيذ كل ما فعلناه بوحدة <code>subprocess</code> باستخدام <code>os.system</code>، لكن هذا على وشك التغير الآن حيث سنتعرف على كيفية تبادل البيانات مع عملية جارية بدأناها باستخدام <code>subprocess.Popen</code>، فإذا عدنا قليلًا إلى أمر <code>ps</code> الذي نفذناه من قبل فسنجد أننا شغلنا البرنامج، لكننا لم نستطع الوصول إلى خرجه، ونحتاج إلى إجراء تعديل بسيط لنتمكن من الوصول إلى ذلك الخرج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_82" style=""><span class="kwd">import</span><span class="pln"> subprocess </span><span class="kwd">as</span><span class="pln"> sub
ps </span><span class="pun">=</span><span class="pln"> sub</span><span class="pun">.</span><span class="typ">Popen</span><span class="pun">([</span><span class="str">'ps'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'-ef'</span><span class="pun">],</span><span class="pln"> shell</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> stdout</span><span class="pun">=</span><span class="pln">sub</span><span class="pun">.</span><span class="pln">PIPE</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> ps</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">read</span><span class="pun">().</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	لقد أضفنا وسيطًا هنا يخبر <code>Popen</code> أن يرسل خرجه القياسي <code>stdout</code> إلى أنبوب عملية فرعية subprocess Pipe وقد شرحنا مجرى الخرج والدخل القياسيين <code>stdin وstdout</code> في مقال <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D9%82%D8%B1%D8%A7%D8%A1%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D8%A7%D9%85%D8%AC-%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-r1311/" rel="">قراءة البيانات من المستخدم</a>، ونستطيع الآن أن نصل إلى بيانات الخرج باستخدام الخاصية <code>stdout</code> لنسخة <code>Popen</code> التي نقرؤها مثل أي ملف عادي، وتُحوَّل سلسلة البايتات bytestring الناتجة إلى محارف يونيكود باستخدام <code>decode</code> ثم تُطبع، لكن من الممكن أن نسندها إلى متغير ونعالجها بأي طريقة نشاء.
</p>

<p>
	انتبه، توجد بعض المشاكل في الوصول إلى مجرى الخرج القياسي بهذه الطريقة، تدور أغلبها حول إدارة عدة عمليات متزامنة معًا، لذا نشجع استخدام التابع <code>Popen.communicating</code> الذي يعيد قائمةً من مجاري البيانات data streams يكون مجرى الخرج القياسي <code>stdout</code> هو الأول فيها، ثم مجرى الدخل القياسي <code>stdin</code>، ثم مجرى الخطأ القياسي <code>stderr</code>.
</p>

<p>
	فإن مثّل ذلك مشكلةً فيمكن إعادة كتابة المثال السابق كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_84" style=""><span class="kwd">import</span><span class="pln"> subprocess </span><span class="kwd">as</span><span class="pln"> sub
ps </span><span class="pun">=</span><span class="pln"> sub</span><span class="pun">.</span><span class="typ">Popen</span><span class="pun">([</span><span class="str">'ps'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'-ef'</span><span class="pun">],</span><span class="pln"> shell</span><span class="pun">=</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> stdout</span><span class="pun">=</span><span class="pln">sub</span><span class="pun">.</span><span class="pln">PIPE</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> ps</span><span class="pun">.</span><span class="pln">communicate</span><span class="pun">()[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">decode</span><span class="pun">(</span><span class="str">'utf8'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	نستخدم <code>Popen.communicate([0])‎</code> هنا للوصول إلى أنبوب الخرج القياسي بطريقة آمنة.
</p>

<p>
	ويمكن إرسال بيانات إلى عملية ما بطريقة مشابهة، إذ نخبر الصنف Popen أن يستخدم أنبوبًا لمجرى الدخل القياسي، ثم يكتب البيانات إلى مجرى البيانات ذاك مثل الملف العادي، رغم أن البيانات هنا يجب أن تكون سلسلة بايتات وليست سلسلة محارف.
</p>

<p>
	في المثال التالي، نفتح محرر أسطر يونكس <code>ex</code>، ونرسل إليه بعض البيانات لينشئ ملفًا نصيًا، ثم نغلق المحرر قبل التحقق من وجود ذلك الملف الجديد:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_86" style=""><span class="kwd">import</span><span class="pln"> subprocess </span><span class="kwd">as</span><span class="pln"> sub
</span><span class="kwd">import</span><span class="pln"> os
ex </span><span class="pun">=</span><span class="pln"> sub</span><span class="pun">.</span><span class="typ">Popen</span><span class="pun">([</span><span class="str">'ex'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'/tmp/testex.txt'</span><span class="pun">],</span><span class="pln">stdin</span><span class="pun">=</span><span class="pln">sub</span><span class="pun">.</span><span class="pln">PIPE</span><span class="pun">)</span><span class="pln">
ex</span><span class="pun">.</span><span class="pln">stdin</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">b</span><span class="str">'i\nthis is some text\n.\n'</span><span class="pun">)</span><span class="pln">
ex</span><span class="pun">.</span><span class="pln">stdin</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">b</span><span class="str">'wq\n'</span><span class="pun">)</span><span class="pln">
ex</span><span class="pun">.</span><span class="pln">stdin</span><span class="pun">.</span><span class="pln">close</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">listdir</span><span class="pun">(</span><span class="str">'/tmp'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	لاحظ أن المدخلات كلها سلاسل بايتات، وأننا نحتاج إلى إدراج محارف الإرجاع carriage return على أنها محددات أسطر جديدة في السلاسل، كما أن النقطة الموجودة في أول سطر من <code>ex.stdin.write</code> هي التي تخبر <code>ex</code> بانتهاء تسلسل الدخل، ويؤكد استدعاء <code>os.listdir</code> أن الملف الجديد موجود في مجلد <code>tmp</code>، أو يمكن أن نتحقق من ذلك بواسطة مدير الملفات.
</p>

<p>
	وبهذا استطعنا الكتابة في عملية جارية، رغم أنها أقل شيوعًا من القراءة من العمليات.
</p>

<h2>
	مسألة الأمان
</h2>

<p>
	توفر نظم التشغيل هذه الأيام أغلب الأدوات التي تضمن أمان بيئة التشغيل التي يستخدمها المستخدم على الحاسوب، يمكن الوصول إليها -مثل باقي أدوات نظم التشغيل- من خلال واجهة برمجة التطبيقات <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاصة بنظام التشغيل نفسه، لكن مع شرط تنظيم نظام التشغيل وصولنا إلى مزايا معينة؛ وفقًا للصلاحيات الممنوحة للمستخدم الذي يشغل البرنامج، فإذا أردنا الوصول إلى ملفات أحد المستخدمين فلا بد أن نملك صلاحية الوصول إلى ملفاته أولًا، وهذا لا يعني ترك مزايا الأمان المضمنة في نظام التشغيل.
</p>

<p>
	سندرس بعض الدوال المتعلقة بالأمان، مثل تحديد معرِّف المستخدم، وتغيير ملكية الملفات، واستخدام متغيرات البيئة للحصول على بيانات حول بيئة المستخدم الحالي.
</p>

<h3>
	المستخدمون وملكية الملفات
</h3>

<p>
	يمكن الحصول على معرِّفات المستخدمين باستخدام دالة <code>os.getuid</code>، والتي تعيد معرّف المستخدم في صورة رقم، ولا نحتاج إلى تحويله إلى اسم المستخدم نفسه إلا نادرًا،لأننا نستطيع الحصول على ذلك الاسم باستخدام دالة <code>getpass.getuser()‎</code> التي تنظر في متغيرات البيئة التي قد تحمل تلك المعلومة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_88" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> getpass
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> getpass</span><span class="pun">.</span><span class="pln">getuser</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	أما معرف المستخدم فيكون القيمة التي يحتاج البرنامج إليها لتغيير إعدادات الأمان، ونحصل عليه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_91" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> os
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getuid</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span></pre>

<p>
	لعل أشهر استخدام لذلك هو تغيير ملكية ملف -أنشأناه سابقًا في جزء من برنامجنا- برمجيًا، فمثلًا سنستخدم أحد الملفات التي أنشأناها سابقًا في هذا المقال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_93" style=""><span class="kwd">import</span><span class="pln"> os

os</span><span class="pun">.</span><span class="pln">chdir</span><span class="pun">(</span><span class="str">'src/Python/Root'</span><span class="pun">)</span><span class="pln">
os</span><span class="pun">.</span><span class="pln">system</span><span class="pun">(</span><span class="str">'ls -l *.txt'</span><span class="pun">)</span><span class="pln">
id </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getuid</span><span class="pun">()</span><span class="pln">
os</span><span class="pun">.</span><span class="pln">chown</span><span class="pun">(</span><span class="str">'FA.txt'</span><span class="pun">,</span><span class="pln">id</span><span class="pun">,-</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
os</span><span class="pun">.</span><span class="pln">system</span><span class="pun">(</span><span class="str">'ls -l *.txt'</span><span class="pun">)</span></pre>

<p>
	نستخدم <code>os.chdir</code> لضبط مجلد العمل ليكون المكان الذي فيه الملفات، ثم نستخدم <code>system()‎</code> لعرض قائمة المجلدات بما فيها صلاحيات الملفات قبل وبعد استدعاء <code>chown()‎</code>، لنستطيع أن نرى التغيرات إن وجدت.
</p>

<p>
	نستدعي <code>chown()‎</code> مع معرف المستخدم الذي حصلنا عليه من <code>getuid()‎</code>، ونستخدم <code>‎-1</code> للمعامل الثالث الخاص بالدالة <code>chown()‎</code>، للإشارة إلى أننا لا نريد تغيير ملكية المجموعة، لكن إذا أردنا ذلك فسنستخدم الدالة <code>os.getid()‎</code> التي تجلب معرف المجموعة.
</p>

<p>
	لاحظ أنه لن يكون للسكربت تأثير إلا إذا شغلناه من مستخدم مختلف عن الحالي، ويجب أن يكون للمستخدم صلاحيات تلك التغييرات، لذا ننصح أن تسجل الدخول بالمستخدم المدير administrator أو الجذر root.
</p>

<p>
	لا تخبرنا <code>chown()‎</code> أي معلومات عن الخرج، لذا إذا أردنا التحقق من النتيجة فيجب أن نستخدم شيئًا مثل <code>os.stat</code> للتحقق من قيمة معرف المستخدم قبل استدعائها وبعده، وبهذا نتحقق من حدوث التغييرات التي نتوقعها.
</p>

<h3>
	بيئة المستخدم
</h3>

<p>
	يرث البرنامج عند بداية تشغيله سياق الذاكرة من البرنامج الذي شغّله، حيث يكون البرنامج المشغِّل غالبًا صدفة سطر الأوامر الخاصة بالمستخدم، CMD في ويندوز أو Bash أو غيرها في يونكس، وتشمل بيئة المستخدم تلك معلومات كثيرةً عن النظام، مثل اسم المستخدم، ومجلد home، والمجلد الحالي، والمجلد المؤقت، ومسارات البحث، وهذا يعني أن إعداد متغيرات البيئة المختلفة يمكّن كل مستخدم من تخصيص كيفية عمل نظام التشغيل وبرامجه إلى حد ما، فمثلًا تراقب بايثون متغير البيئة <code>PYTHONPATH</code> عند البحث عن الوحدات، لذلك قد يكون لكل مستخدم على نفس الحاسوب مسارات بحث مستقلة للوحدات، بما أن كل واحد منها يضبط قيمةً مختلفةً للمتغير <code>PYTHONPATH</code>، ويستفيد المبرمجون من ذلك بتعريف بعض متغيرات البيئة لبرنامج ما بحيث يستطيع المستخدم تغيير قيم البرنامج الافتراضية، ويجب أن نتمكن من قراءة البيئة الحالية للعثور على تلك القيم، بأن نقرأ متغيرًا واحدًا باستخدام دالة <code>os.getenv()‎</code>، أو نقرأ جميع المتغيرات المضبوطة حاليًا بالنظر في متغير <code>os.environ</code> الذي يحتوي قاموسًا بأزواج الاسم/القيمة.
</p>

<p>
	سنطبع قائمةً بجميع متغيرات البيئة، حيث تحتوي تلك القائمة على معلومات كثيرة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_95" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">import</span><span class="pln"> os
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">environ </span><span class="pun">)</span></pre>

<p>
	لا شك أننا نستطيع استخدام عمليات القاموس والسلاسل النصية المعتادة، لكن يُفضل في أغلب الحالات أن نحصل على قيم المتغيرات قيمةً قيمةً، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_97" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getenv</span><span class="pun">(</span><span class="str">'PYTHONPATH'</span><span class="pun">)</span></pre>

<p>
	أو:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_99" style=""><span class="pun">&gt;&gt;&gt;</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">environ</span><span class="pun">[</span><span class="str">'PYTHONPATH'</span><span class="pun">]</span></pre>

<p>
	يبين لنا هذا هل ضبطنا المتغير <code>PYTHONPATH</code> أم لا؟ وعلى أي قيمة أيضًا.
</p>

<p>
	تتيح <code>getenv()‎</code> إمكانية ضبط نتيجة افتراضية إذا لم يكن المتغير معرَّفًا، نظرًا لخطورة عدم تعريفه، ويمكن استخدام تابع القاموس القياسي <code>get()‎</code> لتنفيذ نفس الشيء في قاموس <code>environ</code>، ويستخدَم هذا عادةً أثناء تهيئة البرامج، حين نضبط الإعدادات، مثل المجلد الذي ستوجد فيه ملفات البيانات، ففي المثال التالي سننظر أين يجب تخزين دليل جهات الاتصال الخاص بنا، باستخدام الإعداد الافتراضي للمجلد الحالي إن لم يكن ثمة متغير:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_101" style=""><span class="com"># ... خطوات تهيئة هنا.</span><span class="pln">
folder </span><span class="pun">=</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getenv</span><span class="pun">(</span><span class="str">'PY_ADDRESSES'</span><span class="pun">,</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">getcwd</span><span class="pun">())</span><span class="pln">
</span><span class="com"># ... بقية البرنامج</span></pre>

<p>
	تعيد <code>getenv()‎</code> وسيطها الثاني عند عدم وجود قيمة افتراضية للمتغير <code>PY_ADDRESSES</code>، وهو الموقع الافتراضي لنا.
</p>

<p>
	ينشئ المستخدم متغيرات البيئة تلك ويضبطها يدويًا باستخدام نظام التشغيل، ففي ويندوز مثلًا ينفّذ ذلك بتسلسل الإعدادات التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_103" style=""><span class="typ">MyComputer</span><span class="pun">-&gt;</span><span class="typ">Properties</span><span class="pun">-&gt;</span><span class="typ">Advanced</span><span class="pun">-&gt;</span><span class="typ">Environment</span><span class="pln"> </span><span class="typ">Variables</span></pre>

<p>
	أما في لينكس وماك فيُنفذ في سطر الأوامر باستخدام الأمرين <code>export</code> و<code>setenv</code> وفقًا للصدفة المستخدمة.
</p>

<p>
	يمكن تغيير قيم متغير البيئة الحالي في بعض أنظمة التشغيل، لكن يجب استخدام ذلك بحذر، إذ قد يؤدي إلى الكتابة فوق قيم أخرى، كما قد تعكس بعض أنظمة التشغيل تلك التغييرات على بيئات المستخدمين، غير أن هذا لا ينطبق إلا في سياق عملية الكتابة غالبًا، فإذا كان نظام التشغيل يدعم تلك العملية فنستطيع كتابة قيمة المجلد الافتراضي الخاصة بنا إلى بيئة المستخدمين لضمان استخدام نسخ برنامجنا الأخرى لنفس الموقع، إلا أننا لا ننصح بذلك لهشاشة تلك الآلية، وإنما ننصح باستخدام ملف config لأنه أكثر موثوقيةً لمثل هذه الإعدادات.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_7189_105" style=""><span class="com"># شيفرة أخرى كما سبق</span><span class="pln">
putenv</span><span class="pun">(</span><span class="str">'PY_ADDRESSES'</span><span class="pun">,</span><span class="pln"> folder</span><span class="pun">)</span><span class="pln">
</span><span class="com"># ... بقية البرنامج</span></pre>

<p>
	تُستخدم بعض متغيرات البيئة في يونكس من قبل برامج عديدة، منها على سبيل المثال:
</p>

<ul>
	<li>
		EDITOR: يحدد هذا المتغير برنامج التحرير الذي يفضله المستخدم، ويكون ed أو vi أو ViM أو Emacs، فتشغّل البرامج الأخرى هذا المحرر إذا احتاج المستخدم إلى تعديل ملف نصي في جزء من البرنامج، فمثلًا يمكننا استخدام <code>getenv ('EDITOR')‎</code> لإطلاق المحرر الخاص بالمستخدم في مثال <code>os.system</code> أعلاه، بدلًا من ضبط المحرر nano ليكون هو الافتراضي.
	</li>
	<li>
		PRINTER: يحدد هذا المتغير إعدادات المستخدم المفضلة لطباعة الملفات.
	</li>
	<li>
		PAGER: يحدد هذا المتغير برنامج عرض الملفات الذي يفضله المستخدم، ويُضبط غالبًا على more أو less أو view.
	</li>
</ul>

<p>
	سنكتفي بهذا الشرح عن البيئات، وسنعود إليها في مقال لاحق، لكن نذكر أنه عند الحاجة إلى الحصول على بيانات تخص المستخدم فيجب التحقق من وجود متغير بيئة بالفعل أم لا، أو نتيح للمستخدم إمكانية ضبط ذلك من خلال متغير بيئة خاص ببرنامجنا.
</p>

<h2>
	مزيد من المعلومات حول نظم التشغيل
</h2>

<p>
	تحتوي وحدة <code>os</code> ومثيلاتها على إمكانيات أكثر مما يمكن أن نشرحه هنا، بل إن توثيق بايثون يحتاج إلى عدة صفحات <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> لشرح وحدة <code>os</code> وحدها، وصفحة لكل وحدة أخرى، ويمكن الرجوع إلى تلك التوثيقات لتصفح وظائف تلك الوحدات، وستجد فيها أسماءً غريبةً، تأتي أغلبها من يونكس وواجهة برمجة تطبيقاته.
</p>

<p>
	توفر <code>os</code> وظائف مكافئةً على أي نظام تشغيل، لكن إذا أردت معرفة المزيد عن وظائف تلك الدوال فيجب الرجوع إلى توثيق يونكس نفسه، فإن لم يكن لديك أحد أنظمة يونكس فيمكن الرجوع إلى كتاب Unix Systems Programming for SVR4، وإذا أعجبك ما شرحناه عن نظم التشغيل فربما تود قراءة كتاب Fundamentals of Operating Systems لصاحبه ليستر A. M. Lister، فهو كتاب قصير ويسهل فهم شرحه المدعوم بمخططات توضيحية، أما إذا رغبت في إلقاء نظرة فاحصة على نظم التشغيل فانظر كتاب أندرو تانينباوم Andrew Tanenbaum الشهير Operating Systems: Design and Implementation، الذي شجع لينوس تورفالدز Linus Torvalds على كتابة نواة لينكس.
</p>

<h2>
	خاتمة
</h2>

<p>
	نرجو في نهاية هذا المقال أن تتذكر ما يلي:
</p>

<ul>
	<li>
		يوفر نظام التشغيل بيئةً يمكن تشغيل العمليات فيها.
	</li>
	<li>
		كما يوفر وصولًا إلى عتاد الحاسوب.
	</li>
	<li>
		يمكن الوصول إلى نظام التشغيل من خلال واجهة برمجة التطبيقات، التي تكتَب عادةً بلغة C.
	</li>
	<li>
		توفر وحدة <code>os</code> الخاصة ببايثون مغلِّفًا لواجهة برمجة التطبيقات الخاصة بنظام التشغيل.
	</li>
	<li>
		تسهل الوحدتان <code>os.path</code> و<code>glob</code> الوصول إلى الملفات.
	</li>
	<li>
		توفر كل من <code>os.system()‎</code> و<code>subprocess.Popen</code> مستويات مختلفةً من التحكم في العمليات والتواصل بينها IPC.
	</li>
	<li>
		تسمح <code>getuid()‎</code> و<code>getenv()‎</code> والمزايا الشبيهة بهما بالتعرف على المستخدم وإعداداته المفضلة.
	</li>
</ul>

<p>
	ترجمة -بتصرف- <a href="http://www.alan-g.me.uk/l2p2/tutos.htm" rel="external nofollow">للفصل الخامس والعشرين: Working with the Operating System</a> من كتاب Learning To Program لصاحبه Alan Gauld.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D9%88%D8%A7%D8%B5%D9%84-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1521/" rel="">التواصل بين العمليات في البرمجة</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/devops/servers/databases/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-r602/" rel="">التعامل مع قواعد البيانات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/linux/%D8%A3%D9%87%D9%85-20-%D8%A3%D9%85%D8%B1%D8%A7-%D9%81%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%8A%D9%86%D9%83%D8%B3-%D9%84%D8%AC%D9%85%D9%8A%D8%B9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86-r585/" rel="">أهم 20 أمرا في نظام التشغيل لينكس لجميع المستخدمين</a>
	</li>
	<li>
		النسخة الكاملة لكتاب: <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1520</guid><pubDate>Wed, 06 Apr 2022 15:01:00 +0000</pubDate></item><item><title>&#x62D;&#x627;&#x648;&#x64A;&#x629; &#x627;&#x644;&#x639;&#x646;&#x648;&#x627;&#x646; label &#x648;&#x635;&#x646;&#x627;&#x62F;&#x64A;&#x642; &#x627;&#x644;&#x645;&#x62F;&#x62E;&#x644;&#x627;&#x62A; Entry &#x641;&#x64A; &#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/6238aeef9eabe_--label---Entry------.png.ff3b9629d6a5a28037e50a8e737b2bc6.png" /></p>

<p>
	نستكمل في هذا المقال ما كنا قد بدأناه في سلسلة مقالات <a href="https://academy.hsoub.com/tags/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA%20%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%20%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9%20%D9%81%D9%8A%20%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">واجهات المستخدم الرسومية في بايثون</a> باستخدام مكتبة TKinter، فبعد أن تحدثنا عن النافذة الرئيسية ومجموعة العناصر التي من الممكن أن نضعها عليها بشرح مجمل، سننتقل الآن إلى شرح تفصيلي عن العناصر وسنبدأ بشرح حاوية العنوان label و صناديق المدخلات Entry وسنكمل في مقالات لاحقة من السلسلة بقية العناصر.
</p>

<p>
	هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter.</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون</a>.
	</li>
	<li>
		حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
</ul>
<h2>
	حاوية العنوان label
</h2>

<p>
	تعد حاوية عنوان <code>label</code> من أسهل العناصر التي يمكن إضافتها إلى النافذة الرئيسية وتُستخدم لتوضيح عناوين صناديق المدخلات <code>Entry</code> أو لعرض مخرجات نصية أو صورة.
</p>

<h3>
	إنشاء حاوية العنوان داخل النافذة
</h3>

<p>
	يتم اختيار اسم الكائن المطلوب ليمثل حاوية العنوان ويستخدم الصنف <code>Label</code> من مكتبة <code>Tkinter</code> ، ويعطى الصنف أولًا المكان الذي نريد وضع الحاوية فيه وهو النافذة الرئيسية، ومن ثم النص المراد عرضه داخل الحاوية باستخدام الخاصية <code>text</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_6" style="">
<span class="typ">Label_name</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"النص"</span><span class="pun">)</span></pre>

<p>
	تنشئ الشفرة التالية حاوية عنوان تعرض عبارة (واجهات المستخدم الرسومية في<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel=""> بايثون</a>) ومن ثم تضعها على النافذة الرئيسية باستخدام الدالة pack:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_8" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" حاوية العنوان "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="pln"> </span><span class="str">"واجهات المستخدم الرسومية في بايثون "</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	بتشغيل الشفرة السابقة نحصل على النافذة التالية:
</p>

<p style="text-align: center;">
	<img alt="001_GUI3_label.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94338" data-unique="3gc2y4gjk" src="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI3_label.jpg.bbb97496e36080e51e1d785c8bb90564.jpg" style="width: 400px; height: auto;"></p>

<p style="text-align: center;">
	حاوية العنوان
</p>

<h3>
	خصائص حاوية العنوان label
</h3>

<p>
	يمكننا تعديل الكثير من خصائص مظهر حاوية العنوان، النقاط التالية تستعرض أهم الخصائص التي من الممكن استخدامها:
</p>

<h4>
	اللون
</h4>

<p>
	بالإمكان تغيير لون النص والذي يعرف باسم <code>foreground</code> ويمكن كتابته اختصارًا <code>fg</code>، وكذلك تغيير لون الخلفية والذي يعرف باسم <code>background</code> ويكتب اختصارًا <code>bg،</code> ونستخدم إما صيغ نصية للألوان فنكتب اسم اللون المطلوب مثل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_12" style="">
<span class="str">"white"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"red"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"green"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cyan"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"yellow"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"magenta"</span></pre>

<p>
	أو يمكننا استخدام سلسلة تحدد نسبة الأحمر والأخضر والأزرق بالأرقام الست عشرية. على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_14" style="">
<span class="str">"#FFFF00"</span><span class="pln">  </span><span class="pun">أصفر</span><span class="pln">
</span><span class="str">"#FFFFFF"</span><span class="pln"> </span><span class="pun">أبيض</span><span class="pln">
</span><span class="str">"#00FF00"</span><span class="pln"> </span><span class="pun">أخضر</span></pre>

<h4>
	نوع الخط
</h4>

<p>
	يُعرف الخط باستخدام الخاصية <code>font</code> وتعطى قيمة صفوف <code>tuple</code> مكونة من اسم الخط وحجمه، ومن الممكن تحديد ما إذا كان الخط عريضًا <code>bold</code> أو مائلًا <code>italic</code> مثال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_16" style="">
<span class="pun">(</span><span class="str">"Helvetica"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"16"</span><span class="pun">)</span><span class="pln"> 
</span><span class="pun">(</span><span class="str">"Times"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"24"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bold italic"</span><span class="pun">)</span></pre>

<h4>
	شكل مؤشر الفأرة
</h4>

<p>
	بالإمكان التحكم بشكل المؤشر أثناء مروره على حاوية العنوان باستخدام الخاصية <code>cursor</code> والتي من الممكن أن تأخذ قيمًا مختلفة مثال على بعض القيم التي من الممكن إسنادها لها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_18" style="">
<span class="pln"> </span><span class="str">"circle"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"clock"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cross"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"dotbox"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"watch"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"heart"</span></pre>

<p>
	وبطبيعة الحال قد يختلف شكل المؤشر اختلافًا طفيفًا مع اختلاف <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">نظام التشغيل</a> المستخدم.
</p>

<h4>
	الحدود الخارجية والداخلية
</h4>

<p>
	تترك الخاصية <code>pad</code> مساحة حول العنصر بمحيطه الخارجي عبر ضبط الخاصيتين التاليتين:
</p>

<ul>
<li>
		<code>pady</code> تترك مساحة حول العنصر على الإحداثي الصادي.
	</li>
	<li>
		<code>padx</code> تترك مساحة حول العنصر على الإحداثي السيني.
	</li>
</ul>
<p>
	وتوجد أيضًا الخاصية <code>ipad</code> التي تترك مساحة حول العنصر بمحيطة الداخلي عبر ضبط الخاصيتين التاليتين:
</p>

<ul>
<li>
		<code>ipady</code> تترك مساحة بين العنصر وحدوده الداخلية على المحور الصادي.
	</li>
	<li>
		<code>ipadx</code> تترك مساحة بين العنصر وحدوده الداخلية على المحور السيني.
	</li>
</ul>
<p>
	وتستخدم هذه الخصائص مع الدالة <code>Pack</code> التي ستضع الحاوية على النافذة الرئيسية.
</p>

<p>
	سنقوم بتجربة الخاصيات السابقة في برنامج متكامل، سنضيف رقة تعريف مكتوب بها نص بسيط بحجم 30 ونوع خط <code>Stencil</code> ونجعله ثخينًا ومائلًا، ونحدد لون الخلفية إلى أزرق باستخدام الصيغة النصية، مع لون نص أبيض باستخدام نظام الأرقام الست عشرية، ومؤشر فأرة يتغير إلى شكل نجمة عند مروره على حاوية العنوان، وسنترك مساحة حول الحاوية من حدودها الداخلية والخارجية، ستكون الشفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_21" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" حاوية العنوان "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"Sample text "</span><span class="pun">,</span><span class="pln">\
font</span><span class="pun">=(</span><span class="str">"Stencil"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"30"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bold italic"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">,</span><span class="pln">\
           fg </span><span class="pun">=</span><span class="str">"#FFFFFF"</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="str">"star"</span><span class="pun">,)</span><span class="pln">

</span><span class="typ">First_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> pady</span><span class="pun">=</span><span class="lit">50</span><span class="pln"> </span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">100</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">100</span><span class="pln"> </span><span class="pun">,</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تشغيل الشفرة سنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94339" href="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI3_Customized_label.jpg.8ee5f376eebe0a7b1e7f495b4018ad13.jpg" rel=""><img alt="002_GUI3_Customized_label.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94339" data-unique="29gz9bc79" src="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI3_Customized_label.jpg.8ee5f376eebe0a7b1e7f495b4018ad13.jpg" style="width: 400px; height: auto;"></a>
</p>

<p style="text-align: center;">
	حاوية العنوان مخصصة
</p>

<h4>
	تغيير النص
</h4>

<p>
	قمنا بتحديد النص الذي نرغب بظهوره في حاوية العنوان أثناء إنشائها من خلال خاصية <code>text</code>، بالإمكان كذلك جعل النص فارغًا عند الإنشاء وتحديده في خطوة لاحقة، لتنفيذ ذلك نستخدم الخاصية <code>textvariable</code> ونسند لها قيمة كائن من الصنف <code>StringVar</code>، والذي نقوم بتعريفه ونختار له اسمًا لنفترض أنه <code>mytext</code> فتكون الشفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_25" style="">
<span class="typ">Mytext</span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
</span><span class="typ">Label_name</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mytext</span><span class="pun">)</span></pre>

<p>
	نستطيع إسناد قيمة له في خطوة لاحقة باستخدام الدالة <code>set</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_27" style="">
<span class="typ">Mytext</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="str">"sample text"</span><span class="pun">)</span></pre>

<p>
	بإمكاننا إعادة كتابة الشفرة السابقة بهذه الطريقة وسنحصل على نفس النتيجة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_29" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" حاوية العنوان  "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">Mytext</span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
</span><span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="typ">Mytext</span><span class="pun">,</span><span class="pln">\
font</span><span class="pun">=(</span><span class="str">"Stencil"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"30"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"bold italic"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">,</span><span class="pln">\
           fg </span><span class="pun">=</span><span class="str">"#FFFFFF"</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> cursor </span><span class="pun">=</span><span class="str">"star"</span><span class="pun">,)</span><span class="pln">

</span><span class="typ">First_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> pady</span><span class="pun">=</span><span class="lit">50</span><span class="pln"> </span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">100</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">100</span><span class="pln"> </span><span class="pun">,</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">
</span><span class="typ">Mytext</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="str">"sample text"</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<h4>
	إدراج صورة
</h4>

<p>
	بالإمكان استبدال صورة بالنص الموجود في حاوية العنوان ونحتاج لفعل ذلك تعريف كائن من الصنف <code>PhotoImage</code> بتسميته باسم <code>myphoto</code> و سنعطيه باستخدام الخاصية <code>file</code> مسار الصورة (مكان تواجدها بالجهاز) على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_31" style="">
<span class="pln">myphoto </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PhotoImage</span><span class="pun">(</span><span class="pln">file</span><span class="pun">=</span><span class="str">'Photo\path'</span><span class="pun">)</span></pre>

<p>
	ومن ثم نستخدم الخاصية <code>image</code> أثناء تعريف الحاوية ونسند لها قيمة الكائن الذي أنشأناه ستكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_33" style="">
<span class="pln"> </span><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" إدراج صورة "</span><span class="pun">)</span><span class="pln">

myphoto </span><span class="pun">=</span><span class="pln"> </span><span class="typ">PhotoImage</span><span class="pun">(</span><span class="pln">file</span><span class="pun">=</span><span class="str">'Hsoub.png'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">image</span><span class="pun">=</span><span class="pln">myphoto</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> pady</span><span class="pun">=</span><span class="lit">50</span><span class="pln"> </span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">100</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">100</span><span class="pln"> </span><span class="pun">,</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ونحصل على النتيجة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94340" href="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI3_label_image.jpg.389c3a4c828a695328265e0f6e94d161.jpg" rel=""><img alt="003_GUI3_label_image.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94340" data-unique="l4p3gspab" src="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI3_label_image.thumb.jpg.55d2bf71af10a0f3fb6feb97fe2e6e19.jpg" style="width: 500px; height: auto;"></a>
</p>

<p style="text-align: center;">
	حاوية عنوان بصورة
</p>

<p>
	استخدمنا في المثال السابق الصورة المسماة Hsoub.png وكانت موجودة في نفس مسار وجود ملف الشفرة، لذلك وضعنا اسمها مباشرة، ولو كانت في مكان مختلف لقمنا بتحديد مكان وجودها، للأسف الكائن <code>PhotoImage</code> يدعم فقط صيغ محدودة وهي: GIF, PGM, PPM, PNG
</p>

<p>
	ولو أردنا أن نعرض نصًا وصورة في نفس الحاوية يجب علينا أن نستعين بالخاصية <code>compound</code> والتي تحدد مكان ظهور الصورة بالنسبة للنص وتأخذ قيمة من الاحتمالات التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_36" style="">
<span class="str">'top'</span><span class="pun">,</span><span class="str">'bottom'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'left'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'right'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'center'</span><span class="pun">,</span><span class="str">'none'</span></pre>

<p>
	يعرض السطر التالي الصورة فوق النص:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_38" style="">
<span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">image</span><span class="pun">=</span><span class="pln">myphoto </span><span class="pun">,</span><span class="pln">
                    text</span><span class="pun">=</span><span class="str">'شعار شركة حسوب'</span><span class="pln"> </span><span class="pun">,</span><span class="pln">compound</span><span class="pun">=</span><span class="str">'top'</span><span class="pun">)</span></pre>

<p>
	وتكون النافذة بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94341" href="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI3_combound_label.jpg.1b09316b10fd0a6c5f4ae8a851fdc239.jpg" rel=""><img alt="004_GUI3_combound_label.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94341" data-unique="8a4sc4kyo" src="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI3_combound_label.thumb.jpg.982a7ee5e2548571d15d39e42ff75abe.jpg" style="width: 500px; height: auto;"></a>
</p>

<p style="text-align: center;">
	حاوية مدمجة
</p>

<h2>
	صناديق المدخلات Entry
</h2>

<p>
	تعتبر صناديق المدخلات من العناصر الأساسية لواجهات المستخدم الرسومية، لأنها تمكننا من قراءة المدخلات التي يدخلها المستخدم عبر/من لوحة المفاتيح، وتكون على شكل صندوق يسمح للمستخدم أن يدخل فيه نصًا.
</p>

<h3>
	إنشاء صناديق المدخلات داخل نافذة
</h3>

<p>
	يجب علينا تعريف كائن من الصنف <code>Entry</code> من مكتبة <code>Tkinter</code> حتى ننشئ مُدخلًا، ويُعطى الصنف أولًا المكان الذي نريد وضع المدخل فيه وهو النافذة الرئيسية، ومن ثم بإمكاننا تحديد عدة خيارات للمدخل، ويوضح السطر التالي عملية الإنشاء:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_41" style="">
<span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> option</span><span class="pun">.........)</span></pre>

<p>
	يسبق صندوق المُدخلات في الغالب حاوية عنوان لبيان الغرض منه أو ما الذي يتوجب على المستخدم إدخاله، ويتم الاستغناء في بعض الحالات عن الحاوية وتبديلها بنص يوضع داخل الصندوق نفسه يبين الغرض منه، ويتم ذلك باستخدام الدالة <code>insert</code> وإعطائها مكان النص المراد ظهوره (يعطى قيمة صفر كبداية المدخل) وتعطى أيضًا النص المراد كتابته على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_44" style="">
<span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,’</span><span class="pln">text</span><span class="pun">’)</span></pre>

<p>
	ترسم الشفرة التالية صندوق مدخلات ليدخل المستخدم اسمه فيه على سبيل المثال ومن ثم يضيفه على النافذة الرئيسية باستخدام الدالة <code>pack</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_46" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" إدراج مدخل"</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window </span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">insert</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="str">'الاسم'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	يظهر الشكل التالي عند تشغيل الشفرة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94342" href="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI3_Entry.jpg.1e10ed11e363a8d75a2552b2f7d7c3c5.jpg" rel=""><img alt="005_GUI3_Entry.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94342" data-unique="n8550rawn" src="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI3_Entry.jpg.1e10ed11e363a8d75a2552b2f7d7c3c5.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	صندوق إدخال
</p>

<h3>
	خصائص صندوق المدخلات
</h3>

<p>
	يتميز صندوق المدخلات بالكثير من الخصائص التي تشبه ما تم الحديث عنه في حاوية العنوان، فيمكننا تغيير لون النص ولون الخلفية ونوع الخط وشكل المؤشر بنفس الطريقة وبنفس الخصائص المستخدمة مع حاوية العنوان .
</p>

<p>
	كما يمكننا تعديل حدود الصندوق باستخدام الخاصية <code>bd</code> لتكون أكثر سمكًا حيث أن القيمة الافتراضية لها هي 2، وكذلك نستطيع تغيير الحالة للصندوق باستخدام الخاصية <code>state</code> حيث يكون الصندوق فعالًا ويسمح بالكتابة فيه بالقيمة <code>normal</code> وهي الافتراضية، ويمكن تغييرها للقيمة <code>disable</code> والتي تجعله غير فعال ولا يسمح بالكتابة فيه.
</p>

<p>
	بتطبيق ذلك، يرسم السطر التالي صندوق مدخلات بحدود عرض بقيمة 5 مع تعطيل خاصية قبول المدخلات فيه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_49" style="">
<span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> bd</span><span class="pun">=</span><span class="lit">10</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> state</span><span class="pun">=</span><span class="str">"disable"</span><span class="pun">)</span></pre>

<p>
	ويكون بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94343" href="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI3_customized_Entry.jpg.87f763c7b088965251eb26736d4c5188.jpg" rel=""><img alt="006_GUI3_customized_Entry.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94343" data-unique="pq9962cr2" src="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI3_customized_Entry.jpg.87f763c7b088965251eb26736d4c5188.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	صندوق ادخال مخصص
</p>

<p>
	تمكننا الخاصية <code>selectforeground</code> من تحديد لون النص والخاصية <code>selectbackground</code> من تحديد لون خلفية النص المدخل إذا ماتم تحديده من المستخدم.
</p>

<h3>
	الحصول على قيمة المُدخلات
</h3>

<p>
	لنحصل على قيمة المدخل من المستخدم نستخدم الدالة <code>get</code> والتي ترجع ما أدخل في الحقل بصيغة نص:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_52" style="">
<span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">get</span><span class="pun">()</span><span class="pln"> </span></pre>

<p>
	تُسند قيمة مدخلات المستخدم غالبًا إلى كائن من الصنف <code>StringVar</code> ونقوم بإعطائه للخاصية <code>textvariable</code> بعد عملية تعريفه على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_54" style="">
<span class="pln">mytext </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
</span><span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mytext</span><span class="pun">)</span></pre>

<p>
	ونستطيع استخدام الدالة <code>get</code> معه أيضًا للحصول على قيمة المدخل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_56" style="">
<span class="pln">mytext</span><span class="pun">.</span><span class="pln">get</span><span class="pun">()</span></pre>

<p>
	يجب الانتباه إلى أن القيمة التي تعيدها الدالة <code>get</code> تكون على صيغة نص كما سبق وأشرنا، فعند إدخال الأرقام يجب تحويلها من نص إلى رقم باستخدام الدالة <code>int</code> أو الدالة <code>float</code> حسب نوع القيمة إذا ما أردنا استخدامها في العمليات الحسابية.
</p>

<p>
	يمكننا إضافة أهمية على المدخل بزيادة التركيز عليه باستخدام الدالة <code>focus</code> والتي ستقوم تلقائيًا بوضع المؤشر على المُدخل ليتمكن المستخدم من الكتابة مباشرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_58" style="">
<span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">()</span></pre>

<h3>
	إخفاء معلومات المدخلات
</h3>

<p>
	تكون بعض القيم التي يدخلها المستخدم حساسة ويجب إخفائها أثناء الإدخال مثل كلمات السر مثلًا ويمكننا عمل ذلك باستخدام الخاصية <code>show</code> ونعطيها قيمة الرمز أو الحرف الذي نريد ظهورة، في المثال التالي سيظهر الرمز * بدلًا مما يقوم المستخدم بإدخاله:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_60" style="">
<span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mytext</span><span class="pun">,</span><span class="pln"> show</span><span class="pun">=</span><span class="str">'*'</span><span class="pun">)</span></pre>

<h3>
	استجابة صندوق المدخلات
</h3>

<p>
	نستطيع ربط صندوق المدخلات بدالة معينة تتنفذ عندما يقوم المستخدم بالنقر على زر الإدخال <code>Enter</code> ونستخدم الدالة <code>bind</code> لنربط صندوق المدخلات بالنقر على <code>Enter</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_62" style="">
<span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="str">'&lt;Return&gt;'</span><span class="pun">,</span><span class="pln">handler</span><span class="pun">)</span></pre>

<p>
	حيث أن <code>handler</code> هو اسم الدالة التي سيتم تنفيذها، ونستطيع كذلك ربط العديد من الأزرار بنفس الطريقة مع الدالة باستخدام اسم الزر المناسب، مثل زر المسافة <code>Space</code> وزر الهروب <code>Escape</code> وزر المسح <code>Delete</code>.
</p>

<p>
	لنكتب الشيفرة التالية حيث سننشئ صندوق مدخلات وعند نقر المستخدم على زر الإدخال <code>Enter</code> نقوم بطباعة عدد الأحرف التي تم إدخالها في حاوية العنوان:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_64" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"Entry response "</span><span class="pun">)</span><span class="pln">

</span><span class="kwd">def</span><span class="pln"> handler</span><span class="pun">(</span><span class="pln">e</span><span class="pun">):</span><span class="pln">
     L</span><span class="pun">=</span><span class="pln">myentertext</span><span class="pun">.</span><span class="pln">get</span><span class="pun">()</span><span class="pln">
     mylabeltext</span><span class="pun">.</span><span class="pln">set</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">L</span><span class="pun">))</span><span class="pln">

myentertext </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">
mylabeltext</span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">

</span><span class="typ">First_Entry</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">myentertext</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">10</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Entry</span><span class="pun">.</span><span class="pln"> pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">()</span><span class="pln">

tag_label</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">'عدد الأحرف'</span><span class="pun">)</span><span class="pln">
tag_label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> BOTTOM</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_Label</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> textvariable</span><span class="pun">=</span><span class="pln">mylabeltext </span><span class="pun">)</span><span class="pln">
</span><span class="typ">First_Label</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> BOTTOM</span><span class="pun">)</span><span class="pln">

</span><span class="typ">First_Entry</span><span class="pun">.</span><span class="pln">bind</span><span class="pun">(</span><span class="str">'&lt;Return&gt;'</span><span class="pun">,</span><span class="pln">handler</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تجربة الشفرة ستظهر النتيجة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94344" href="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI3_Entry_response.jpg.510cf0f87a5543adfd8ec233bd28d675.jpg" rel=""><img alt="007_GUI3_Entry_response.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94344" data-unique="yrgmh4zv7" src="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI3_Entry_response.jpg.510cf0f87a5543adfd8ec233bd28d675.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	استجابة المدخلات
</p>

<h2>
	تطبيق الآلة الحاسبة (الجزء الثالث)
</h2>

<p>
	لننتقل لجزئية التطبيق على مشروع آلتنا الحاسبة، كنا قد رسمنا النافذة الرئيسية للآلة و وضعنا داخلها إطارين بحاوية عنوان العلوية للمدخلات والسفلية لأزرار العمليات والأرقام، في هذا التطبيق سنضع في الإطار الأول حاوية عنوان نسميها input_label نكتب فيها عبارة "أدخل المعادلة الحسابية"، على جهة اليمين ونحددها بلون خلفية من درجات الأزرق بقيمة "‎#007CFF" ولون خط أبيض، وفي الجهة اليسرى نضع صندوق مدخلات نسميه input_field يعرض الأرقام والعمليات التي ينقر عليها المستخدم، إذ حددنا له عرضًا يتناسب مع مقاس النافذة بمقدار 18، وحددنا له نوع الخط 'arial' بمقياس 12 وجعلناه ثخينًا، وقمنا بتعريف متغير من نوع StringVar أسميناه input_text ليكون محتواه متغيرًا يختلف مع كل نقرة من المستخدم على الأزرار، تكون شفرتهما على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_68" style="">
<span class="pln">input_label</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"أدخل المعادلة الحسابية "</span><span class="pun">,</span><span class="pln">
                    bg</span><span class="pun">=</span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln">fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
input_field </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">=</span><span class="lit">18</span><span class="pun">,</span><span class="pln">
                    font</span><span class="pun">=(</span><span class="str">'arial'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bold'</span><span class="pun">),</span><span class="pln">textvariable</span><span class="pun">=</span><span class="pln">input_text</span><span class="pun">)</span></pre>

<p>
	لوضع هذه العناصر في داخل الإطار استخدمنا خاصية grid وأضفنا حدودًا داخلية على المحور الصادي بمقدار 10، ندمج هذه الإضافات على الشفرة السابقة ونحصل على التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_6439_70" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

</span><span class="kwd">from</span><span class="pln"> ctypes </span><span class="kwd">import</span><span class="pln"> windll
windll</span><span class="pun">.</span><span class="pln">shcore</span><span class="pun">.</span><span class="typ">SetProcessDpiAwareness</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln">

mycalculator </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"آلة حاسبة بسيطة"</span><span class="pun">)</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="str">"575x740"</span><span class="pun">)</span><span class="pln">  
mycalculator</span><span class="pun">.</span><span class="pln">resizable</span><span class="pun">(</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span><span class="pln"> 


input_frame </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"المدخلات "</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">550</span><span class="pun">)</span><span class="pln">
input_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln">TOP</span><span class="pun">)</span><span class="pln">

btns_frame </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">" لوحة الأرقام والعمليات "</span><span class="pun">,</span><span class="pln">
                        bg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">550</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
btns_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

input_text </span><span class="pun">=</span><span class="pln"> </span><span class="typ">StringVar</span><span class="pun">()</span><span class="pln">

input_label</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"أدخل المعادلة الحسابية "</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"#007CFF"</span><span class="pun">,</span><span class="pln">fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
input_label</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">10</span><span class="pun">)</span><span class="pln">
input_field </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Entry</span><span class="pun">(</span><span class="pln">input_frame</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">=</span><span class="lit">18</span><span class="pun">,</span><span class="pln">
                    font</span><span class="pun">=(</span><span class="str">'arial'</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln"> </span><span class="str">'bold'</span><span class="pun">),</span><span class="pln">textvariable</span><span class="pun">=</span><span class="pln">input_text</span><span class="pun">)</span><span class="pln">
input_field</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">10</span><span class="pun">)</span><span class="pln">


mycalculator</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ونحصل على النتيجة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94345" href="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI3_calculator3.jpg.ae5f17e13be7c2666aa07717b78770a9.jpg" rel=""><img alt="008_GUI3_calculator3.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94345" data-unique="m4yxbqvu0" src="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI3_calculator3.thumb.jpg.be2772b5d414bd6ee60c8646f577ed83.jpg" style="width: 350px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة الثانية
</p>

<p>
	يتبقى لآلتنا جزئية الإطار السفلي والذي يحتوي على الأزرار التي تحوي الأرقام والعمليات الحسابية، وهذا ما سنتناوله في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">المقال القادم</a> كيفية إدراج الأزرار وربطها بوظائف معينة تقوم بها عند النقر عليها.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-label/" rel="external nofollow">Tkinter Label</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_colors.htm" rel="external nofollow">Python - Tkinter Colors</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_fonts.htm" rel="external nofollow">Python - Tkinter Fonts</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_cursors.htm" rel="external nofollow">Python - Tkinter Cursors</a>
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-photoimage/" rel="external nofollow">Tkinter PhotoImage</a>
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-entry/" rel="external nofollow">Tkinter Entry</a>
	</li>
	<li>
		<a href="https://coderslegacy.com/python/python-gui/python-tkinter-entry/" rel="external nofollow">Python Tkinter Entry</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/how-to-bind-the-spacebar-key-to-a-certain-method-in-tkinter" rel="external nofollow">?How to bind the spacebar key to a certain method in Tkinter</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/how-to-bind-the-escape-key-to-close-a-window-in-tkinter" rel="external nofollow">?How to bind the Escape key to close a window in Tkinter</a>
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-event-binding/" rel="external nofollow">Tkinter Event Binding</a>
	</li>
</ul>
<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1378/" rel="">برمجة الواجهات الرسومية باستخدام Tkinter</a>
	</li>
	<li>
		النسخة العربية الكاملة من كتاب: <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1503</guid><pubDate>Mon, 28 Mar 2022 15:03:00 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x639;&#x646;&#x627;&#x635;&#x631; &#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629;  Widgets &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/62378bd33bdc3_------Widgets--.png.72730e80294aa6f5490e63c6e3838d5e.png" /></p>

<p>
	تحدثنا في <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">المقال السابق</a> عن الواجهات الرسومية للتطبيقات باستخدام TKinter، وتطرقنا للنافذة الرئيسية التي هي أساس أي تطبيق مكتوب باستخدام الواجهات وشرحنا مجموعة من خصائصها، سنكمل في هذا المقال ونتحدث عن العناصر التي نضعها على النافذة الرئيسية، والتي يطلق عليها مسمى Widgets.
</p>

<p>
	هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:
</p>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter.</a>
	</li>
	<li>
		مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
</ul>
<h2>
	Widgets
</h2>

<p>
	هي عبارة عن عناصر التحكم التي نضعها على النافذة ليتمكن المستخدم من التفاعل معها، حيث توضح له معنى أو تطلب منه أدخال أمر معين أو تأخذ منه أمرًا لتنفيذ مهمة مطلوبة مثل الأزرار، ومربعات الإدخال وحاويات العناوين ومربعات الإختيار والقوائم وغيرها، الجدول التالي يوضح مجموعة من أهم العناصر التي يمكننا إضافتها على النافذة:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				اسم العنصر
			</th>
			<th>
				 
			</th>
			<th>
				الوصف
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				Label
			</td>
			<td>
				حاوية العنوان
			</td>
			<td>
				يستخدم لعرض سلسلة نصية أو صورة .
			</td>
		</tr>
<tr>
<td>
				Message
			</td>
			<td>
				الرسالة
			</td>
			<td>
				شبيه بحاوية العنوان يستخدم لعرض سلسلة نصية مكونة من أكثر من سطر.
			</td>
		</tr>
<tr>
<td>
				Buttons
			</td>
			<td>
				الأزرار
			</td>
			<td>
				يستخدم لإضافة أزرار تفاعلية في النافذة عند النقر عليها يتم نداء دالة معينة تؤدي وظيفة معينة.
			</td>
		</tr>
<tr>
<td>
				Entry
			</td>
			<td>
				صندوق المدخلات
			</td>
			<td>
				يستخدم لقراءة المدخلات التي يكتبها المستخدم باستخدام لوحة المفاتيح
			</td>
		</tr>
<tr>
<td>
				Frame
			</td>
			<td>
				الإطار
			</td>
			<td>
				يستخدم لترتيب العناصر داخل النافذة بحيث يكون كالحاوية لهم.
			</td>
		</tr>
<tr>
<td>
				Menus
			</td>
			<td>
				القوائم
			</td>
			<td>
				تستخدم لعرض قائمة منسدلة أو منبثقة مكونة من مجموعة من أزرار القائمة.
			</td>
		</tr>
<tr>
<td>
				Menubutton
			</td>
			<td>
				أزرار القائمة
			</td>
			<td>
				عنصر عبارة عن مزيج يدمج ما بين الأزرار والقائمة بحيث يكون من ضمن مجموعة من الخيارات عند النقر عليه يتم تنفيذ أمر معين.
			</td>
		</tr>
<tr>
<td>
				ListBox
			</td>
			<td>
				مربع القائمة
			</td>
			<td>
				يستخدم لعرض قائمة يختار المستخدم منها عنصر.
			</td>
		</tr>
<tr>
<td>
				Checkbutton
			</td>
			<td>
				صناديق التأشير
			</td>
			<td>
				عبارة عن مربعات تسمح للمستخدم بأن يختار منها (تسمح بأكثر من خيار) وتكون بوضعية مفعل أو غير مفعل.
			</td>
		</tr>
<tr>
<td>
				Radiobutton
			</td>
			<td>
				أزرار الانتقاء
			</td>
			<td>
				شبيهة بصناديق التأشير تستخدم بالغالب كمجموعة خيارات لابد من اختيار واحد منها فقط.
			</td>
		</tr>
</tbody>
</table>
<p>
	يوجد جيلين من العناصر في مكتبة Tkinter:
</p>

<ul>
<li>
		العناصر الكلاسيكية التقليدية والتي تم طرحها عام 1991.
	</li>
	<li>
		العناصر الحديثة التي تمت إضافتها عام 2007 تسمى عناصر TTk اختصارًا لكلمة Tk themed تستبدل العديد من العناصر القديمة وليس كلها .
	</li>
</ul>
<p>
	من مميزات استخدام الجيل الجديد من العناصر الحديثة أن مظهر العناصر يختلف باختلاف نظام تشغيل جهاز الحاسب الذي تعمل عليه ليصبح متوافقًا معه، كذلك تسمح العناصر الحديثة بالفصل بين خصائص مظهرها وبين الأوامر التفاعلية التي تقوم بها في ملفات تنسيق مختلفة عن بعضها، ويعتبر من الجيد دائمًا استخدام الجيل الجديد من العناصر باستخدام القالب tkinter.ttk، السطر التالي يستدعي العناصر الكلاسيكية والجديدة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_7" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
</span><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> ttk</span></pre>

<p>
	توضح الشفرة التالية كيفية إنشاء حاوية عنوان Label و زرار Button باستخدام <strong>الطريقة الكلاسيكية</strong>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_9" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln"> </span><span class="com"># النافذة الرئيسية</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" (الطريقة الكلاسيكية) "</span><span class="pun">)</span><span class="pln">

classicL</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'Classic Label'</span><span class="pun">)</span><span class="pln">
classicL</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

classicB</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'Classic Button'</span><span class="pun">)</span><span class="pln">
classicB</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	توضح الشفرة التالية كيفية إنشائهما بإستخدام <strong>الطريقة الحديثة</strong> بإضافة اسم الصنف ttk قبل اسم العنصر:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_11" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> ttk

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln"> </span><span class="com"># النافذة الرئيسية</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"(الطريقة الحديثة ) "</span><span class="pun">)</span><span class="pln">

</span><span class="typ">ThemedL</span><span class="pun">=</span><span class="pln">ttk</span><span class="pun">.</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'Themed Label'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">ThemedL</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

</span><span class="typ">ThemedB</span><span class="pun">=</span><span class="pln">ttk</span><span class="pun">.</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'Themed Button'</span><span class="pun">)</span><span class="pln">
</span><span class="typ">ThemedB</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ستبدو المخرجات عند تشغيل الشيفرة بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94279" href="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI2_Classic_Themed_Widgets.jpg.fc7457fb6d325873a5d18bf350c58e21.jpg" rel=""><img alt="001_GUI2_Classic_Themed_Widgets.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94279" data-unique="w94pcovx5" src="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI2_Classic_Themed_Widgets.thumb.jpg.d3902f974f2257beab3cd5259ad9f95b.jpg" style="width: 700px; height: auto;"></a>
</p>

<p style="text-align: center;">
	الطريقة الحديثة والكلاسيكية لعناصر واجهات المستخدم
</p>

<p>
	قد لا يبدو الاختلاف بينهما واضحًا، لكن كما سبق وقلنا يعتمد المظهر كثيرًا على نوع نظام التشغيل الذي تعمل عليه.
</p>

<h2>
	التحكم بخصائص العناصر
</h2>

<p>
	جميع هذه العناصر لها مجموعة متنوعة من الخصائص بخيارات مختلفة، تختلف باختلاف العناصر حسب ما يتميز به كل عنصر، كما توجد خصائص مشتركة بين غالبية العناصر مثل: لون الخلفية، ولون الخلفية عندما يكون العنصر فعالًا، والنص المكتوب، ولون النص ونوع الخط، ولون النص عندما يكون العنصر فعالًا، والعرض والارتفاع بالبكسل، وشكل المؤشر …إلخ. ويُسمح لنا بالتحكم فيها من خلال 3 طرق إما في وقت الإنشاء من خلال معاملات الدوال أو بعد إنشاء العنصر من خلال استخدام القواميس أو الدالة <code>config</code>:
</p>

<h3>
	1. في وقت الإنشاء من خلال معاملات الدوال keyword arguments
</h3>

<p>
	وهذه هي الطريقة التي استخدمناها حتى الآن لوضع نص في حاوية العنوان أو في الزر كما في المثال السابق بتحديد اسم معامل الدالة بنوع الخاصية المطلوبة وهي <code>text</code> ومن ثم إعطائه قيمة النص المطلوبة أو مثلًا خاصية لون الخط <code>foreground</code> ونضعه مثلًا أزرق <code>blue</code>.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_14" style="">
<span class="pln">L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">'نص باللون الأزرق'</span><span class="pln"> </span><span class="pun">,</span><span class="pln">foreground</span><span class="pun">=</span><span class="str">'blue'</span><span class="pun">)</span></pre>

<p>
	سيبدو الشكل على النحو التالي :
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94280" href="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI2_creation_time.jpg.58a170614140b65a975d5c8643df33b9.jpg" rel=""><img alt="002_GUI2_creation_time.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94280" data-unique="2u0xq24c9" src="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI2_creation_time.jpg.58a170614140b65a975d5c8643df33b9.jpg" style="width: 450px; height: auto;"></a>
</p>

<p style="text-align: center;">
	إضافة خصائص وقت الإنشاء
</p>

<h3>
	2. بعد إنشاء العنصر من خلال استخدام القواميس dictionary index
</h3>

<p>
	تفعل الشفرة التالية نفس الشيء ولكن من خلال الاستفادة من فكرة القواميس، فنقوم بمعاملة العنصر كقاموس ونستخدم اسم الخاصية المراد وضع قيمة لها كمفتاح للقاموس ونضع القيمة المرغوبة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_20" style="">
<span class="pln">L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span><span class="pln"> 
L</span><span class="pun">[</span><span class="str">'text'</span><span class="pun">]=</span><span class="str">'نص باللون الأزرق'</span><span class="pln"> 
</span><span class="pun">[</span><span class="str">'foreground'</span><span class="pun">]=</span><span class="pln"> </span><span class="str">'blue'</span></pre>

<h3>
	3. بعد إنشاء العنصر باستخدام الدالة config
</h3>

<p>
	سنكرر نفس ما قمنا به ولكن باستخدام الدالة <code>config</code> وإرسال المعاملات المطلوب تعديلها بشكل مشابه للطريقة الأولى على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_22" style="">
<span class="pln">L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span><span class="pln"> 
L</span><span class="pun">.</span><span class="pln">config</span><span class="pun">(</span><span class="pln">text</span><span class="pun">=</span><span class="str">'نص باللون الأزرق'</span><span class="pln"> </span><span class="pun">,</span><span class="pln">foreground</span><span class="pun">=</span><span class="str">'blue'</span><span class="pun">)</span></pre>

<h2>
	كيفية وضع العناصر على النافذة
</h2>

<p>
	يوجد ثلاثة طرق مختلفة لوضع العناصر على النافذة الرئيسية باستخدام ثلاثة دوال:
</p>

<ul>
<li>
		الدالة<code>Pack</code>: تضع العناصر على النافذة في مجموعة من المربعات الطولية والأفقية وهي محدودة بأربعة خيارات: يمين، يسار، أسفل، أعلى النافذة.
	</li>
	<li>
		الدالة <code>Place</code>: تضع العناصر على النافذة باستخدام الإحداثيات السينية X والصادية Y في المستوي الثنائي.
	</li>
	<li>
		الدالة <code>grid</code>: تضع العناصر على النافذة باستخدام شبكة ثنائية مكونة من أعمدة وصفوف.
	</li>
</ul>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		<strong>ملاحظة مهمة</strong>: لا ينبغي الدمج بين الدوال الثلاثة في نفس النافذة الرئيسية، بل يفضل اختيار واحدة منها والالتزام باستخدامها.
	</p>
</blockquote>

<h3>
	إضافة العناصر باستخدام الدالة Pack
</h3>

<p>
	تعد الدالة <code>Pack</code> أسهل طريقة لوضع العناصر على النافذة، و ترتب العناصر بجانب بعضها البعض عموديًا أو افقيًا بدلًا من تحديد الموضع الدقيق للعنصر، بكل بساطة يتم نداء الدالة بعد اسم العنصر المطلوب وضعه على النافذة كما شاهدنا في الأمثلة السابقة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_24" style="">
<span class="typ">Widget_name</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span></pre>

<p>
	يمكن التحكم بمكان العنصر من خلال استخدام الخاصية <code>side</code> والتي يمكننا إعطائها 4 قيم مختلفة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_26" style="">
<span class="typ">Side</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> LEFT </span><span class="kwd">or</span><span class="pln"> RIGHT </span><span class="kwd">or</span><span class="pln"> TOP </span><span class="kwd">or</span><span class="pln"> BOTTOM</span></pre>

<p>
	سنرسم نافذة رئيسية نضيف لها 3 حاويات عنوان <code>Label</code> باسم <code>L1,L2,L3</code> على الترتيب باستخدام الدالة <code>Pack</code> نضع لكل حاوية نصًا بلون أبيض باستخدام الخاصية <code>fg</code> وهي اختصار <code>foreground</code> أي لون النص، وخلفية بلون مختلف لكل حاوية باستخدام الخاصية <code>bg</code> وهي اختصار <code>Background</code> أي لون الخلفية، واستخدمنا الخاصية <code>pady</code> لتبعد كل حاوية عن الأخرى مسافة 20 بكسل على المحور الصادي، ستكون الشفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_28" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">

L1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أحمر"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"red"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L1</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">
L2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أخضر"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"green"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L2</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">
L3 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أزرق"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L3</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">pady</span><span class="pun">=</span><span class="lit">20</span><span class="pun">,</span><span class="pln"> side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	سنعيد تشغيل الشيفرة أكثر من مرة وسنستخدم في كل مرة اتجاهًا مختلفًا للخاصية <code>side</code> ونرى كيف تقوم الدالة بترتيبهم مع بعضهم البعض في النافذة، عموديا عند استخدام الخيار <code>TOP</code> أو <code>BOTTOM</code> وأفقيًا عند اختيار <code>LEFT</code> أو <code>RIGHT</code> سنحصل على النتائج التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94281" href="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI2_pack.jpg.4d1cdeb02a25d55e22e650b705f48642.jpg" rel=""><img alt="003_GUI2_pack.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94281" data-unique="if4z0nf9x" src="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI2_pack.thumb.jpg.cbf4d44650d7df96406f1ef71ef108ce.jpg" style="width: 400px; height: auto;"></a>
</p>

<p style="text-align: center;">
	استخدام الدالة pack
</p>

<p>
	كما أشرنا آنفًا، تترك الخاصية <code>pad</code> مساحة حول العنصر بمحيطه الخارجي تتمثل فيه الخاصيتين التاليتين:
</p>

<ul>
<li>
		<code>pady</code> تترك مساحة حول العنصر على الإحداثي الصادي .
	</li>
	<li>
		<code>padx</code> تترك مساحة حول العنصر على الإحداثي السيني.
	</li>
</ul>
<p>
	وتوجد أيضًا الخاصية <code>ipad</code> التي تترك مساحة حول العنصر بمحيطه الداخلي تتمثل في الخاصيتين التاليتين:
</p>

<ul>
<li>
		<code>ipady</code> تترك مساحة بين العنصر وحدوده الداخلية على المحور الصادي.
	</li>
	<li>
		<code>ipadx</code> تترك مساحة بين العنصر وحدوده الداخلية على المحور السيني.
	</li>
</ul>
<p>
	تضع الشفرة التالية حدودًا حول العنصر من محيطه الخارجي ومحيطه الداخلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_33" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">

L1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أحمر"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"red"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L1</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx</span><span class="pun">=</span><span class="lit">150</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> pady</span><span class="pun">=</span><span class="lit">50</span><span class="pln"> </span><span class="pun">,</span><span class="pln">ipadx</span><span class="pun">=</span><span class="lit">150</span><span class="pun">,</span><span class="pln">ipady</span><span class="pun">=</span><span class="lit">200</span><span class="pln"> </span><span class="pun">,</span><span class="pln">side</span><span class="pun">=</span><span class="pln"> TOP</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	فنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94282" href="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI2_pad_ipad.jpg.7aedf99f71142ba0d0b98dab8d2fe8e3.jpg" rel=""><img alt="004_GUI2_pad_ipad.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94282" data-unique="b8bxlbofn" src="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI2_pad_ipad.thumb.jpg.fbbbd6d9ffcc7f72b783a04064160ff1.jpg" style="width: 450px; height: auto;"></a>
</p>

<p style="text-align: center;">
	استخدام الخاصية pad و ipad
</p>

<h3>
	إضافة العناصر باستخدام الدالة place
</h3>

<p>
	تستخدم الدالة <code>place</code> الإحداثيات السينية والصادية لوضع العناصر على النافذة، فيبدأ رسم العنصر من الزاوية العلوية اليسرى، وسنعيد استخدام الشفرة السابقة مع تبديل الدالة <code>Pack</code> إلى الدالة <code>place</code> وسنحاول وضع العناصر أسفل بعضها البعض متدرجة فنكتب الشفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_37" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">

L1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أحمر"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"red"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L1</span><span class="pun">.</span><span class="pln">place</span><span class="pun">(</span><span class="pln">x</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
L2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أخضر"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"green"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L2</span><span class="pun">.</span><span class="pln">place</span><span class="pun">(</span><span class="pln">x</span><span class="pun">=</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">=</span><span class="lit">40</span><span class="pun">)</span><span class="pln">
L3 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أزرق"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L3</span><span class="pun">.</span><span class="pln">place</span><span class="pun">(</span><span class="pln">x</span><span class="pun">=</span><span class="lit">110</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">=</span><span class="lit">80</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ونحصل على النتيجة التالية:
</p>

<p style="text-align: center;">
	<img alt="005_GUI2_place.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94283" data-unique="b0fqqsv7z" src="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI2_place.jpg.70e840b5b7afbbb6a61c33084fdd567b.jpg" style="width: 200px; height: auto;"></p>

<p style="text-align: center;">
	استخدام الدالة place
</p>

<p>
	قد لا يكون استخدام الدالة <code>place</code> متوافقًا مع جميع شاشات الأجهزة، لو قمت بتشغيل الشفرة السابقة على جهاز يعمل بنظام تشغيل ويندوز بدقة شاشة 1024* 768 فستظهر العناصر بشكل متناسق، بينما لو اختلفت دقة الشاشة وكانت عالية الدقة مثلًا فستظهر العناصر شديدة الصغر ومتداخلة، وعلى العكس لو كانت الدقة منخفضة قد لا تظهر العناصر كاملة، لذلك يفضل تجنب استخدامها قدر المستطاع حتى لا نقع في مثل هذه المشاكل عند اختلاف الدقة.
</p>

<h3>
	إضافة العناصر باستخدام الدالة grid
</h3>

<p>
	تقسم الدالة <code>grid</code> النافذة الرئيسية إلى مجموعة من الأعمدة والصفوف بشكل افتراضي، لتكون بشكل تقريبي كالشبكة فتوضع العناصر فيها بتحديد رقم العمود ورقم الصف، يبدأ الترقيم من الصفر، وسنعيد ترتيب عناصرنا كما المثال السابق باستخدام الدالة <code>grid</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_40" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">

L1 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أحمر"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">"red"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L1</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
L2 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أخضر"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"green"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L2</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
L3 </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"أزرق"</span><span class="pun">,</span><span class="pln"> bg</span><span class="pun">=</span><span class="str">"blue"</span><span class="pun">,</span><span class="pln"> fg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">)</span><span class="pln">
L3</span><span class="pun">.</span><span class="pln">grid</span><span class="pun">(</span><span class="pln">row</span><span class="pun">=</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="lit">6</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على نفس شكل النافذة السابق.
</p>

<p>
	ومن الخصائص التي يمكن التحكم بها لهذه الشبكة هي عرض الأعمدة والصفوف وقد قدمت لنا مكتبة Tkinter دالتين للتحكم بإعدادات الشبكة قبل بدء وضع العناصر عليها وهما الدالة <code>columnconfigure</code> والتي تأخذ رقم العمود والدالة <code>rowconfigure</code> والتي تأخذ رقم الصف ومن ثم تُعطى الدالة العرض المراد تحديده لكلٍ منهما وتكتب على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_42" style="">
<span class="pln">main_window</span><span class="pun">.</span><span class="pln">columnconfigure</span><span class="pun">(</span><span class="pln">index</span><span class="pun">,</span><span class="pln"> weight</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">rowconfigure</span><span class="pun">(</span><span class="pln">index</span><span class="pun">,</span><span class="pln"> weight</span><span class="pun">)</span></pre>

<p>
	حيث تمثل <code>index</code> رقم الصف أو العمود وتمثل <code>weight</code> العرض المراد تحديده لهما، بالطبع العرض يكون كنسبة وتناسب مع بقية الأعمدة في الشبكة. كما نستطيع أيضًا أن نستخدم مع هذه الدالة الخاصيتان <code>pad</code> و <code>ipad</code> بالنفس الطريقة التي سبق شرحها.
</p>

<h2>
	الإطارات Frames
</h2>

<p>
	تعد الإطارات من العناصر المهمة لواجهات المستخدم الرسومية، فهي تستخدم لتجميع وتنظيم العناصر الأخرى بداخلها فتكون بمثابة الحاوية لهم، وتكون بمثابة مساحات مستطيلة توضع على النافذة الرئيسية لتنظيم تخطيط النافذة وترتيب وضع أماكن بقية العناصر عليها، كما يمكننا إنشاء إطار بداخل إطار آخر مما يعطينا قدرة أكبر على الترتيب.
</p>

<p>
	والإطارات بالنسبة للنافذة كما الهيكل العظمي للجسد يساعد على استقامته ويعطيه المظهر الملائم والهيئة المطلوبة.
</p>

<h3>
	إنشاء الإطار داخل النافذة
</h3>

<p>
	يجري اختيار اسم المتغير المطلوب ليمثل الإطار ويستخدم الصنف Frame من مكتبة Tkinter، ويعطى الصنف أولًا المكان الذي نريد وضع الإطار فيه وهو النافذة الرئيسية، ومن ثم بإمكاننا إضافة بقية الخصائص على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_44" style="">
<span class="typ">First_frame</span><span class="pun">=</span><span class="pln"> </span><span class="typ">Frame</span><span class="pln"> </span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">)</span></pre>

<h3>
	خصائص الإطار
</h3>

<p>
	يمكننا تغيير الكثير من خصائص الإطار كتعديل عرض width وطول height الإطار وكذلك تغيير لون الخلفية bg، وسمك الحدود bd، ونوع الحدود relief حيث يمكن إعطاءها قيم مختلفة مثل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_46" style="">
<span class="pln">raised</span><span class="pun">,</span><span class="pln"> flat</span><span class="pun">,</span><span class="pln"> ridge</span><span class="pun">,</span><span class="pln"> groove </span><span class="pun">,</span><span class="pln"> sunken</span><span class="pun">.</span></pre>

<p>
	الشفرة التالية ترسم إطارين على النافذة الرئيسية أحدهما أخضر على اليمين والآخر أزرق على اليسار باستخدام الدالة <code>pack</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_48" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الإطارات "</span><span class="pun">)</span><span class="pln"> 

right_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">300</span><span class="pun">,</span><span class="pln">height</span><span class="pun">=</span><span class="lit">300</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'green'</span><span class="pun">)</span><span class="pln">
right_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'right'</span><span class="pun">)</span><span class="pln">

left_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">300</span><span class="pun">,</span><span class="pln">height</span><span class="pun">=</span><span class="lit">300</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'blue'</span><span class="pun">)</span><span class="pln">
left_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'left'</span><span class="pun">)</span><span class="pln"> 

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تشغيل الشفرة نحصل على التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94284" href="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI2_frame.jpg.c7cd7b72b99a5f4cd334b5471cc66dca.jpg" rel=""><img alt="006_GUI2_frame.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94284" data-unique="ie260c7p9" src="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI2_frame.jpg.c7cd7b72b99a5f4cd334b5471cc66dca.jpg" style="width: 400px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بإطار
</p>

<p>
	كما قلنا سابقًا، الهدف الأساسي من الإطارات تنظيم النافذة، سنقوم برسم نافذة تحتوي على عدة إطارات في كل اتجاه، و سنوزع العناصر فيها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_60" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الإطارات "</span><span class="pun">)</span><span class="pln"> 

top_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'red'</span><span class="pun">)</span><span class="pln">
top_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'top'</span><span class="pun">)</span><span class="pln">
top_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">top_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار علوي'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
top_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

right_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'green'</span><span class="pun">)</span><span class="pln">
right_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'right'</span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">right_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار على اليمين'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

left_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'blue'</span><span class="pun">)</span><span class="pln">
left_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'left'</span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">left_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار على اليسار'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

bottom_frame</span><span class="pun">=</span><span class="typ">Frame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'yellow'</span><span class="pun">)</span><span class="pln">
bottom_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'bottom'</span><span class="pun">)</span><span class="pln">
bottom_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">bottom_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار سفلي'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
bottom_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94285" href="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI2_colored_frame.jpg.624725baaeae9d42da12c8f2cd6dd3e5.jpg" rel=""><img alt="007_GUI2_colored_frame.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94285" data-unique="14u89y7d5" src="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI2_colored_frame.thumb.jpg.4774b0e46221d4148eb7392352d119e0.jpg" style="width: 600px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بإطارات ملونة
</p>

<h3>
	الإطارات بحاوية عنوان LabelFrame
</h3>

<p>
	هي نوع مخصص من الإطارات حيث تكون كالإطارات العادية من حيث أنها تكون حاوية لعناصر واجهات المستخدم الرسومية وتزيد عليها باحتوائها على حاوية عنوان تزيد من وضوح الهدف من الإطار وإظهارًا لمزيد من الترتيب في النوافذ المعقدة التي تحتوي الكثير من العناصر.
</p>

<p>
	تُنشأ الإطارات بحاوية عنوان باختيار اسم المتغير المطلوب ليمثل الإطار ويُستخدم الصنف LabelFrame من مكتبة Tkinter لذلك، ويعطى الصنف أولًا المكان الذي نريد وضع الإطار فيه وهو النافذة الرئيسية، ومن ثم نعطيه النص الذي نريد إظهاره كعنوان للإطار باستخدام الخاصية text ومن ثم يمكننا إضافة بقية الخصائص على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_65" style="">
<span class="pln">labeled_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">10</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"test"</span><span class="pun">)</span></pre>

<p>
	سنعيد كتابة الشفرة السابقة باستخدام الإطارات بحاوية عنوان، وتكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_67" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">" الإطارات "</span><span class="pun">)</span><span class="pln"> 

top_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"علوي"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'red'</span><span class="pun">)</span><span class="pln">
top_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'top'</span><span class="pun">)</span><span class="pln">
top_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">top_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار علوي'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
top_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

right_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"أيمن"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'green'</span><span class="pun">)</span><span class="pln">
right_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'right'</span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">right_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار على اليمين'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

left_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"أيسر"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'blue'</span><span class="pun">)</span><span class="pln">
left_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'left'</span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">left_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار على اليسار'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
right_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

bottom_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"سفلي"</span><span class="pun">,</span><span class="pln">bg</span><span class="pun">=</span><span class="str">'yellow'</span><span class="pun">)</span><span class="pln">
bottom_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="str">'bottom'</span><span class="pun">)</span><span class="pln">
bottom_button</span><span class="pun">=</span><span class="typ">Button</span><span class="pun">(</span><span class="pln">bottom_frame</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">' زرار في إطار سفلي'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
bottom_button</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">padx </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">,</span><span class="pln"> pady </span><span class="pun">=</span><span class="pln"> </span><span class="lit">40</span><span class="pun">)</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	وسنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94286" href="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI2_Labelframe.jpg.44f1a946b87b80fc92ba960829b72cb2.jpg" rel=""><img alt="008_GUI2_Labelframe.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94286" data-unique="hn17mqcyy" src="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI2_Labelframe.thumb.jpg.e4d8e59dda54ed77ed5bb039a9e702e8.jpg" style="width: 600px; height: auto;"></a>
</p>

<p style="text-align: center;">
	نافذة بإطار بحاوية عنوان
</p>

<h2>
	تطبيق الآلة الحاسبة (الجزء الثاني)
</h2>

<p>
	سنطبق ما تعلمناه في هذا المقال على تطبيق الآلة الحاسبة، فسنحاول تقسيم النافذة إلى جزئين جزء تعرض فيه المدخلات وهي نتيجة ما يتم النقر عليه، وجزء ثانيٍ وهو جزء الأزرار والعمليات، سنقسم الشاشة إلى إطارين باستخدام الخاصية LabelFrame، الأول للجزء العلوي ونعطيه اسم input_frame و نعنونه بالعنوان "المدخلات"، والآخر للجزء السفلي باسم btns_frame ونعنونه بالعنوان "لوحة الأرقام والعمليات"، سنستخدم هنا نوعين من الإضافة أولًا للإطارات باستخدام الدالة pack ومن ثم باستخدام الشبكة grid لبقية العناصر داخل الإطارات، نضيف هذا الجزء على الشفرة السابقة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_9946_70" style="">
<span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

</span><span class="kwd">from</span><span class="pln"> ctypes </span><span class="kwd">import</span><span class="pln"> windll
windll</span><span class="pun">.</span><span class="pln">shcore</span><span class="pun">.</span><span class="typ">SetProcessDpiAwareness</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln">

mycalculator </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"آلة حاسبة بسيطة"</span><span class="pun">)</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="str">"575x740"</span><span class="pun">)</span><span class="pln">  
mycalculator</span><span class="pun">.</span><span class="pln">resizable</span><span class="pun">(</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span><span class="pln"> 

input_frame</span><span class="pun">=</span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">"المدخلات"</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">575</span><span class="pun">)</span><span class="pln">
input_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">(</span><span class="pln">side</span><span class="pun">=</span><span class="pln">TOP</span><span class="pun">)</span><span class="pln">

btns_frame </span><span class="pun">=</span><span class="pln"> </span><span class="typ">LabelFrame</span><span class="pun">(</span><span class="pln">mycalculator</span><span class="pun">,</span><span class="pln">text</span><span class="pun">=</span><span class="str">" لوحة الأرقام والعمليات "</span><span class="pun">,</span><span class="pln">
                        bg</span><span class="pun">=</span><span class="str">"white"</span><span class="pun">,</span><span class="pln">width</span><span class="pun">=</span><span class="lit">575</span><span class="pun">,</span><span class="pln">bd</span><span class="pun">=</span><span class="lit">3</span><span class="pun">)</span><span class="pln">
btns_frame</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

mycalculator</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	حددنا عرض الإطارات بنفس عرض النافذة، و بحدود bd بمقدار عرض 3، وضعنا خلفية الإطار الثاني بلون أبيض.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="94287" href="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI2_calculator_1.jpg.8f7ca18aa0e9c44f23e9e1a3f093c3f8.jpg" rel=""><img alt="009_GUI2_calculator_1.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94287" data-unique="cj1k67wh9" src="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI2_calculator_1.thumb.jpg.756dc3770abcd9a8f998d76cb77b1882.jpg" style="width: 300px; height: auto;"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة الثانية
</p>

<p>
	عند تشغيل الشفرة قد لا تبدو مختلفة عن الشفرة السابقة وذلك نتيجة لعدم وضعنا أي عنصر داخل الإطارات فقد لا تبدو حدودها واضحة، سنكمل في <a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">المقال التالي</a> تطبيقنا ونضع العناصر داخل النافذة لتظهر لنا الإطارات بصورة واضحة طبعًا بعد التعرف عليها.
</p>

<p>
	سنتناول في المقال التالي حاوية العنوان وصناديق المدخلات وهي ما يحتوي عليه الإطار العلوي في نافذة الآلة الحاسبة.
</p>

<h2>
	المصادر
</h2>

<ul>
<li>
		<a href="https://www.geeksforgeeks.org/introduction-to-tkinter/?ref=lbp" rel="external nofollow">Introduction to Tkinter</a>.
	</li>
	<li>
		<a href="https://coderslegacy.com/python/python-gui/" rel="external nofollow">Python GUI with Tkinter</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-options/" rel="external nofollow">Ttk Widgets</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-pack/" rel="external nofollow">Tkinter Pack</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-grid/" rel="external nofollow">Tkinter Grid</a>.
	</li>
	<li>
		<a href="https://runestone.academy/runestone/books/published/thinkcspy/GUIandEventDrivenProgramming/09_modifying_widgets.html" rel="external nofollow">Common Widget Properties</a>.
	</li>
	<li>
		<a href="https://www.activestate.com/resources/quick-reads/how-to-position-widgets-in-tkinter/" rel="external nofollow">How To Position Widgets In Tkinter</a>.
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-frame/" rel="external nofollow">Tkinter Frame</a>.
	</li>
	<li>
		<a href="https://coderslegacy.com/python/python-gui/python-tkinter-frame/" rel="external nofollow">Python Tkinter Frame</a>.
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/tk_labelframe.htm" rel="external nofollow">Python - Tkinter LabelFrame</a>.
	</li>
	<li>
		Introduction to Programming Using Python by Y. Daniel Liang ) Chapter 9 GUI Programming Using Tkinter).
	</li>
	<li>
		Tony Gaddis - Starting Out with Python, Global Edition (2018, Pearson Education),(Chapter 13 GUI Programming).
	</li>
</ul>
<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>
	</li>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/" rel="">واجهات المستخدم الرسومية في بايثون باستخدام TKinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1378/" rel="">برمجة الواجهات الرسومية باستخدام Tkinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
	<li>
		النسخة العربية الكاملة من كتاب: <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1502</guid><pubDate>Fri, 25 Mar 2022 16:04:00 +0000</pubDate></item><item><title>&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x633;&#x62A;&#x62E;&#x62F;&#x645; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; TKinter</title><link>https://academy.hsoub.com/programming/python/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1501/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/623788bc0501a_------TKinter.png.66ae76543bea1c88e12a53c59d7569ff.png" /></p>
<p>
	يستخدم المبرمج في بداية <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a> واجهة <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%A7-%D9%87%D9%88-%D8%B3%D8%B7%D8%B1-%D8%A7%D9%84%D8%A3%D9%88%D8%A7%D9%85%D8%B1-%D8%9F-r353/" rel="">سطر الأوامر</a> command-line interface، وهي واجهة تعتمد اعتمادًا كليًا على المدخلات من خلال لوحة المفاتيح، يكون فيها ترتيب الأحداث معتمدًا على طريقة كتابة المبرمج للأوامر ولا رأي للمستخدم في ترتيب سير الأحداث، وعلى العكس من ذلك تكون الواجهات الرسومية Graphical Interface التي يتفاعل فيها المستخدم مع البرنامج من خلال مؤشر الفأرة ويكون المتحكم في سير الأوامر من خلال ما يختار أن ينقر عليه أو أن يقوم بتعبئته من المدخلات بالترتيب الذي يرغبه، فيتفاعل مع العديد من العناصر كالأزرار والقوائم وصناديق المدخلات وغيرها.
</p>

<p>
	تقدم لغة البرمجة بايثون العديد من الخيارات لتطوير واجهة المستخدم الرسومية GUI اختصارًا للعبارة <code>Graphical User Interface</code> فعلى سبيل المثال:
</p>

<ul>
	<li>
		<a href="https://wxpython.org/" rel="external nofollow">wxPython</a> وهي أداة مفتوحة المصدر لتطوير الواجهات على عدة منصات مثل ويندوز وماك وغيرهما.
	</li>
	<li>
		<a href="https://www.jython.org/" rel="external nofollow">JPython</a> وهو تطبيق جافا للبايثون يجمع ما بين قوة التعبير والوضوح.
	</li>
	<li>
		<code>Tkinter</code> وهي مكتبة واجهات رسومية مدمجة مع نسخة بايثون.
	</li>
</ul>

<p>
	سنشرح في سلسلة مقالاتنا هذه أساسيات المكتبة Tkinter بالتفصيل.
</p>

<p>
	هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:
</p>

<ul>
	<li>
		واجهات المستخدم الرسومية في بايثون باستخدام TKinter.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AD%D8%A7%D9%88%D9%8A%D8%A9-%D8%A7%D9%84%D8%B9%D9%86%D9%88%D8%A7%D9%86-label-%D9%88%D8%B5%D9%86%D8%A7%D8%AF%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-entry-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1503/" rel="">حاوية العنوان label وصناديق المدخلات Entry في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-buttons-%D9%88%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-messagebox-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1510/" rel="">الأزرار Buttons ومربعات الرسائل messagebox في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D9%8A%D8%A7%D8%B1-%D9%88%D8%A3%D8%B2%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D9%82%D8%A7%D8%A1-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-%D9%81%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1511/" rel="">مربعات الاختيار وأزرار الانتقاء والقوائم في واجهة المستخدم الرسومية في بايثون</a>.
	</li>
</ul>

<h2>
	Tkinter
</h2>

<p>
	تعد مكتبة <code>Tkinter</code> -وتنطق <code>T-K-Inter</code> اختصار لكلمة <code>Tk Interface</code> أي واجهات <code>TK</code>- الأشهر والأكثر استخدامًا، ومن حسن الحظ أنها تأتي مدمجة مع <a href="https://wiki.hsoub.com/Python" rel="external">لغة البايثون</a> النسخة 3، فلا حاجة لتثبيت أي مكون إضافي لاستخدامها، فهي مكتبة لتطوير الواجهات الرسومية تحوي مجموعة أدوات لعناصر واجهة المستخدم وهي مفتوحة المصدر تستخدمها عدة لغات لتطوير الواجهات لأنظمة ويندوز وماك ويونكس.
</p>

<p>
	تعد المكتبة <code>Tkinter</code> في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a> اختيارًا جيدًا لإنشاء الواجهات الرسومية لعدة أسباب أهمها أنها سهلة التعلم، ويستخدم فيها القليل جدًا من التعليمات البرمجية لإنشاء تطبيق سطح مكتب يعمل بشكل ممتاز، ونستطيع تشغيلها على مختلف <a href="https://academy.hsoub.com/files/24-%D8%A3%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/" rel="">أنظمة التشغيل</a>، وكما قلنا مسبقًا لا نحتاج أي مجهودٍ في تثبيتها فهي تأتي مع البايثون بشكل افتراضي. كل هذه المميزات تجعلها نقطة انطلاق قوية للمبتدئين والمتوسطين لتعلم الواجهات الرسومية في البايثون.
</p>

<p>
	سنسلط الضوء في هذه السلسلة على المكونات الأساسية في مكتبة <code>Tkinter</code> لصنع واجهة مستخدم رسومية للشفرات البرمجية في بايثون، ونفترض هنا أن القارئ لديه خبرة بأساسيات لغة <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> لذا ننصحك إن لم يكن لديك خبرة ببايثون بالرجوع إلى سلسلة مقالات <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">المرجع الشامل إلى تعلم لغة بايثون</a>.
</p>

<p>
	تعد عملية إنشاء واجهة رسومية باستخدام <code>Tkinter</code> مهمة سهلة تتضمن مجموعة من الخطوات:
</p>

<ul>
	<li>
		استيراد الوحدة <code>Tkinter</code>.
	</li>
	<li>
		إنشاء النافذة الرئيسية (التي ستحتوي على جميع العناصر الرسومية).
	</li>
	<li>
		إضافة أي عدد من المكونات التي تحتاجها إلى النافذة الرئيسية.
	</li>
	<li>
		تفعيل دوال الاستجابة لمكوناتك الرسومية.
	</li>
</ul>

<p>
	هذه هي ببساطة خطوات إنشاء واجهة رسومية لبرنامج.
</p>

<div class="banner-container ipsBox ipsPadding">
	<div class="inner-banner-container">
		<p class="banner-heading">
			دورة تطوير التطبيقات باستخدام لغة Python
		</p>

		<p class="banner-subtitle">
			احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة
		</p>

		<div>
			<a class="ipsButton ipsButton_large ipsButton_primary ipsButton_important" href="https://academy.hsoub.com/learn/python-application-development" rel="">اشترك الآن</a>
		</div>
	</div>

	<div class="banner-img">
		<img alt="دورة تطوير التطبيقات باستخدام لغة Python" src="https://academy.hsoub.com/learn/assets/images/courses/python-application-development.png">
	</div>
</div>

<h2>
	إنشاء أول واجهة رسومية لبرنامجك
</h2>

<p>
	سنصمم أبسط واجهة رسومية، وهي عبارة عن نافذة تحتوي على عبارة "مرحبًا بواجهات بايثون".
</p>

<p>
	الخطوة الأولى هي استدعاء المكتبة <code>Tkinter</code> باستخدام الأمر <code>import</code> ويمكن القيام بذلك بصيغتين مختلفتين
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3347_23" style=""><span class="kwd">import</span><span class="pln"> tkinter
</span><span class="com"># أو</span><span class="pln">
</span><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span></pre>

<p>
	هنالك اختلاف طفيف في التعامل مع كل صيغة، سنبدأ باستخدام <code>الصيغة الأولى</code>، فبعد استدعاء المكتبة نحتاج لإنشاء النافذة الرئيسية وذلك بإنشاء كائن <code>object</code> من الصنف <code>TK class</code>، ونختار له اسمًا لنفترض مثلًا سنُطلق عليه النافذة الرئيسية <code>main_window</code>، سيكون الأمر البرمجي بهذا الشكل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3347_25" style=""><span class="kwd">import</span><span class="pln"> tkinter

main_window </span><span class="pun">=</span><span class="pln"> tkinter</span><span class="pun">.</span><span class="typ">Tk</span><span class="pun">()</span></pre>

<p>
	عند استخدام الصيغة الأولى من الاستدعاء يُفترض أن تستخدم محتويات المكتبة باستخدام اسم المكتبة في كل مرة <code>model.class</code> وقد تكون الكتابة بهذة الصيغة طويلة قليلًا لذلك يمكننا اختيار اسم مختصر للمكتبة أثناء الاستدعاء واستخدامه بديلًا عن الاسم الأساسي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3347_28" style=""><span class="kwd">import</span><span class="pln"> tkinter </span><span class="kwd">as</span><span class="pln"> TK

main_window </span><span class="pun">=</span><span class="pln"> TK</span><span class="pun">.</span><span class="typ">Tk</span><span class="pun">()</span></pre>

<p>
	بعد ذلك نقوم باستدعاء الدالة <code>mainloop</code> والتي تقوم بعمل استمرار لانهائي لظهور النافذة بانتظار بانتظار أن يتفاعل معها المستخدم أو يغلقها، ويكون ذلك بإضافة السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3347_30" style=""><span class="kwd">import</span><span class="pln"> tkinter </span><span class="kwd">as</span><span class="pln"> TK

main_window </span><span class="pun">=</span><span class="pln"> TK</span><span class="pun">.</span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تشغيل هذه الشفرة ستظهر لنا نافذة بالشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94266" href="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI_first_window.jpg.4d96d7c22665a26f0fbebdb299cf3011.jpg" rel=""><img alt="001_GUI_first_window.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94266" data-unique="vzumwi3jt" style="width: 250px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/001_GUI_first_window.jpg.4d96d7c22665a26f0fbebdb299cf3011.jpg"></a>
</p>

<p style="text-align: center;">
	النافذة الرئيسية
</p>

<p>
	يمكننا إعادة كتابة الشيفرة باستخدام الصيغة الثانية لاستدعاء المكتبة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_3347_33" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	يستدعي السطر الأول جميع محتويات المكتبة <code>Tkinter</code> من أصناف ودوال وثوابت لتكون من ضمن برنامجك، لذلك نستخدم اسم المصنف فقط دون الحاجة إلى استخدام اسم المكتبة قبل اسم المصنف كما في السابق، وعند تشغيل الشفرة سنحصل على نفس النتيجة للشفرة بالصيغة الأولى.
</p>

<p>
	لنقم الآن بإضافة عنصر داخل نافذتنا الرئيسية لنكتب فيه النص المراد إظهاره، لعمل ذلك نحتاج أن ننشئ كائنًا (نختار له اسمًا) من نوع المصنف <code>Label</code> يحتاج هذا الكائن على الأقل إلى تحديد المكان الذي يجب أن يوضع فيه بالإضافة إلى النص الذي تريد كتابته، فتكتب الشفرة على النحو التالي:
</p>

<pre class="ipsCode">L=Label(main_window, text="مرحباً بواجهات بايثون ")
</pre>

<p>
	بعد ذلك نقوم باستخدام الدالة <code>pack</code> والتي تقوم بوضع الكائن على نافذتنا الرئيسية لتكون الشفرة كاملةً على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_6" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> 
main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">

L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"مرحباً بواجهات بايثون "</span><span class="pun">)</span><span class="pln"> 
L</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	عند تشغيل البرنامج سنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94267" href="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI_window_with_label.jpg.de6276362b059a8e9ac1d56e6c435067.jpg" rel=""><img alt="002_GUI_window_with_label.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94267" data-unique="e0qpeac4q" style="width: 150px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/002_GUI_window_with_label.jpg.de6276362b059a8e9ac1d56e6c435067.jpg"></a>
</p>

<p style="text-align: center;">
	نافذة بنص
</p>

<h2>
	خصائص النافذة الرئيسية
</h2>

<p>
	هناك الكثير من الخصائص التي يمكن تنسيقها للنافذة لترتيب مظهرها من أبعاد ولون خلفية وحدود ونحوه، الفقرات التالية تشرح أهم الخصائص:
</p>

<h3>
	عنوان النافذة
</h3>

<p>
	نضيف عنوانًا لنافذتنا الرئيسية باستخدام الدالة <code>title</code> مع الكائن الذي قمنا بتعريفه لها، بطبيعة الأمر سنقوم بإرسال النص الذي نريد كتابته كعنوان للدالة <code>title</code> ونضيفه إلى الشفرة السابقة في السطر الثالث بهذه الطريقة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_9" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> 
main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"أول واجهة رسومية "</span><span class="pun">)</span><span class="pln">

L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"مرحباً بواجهات بايثون "</span><span class="pun">)</span><span class="pln"> 
L</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	فنحصل على الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94268" href="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI_window_with_title.jpg.5c9db9db12dcbf565da5228d8d83b72b.jpg" rel=""><img alt="003_GUI_window_with_title.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94268" data-unique="ylxk3ud1y" style="width: 300px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/003_GUI_window_with_title.jpg.5c9db9db12dcbf565da5228d8d83b72b.jpg"></a>
</p>

<p style="text-align: center;">
	نافذة بعنوان
</p>

<p>
	وعلى عكس العملية السابقة بإمكاننا استرجاع عنوان أي نافذة باستخدام القيمة المرجعة من نفس الدالة <code>title</code> ولكن بدون إرسال أي شي لها على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_12" style=""><span class="pln">the_title</span><span class="pun">=</span><span class="pln">main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">()</span></pre>

<p>
	وبناءً على مثالنا السابق يفترض أن تكون قيمة المتغير <code>the_title</code> هي جملة "أول واجهة رسومية".
</p>

<h3>
	حجم ومكان ظهور النافذة
</h3>

<p>
	نتحكم بحجم النافذة وكذلك مكان ظهورها باستخدام الدالة <code>geometry</code> وفيها نحتاج لتحديد الطول والعرض بالبكسل، وكذلك الإحداثي السيني والصادي للمكان المراد ظهور النافذة فيه في شاشة العرض، ويوضح الشكل التالي التفاصيل بالنسبة للشاشة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94269" href="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI_window_coordinates.jpg.53032d867158e14b241d980d546678b0.jpg" rel=""><img alt="004_GUI_window_coordinates.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94269" data-unique="lbv4m02be" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/004_GUI_window_coordinates.jpg.53032d867158e14b241d980d546678b0.jpg"></a>
</p>

<p style="text-align: center;">
	احداثيات النافذة
</p>

<p>
	الطريقة العامة لاستخدام الدالة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4218_15" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(‘</span><span class="typ">WidthxLength</span><span class="pun">+</span><span class="pln">x</span><span class="pun">(</span><span class="pln">horizontal</span><span class="pun">)+</span><span class="pln">y</span><span class="pun">(</span><span class="pln">vertical</span><span class="pun">)’)</span></pre>

<p>
	يجب مراعاة أن التفاصيل تعطى على صيغة سلسلة نصية أي أنها محاطة بعلامات التنصيص.
</p>

<p>
	لنحدد طولًا وعرضًا لنافذتنا السابقة، لنفرض أننا نرغب أن تكون بشكل مربع بطول وعرض 400 بكسل، وأن تظهر مبتعدة عن الجانب الأيسر من الشاشة بمقدار 200 بكسل ومن أعلى الشاشة بمقدار 300 بكسل فتكتب الشفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_17" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="str">'400x400+200+300'</span><span class="pun">)</span></pre>

<p>
	لنفترض أنك تريد أن تُظهر نافذة برنامجك في وسط الشاشة تمامًا، لفعل ذلك تحتاج أن تحصل على طول وعرض شاشة العرض في جهاز الحاسوب لتحقيق ذلك نستخدم الدالة <code>winfo_screenwidth</code> للحصول على عرض الشاشة، والدالة <code>winfo_screenheight</code> لنحصل على طولها، يتم استدعائها باستخدام النافذة الرئيسية على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_4218_19" style=""><span class="pln">screen_width </span><span class="pun">=</span><span class="pln"> main_window</span><span class="pun">.</span><span class="pln">winfo_screenwidth</span><span class="pun">()</span><span class="pln">
screen_height </span><span class="pun">=</span><span class="pln"> main_window</span><span class="pun">.</span><span class="pln">winfo_screenheight</span><span class="pun">()</span></pre>

<p>
	للحصول على إحداثيات نقطة منتصف الشاشة نقسم الطول والعرض على 2، فنحصل على النقطة (ارتفاع الشاشة/2 ،عرض الشاشة/2)، وعند كتابة هذه النقطة في الدالة سيكون موضع ظهور النافذة في الجزء السفلي الأيمن من الشاشة، حيث أن بداية رسم النافذة يكون من النقطة العلوية اليسرى، الشكل التالي يوضح المقصود:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94270" href="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI_window_in_screen.jpg.098b1ec76134026618c05b12f9796f94.jpg" rel=""><img alt="005_GUI_window_in_screen.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94270" data-unique="bsv59ysqp" style="width: 450px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/005_GUI_window_in_screen.jpg.098b1ec76134026618c05b12f9796f94.jpg"></a>
</p>

<p style="text-align: center;">
	موضع النافذة في الشاشة
</p>

<p>
	لذلك سنقوم بوضع منتصف نافذتنا الرئيسية في منتصف شاشة العرض الأساسية فتكون الصياغة العامة للإحداثي السيني على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_23" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">winfo_screenwidth</span><span class="pun">()//</span><span class="lit">2</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> main_window_width</span><span class="pun">//</span><span class="lit">2</span></pre>

<p>
	قمنا باستخدام <a href="https://wiki.hsoub.com/Python/numeric_operations" rel="external">القسمة الصحيحة</a> للحصول على عددٍ صحيح كامل بلا فواصل، وعلى نفس النهج نحصل على الإحداثي الصادي.
</p>

<p>
	يجب مراعاة أن المتغيرات التي أنشئت لحساب الإحداثيات عددية النوع بينما الدالة <code>geometry</code> تأخذ سلسلة نصية لذلك أضفنا تنسيق لنحول الأعداد إلى نص. سنُظهر الآن نافذتنا ذات الأبعاد 400*400 بكسل طولًا وعرضًا في منتصف الشاشة، فتكون الشفرة كاملة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_25" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

main_window </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"أول واجهة رسومية "</span><span class="pun">)</span><span class="pln">

screen_width </span><span class="pun">=</span><span class="pln"> main_window</span><span class="pun">.</span><span class="pln">winfo_screenwidth</span><span class="pun">()</span><span class="pln">
screen_height </span><span class="pun">=</span><span class="pln"> main_window</span><span class="pun">.</span><span class="pln">winfo_screenheight</span><span class="pun">()</span><span class="pln">

middle_width</span><span class="pun">=</span><span class="pln"> screen_width </span><span class="pun">//</span><span class="lit">2</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">400</span><span class="pun">//</span><span class="lit">2</span><span class="pln">
middle_hight</span><span class="pun">=</span><span class="pln"> screen_height</span><span class="pun">//</span><span class="lit">2</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">400</span><span class="pun">//</span><span class="lit">2</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="pln">f</span><span class="str">'400x400+{middle_width}+{middle_hight}'</span><span class="pun">)</span><span class="pln">

L</span><span class="pun">=</span><span class="typ">Label</span><span class="pun">(</span><span class="pln">main_window</span><span class="pun">,</span><span class="pln"> text</span><span class="pun">=</span><span class="str">"مرحباً بواجهات بايثون "</span><span class="pun">)</span><span class="pln"> 
L</span><span class="pun">.</span><span class="pln">pack</span><span class="pun">()</span><span class="pln">

main_window</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ويظهر الشكل التالي نتيجة الشفرة عند تشغيلها:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94271" href="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI_window_in_screen.jpg.57ceed2e8bb6abe8bb9e866ef244ee00.jpg" rel=""><img alt="006_GUI_window_in_screen.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94271" data-unique="vuv88lt5y" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/006_GUI_window_in_screen.jpg.57ceed2e8bb6abe8bb9e866ef244ee00.jpg"></a>
</p>

<p style="text-align: center;">
	موضع النافذة في الشاشة
</p>

<h3>
	التعديل على حجم النافذة
</h3>

<p>
	يُسمح للمستخدم بشكل افتراضي أن يقوم بتعديل حجم النافذة (الطول والعرض) باستخدام الفأرة، وتستطيع أن تمنع ذلك باستخدام الدالة <code>resizable(width,height)‎</code>، حيث نعطيها قيمة خطأ <code>False</code> لكلٍ من الطول والعرض فيمنع ذلك تعديلهم من المستخدم، تكتب كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_28" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">resizable</span><span class="pun">(</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span></pre>

<p>
	بإمكانك ترك النافذة كما في الإعدادات الإفتراضية قابلة للتعديل ويمكنك أن تضبط .. أقصى طول وعرض تسمح للنافذة أن تتوسع له وأقل طول وعرض يمكن أن تنكمش له باستخدام الدالتين <code>maxsize</code> و <code>minsize</code> على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_30" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">minsize</span><span class="pun">(</span><span class="pln">min_width</span><span class="pun">,</span><span class="pln"> min_height</span><span class="pun">)</span><span class="pln">
main_window</span><span class="pun">.</span><span class="pln">maxsize</span><span class="pun">(</span><span class="pln">min_height</span><span class="pun">,</span><span class="pln"> max_height</span><span class="pun">)</span></pre>

<h3>
	تغيير نوع وحجم خط عناصر النافذة
</h3>

<p>
	نستطيع تغيير خط العناصر على النافذة الرئيسية باستخدام الدالة <code>option_add</code>، ونعطيها نوع الخط وحجم الخط الذي نرغب بعرضه وسيطبق على جميع العناصر التي توضع على النافذة، لنحدد حجم الخط <code>20</code> ونوعه <code>Times</code> مثلًا سنكتب السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_32" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">option_add</span><span class="pun">(</span><span class="str">'*Font'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'Times 20'</span><span class="pun">)</span></pre>

<p>
	وتكون النتيجة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94272" href="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI_window_font.jpg.83fbc6c75a3f012adce49efb68cd696d.jpg" rel=""><img alt="007_GUI_window_font.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94272" data-unique="efg92d3k5" style="width: 250px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/007_GUI_window_font.jpg.83fbc6c75a3f012adce49efb68cd696d.jpg"></a>
</p>

<p>
	نوع وحجم خط النافذة للحصول على قائمة بأسماء الخطوط المتوفرة بإمكاننا تشغيل الشفرة التالية وسيخرج لنا قائمة بالخطوط:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_38" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">,</span><span class="pln"> font
root </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
</span><span class="kwd">print</span><span class="pun">(</span><span class="pln">font</span><span class="pun">.</span><span class="pln">families</span><span class="pun">())</span></pre>

<h3>
	شفافية النافذة
</h3>

<p>
	تسمح مكتبة Tkinter للمستخدم بالتحكم بشفافية النافذة الرئيسية باستخدام الخاصية ألفا <code>alpha</code> وإعطائها قيمة تتراوح ما بين 0.0 (شفافة بالكامل) إلى 1.0 (معتم بالكامل)، السطر التالي يجعل النافذة الرئيسية شفافة بنسبة 50%:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_40" style=""><span class="pln">main_window</span><span class="pun">.</span><span class="pln">attributes</span><span class="pun">(</span><span class="str">'-alpha'</span><span class="pun">,</span><span class="lit">0.5</span><span class="pun">)</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94273" href="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI_window_transparency.jpg.2ee2db2963803d1353532e06e65bfa25.jpg" rel=""><img alt="008_GUI_window_transparency.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94273" data-unique="ij7v3jd58" src="https://academy.hsoub.com/uploads/monthly_2022_03/008_GUI_window_transparency.jpg.2ee2db2963803d1353532e06e65bfa25.jpg"></a>
</p>

<p style="text-align: center;">
	شفافية النافذة
</p>

<h3>
	تحسين دقة النافذة
</h3>

<p>
	من شكل النافذة السابق قد تبدو لك جملة (مرحباً بواجهات بايثون) ضبابية قليلًا وأقل وضوحًا من عنوان النافذة، لنحسن دقة عناصر النافذة نضيف لبرنامجنا أسطر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_42" style=""><span class="kwd">from</span><span class="pln"> ctypes </span><span class="kwd">import</span><span class="pln"> windll
windll</span><span class="pun">.</span><span class="pln">shcore</span><span class="pun">.</span><span class="typ">SetProcessDpiAwareness</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	فنحصل على النتيجة في الشكل التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94274" href="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI_window_clarity.jpg.ea76dddbf27f5ada27d35284475d193e.jpg" rel=""><img alt="009_GUI_window_clarity.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94274" data-unique="hp930bspf" style="width: 300px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/009_GUI_window_clarity.jpg.ea76dddbf27f5ada27d35284475d193e.jpg"></a>
</p>

<p style="text-align: center;">
	دقة النافذة
</p>

<p>
	ويتضح هنا أن دقة النص تحسنت بشكل ملحوظ، ولكن يجب الأخذ بعين الاعتبار هنا أن استخدامك لهذه الأسطر البرمجية سيؤثر على حجم النافذة الرئيسية، فيجب الانتباه لذلك ووضع قياسات مناسبة للحجم الذي تريد استخدامه.
</p>

<h2>
	تطبيق الآلة الحاسبة (الجزء الأول)
</h2>

<p>
	سنعمل خلال هذه السلسة من الدروس بتطبيق ما تعلمناه مباشرةً من خلال إنشاء آلة حاسبة بسيطة باستخدام الواجهات الرسومية، و ننفذها على عدة أجزاء في كل درس باستخدام ما تم شرحه فيه.
</p>

<p>
	نحتاج في البداية لتخيل شكل الآلة، فهي عبارة عن نافذة تحتوي مجموعة من الأزرار جزء منها للأرقام وجزء للعمليات، ومكان لإظهار النتائج، مرتبة بشكل يشبه الآلة الحاسبة الاعتيادية، ستبدو واجهتنا على النحو التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94275" href="https://academy.hsoub.com/uploads/monthly_2022_03/010_GUI_calculater.jpg.88e911a08e85e4f73c5bf151d27986a0.jpg" rel=""><img alt="010_GUI_calculater.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94275" data-unique="mup619e8m" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/010_GUI_calculater.thumb.jpg.15b934b5600fbe92621cfe3a0ae28b8d.jpg"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة
</p>

<p>
	سنبدأ برسم النافذة من خلال ما تعلمنا في درس اليوم، فنبدأ باستدعاء مكتبة tkinter ونرسم النافذة بحدود تقريبية بما نراه يتناسب مع حجم النافذة المطلوب (قد نضطر لتعديله لاحقًا بعد وضع العناصر)، نستخدم الدالة <code>geometry</code> بحدود 575 عرض و طول 740، ونضع عنوانًا باستخدام الخاصية title فنكتب (آلة حاسبة بسيطة)، يجب أن لا ننسى استخدام جزء الشفرة الذي يحافظ على دقة محتويات النافذة، ونثبت مقياس النافذة باستخدام الخاصية <code>resizable</code>، فيصبح محتوى شيفرتنا على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_4218_46" style=""><span class="kwd">from</span><span class="pln"> tkinter </span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln">

</span><span class="kwd">from</span><span class="pln"> ctypes </span><span class="kwd">import</span><span class="pln"> windll
windll</span><span class="pun">.</span><span class="pln">shcore</span><span class="pun">.</span><span class="typ">SetProcessDpiAwareness</span><span class="pun">(</span><span class="lit">1</span><span class="pun">)</span><span class="pln">

mycalculator </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Tk</span><span class="pun">()</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">title</span><span class="pun">(</span><span class="str">"آلة حاسبة بسيطة"</span><span class="pun">)</span><span class="pln">
mycalculator</span><span class="pun">.</span><span class="pln">geometry</span><span class="pun">(</span><span class="str">"575x740"</span><span class="pun">)</span><span class="pln">  
mycalculator</span><span class="pun">.</span><span class="pln">resizable</span><span class="pun">(</span><span class="kwd">False</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">False</span><span class="pun">)</span><span class="pln"> 

mycalculator</span><span class="pun">.</span><span class="pln">mainloop</span><span class="pun">()</span></pre>

<p>
	ونحصل على النافذة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="94276" href="https://academy.hsoub.com/uploads/monthly_2022_03/011_GUI_calculator_1.jpg.95aee4808d800040cd798884ae03248a.jpg" rel=""><img alt="011_GUI_calculator_1.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="94276" data-unique="15ujqm5tp" style="width: 350px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2022_03/011_GUI_calculator_1.thumb.jpg.a9a2def2a6658b7d2922bcf9e67b85a5.jpg"></a>
</p>

<p style="text-align: center;">
	واجهة الآلة الحاسبة الأولى
</p>

<p>
	في <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">المقال القادم</a>، سنتكلم عن ماهية العناصر التي توضع على النافذة، وكيفية وضعها وترتيبها عليها، ونطبق ما سنتعلمه على آلتنا الحاسبة.
</p>

<h2>
	المصادر
</h2>

<ul>
	<li>
		<a href="https://likegeeks.com/python-gui-examples-tkinter-tutorial/" rel="external nofollow">Python GUI Examples (Tkinter Tutorial) - Like Geeks</a>
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-window/" rel="external nofollow">An Essential Guide to Tkinter Window</a>
	</li>
	<li>
		<a href="https://www.pythontutorial.net/tkinter/tkinter-hello-world/" rel="external nofollow">Tkinter Hello, World!</a>
	</li>
	<li>
		<a href="https://coderslegacy.com/python/problem-solving/improve-tkinter-resolution/" rel="external nofollow">How to improve Tkinter window resolution - CodersLegacy</a>
	</li>
	<li>
		<a href="https://coderslegacy.com/python/problem-solving/change-font-in-tkinter/" rel="external nofollow">How to change font type and size in Tkinter? - CodersLegacy</a>
	</li>
	<li>
		<a href="https://www.tutorialspoint.com/python/python_gui_programming.htm" rel="external nofollow">Python - GUI Programming (Tkinter)</a>
	</li>
	<li>
		Introduction to Programming Using Python by Y. Daniel Liang
	</li>
	<li>
		Tony Gaddis - Starting Out with Python, Global Edition (2018, Pearson Education)‎.
	</li>
</ul>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-widgets-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1502/" rel="">مدخل إلى عناصر واجهات المستخدم الرسومية Widgets في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-tkinter-r1378/" rel="">برمجة الواجهات الرسومية باستخدام Tkinter</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r662/" rel="">تعلم البرمجة</a>
	</li>
	<li>
		تعرف على <a href="https://academy.hsoub.com/programming/python/%D9%85%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">أبرز مميزات لغة بايثون</a>
	</li>
	<li>
		النسخة العربية الكاملة من كتاب: <a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1501</guid><pubDate>Mon, 21 Mar 2022 16:06:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x646;&#x62D;&#x62F;&#x627;&#x631; &#x627;&#x644;&#x625;&#x62D;&#x635;&#x627;&#x626;&#x64A; regression &#x648;&#x62F;&#x648;&#x631;&#x647; &#x641;&#x64A; &#x645;&#x644;&#x627;&#x621;&#x645;&#x629; &#x627;&#x644;&#x646;&#x645;&#x627;&#x630;&#x62C; &#x627;&#x644;&#x645;&#x62E;&#x62A;&#x644;&#x641;&#x629; &#x645;&#x639; &#x623;&#x646;&#x648;&#x627;&#x639; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x62A;&#x627;&#x62D;&#x629;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-regression-%D9%88%D8%AF%D9%88%D8%B1%D9%87-%D9%81%D9%8A-%D9%85%D9%84%D8%A7%D8%A1%D9%85%D8%A9-%D8%A7%D9%84%D9%86%D9%85%D8%A7%D8%B0%D8%AC-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D9%81%D8%A9-%D9%85%D8%B9-%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%AA%D8%A7%D8%AD%D8%A9-r1493/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2022_03/622c3dbe1e772_.png.d09e9d688bb9ba6d25a15ba7775199db.png" /></p>

<p>
	تُعَدّ ملاءمة المربعات الصغرى الخطية التي ذكرناها في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%BA%D8%B1%D9%89-%D8%A7%D9%84%D8%AE%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1412/" rel="">المقال السابق</a> مثالًا عن الانحدار regression، وهو المشكلة الأكثر عمومية لمسألة ملاءمة النماذج المختلفة مع أي نوع من البيانات، علمًا أنّ استخدام مصطلح الانحدار regression هو مصادفة تاريخية فهو مرتبط بالمعنى الأصلي للكلمة الأجنبية ارتباطًا غير مباشر ليس إلا، ويتمثَّل هدف تحليل الانحدار في وصف العلاقة بين مجموعة واحدة من المتغيرات التي تُدعى بالمتغيرات التابعة dependent variables ومجموعة أخرى من البيانات والتي تُدعى بالمتغيرات التوضيحية explanatory variables أو المتغيرات المستقلة independent.
</p>

<p>
	استخدمنا في المقال السابق عمر الأم على أساس متغير توضيحي للتنبؤ بوزن الطفل على أساس متغير تابع، علمًا أنّ الانحدار البسيط simple regression هو الحالة التي توجد فيها متغير تابع واحد فقط ومتغير توضيحي واحد فقط، وسنتناول في هذا المقال الانحدار المتعدد multiple regression الذي يحوي أكثر من متغير توضيحي، لكن إذا كان هناك أكثر من متغير تابع واحد، فسيكون الانحدار من نوع الانحدار متعدد المتغيرات multivariate regression، وإذا كانت العلاقة بين المتغير التوضيحي والمتغير التابع خطيةً، فسيكون الانحدار من نوع الانحدار الخطي linear regression، فإذا كان مثلًا المتغير التابع <code>y</code> والمتغيران التوضيحيان هما x<sub>1</sub>‎ وx<sub>2</sub>‎، فيمكننا صياغة نموذج الانحدار الخطي كما يلي:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell" style="text-align: center;">
				<span style="font-size:medium"><span style="font-style:italic">y</span> = β</span><sub><span style="font-size:medium">0</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> + ε </span>
			</td>
		</tr></tbody></table>
<p>
	حيث يكون β<sub>0</sub>‎ هو نقطة التقاطع وβ<sub>1</sub>‎ هو المُعامِل المرتبط بالمتغير x<sub>1</sub>‎ ويكون β<sub>2</sub>‎ هو الوسيط المرتبط بالمتغير x<sub>2</sub>‎، في حين يكون ε هو الراسب residual (أو الباقي) الناتج عن إما تباين عشوائي أو عوامل أخرى غير معروفة، فإذا كان لدينا متسلسلةً من قيم متسلسلةً من قيم <code>y</code> ومتسلسلتَين من `x<sub>1</sub> و x<sub>2</sub>‎، فسيكننا إيجاد المُعامِلات β<sub>0</sub>‎ وβ<sub>1</sub>‎ وβ<sub>2</sub>‎ التي تقلل من مجموع ε<sup>2</sup>‎، وتُدعى هذه العملية بالمربعات الصغرى العادية ordinary least squares.
</p>

<p>
	يُعَدّ هذا الحساب مشابهًا لـ <code>thinkstats2.LeastSquare</code> لكنه معمَّم للتعامل مع أكثر من متغير توضيحي واحد، وللمزيد من التفاصيل يمكنك زيارة <a href="https://ar.wikipedia.org/wiki/%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA_%D8%B5%D8%BA%D8%B1%D9%89_%D8%B9%D8%A7%D8%AF%D9%8A%D8%A9" rel="external nofollow">صفحة ويكيبيديا</a>، كما توجد الشيفرة الخاصة بهذا المقال في <code>regression.py</code>.
</p>

<h2>
	الحزمة StatsModels
</h2>

<p>
	تناولنا في المقال السابق التابع <code>thinkstats2.LeastSquares</code>، الذي يُعَدّ تنفيذًا للانحدار الخطي البسيط وهو مصمم ليكون سهل القراءة؛ أما بالنسبة للانحدار المتعدد multiple regression، فسننتقل للتعامل مع الحزمة <code>StatsModels</code> وهي <a href="https://academy.hsoub.com/programming/python/django/%D8%AD%D8%B2%D9%85-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-%D8%A7%D9%84%D8%AB%D9%85%D8%A7%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%8A-%D8%AA%D8%B3%D9%87%D9%84-%D8%AA%D8%B9%D8%A7%D9%85%D9%84%D9%83-%D9%85%D8%B9-django-r656/" rel="">حزمة بايثون</a> تزودنا بعدة أشكال من الانحدار بالإضافة إلى عدة تحليلات أخرى، علمًا أنه ستكون هذه الحزمة مثبتةً لديك في حال كنت تستخدم أناكوندا Anaconda، وإلا فقد تضطر إلى تثبيتها، حيث سننفِّذ النموذج الذي تناولناه في الفصل السابق على أساس مثال على ذلك لكن باستخدام الحزمة <code>StatsModels</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_10" style="">
<span class="pln">    </span><span class="kwd">import</span><span class="pln"> statsmodels</span><span class="pun">.</span><span class="pln">formula</span><span class="pun">.</span><span class="pln">api </span><span class="kwd">as</span><span class="pln"> smf

    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    formula </span><span class="pun">=</span><span class="pln"> </span><span class="str">'totalwgt_lb ~ agepreg'</span><span class="pln">
    model </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="pln">formula</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">live</span><span class="pun">)</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">fit</span><span class="pun">()</span></pre>

<p>
	توفِّر الحزمة <code>statsmodels</code> واجهتَين من نوع <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">واجهات برمجة التطبيقات APIs</a>، حيث تستخدِم المعادلة formula الخاصة بواجهة برمجة التطبيقات <a href="https://academy.hsoub.com/questions/12814-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-strings-in-python/" rel="">السلاسل strings</a> لتحديد المتغيرات التابعة والمتغيرات التوضيحية، كما تستخدِم صيغة قواعدية syntax تُدعى <code>patsy</code>، تكون مهمة العامِل <code>~</code> في هذا المثال فصل المتغير التابع عن المتغيرات التوضيحية بحيث يضع المتغير التابع في الجهة اليسرى والمتغيرات التوضيحية في الجهة اليمنى.
</p>

<p>
	يأخذ التابع <code>smf.ols</code> السلسلة <code>formula</code> وإطار البيانات <code>live</code> ويُعيد كائن OLS الذي يمثِّل النموذج علمًا أنّ تسمية <code>ols</code> اختصار لمصطلح المربعات الصغرى العادية ordinary least squares؛ أما التابع <code>fit</code> فهو يلائم النموذج مع البيانات ويُعيد الكائن RegressionResults الذي يحتوي على النتائج، علمًا أنّ النتائج متوافرة على صورة سمات attributes، وتكون <code>params</code> هي سلسلة Series تحوِّل أسماء المتغيرات إلى معامِلاتها لكي نحصل على الميل ونقطة التقاطع كما في الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_16" style="">
<span class="pln">    inter </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">params</span><span class="pun">[</span><span class="str">'Intercept'</span><span class="pun">]</span><span class="pln">
    slope </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">params</span><span class="pun">[</span><span class="str">'agepreg'</span><span class="pun">]</span></pre>

<p>
	المعامِلان المقدَّران هما 6.83 و0.0175 أي تمامًا مثل <code>LeastSquares</code>. تُعَدّ <code>pvalues</code> سلسلةً Series تحول أسماء المتغيرات إلى القيمة الاحتمالية المرتبطة بها لكي نتحقق فيما إن كان الميل المقدَّر ذا دلالة إحصائية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_18" style="">
<span class="pln">    slope_pvalue </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">pvalues</span><span class="pun">[</span><span class="str">'agepreg'</span><span class="pun">]</span></pre>

<p>
	القيمة الاحتمالية p-value المرتبطة بالمتغير <code>agepreg</code> هي <code>5.7e-11</code> أي أقل من 0.001 كما هو متوقع تمامًا.
</p>

<p>
	يحتوي <code>results.rsquared</code> على R<sup>2</sup>‎ التي تبلغ قيمتها 0.0047، ويزودنا <code>results</code> بـ <code>f_pvalue</code> وهي القيمة الاحتمالية المرتبطة بالنموذج بأكمله بصورة مشابهة لاختبار فيما إن كان R<sup>2</sup> ذي دلالة إحصائية، كما يزودنا <code>results</code> بـ <code>resid</code> وهو متسلسلة من الرواسب، وبـ <code>fittedvalues</code> وهو متسلسلة من القيم الملاءمة المقابلة لـ <code>agepreg</code>، كما يزودنا الكائن results بالدالة <code>summary()</code> التي تمثُِل النتائج بصيغة مقروءة.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_22" style="">
<span class="pln">    </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">results</span><span class="pun">.</span><span class="pln">summary</span><span class="pun">())</span></pre>

<p>
	لكن تطبع هذه الدالة الكثير من المعلومات التي لا تهمنا حاليًا، لذا سنستخدِم دالةً أبسط تُدعى <code>SummarizeResults</code>، إليك نتائج هذا النموذج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_24" style="">
<span class="typ">Intercept</span><span class="pln">       </span><span class="lit">6.83</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
agepreg         </span><span class="lit">0.0175</span><span class="pln">  </span><span class="pun">(</span><span class="lit">5.72e-11</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.004738</span><span class="pln">
</span><span class="typ">Std</span><span class="pun">(</span><span class="pln">ys</span><span class="pun">)</span><span class="pln"> </span><span class="lit">1.408</span><span class="pln">
</span><span class="typ">Std</span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="lit">1.405</span></pre>

<p>
	يُعَدّ <code>Std(ys)</code> الانحراف المعياري للمتغير التابع، وهو جذر متوسط مربع الخطأ RMSE نفسه إذا خمّنت أوزان الولادات بدون متغيرات توضيحية؛ أما <code>Std(res)</code> فهو الانحراف المعياري للرواسب وهو خطأ الجذر التربيعي المتوسط RMSE إذا كانت تخميناتك مبينةً على عمر الأم، حيث أنّ عمر الأم -كما رأينا سابقًا- لا يقدِّم أيّ تحسين جوهري إلى التنبؤات.
</p>

<h2>
	الانحدار المتعدد
</h2>

<p>
	رأينا في مقال <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D9%83%D9%85%D9%8A-cumulative-distribution-functions-r1331/" rel="">دوال التوزيع التراكمي Cumulative distribution functions</a> ميل الأطفال الأوائل ليكونوا أقل وزنًا من بقية الأطفال، ويُعَدّ هذا التأثير ذا دلالة إحصائية لكنه نتيجةً غريبةً بسبب عدم وجود آلية واضحة تتسبب في جعل الأطفال الأوائل أقل وزنًا من غيرهم، لذا قد نتساءل فيما إذا كانت هذه العلاقة زائفةً spurious.
</p>

<p>
	يوجد تفسير محتمل لهذا التأثير في الواقع، فقد رأينا اعتماد وزن الطفل عند الولادة على عمر الأم، لذا قد نتوقع أنّ أمهات الأطفال الأوائل أصغر عمرًا من غيرهن، ويمكننا التحقق مما إن كان هذا التفسير معقولًا من خلال بعض العمليات الحسابية ثم سنستخدِم الانحدار المتعدد لإجراء تحقيق أكثر دقة، لذا سنرى في البداية حجم الفرق في الوزن ما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_27" style="">
<span class="pln">diff_weight </span><span class="pun">=</span><span class="pln"> firsts</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> others</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span></pre>

<p>
	عادةً ما يكون الأطفال الأوائل أخف وزنًا من غيرهم بمقدار 0.125 رطلًا أو 2 أوقية أو ما يعادل 0.1 كيلوغرامًا؛ أما الفرق في الأعمار فهو:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_29" style="">
<span class="pln">diff_weight </span><span class="pun">=</span><span class="pln"> firsts</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> others</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span></pre>

<p>
	أي أن أمهات الأطفال الأوائل أصغر من أمهات بقية الأطفال بـ 3.59 عامًا، حيث يمكننا الحصول على الفرق في وزن الطفل عند الولادة على أساس دالة العمر عن طريق تشغيل النموذج الخطي مرةً أخرى:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_31" style="">
<span class="pln">results </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="str">'totalwgt_lb ~ agepreg'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">live</span><span class="pun">).</span><span class="pln">fit</span><span class="pun">()</span><span class="pln">
slope </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">params</span><span class="pun">[</span><span class="str">'agepreg'</span><span class="pun">]</span></pre>

<p>
	يقدَّر الميل بـ 0.0175 رطلًا في العام الواحد، فإذا أجرينا عملية جداء بين الميل والفرق في الأعمار، فسنحصل على الفرق المتوقَّع في وزن الطفل عند الولادة للأطفال الأوائل وبقية الأطفال والناتج عن عمر الأم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_33" style="">
<span class="pln">slope </span><span class="pun">*</span><span class="pln"> diff_age</span></pre>

<p>
	النتيجة هي 0.063 وتساوي نصف الفرق المرصود، لذا نستنتج مبدئيًا أنه يمكن تفسير الفرق الملحوظ في وزن الطفل عند الولادة بالاختلاف في عمر الأم، كما يمكننا استكشاف هذه العلاقات بطريقة منهجية باستخدام الانحدار المتعدد كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_35" style="">
<span class="pln">    live</span><span class="pun">[</span><span class="str">'isfirst'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">birthord </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
    formula </span><span class="pun">=</span><span class="pln"> </span><span class="str">'totalwgt_lb ~ isfirst'</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="pln">formula</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">live</span><span class="pun">).</span><span class="pln">fit</span><span class="pun">()</span></pre>

<p>
	ينشئ السطر الأول من الشيفرة السابقة عمودًا جديدًا باسم <code>isfirst</code> وقيمته البوليانية صحيحية True للأطفال الأوائل وخاطئة false ما عدا ذلك، ثم نستخدِم العمود <code>isfirst</code> على أساس متغير توضيحي لكي نلائم نموذجًا، وإليك النتائج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_37" style="">
<span class="typ">Intercept</span><span class="pln">         </span><span class="lit">7.33</span><span class="pln">   </span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
isfirst</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">  </span><span class="pun">-</span><span class="lit">0.125</span><span class="pln">  </span><span class="pun">(</span><span class="lit">2.55e-05</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.00196</span></pre>

<p>
	تعامِل <code>ols</code> العمود <code>isfirst</code> على أنه متغير فئوي categorical variable نظرًا لأنه من النوع البولياني boolean، أي أن القيم تندرج في فئتين هما True وFalse ولا ينبغي معاملتها على أساس أعداد، علمًا أن المعامِلات المقدَّرة estimated parameter هي التأثير على وزن الطفل عند الولادة في حال كانت قيمة <code>isfirst</code> هي true، لذا تكون النتيجة المقدَّرة بـ ‎-0.125‎‎ رطل هي الفرق في وزن الطفل عند الولادة بين الأطفال الأوائل وبقية الأطفال.
</p>

<p>
	يملك الميل slope ونقطة التقاطع intercept دلالةً إحصائيةً، أي أنه من غير المحتمل حدوث التأثير صدفةً، لكن قيمة R<sup>2</sup>‎ الخاصة بالنموذج صغيرةً، مما يعني أنّ <code>isfirst</code> لا يمثِّل جزءًا كبيرًا من التباين في وزن الطفل عند الولادة، كما تتشابه النتائج مع النتائج التي ظهرت مع <code>agepreg</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_39" style="">
<span class="typ">Intercept</span><span class="pln">       </span><span class="lit">6.83</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
agepreg         </span><span class="lit">0.0175</span><span class="pln">  </span><span class="pun">(</span><span class="lit">5.72e-11</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.004738</span></pre>

<p>
	تملك المعامِلات -كما ذكرنا سابقًا- دلالةً إحصائيةً لكن قيمة R<sup>2</sup>‎ منخفضةً، كما تؤكِّد هذه النماذج النتائج التي رأيناها بالفعل، لكن يمكننا الآن ملاءمة نموذج واحد يتضمن كلا المتغيرين بحيث نحصل على ما يلي باستخدام المعادلة <code>totalwgt_lb ~ isfirst + agepreg</code>:
</p>

<pre class="ipsCode prettyprint lang-ruby prettyprinted" id="ips_uid_5654_41" style="">
<span class="typ">Intercept</span><span class="pln">        </span><span class="lit">6.91</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
isfirst</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="typ">True</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.0698</span><span class="pln">  </span><span class="pun">(</span><span class="lit">0.0253</span><span class="pun">)</span><span class="pln">
agepreg          </span><span class="lit">0.0154</span><span class="pln">  </span><span class="pun">(</span><span class="lit">3.93e-08</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.005289</span></pre>

<p>
	إنّ قيمة المعامِل <code>isfirst</code> أصغر بحوالي النصف في النموذج المشترك، مما يعني أنه يتم حساب جزء من التأثير الظاهر للعمود <code>isfirst</code> بواسطة <code>agepreg</code>، كما أن القيمة الاحتمالية للعمود <code>isfirst</code> هي حوالي 2.5‎%‎ وهي على حدود الدلالة الإحصائية، لكن قيمة R<sup>2</sup> في هذا النموذج أعلى بقليل، أي أنّ المتغيرين معًا يمثلان تباينًا أكبر من التباين الذي يمثله كل منهما بمفرده في وزن المواليد ولكن الفرق ليس بكبير.
</p>

<h2>
	العلاقات اللاخطية
</h2>

<p>
	قد تكون المساهمة التي قدمها <code>agepreg</code> لاخطيةً، لذا قد نفكر في إضافة متغير يصف العلاقة وصفًا أفضل، ويتمثل أحد الخيارات في إنشاء عمود آخر باسم <code>agepreg2</code> يحتوي على مربعات الأعمار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_43" style="">
<span class="pln">    live</span><span class="pun">[</span><span class="str">'agepreg2'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">agepreg</span><span class="pun">**</span><span class="lit">2</span><span class="pln">
    formula </span><span class="pun">=</span><span class="pln"> </span><span class="str">'totalwgt_lb ~ isfirst + agepreg + agepreg2'</span></pre>

<p>
	يمكننا ملاءمة قطع مكافئ parabola ملائمةً فعالةً عن طريق تقدير المعامِلَين <code>agepreg</code> و<code>agepreg2</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_45" style="">
<span class="typ">Intercept</span><span class="pln">        </span><span class="lit">5.69</span><span class="pln">     </span><span class="pun">(</span><span class="lit">1.38e-86</span><span class="pun">)</span><span class="pln">
isfirst</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="kwd">True</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.0504</span><span class="pln">   </span><span class="pun">(</span><span class="lit">0.109</span><span class="pun">)</span><span class="pln">
agepreg          </span><span class="lit">0.112</span><span class="pln">    </span><span class="pun">(</span><span class="lit">3.23e-07</span><span class="pun">)</span><span class="pln">
agepreg2        </span><span class="pun">-</span><span class="lit">0.00185</span><span class="pln">  </span><span class="pun">(</span><span class="lit">8.8e-06</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.007462</span></pre>

<p>
	لدينا المعامِل <code>agepreg2</code> سالبًا لذا ينحني القطع المكافئ إلى الأسفل، وهذا متوافق تمامًا مع شكل الخطوط في الشكل 10.2 في هذا الفصل، ويمثِّل النموذج التربيعي quadratic model للمعامِل <code>agepreg2</code> تباينًا أكبر في وزن الطفل عند الولادة، كما أنّ <code>isfirst</code> أصغر في هذا النموذج ولم يعُد ذا دلالة إحصائية.
</p>

<p style="text-align: center;">
	<img alt="الشكل 10.2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="93607" data-unique="epe3rudow" src="https://academy.hsoub.com/uploads/monthly_2022_03/622c31c2320a9_10.2.png.a43918bcf1de4299af0771b3ddf79fcf.png"></p>

<p>
	يُعَدّ استخدام متغيرات محسوبة مثل <code>agepreg2</code> طريقةً شائعةً لملاءمة كثيرات الحدود ودوال أخرى مع البيانات، ولا تزال هذه العملية مندرجةً تحت نوع الانحدار الخطي لأن المتغير التابع هو دالة خطية للمتغيرات التوضيحية بغض النظر عما إذا كانت بعض المتغيرات تمثِّل دوالًا لاخطيةً لغيرها، كما يلخِّص الجدول التالي نتائج تحليلات الانحدار هذه:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}</style>
<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				isfirst
			</th>
			<th>
				agepreg
			</th>
			<th>
				agepreg2
			</th>
			<th>
				R<sup>2</sup>
</th>
		</tr></thead>
<tbody>
<tr>
<td>
				النموذج الأول
			</td>
			<td>
				-0.125 *
			</td>
			<td>
				-
			</td>
			<td>
				-
			</td>
			<td>
				0.002
			</td>
		</tr>
<tr>
<td>
				النموذج الثاني
			</td>
			<td>
				-
			</td>
			<td>
				0.0175 *
			</td>
			<td>
				-
			</td>
			<td>
				0.0047
			</td>
		</tr>
<tr>
<td>
				النموذج الثالث
			</td>
			<td>
				-0.0698 (0.025)
			</td>
			<td>
				0.0154 *
			</td>
			<td>
				-
			</td>
			<td>
				0.0053
			</td>
		</tr>
<tr>
<td>
				النموذج الرابع
			</td>
			<td>
				-0.0504 (0.11)
			</td>
			<td>
				0.112*
			</td>
			<td>
				-0.00185 *
			</td>
			<td>
				0.0075
			</td>
		</tr>
</tbody>
</table>
<p>
	تمثِّل أعمدة هذا الجدول المتغيرات التوضيحية بالإضافة إلى مُعامِل التحديد R<sup>2</sup>، حيث تُعَدّ كل خانة من الجدول معامِلًا مقدَّرًا وإما قيمةً احتماليةً بين قوسين أو قيمةً احتماليةً بجانبها علامة نجمية <code>*</code> تشير إلى أن القيمة الاحتمالية أقل من 0.001.
</p>

<p>
	نستنتج مما سبق أنّ الاختلاف الواضح في وزن الطفل عند الولادة هو بسبب الفرق في عمر الأم بصورة جزئية على الأقل، إذ يصغر تأثير <code>isfirst</code> عندما نضيف عمر الأم إلى النموذج، وقد يكون التأثير المتبقي هو بسبب الصدفة فقط، ويكون عمر الأم في هذا المثال متغير تحكم control variable حيث أن إضافة المتغير <code>agepreg</code> إلى النموذج "يتحكم" بالفرق في العمر بين أمهات الأطفال الأوائل وأمهات بقية الأطفال مما يجعل عزل تأثير <code>isfirst</code> ممكنًا إذا وجد.
</p>

<h2>
	التنقيب في البيانات
</h2>

<p>
	لم نستخدِم حتى الآن سوى نماذج الانحدار لتفسير التأثيرات، فقد اكتشفنا في القسم السابق أن الفرق الواضح في وزن الطفل عند الولادة ناتج عن فرق في عمر الأم، لكن قيم R<sup>2</sup> لهذه النماذج هي قيم منخفضة جدًا، مما يعني أن قوتها التنبؤية ضئيلة، لذا سنحاول إيجاد طريقة أفضل في هذا المقال.
</p>

<p>
	لنفترض أنّ أحد زملائك يتوقع ولادة طفله في الوقت القريب القادم وهناك لعبة تخمين في المكتب لتوقُّع وزن الطفل عند الولادة، فإذا افترضنا الآن أنه لديك رغبةً كبيرةً في ربح هذه اللعبة، ما الذي يمكنك أن تفعله لتحسين احتمال فوزك؟ تحتوي مجموعة بيانات المسح الوطني لنمو الأسرة على 244 متغير لكل حالة حمل وحوالي 3087 متغير لكل مستجيب، فقد يكون لبعض هذه المتغيرات قوةً تنبؤيةً، لذا لِمَ لا نجربها جميعًا لنعلم أي منها هو الأكثر فائدةً؟
</p>

<p>
	تُعَدّ عملية اختبار المتغيرات في جدول الحَمل عمليةً سهلةً، لكن سيتوجب علينا مطابقة كل حالة حمل مع مستجيب إذا أردنا استخدام متغيرات جدول المستجيبين، ويمكننا نظريًا المرور على صفوف جدول الحمل ومن ثم استخدام المتغير <code>caseid</code> لإيجاد المستجيب الموافق لحالة الحمل ثم نسخ القيم من جدول المستجيبين إلى جدول حالات الحمل لكن ستتطلب هذه العملية وقتًا كبيرًا.
</p>

<p>
	يوجد خيار أفضل وهو عملية الضم join المُعرَّفة في <a href="https://academy.hsoub.com/programming/sql/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B3%D8%B1%D9%8A%D8%B9%D8%A9-%D8%B9%D9%84%D9%89-%D9%84%D8%BA%D8%A9-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D9%84%D8%A7%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-sql-r1368/" rel="">لغة الاستعلامات المهيكلة SQL</a>، كما يمكنك الاطلاع على مقال <a href="https://academy.hsoub.com/programming/sql/%D8%A7%D9%84%D8%AF%D9%85%D8%AC-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%81%D9%8A-sql-r849/" rel="">الدمج بين الجداول في SQL</a> لمزيد من المعلومات حول لمزيد من المعلومات حول العملية، علمًا أنّ التنفيذ البرمجي لهذه العملية هنا هو على صورة إطار بيانات، لذا يمكننا إجراءها كما يلي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_5654_48" style="">
<span class="pln">    live </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">[</span><span class="pln">live</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">&gt;</span><span class="lit">30</span><span class="pun">]</span><span class="pln">
    resp </span><span class="pun">=</span><span class="pln"> chap01soln</span><span class="pun">.</span><span class="typ">ReadFemResp</span><span class="pun">()</span><span class="pln">
    resp</span><span class="pun">.</span><span class="pln">index </span><span class="pun">=</span><span class="pln"> resp</span><span class="pun">.</span><span class="pln">caseid
    join </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">,</span><span class="pln"> on</span><span class="pun">=</span><span class="str">'caseid'</span><span class="pun">,</span><span class="pln"> rsuffix</span><span class="pun">=</span><span class="str">'_r'</span><span class="pun">)</span></pre>

<p>
	يحدِّد السطر الأول سجلات حالات الحمل التي تزيد مدتها عن 30 أسبوعًا بافتراض أنّ لعبة التخمين في المكتب قد بدأت قبل عدة أسابيع من موعد الولادة، في حين يقرأ السطر التالي ملف المستجيبين، وتكون النتيجة هي إطار بيانات يحوي فهارس صحيحة integer، لكننا استبدلنا <code>resp.caseid</code> مكان <code>resp.index</code> من أجل البحث عن المستجيبين بكفاءة عالية.
</p>

<p>
	استُدعِي التابع <code>join</code> في <code>live</code> وهو يمثِّل الجدول الأيسر، ومرِّر <code>resp</code>الذي يمثِّل الجدول الأيمن؛ أما الوسيط المحجوز <code>on</code> فيشير إلى المتغير المستخدَم لمطابقة الصفوف من الجدولين، وتظهر في هذا المثال بعض أسماء الجداول في الجدولين، لذا يجب توفير المتغير <code>rsuffix</code> الذي يُعَدّ سلسلةً نصيةً string والتي ستُضاف إلى نهاية أسماء الأعمدة المتكررة في الجدول الأيمن، فقد يحتوي مثلًا كلا الجدولين على عمود باسم <code>race</code> يرمز لعِرق المستجيب، وبالتالي ستحتوي نتيجة الضم على عمود باسم <code>race</code> وعمود باسم <code>race_r</code>.
</p>

<p>
	يُعَدّ التنفيذ البرمجي لبانداز pandas سريعًا، حيث لا يستغرق ضم جداول المسح الوطني لنمو الأسرة أكثر من ثانية واحدة وباستخدام حاسوب عادي، ويمكننا الآن البدء باختبار المتغيرات.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_50" style="">
<span class="pln">    t </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> name </span><span class="kwd">in</span><span class="pln"> join</span><span class="pun">.</span><span class="pln">columns</span><span class="pun">:</span><span class="pln">
        </span><span class="kwd">try</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> join</span><span class="pun">[</span><span class="pln">name</span><span class="pun">].</span><span class="pln">var</span><span class="pun">()</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">1e-7</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">continue</span><span class="pln">

            formula </span><span class="pun">=</span><span class="pln"> </span><span class="str">'totalwgt_lb ~ agepreg + '</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> name
            model </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="pln">formula</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">join</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">if</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">nobs </span><span class="pun">&lt;</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">join</span><span class="pun">)/</span><span class="lit">2</span><span class="pun">:</span><span class="pln">
                </span><span class="kwd">continue</span><span class="pln">

            results </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">fit</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">except</span><span class="pln"> </span><span class="pun">(</span><span class="typ">ValueError</span><span class="pun">,</span><span class="pln"> </span><span class="typ">TypeError</span><span class="pun">):</span><span class="pln">
            </span><span class="kwd">continue</span><span class="pln">

        t</span><span class="pun">.</span><span class="pln">append</span><span class="pun">((</span><span class="pln">results</span><span class="pun">.</span><span class="pln">rsquared</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">))</span></pre>

<p>
	يمكننا إنشاء نموذج لكل متغير ثم حساب R<sup>2</sup> وإضافة النتيجة إلى قائمة، كما تحتوي النماذج كلها على <code>agepreg</code> نظرًا لأننا نعلم مسبقًا أنها تمتلك بعض القوة التنبؤية، كما تحققنا أن لكل لمتغير توضيحي بعض التباين، وإلا لن يكون من الممكن الاعتماد على نتائج الانحدار، وتحققنا أيضًا من عدد المرات التي رُصد فيها كل نموذج، فلا يمكن أن تكون المتغيرات التي تحتوي على عدد كبير من قيم <code>nans</code> جيدةً للتنبؤ.
</p>

<p>
	لم يُطبَّق على معظم هذه المتغيرات أيّ عملية تنظيف للبيانات، حيث أن بعضها مرمَّز بطريقة غير مناسبة كثيرًا للانحدار الخطي، ونتيجة لهذا فقد نتجاهل بعض المتغيرات التي قد تكون مفيدةً إذا نُقّيَت تنقيةً صحيحةً، لكن ربما سنجد بعض المتغيرات التي يحتمل أن تكون مناسبة للتنبؤ.
</p>

<h2>
	التنبؤ
</h2>

<p>
	تتمثل الخطوة التالية في فرز النتائج وتحديد المتغيرات التي تنتج أعلى قيم R<sup>2</sup>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_52" style="">
<span class="pln">    t</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">reverse</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> mse</span><span class="pun">,</span><span class="pln"> name </span><span class="kwd">in</span><span class="pln"> t</span><span class="pun">[:</span><span class="lit">30</span><span class="pun">]:</span><span class="pln">
        </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">name</span><span class="pun">,</span><span class="pln"> mse</span><span class="pun">)</span></pre>

<p>
	يُعَدّ <code>totalwgt_lb</code> أول متغير على القائمة ثم <code>birthwgt_lb</code>، لكن من الواضح أنه لا يمكننا استخدام وزن الطفل عند الولادة لتوقع الوزن نفسه، كما يملك المتغير <code>prglngth</code> قوةً تنبؤيةً مفيدةً، لكننا سنفترض أثناء تعاملنا مع لعبة التخمين في المكتب أنّ مدة الحمل وبقية المتغيرات ذات الصلة غير معروفة بعد.
</p>

<p>
	يُعَدّ <code>babysex</code> أول متغير تنبؤي مفيد والذي يشير إلى جنس الطفل فيما إذا هو ذكر أو أنثى، ويكون الصبيان في مجموعة بيانات المسح الوطني لنمو الأسرة أكبر وزنًا بحوالي 0.3 رطلًا -أي 0.13 كيلوغرامًا تقريبًا-، لذا سنتمكن من استخدام جنس المولود في التنبؤ إذا افترضنا أنه معروف.
</p>

<p>
	يشير المتغير <code>race</code> الذي إلى عِرق المستجيب فيما إذا هو أسود البشرة أو أبيض البشرة أو غير ذلك، وقد يكون العِرق إشكاليًا إن تعاملنا معه على أساس متغير توضيحي، لكن يرتبط المتغير <code>race</code> في بيانات المسح الوطني لنمو الأسرة بالعديد من المتغيرات الأخرى بما في ذلك الدخل والعوامل الاجتماعية والاقتصادية الأخرى، علمًا أنه في نموذج الانحدار كان العِرق متغيرًا وكيلًا proxy variable، لذلك غالبًا ما تكون الارتباطات الظاهرة مع العِرق ناتجةً عن عوامل أخرى بصورة جزئية على الأقل.
</p>

<p>
	يكون المتغير التالي في القائمة هو <code>nbrnaliv</code> والذي يشير إلى ما إذا كان الحمل قد أدى إلى ولادة متعددة -أي ولادة توأم من طفلين أو أكثر-، وعادةً ما يكون التوأم من طفلين أو ثلاث أطفال أقل وزنًا من غيرهم، لذا فقد يساعدنا معرفة فيما إذا كان زميلنا الافتراضي يتوقع ولادة توأم؛ أما المتغير <code>paydu</code> فيشير إلى ما إذا كان المستجيب يمتلك منزله أم لا، وهو أحد المتغيرات التي تتعلق بالدخل بالإضافة إلى عدة متغيرات أخرى والتي توضح أنها تنبؤية، ويكون الدخل والثروة في مجموعة بيانات مثل المسح الوطني لنمو الأسرة مرتبطَين بكل شي تقريبًا، حيث يرتبط الدخل في هذا المثال بالحمية الغذائية والصحة والعناية الصحية وعوامل أخرى من المرجح أن تؤثِّر على وزن الطفل عند الولادة.
</p>

<p>
	توجد متغيرات أخرى في القائمة لكنها أمور لن نعرفها إلا في وقت لاحق مثل <code>bfeedwks</code> والذي يمثِّل عدد الأسابيع التي رضع فيها الطفل رضاعةً طبيعيةً، وعلى الرغم أنه لا يمكننا استخدام هذه المتغيرات للتنبؤ إلا أنك قد ترغب في التكهن بالأسباب التي قد يرتبط فيها المتغير <code>bfeedwks</code> مع الوزن عند الولادة، وقد تلجأ في بعض الأحيان إلى البدء بنظرية ومن ثم اختبارها باستخدام بيانات معينة، وفي بعض الأحيان قد تبدأ ببيانات ومن ثم تبحث عن نظريات ممكنة.
</p>

<p>
	سنستعرِض في هذا القسم الطريقة الثانية والتي تدعى التنقيب في البيانات data mining، ومن مزايا التنقيب في البيانات هو اكتشاف أنماط غير متوقعة، لكن الخطر هو أنه قد تكون العديد من الأنماط المُكتشَفة عشوائيةً أو زائفةً، وقد اختبرنا بعد تحديد المتغيرات التوضيحية المحتمَلة بعض النماذج واستقرَّينا على هذا النموذج:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_54" style="">
<span class="pln">    formula </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="str">'totalwgt_lb ~ agepreg + C(race) + babysex==1 + '</span><span class="pln">
               </span><span class="str">'nbrnaliv&gt;1 + paydu==1 + totincr'</span><span class="pun">)</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">ols</span><span class="pun">(</span><span class="pln">formula</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">join</span><span class="pun">).</span><span class="pln">fit</span><span class="pun">()</span></pre>

<p>
	تستخدِم هذه المعادلة formula صياغة syntax لم نرها بعد مثل <code>C(race)</code> التي تطلب من محلل الصيغة باتسي Patsy معاملة العِرق على أساس متغير فئوي على الرغم أنه مرمَّز بصورة عددية، كما يكون ترميز المتغير <code>babysex</code> هو 1 للذكر و2 للأنثى، لكن إذا كتبنا <code>babysex==1</code>، فسيتحول المتغير إلى النوع البولياني ليصبح True للذكر وfalse للأنثى، وبالمثل يكون <code>nbrnaliv&gt; 1</code> هو <code>True</code> للولادات المتعددة ويكون <code>paydu == 1</code> هو <code>True</code> للمستجيبين الذين يمتلكون منازلهم، علمًا أن المتغير <code>totincr</code> مرمز عدديًا من 1‎-14، حيث تمثِّل كل زيادة حوالي 5000 دولارًا أمريكيًا في الدخل السنوي، لذا يمكننا التعامل مع هذه القيم على أنها عددية ونعبِّر عنها بوحدات من 5000 دولارًا أمريكيًا، وإليك نتائج النموذج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_56" style="">
<span class="typ">Intercept</span><span class="pln">               </span><span class="lit">6.63</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0</span><span class="pun">)</span><span class="pln">
C</span><span class="pun">(</span><span class="pln">race</span><span class="pun">)[</span><span class="pln">T</span><span class="pun">.</span><span class="lit">2</span><span class="pun">]</span><span class="pln">            </span><span class="lit">0.357</span><span class="pln">   </span><span class="pun">(</span><span class="lit">5.43e-29</span><span class="pun">)</span><span class="pln">
C</span><span class="pun">(</span><span class="pln">race</span><span class="pun">)[</span><span class="pln">T</span><span class="pun">.</span><span class="lit">3</span><span class="pun">]</span><span class="pln">            </span><span class="lit">0.266</span><span class="pln">   </span><span class="pun">(</span><span class="lit">2.33e-07</span><span class="pun">)</span><span class="pln">
babysex </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">    </span><span class="lit">0.295</span><span class="pln">   </span><span class="pun">(</span><span class="lit">5.39e-29</span><span class="pun">)</span><span class="pln">
nbrnaliv </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">1</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">   </span><span class="pun">-</span><span class="lit">1.38</span><span class="pln">    </span><span class="pun">(</span><span class="lit">5.1e-37</span><span class="pun">)</span><span class="pln">
paydu </span><span class="pun">==</span><span class="pln"> </span><span class="lit">1</span><span class="pun">[</span><span class="pln">T</span><span class="pun">.</span><span class="kwd">True</span><span class="pun">]</span><span class="pln">      </span><span class="lit">0.12</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0.000114</span><span class="pun">)</span><span class="pln">
agepreg                 </span><span class="lit">0.00741</span><span class="pln"> </span><span class="pun">(</span><span class="lit">0.0035</span><span class="pun">)</span><span class="pln">
totincr                 </span><span class="lit">0.0122</span><span class="pln">  </span><span class="pun">(</span><span class="lit">0.00188</span><span class="pun">)</span></pre>

<p>
	من المفاجئ أن المعامِلات المقدَّرة للعِرق أكبر مما توقعنا خاصةً وأن المتغير التحكم هو الدخل، علمًا أن الترميز هو 1 لذوي البشرة السوداء و2 لذوي البشرة البيضاء و3 غير ذلك، إذًا يكون أطفال الأمهات ذوات البشرة السوداء أقل وزنًا من الأطفال الذي ينتمون إلى عروق أخرى بحوالي 0.27‎ - 0.36 رطلًا -أي بين 0.12 - 0.16 كيلوغرامًا-، وكما رأينا سابقًا فقد يميل الصبيان لأن يكونوا أكثر وزنًا بحوالي 0.3 رطلًا -أي 0.13 كيلوغرامًا تقريبًا-؛ أما التوائم سواءً اثنين أو أكثر فتكون أقل وزنًا بحوالي 1.4 رطلًا -أي 0.6 كيلوغرامًا تقريبًا-.
</p>

<p>
	الأشخاص الذين يمتلكون منازلهم يلدون أطفالًا أكثر وزنًا بحوالي 0.12 رطلًا -أي 0.05 كيلوغرامًا تقريبًا- حتى عندما يكون متغير التحكم هو الدخل، ويكون معامِل عمر الأم هنا أقل من الذي رأيناه في جزئية الانحدار المتعدد في هذا المقال، مما يشير إلى أن بعض المتغيرات الأخرى مرتبطةً بالعمر، بما في ذلك على الأرجح <code>paydu</code> و<code>totincr</code>.
</p>

<p>
	نستنتج أنّ هذه المتغيرات ذو دلالة إحصائية ولبعضها قيمة احتمالية منخفضة جدًا، لكن قيمة R<sup>2</sup> هي 0.06 فقط وهو مقدار صغير جدًا، كما أن قيمة جذر متوسط مربع الخطأ RMSE هي 1.27 رطل -أي حوالي 0.57 كيلوغرام- بدون استخدام النموذج؛ أما في حال استخدمنا النموذج فستنخفض القيمة إلى 1.23 رطل -أي حوالي 0.55 كيلوغرام-، أي لم تتحسن فرصتك في الفوز بلعبة التخمين تسحنًا كبيرًا.
</p>

<h2>
	الانحدار اللوجستي
</h2>

<p>
	وردت في أمثلتنا السابقة عدة متغيرات توضيحية، كان بعضها عدديًا وبعضها الآخر فئويًا بما فيها البولياني، لكن المتغير التابع كان دائمًا عدديًا، لذا يمكن تعميم الانحدار الخطي ليكون قادرًا على التعامل مع أنواع أخرى من المتغيرات التابعة، فإذا كان المتغير التابع بوليانيًا، فسيُدعى النموذج المعمم بالانحدار اللوجستي logistic regression؛ أما إذا كان المتغير التابع عددًا صحيحًا ويمثِّل تعدادًا، فسيُدعى بانحدار بواسون Poisson regression.
</p>

<p>
	دعنا نضع تباينًا على سيناريو لعبة التخمين في المكتب على أساس مثال عن الانحدار اللوجستي، حيث سنفترض أنّ صديقتك حامل وتريد توقُّع فيما إذا كان المولود صبيًا أو بنتًا، إذ يمكنك عندها استخدام البيانات الخاصة بالمسح الوطني لنمو الأسرة لإيجاد العوامل المؤثرة على نسبة الجنس التي عُرِّفت عادةً على أنها احتمال كون المولود صبيًا، فإذا كان المتغير التابع من النوع العددي، بحيث يكون 0 إذا كان المولود بنتًا و1 إذا كان صبيًا على سبيل المثال، ويمكنك عندها تطبيق طريقة المربعات الصغرى العادية لكن سنواجه عندها بعض المشاكل، وقد يكون النموذج الخطي مثل هذا:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell" style="text-align: center;">
				<span style="font-size:medium"><span style="font-style:italic">y</span> = β</span><sub><span style="font-size:medium">0</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> + ε </span>
			</td>
		</tr></tbody></table>
<p>
	حيث يكون y هو المتغير التابع وتكون x<sub>1</sub> وx<sub>2</sub> هي متغيرات توضيحية، أي يمكننا الآن إيجاد المعامِلات التي تقلل من الرواسب residuals، لكن مشكلة هذه الطريقة أنها تنتج تنبؤات يصعب تفسيرها.
</p>

<p>
	نظرًا للمعامِلات المقدَّرة وقيم كل من x<sub>1</sub> وx<sub>2</sub>، فقد يتنبأ النموذج أنّ y=0.5، لكن القيم الوحيدة ذات المعنى للمتغير y هي 0 و1، وقد يكون من المغري تفسير نتيجة مثل هذه على أنها احتمال، فقد نقول مثلًا أنّ المستجيب صاحب القيم المعينة للمتغيرَين x<sub>1</sub> وx<sub>2</sub> لديه فرصة 50‎%‎ في إنجاب صبي، لكن من الممكن أن يتنبأ هذا النموذج بـy=1.1 أو y= -0.1، إلا أنّ هذه الاحتمالات غير صالحة.
</p>

<p>
	يتجنب الانحدار اللوجستي هذه المشكلة عن طريق التعبير عن التنبؤات على صورة أرجحية odds بدلًا من الاحتمالات، وإذا لم يكن لديك فكرةً مسبقةً عن الأرجحية، فسنقول لك أنّ "الأرجحية لصالح" odds in favor حدث ما هي نسبة احتمال حدوثه إلى احتمال عدم حدوثه، فإذا اعتقدنا أنّ للفريق فرصة فوز بنسبة 75‎%‎، فسنقول أنّ الأرجحية لصالحهم هي 3 على 1 لأن فرصة الفوز هي ثلاثة أضعاف فرصة الخسارة، ويمثِّل كل من الأرجحية والاحتمال المعلومات نفسها لكن بطرق مختلفة، كما يمكننا حساب يمكننا حساب الأرجحية إذا كان لدينا احتمال ما باستخدام هذه المعادلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_58" style="">
<span class="pln">   o </span><span class="pun">=</span><span class="pln"> p </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">-</span><span class="pln">p</span><span class="pun">)</span></pre>

<p>
	أما إذا كان لدينا قيمة الأرجحية لصالح، فيمكننا تحويلها إلى احتمال باستخدام هذه المعادلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_60" style="">
<span class="pln">   p </span><span class="pun">=</span><span class="pln"> o </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="pln">o</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	يعتمد الانحدار اللوجستي على النموذج التالي:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell" style="text-align: center;">
				<span style="font-size:medium">log<span style="font-style:italic">o</span> = β</span><sub><span style="font-size:medium">0</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">1</span></sub><span style="font-size:medium"> + β</span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> <span style="font-style:italic">x</span></span><sub><span style="font-size:medium">2</span></sub><span style="font-size:medium"> + ε </span>
			</td>
		</tr></tbody></table>
<p>
	يمثِّل <code>o</code> الأرجحية لصالح نتيجة ما، حيث يكون <code>o</code> هو الأرجحية لصالح ولادة صبي مثلًا، ولنفترض أنه لدينا المعامِلات المقدَّرةβ<sub>0</sub> وβ<sub>1</sub>وβ<sub>2</sub> والتي سنشرحها بعد قليل، ولنفترض أنه لدينا قيم x<sub>1</sub> وx<sub>2</sub>، حيث يمكننا عندها حساب القيمة المتوقعة لـ <code>logo</code> ثم تحويلها إلى احتمال كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_62" style="">
<span class="pln">    o </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">exp</span><span class="pun">(</span><span class="pln">log_o</span><span class="pun">)</span><span class="pln">
    p </span><span class="pun">=</span><span class="pln"> o </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="pln">o</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span></pre>

<p>
	لذا يمكننا في سيناريو لعبة التخمين في المكتب حساب الاحتمال التنبؤي لولادة صبي، لكن كيف يمكننا تقدير المعامِلات؟
</p>

<h2>
	تقدير المعاملات
</h2>

<p>
	لا يحتوي الانحدار الخطي على حل منغلق الشكل على عكس الانحدار الخطي، لذا يمكن حله عن طريق تخمين حل أولي ومن ثم تحسينه في كل تكرار، حيث أنّ الهدف المعتاد هو إيجاد تقدير الاحتمال الأعظم maximum-likelihood estimate -أو MLE اختصارًا- وهو مجموعة من المعامِلات التي تزيد من احتمالية البيانات، ولنفترض مثلًا أنه لدينا البيانات التالية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_64" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> y </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">])</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> x1 </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">])</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> x2 </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">array</span><span class="pun">([</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">])</span></pre>

<p>
	نبدأ بالتخمينات الأولية وهي β<sub>0</sub>= -1.5 و β<sub>1</sub>=2.8و β<sub>2</sub>=1.1:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_66" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> beta </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[-</span><span class="lit">1.5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.8</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.1</span><span class="pun">]</span></pre>

<p>
	ثم نحسب <code>log_o</code> لكل صف بمفرده:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_68" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> log_o </span><span class="pun">=</span><span class="pln"> beta</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> beta</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> x1 </span><span class="pun">+</span><span class="pln"> beta</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> x2 
</span><span class="pun">[-</span><span class="lit">1.5</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.4</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.4</span><span class="pln">  </span><span class="lit">2.4</span><span class="pun">]</span></pre>

<p>
	بعد ذلك نحول الأرجحية اللوغاريتمية إلى احتمالات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_70" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> o </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">exp</span><span class="pun">(</span><span class="pln">log_o</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="pln">  </span><span class="lit">0.223</span><span class="pln">   </span><span class="lit">0.670</span><span class="pln">   </span><span class="lit">0.670</span><span class="pln">  </span><span class="lit">11.02</span><span class="pln">  </span><span class="pun">]</span><span class="pln">

</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> p </span><span class="pun">=</span><span class="pln"> o </span><span class="pun">/</span><span class="pln"> </span><span class="pun">(</span><span class="pln">o</span><span class="pun">+</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="pln"> </span><span class="lit">0.182</span><span class="pln">  </span><span class="lit">0.401</span><span class="pln">  </span><span class="lit">0.401</span><span class="pln">  </span><span class="lit">0.916</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	لاحظ أنه عندما يكون<code>log_o</code> أكبر من الصفر، تكون قيمة <code>o</code> أكبر من الواحد، وتكون قيمة <code>p</code> أكبر من 0.5.
</p>

<p>
	تكون احتمالية likelihood خرج ما هي <code>p</code> عندما يكون <code>1==y</code> وتكون <code>1-p</code> عندما يكون <code>0==y</code>، ولنفترض مثلًا أنه اعتقدنا أن احتمال probability ولادة صبي هي 0.8 وكان الخرج هو ولادة صبي، فستكون الاحتمالية likelihood هي 0.8، وإذا كان الخرج فتاةً، فستتكون الاحتمالية 0.2، كما يمكننا إجراء الحسابات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_73" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> likes </span><span class="pun">=</span><span class="pln"> y </span><span class="pun">*</span><span class="pln"> p </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">-</span><span class="pln">y</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pun">-</span><span class="pln">p</span><span class="pun">)</span><span class="pln">
</span><span class="pun">[</span><span class="pln"> </span><span class="lit">0.817</span><span class="pln">  </span><span class="lit">0.401</span><span class="pln">  </span><span class="lit">0.598</span><span class="pln">  </span><span class="lit">0.916</span><span class="pln"> </span><span class="pun">]</span></pre>

<p>
	تكون الاحتمالية الإجمالية للبيانات هي ناتج ضرب قيم <code>likes</code>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_75" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> like </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">prod</span><span class="pun">(</span><span class="pln">likes</span><span class="pun">)</span><span class="pln">
</span><span class="lit">0.18</span></pre>

<p>
	تكون احتمالية البيانات 0.18 في حال اعتمدنا قيم بيتا <code>beta</code> هذه، علمًا أن هدف الانحدار اللوجستي هو إيجاد المعامِلات التي تزيد من هذه الاحتمالية، ولإجراء هذا تستخدِم معظم الحِزم حلًا تكراريًا مثل تابع نيوتن Newton’s method، كما يمكنك زيارة <a href="https://ar.wikipedia.org/wiki/%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1_%D9%84%D9%88%D8%AC%D8%B3%D8%AA%D9%8A" rel="external nofollow">صفحة ويكيبيديا</a> للمزيد من التفاصيل.
</p>

<h2>
	التنفيذ implementation
</h2>

<p>
	تزودنا StatsModels بتنفيذ برمجي للانحدار اللوجستي ويُدعى <code>logit</code> وسُمي باسم الدالة التي تحول الاحتمال إلى الأرجحية اللوغاريتمية log odds، كما سنبحث عن متغيرات تؤثِّر على نسبة الجنس لتوضيح استخدامه.، وسنُعيد تحميل بيانات المسح الوطني لنمو الأسرة وسنختار حالات الحمل التي تجاوزت مدتها 30 أسبوع:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_77" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    df </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">[</span><span class="pln">live</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">&gt;</span><span class="lit">30</span><span class="pun">]</span></pre>

<p>
	لكن أحد شروط <code>logit</code> هو أن يكون <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r221/" rel="">المتغير</a> التابع ثنائيًا binary عوضًا بدلًا من النوع البولياني boolean، لذا فقد أنشأنا عمودًا جديدًا باسم <code>boy</code> باستخدام <code>astype(int)</code> لتحويل القيم إلى قيم ثنائية صحيحة binary integers:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_79" style="">
<span class="pln">    df</span><span class="pun">[</span><span class="str">'boy'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">df</span><span class="pun">.</span><span class="pln">babysex</span><span class="pun">==</span><span class="lit">1</span><span class="pun">).</span><span class="pln">astype</span><span class="pun">(</span><span class="pln">int</span><span class="pun">)</span></pre>

<p>
	يُعَدّ عمر الوالدَين وترتيب الولادة والعِرق والحالة الاجتماعية عواملًا مؤثرةً في نسبة الجنس، كما يمكننا استخدام الانحدار اللوجستي من أجل التحقق فيما إذا كانت تظهر هذه التأثيرات في بيانات المسح الوطني لنمو الأسرة، حيث سنبدأ أولًا من عمر الأم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_82" style="">
<span class="pln">    </span><span class="kwd">import</span><span class="pln"> statsmodels</span><span class="pun">.</span><span class="pln">formula</span><span class="pun">.</span><span class="pln">api </span><span class="kwd">as</span><span class="pln"> smf

    model </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">logit</span><span class="pun">(</span><span class="str">'boy ~ agepreg'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">df</span><span class="pun">)</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">fit</span><span class="pun">()</span><span class="pln">
    </span><span class="typ">SummarizeResults</span><span class="pun">(</span><span class="pln">results</span><span class="pun">)</span></pre>

<p>
	تأخذ الدالتان <code>logit</code> و<code>ols</code> الوسائط نفسها وهي عبارة عن معادلة بصيغة Patsy بالإضافة إلى إطار بيانات، وتكون نتيجة الدالة <code>logit</code> هي كائن Logit يمثِّل النموذج، حيث يتضمن سمتين بحيث تحتوي السمة الأولى <code>endog</code> على المتغير الداخلي endogenous variable وهو اسم آخر للمتغير التابع؛ أما السمة الثانية <code>exog</code> فتحتوي على المتغيرات الخارجية exogenous variables وهو اسم آخر للمتغيرات التوضيحية، وبما أنهما مصفوفتَي نمباي NumPy arrays، فقد يكون من الأفضل في بعض الأحيان تحويلها إلى أُطر بيانات DataFrames:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_84" style="">
<span class="pln">    endog </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">(</span><span class="pln">model</span><span class="pun">.</span><span class="pln">endog</span><span class="pun">,</span><span class="pln"> columns</span><span class="pun">=[</span><span class="pln">model</span><span class="pun">.</span><span class="pln">endog_names</span><span class="pun">])</span><span class="pln">
    exog </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">(</span><span class="pln">model</span><span class="pun">.</span><span class="pln">exog</span><span class="pun">,</span><span class="pln"> columns</span><span class="pun">=</span><span class="pln">model</span><span class="pun">.</span><span class="pln">exog_names</span><span class="pun">)</span></pre>

<p>
	تكون نتيجة <code>model.fit</code> كائن BinaryResults يشبه كائن RegressionResults الذي ينتج من <code>ols</code>، وإليك تلخيص للنتائج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_86" style="">
<span class="typ">Intercept</span><span class="pln">   </span><span class="lit">0.00579</span><span class="pln">   </span><span class="pun">(</span><span class="lit">0.953</span><span class="pun">)</span><span class="pln">
agepreg     </span><span class="lit">0.00105</span><span class="pln">   </span><span class="pun">(</span><span class="lit">0.783</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">6.144e-06</span></pre>

<p>
	وجدنا أن المعامِل الخاص بـ <code>ageprep</code> موجب الذي يشير إلى أنه من المرجح ولادة الأمهات الأكبر سنًا صبيانًا، لكن القيمة الاحتمالية هي 0.783 التي تعني أنه يمكن أن يكون التأثير الواضح ناتجًا عن الصدفة، ولا ينطبق مُعامِل التحديد R<sup>2</sup> على الانحدار اللوجستي لكن يوجد عدة بدائل وتُدعى قيم R<sup>2</sup> الوهمية أي pseudo-R<sup>2</sup>‎، إذ يمكن أن تكون هذه القيم مفيدةً لموازنة النماذج، وإليك على سبيل المثال نموذج يتضمن عدة عوامل يُقال أنها مرتبطة بنسبة الجنس:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_89" style="">
<span class="pln">    formula </span><span class="pun">=</span><span class="pln"> </span><span class="str">'boy ~ agepreg + hpagelb + birthord + C(race)'</span><span class="pln">
    model </span><span class="pun">=</span><span class="pln"> smf</span><span class="pun">.</span><span class="pln">logit</span><span class="pun">(</span><span class="pln">formula</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">=</span><span class="pln">df</span><span class="pun">)</span><span class="pln">
    results </span><span class="pun">=</span><span class="pln"> model</span><span class="pun">.</span><span class="pln">fit</span><span class="pun">()</span></pre>

<p>
	يتضمن هذا النموذج عمر الأب عند الولادة <code>hpagelb</code> إلى جانب عمر الأم أيضًا، ويمثِّل المتغير <code>birthord</code> ترتيب ولادة الطفل؛ أما العِرق فهو متغير فئوي، وإليك ما نتج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_91" style="">
<span class="typ">Intercept</span><span class="pln">      </span><span class="pun">-</span><span class="lit">0.0301</span><span class="pln">     </span><span class="pun">(</span><span class="lit">0.772</span><span class="pun">)</span><span class="pln">
C</span><span class="pun">(</span><span class="pln">race</span><span class="pun">)[</span><span class="pln">T</span><span class="pun">.</span><span class="lit">2</span><span class="pun">]</span><span class="pln">   </span><span class="pun">-</span><span class="lit">0.0224</span><span class="pln">     </span><span class="pun">(</span><span class="lit">0.66</span><span class="pun">)</span><span class="pln">
C</span><span class="pun">(</span><span class="pln">race</span><span class="pun">)[</span><span class="pln">T</span><span class="pun">.</span><span class="lit">3</span><span class="pun">]</span><span class="pln">   </span><span class="pun">-</span><span class="lit">0.000457</span><span class="pln">   </span><span class="pun">(</span><span class="lit">0.996</span><span class="pun">)</span><span class="pln">
agepreg        </span><span class="pun">-</span><span class="lit">0.00267</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0.629</span><span class="pun">)</span><span class="pln">
hpagelb         </span><span class="lit">0.0047</span><span class="pln">     </span><span class="pun">(</span><span class="lit">0.266</span><span class="pun">)</span><span class="pln">
birthord        </span><span class="lit">0.00501</span><span class="pln">    </span><span class="pun">(</span><span class="lit">0.821</span><span class="pun">)</span><span class="pln">
R</span><span class="pun">^</span><span class="lit">2</span><span class="pln"> </span><span class="lit">0.000144</span></pre>

<p>
	ليس لأي من هذه المعامِلات المُقدَّرة دلالة إحصائية، وعلى الرغم أن قيمة R<sup>2</sup> الوهمية أعلى بقليل إلا أنه من المحتمل أن يكون هذا صدفةً.
</p>

<h2>
	الدقة Accuracy
</h2>

<p>
	أكثر ما يهمنا في سيناريو لعبة التخمين في المكتب هو دقة النموذج، وهي عبارة عن عدد التنبؤات الصحيحة موازنةً مع ما نتوقعه صدفةً، ونظرًا لأن عدد الصبيان يفوق عدد الإناث في بيانات المسح الوطني لنمو الأسرة، فيمكننا تحديد الاستراتيجية الأساسية على أنها تخمين ولادة صبي في كل مرة، وبذلك تكون دقة النموذج هي نسبة الصبيان:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_93" style="">
<span class="pln">    actual </span><span class="pun">=</span><span class="pln"> endog</span><span class="pun">[</span><span class="str">'boy'</span><span class="pun">]</span><span class="pln">
    baseline </span><span class="pun">=</span><span class="pln"> actual</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span></pre>

<p>
	يكون المتوسط mean هو نسبة الصبيان وقيمته 0.507 بما أنه رُمِّز المتغير <code>actual</code> بترميز ثنائي صحيح binary integers، وإليك طريقة حساب دقة النموذج كما يلي:
</p>

<pre class="ipsCode">
    predict = (results.predict() &gt;= 0.5)
    true_pos = predict * actual
    true_neg = (1 - predict) * (1 - actual)
</pre>

<p>
	يُعيد <code>results.predict</code> مصفوفة نمباي NumPy array تحتوي على الاحتمالات والتي نقربها إلى 0 أو 1، علمًا أنه ينتج 1 عن عملية جداء مع المتغير <code>actual</code> إذا توقعنا ولادة صبي وأصبنا التوقُّع، في حين ينتج 0 في غير ذلك، لذا يشير المتغير <code>true_pos</code> إلى true positives أي الإيجابيات الصحيحة، كما يشير المتغير <code>true_neg</code> إلى الحالات التي نتوقع فيها ولادة بنت ويصيب توقعنا، وبلتالي تكون الدقة هي نسبة التخمينات الصحيحة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_95" style="">
<span class="pln">    acc </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sum</span><span class="pun">(</span><span class="pln">true_pos</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">true_neg</span><span class="pun">))</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">actual</span><span class="pun">)</span></pre>

<p>
	ظهرت النتيجة 0.512 وهي أفضل بقليل من القيمة الأساسية 0.507، لكن عليك ألا تأخذ هذه النتيجة على محمل الجد لأننا استخدَمنا البيانات نفسها في عمليتَي بناء واختبار النموذج، لذا قد لا يمتلك النموذج قوةً تنبؤيةً على البيانات الجديدة، لكن دعنا على أيّ حال نستخدِم النموذج للتبنؤ في لعبة التخمين في المكتب ولنفترض أنّ عمر صديقتك 35 عامًا وذات بشرة بيضاء وعمر زوجها 39 عامًا وأنها حامل بطفلهما الثالث:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_5654_97" style="">
<span class="pln">    columns </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">'agepreg'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'hpagelb'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'birthord'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'race'</span><span class="pun">]</span><span class="pln">
    new </span><span class="pun">=</span><span class="pln"> pandas</span><span class="pun">.</span><span class="typ">DataFrame</span><span class="pun">([[</span><span class="lit">35</span><span class="pun">,</span><span class="pln"> </span><span class="lit">39</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">]],</span><span class="pln"> columns</span><span class="pun">=</span><span class="pln">columns</span><span class="pun">)</span><span class="pln">
    y </span><span class="pun">=</span><span class="pln"> results</span><span class="pun">.</span><span class="pln">predict</span><span class="pun">(</span><span class="pln">new</span><span class="pun">)</span></pre>

<p>
	إذا أردنا استدعاء <code>results.predict</code> لحالة حمل جديدة، فسيتوجب علينا إنشاء إطار بيانات DataFrame يحوي عمودًا لكل متغير في النموذج وتكون النتيجة في هذه الحالة 0.52، لذا يجب عليك تخمين ولادة صبي، لكن إذا حسَّن النموذج من فرصك في الفوز، فسيكون الفارق ضئيلًا جدًا.
</p>

<h2>
	التمارين
</h2>

<p>
	يوجد الحل الخاص بنا في <code>chap11soln.ipynb</code>.
</p>

<h3>
	تمرين 1
</h3>

<p>
	لنفترض أن ولادة طفل أحد زملائك في العمل قد اقتربت وقررت المشاركة في لعبة التخمين في المكتب لتوقع موعد الولادة، فإذا افترضنا أنّ التخمينات قد حصلت في الأسبوع الثلاثين من الحمل، فما هي المتغيرات التي ستستخدِمها لتحقيق أفضل تنبؤ؟ يجب أن يقتصر استخدامك للمتغيرات على المتغيرات المعلومة قبل الولادة وتلك التي من المرجح أن تكون معروفةً عند المشاركين في لعبة المكتب.
</p>

<h3>
	تمرين 2
</h3>

<p>
	تقترح فرضية تريفرز-ويلارد Trivers-Willard hypothesis اعتمادية نسبة الجنس بالنسبة للعديد من الثدييات على حالة الأم، أي تعتمد على عوامل مثل عمر الأم وحجمها وصحتها وحالتها الاجتماعية، كما يمكنك الاطلاع على <a href="https://en.wikipedia.org/wiki/Trivers-Willard_hypothesis" rel="external nofollow">صفحة ويكيبيديا</a> للمزيد من التفاصيل.، حيث أظهرت بعض الدراسات وجود هذا التأثير ضمن البشر لكن النتائج مختلطة، إذ أجرينا اختبارات على بعض المتغيرات المتعلِّقة بهذه العوامل لكننا لم نجد أي تأثير ذا دلالة إحصائية على نسبة الجنس.
</p>

<p>
	اختبر المتغيرات الأخرى في ملفات الحمل والمستجيبِين باستخدام طريقة التنقيب عن البيانات، وذلك على أساس تمرين على ما سبق، هل وجدت بعد ذلك أيّ عوامل لها تأثير كبير؟
</p>

<h3>
	تمرين 3
</h3>

<p>
	يمكنك استخدام انحدار بواسون Poisson regression إذا كانت القيمة التي تريد التنبؤ بها تعدادًا، علمًا أنّ تنفيذ انحدار بواسون البرمجي موجود في StatsModels مع دالة اسمها <code>poisson</code>، وهي تعمل بالطريقة نفسها التي تعمل فيها الدالتين <code>ols</code> و<code>logit</code>، لذا دعنا نستخدِم هذه الدالة على أساس تمرين لما سبق للتنبؤ بعدد أطفال امرأة معيّنة في بيانات المسح الوطني لنمو الأسرة، حيث أن اسم المتغير الذي يمثل عدد الأطفال هو <code>numbabes</code>.
</p>

<p>
	لنفترض أنك قابلت امرأةً تبلغ من العمر 35 عامًا سوداء البشرة وخريجة جامعية يتجاوز دخل أسرتها السنوي 75 ألف دولار، فكم تتوقع أن يكون عدد أطفالها؟
</p>

<h3>
	تمرين 4
</h3>

<p>
	يمكنك استخدام الانحدار اللوجستي متعدد الحدود multinomial logistic regression إذا أردت تنبؤ قيمة فئوية، علمًا أنّ تنفيذه البرمجي في StatsModels مع دالة اسمها <code>mnlogit</code>، لذا دعنا نستخدِم هذه الدالة على أساس تمرين لما سبق لتخمين فيما إذا كانت المرأة متزوجة أو أرملة أو منفصلة عن زوجها أو أنها لم تتزوج على الإطلاق، علمًا أنّ اسم المتغير الذي يمثل الحالة الزوجية في المسح الوطني لنمو الأسرة هو <code>rmarital</code>.
</p>

<p>
	لنفترض أنك قابلت امرأةً تبلغ من العمر 25 عامًا بيضاء البشرة وأنهت دراسة المرحلة الثانوية لكنها لم تدرس في المرحلة الجامعية ودخل أسرتها السنوي حوالي 45 ألف دولار، ما هو احتمال أن تكون متزوجة أو أرملة، …إلخ؟
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2012.html" rel="external nofollow">Chapter 11 Regression analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">اختبار الفرضيات الإحصائية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%8A%D8%B1-estimation-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1411/" rel="">التقدير Estimation الإحصائي في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D9%83%D9%85%D9%8A-cumulative-distribution-functions-r1331/" rel="">دوال التوزيع التراكمي Cumulative distribution functions</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">العلاقات بين المتغيرات الإحصائية وكيفية تنفيذها في بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1493</guid><pubDate>Sun, 06 Mar 2022 11:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x645;&#x631;&#x628;&#x639;&#x627;&#x62A; &#x627;&#x644;&#x635;&#x63A;&#x631;&#x649; &#x627;&#x644;&#x62E;&#x637;&#x64A;&#x629; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%BA%D8%B1%D9%89-%D8%A7%D9%84%D8%AE%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1412/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61cc02447da0d_--.png.024649af24f52acd65fa7da392238392.png" /></p>

<p>
	توجد الشيفرة الخاصة بهذا المقال في الملف <code>linear.py</code>.
</p>

<h2>
	ملاءمة المربعات الصغرى
</h2>

<p>
	تقيس معاملات الترابط قوة وإشارة العلاقة لكنها لا تقيس الميل slope، إذ توجد عدة طرق لتقدير الميل وأكثر هذه الطرق شيوعًا هي ملاءمة المربعات الصغرى الخطية linear least squares fit، حيث تُعَدّ الملاءمة الخطية linear fit خطًا ينمذج العلاقة بين المتغيرات؛ أما ملاءمة المربعات الصغرى least squares فتقلل الخطأ التربيعي المتوسط mean squared error -أو MSE اختصارًا- بين الخط والبيانات.
</p>

<p>
	لنفترض أنه لدينا متسلسلةً من النقاط <code>ys</code>، ونريد التعبير عنها على أساس دالة من متسلسلة أخرى <code>xs</code>، فإذا كانت هناك علاقةً خطيةً بين <code>xs</code> و<code>ys</code> مع نقطة تقاطع <code>inter</code> وميل <code>slope</code>، فسنتوقع عندها <code>y</code> ليكون <code>inter + slope * x</code>، علمًا أنّ هذا التوقع تقريبي فحسب أي ليس دقيقًا إلا إذا كان الترابط مثاليًا، وتكون الصيغة الرياضية للانحراف العمودي عن الخط أو ما يُعرف بالراسب residual هي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_7" style="">
<span class="pln">res </span><span class="pun">=</span><span class="pln"> ys </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">inter </span><span class="pun">+</span><span class="pln"> slope </span><span class="pun">*</span><span class="pln"> xs</span><span class="pun">)</span></pre>

<p>
	قد تكون الرواسب ناتجةً عن عوامل عشوائية مثل الخطأ في القياس أو عوامل غير عشوائية وغير معروفة، فإذا حاولنا مثلًا توقّع الوزن من دالة الطول، فستشمل عوامل غير معروفة الحمية الغذائية والتمارين الرياضية ونوع الجسم، وإذا لم يكن حسابنا للمعامِلَين <code>inter</code> و<code>slope</code> صحيحًا، فستكون الرواسب أكبر، لذا من المنطقي أن تكون المعاملات التي نريدها هي تلك التي تقلل من الرواسب.
</p>

<p>
	قد نحاول تقليل القيمة المطلقة للرواسب أو مربعها أو مكعبها، لكن الخيار الأكثر شيوعًا واستخدامًا هو تقليل مجموع مربعات الرواسب <code>sum(res**2)</code>، لكن لمَ تُعَدّ هذه الطريقة هي الأشيع؟ هناك ثلاث أسباب وجيهة وسبب أقل أهمية لذلك كما يلي:
</p>

<ul>
<li>
		يعامِل التربيع الرواسب الموجبة والسالبة بالطريقة نفسها، وهي ميزة مفيدة لنا في أغلب الأحيان.
	</li>
	<li>
		يُعطي التربيع وزنًا -أي ترجيحًا- أكبر للرواسب الكبيرة، لكن ليس للدرجة التي تتسبب فيها هذه الميزة بهيمنة الرواسب الأكبر دائمًا. *إذا لم تكن الرواسب مترابطةً وإذا كان توزيعها طبيعيًا مع متوسط قدره 0 وتباين ثابت لكن غير معروف، فستكون ملاءمة المربعات الصغرى هي نفسها مُقدِّر الاحتمال الأعظم maximum likelihood estimator -أو MLE اختصارًا- لكل من <code>inter</code> و<code>slope</code>، ويمكنك زيارة <a href="https://ar.wikipedia.org/wiki/%D8%A7%D9%86%D8%AD%D8%AF%D8%A7%D8%B1_%D8%AE%D8%B7%D9%8A" rel="external nofollow">صفحة ويكيبيديا</a> لمزيد من المعلومات.
	</li>
	<li>
		يمكن حساب قيم <code>inter</code> و<code>slope</code> التي تقلل من الرواسب المربعة بكفاءة.
	</li>
</ul>
<p>
	يُعَدّ السبب الأخير منطقيًا حينما كانت كفاءة الحسابات أهم من اختيار الطريقة الأنسب للمشكلة التي نحاول حلها لكن الأمر لم يعُد كذلك، لذلك يجدر التفكير فيما إذا كانت الرواسب المربّعة هي ما نريد تقليله أم لا، ولنفترض مثلًا أنك تحاول التنبؤ بقيم <code>ys</code> باستخدام <code>xs</code>، فقد يكون تخمين القيم المرتفعة جدًا أفضل -أو أسوأ- من تخمين القيم المنخفضة جدًا، وقد نرغب في هذه الحالة في حساب بعض من دوال الكلفة لكل راسب من الرواسب ثم تقليل الكلفة الإجمالية <code>sum(cost(res))</code>، ومع ذلك يُعَدّ حساب ملاءمة المربعات الصغرى سريعًا وسهلًا وغالبًا ما يكون جيدًا بدرجة كافية.
</p>

<h2>
	التنفيذ
</h2>

<p>
	يزود المستودع <code>thinkstats2</code> بدوال بسيطة توضِّح المربعات الصغرى الخطية، إليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_9" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">LeastSquares</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ys</span><span class="pun">):</span><span class="pln">
    meanx</span><span class="pun">,</span><span class="pln"> varx </span><span class="pun">=</span><span class="pln"> </span><span class="typ">MeanVar</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
    meany </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Mean</span><span class="pun">(</span><span class="pln">ys</span><span class="pun">)</span><span class="pln">

    slope </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cov</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ys</span><span class="pun">,</span><span class="pln"> meanx</span><span class="pun">,</span><span class="pln"> meany</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> varx
    inter </span><span class="pun">=</span><span class="pln"> meany </span><span class="pun">-</span><span class="pln"> slope </span><span class="pun">*</span><span class="pln"> meanx

    </span><span class="kwd">return</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope</span></pre>

<p>
	يأخذ التابع <code>LeastSquares</code> متسلسلتين هما <code>xs</code> و<code>ys</code> ويُعيد المعامِلَين المُقدِّرَين <code>inter</code> و<code>slop</code>، كما يمكنك الاطلاع على مزيد من التفاصيل حول آلية عمله عن طريق زيارة <a href="https://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares" rel="external nofollow">صفحة ويكيبيديا</a>، كما يزود المستودع <code>thinkstats2</code> بالتابع <code>FitLine</code> أيضًا، حيث يأخذ المعامِلَين <code>inter</code> و<code>slope</code> ويُعيد الخط الملائم للمتسلسلة <code>xs</code>، وإليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_11" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">FitLine</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope</span><span class="pun">):</span><span class="pln">
    fit_xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">sort</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
    fit_ys </span><span class="pun">=</span><span class="pln"> inter </span><span class="pun">+</span><span class="pln"> slope </span><span class="pun">*</span><span class="pln"> fit_xs
    </span><span class="kwd">return</span><span class="pln"> fit_xs</span><span class="pun">,</span><span class="pln"> fit_ys</span></pre>

<p>
	يمكننا استخدام هذه الدوال لحساب ملاءمة المربعات الصغرى لوزن الولادات على أساس دالة لعمر الأم:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_13" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    live </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">dropna</span><span class="pun">(</span><span class="pln">subset</span><span class="pun">=[</span><span class="str">'agepreg'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'totalwgt_lb'</span><span class="pun">])</span><span class="pln">
    ages </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">agepreg
    weights </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">totalwgt_lb

    inter</span><span class="pun">,</span><span class="pln"> slope </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">LeastSquares</span><span class="pun">(</span><span class="pln">ages</span><span class="pun">,</span><span class="pln"> weights</span><span class="pun">)</span><span class="pln">
    fit_xs</span><span class="pun">,</span><span class="pln"> fit_ys </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">FitLine</span><span class="pun">(</span><span class="pln">ages</span><span class="pun">,</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope</span><span class="pun">)</span></pre>

<p>
	تبلغ قيمة نقطة التقاطع المقدَّرة والميل المقدَّر 6.8 رطلًا -أي حوالي 3.08 كيلوغرامًا- و0.017 رطلًا في العام الواحد -أي حوالي 0.007 كيلوغرامًا في العام الواحد-، ومن الصعب تفسير هذه القيم بهذه الصورة، حيث تُعَدّ نقطة التقاطع الوزن المتوقع للطفل إذا كان عمره أمه 0 عام، وهذا ليس منطقيًا في سياقنا، والميل صغير جدًا بحيث لا يمكن فهمه بسهولة.
</p>

<p>
	غالبًا ما يكون من المفيد البدء بنقطة التقاطع عند متوسط <code>x</code> بدلًا من البدء بنقطة التقاطع من القيمة 0 أي x=0، لذا يكون متوسط العمر في هذه الحالة هو 25 تقريبًا ومتوسط وزن الطفل للأم التي يبلغ عمرها 25 عامًا هو 7.3 رطلًا -أي حوالي 3.31 كيلوغرامًا-، بذلك يكون الميل 0.27 رطلًا في العام الواحد أي حوالي 0.12 كيلوغرامًأ- أو 0.17 رطلًا كل 10 أعوام -أي حوالي 0.07 كيلوغرامًا كل 10 أعوام-.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="87349" href="https://academy.hsoub.com/uploads/monthly_2021_12/61cc02420633f_10.1.png.65feebd115fa8699946490364bc3d9a0.png" rel=""><img alt="الشكل 10.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87349" data-unique="cn7d6dqsz" src="https://academy.hsoub.com/uploads/monthly_2021_12/61cc02420633f_10.1.png.65feebd115fa8699946490364bc3d9a0.png"></a>
</p>

<p>
	يوضِّح الشكل السابق مخطط انتشار أوزان الولادات وعمر الأم مع ملاءمة خطية، ومن الجيد النظر إلى شكل مثل هذا بهدف تقييم ما إذا كانت العلاقة خطيةً أم لا وما إذا كان الخط الملائم يبدو نموذجًا جيدًا للعلاقة أم لا.
</p>

<h2>
	الرواسب
</h2>

<p>
	يُعَدّ رسم الرواسب اختبارًا مفيدًا أيضًا، كما يزودنا المستودع <code>thinkstats2</code> بدالة تحسب الرواسب، إليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_15" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Residuals</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ys</span><span class="pun">,</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope</span><span class="pun">):</span><span class="pln">
    xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">asarray</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
    ys </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">asarray</span><span class="pun">(</span><span class="pln">ys</span><span class="pun">)</span><span class="pln">
    res </span><span class="pun">=</span><span class="pln"> ys </span><span class="pun">-</span><span class="pln"> </span><span class="pun">(</span><span class="pln">inter </span><span class="pun">+</span><span class="pln"> slope </span><span class="pun">*</span><span class="pln"> xs</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> res</span></pre>

<p>
	تأخذ الدالة <code>Residuals</code> متسلسلتين هما <code>xs</code> و<code>ys</code>، ومعامِلَين مُقدِّرَين هما <code>inter</code> و<code>slope</code> وتُعيد الفروق بين القيم الفعلية والخط الملائم.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="87350" href="https://academy.hsoub.com/uploads/monthly_2021_12/61cc02426ac77_10.2.png.3208cd7ba670dc555d4ae33d77c29b75.png" rel=""><img alt="الشكل 10.2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87350" data-unique="hdhxem8tm" src="https://academy.hsoub.com/uploads/monthly_2021_12/61cc02426ac77_10.2.png.3208cd7ba670dc555d4ae33d77c29b75.png"></a>
</p>

<p>
	يوضِّح الشكل السابق رواسب الملاءمة الخطية.
</p>

<p>
	جمعنا المستجيبين حسب العمر وحسبنا قيم المئين في كل مجموعة تمامًا من أجل رسم مخططات للرواسب كما رأينا في قسم توصيف العلاقات في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">المقال السابق</a>، حيث يوضِّح الشكل السابق المئين 25 -أي 25th percentile- والمئين 50 -أي 50th percentile- والمئين 75 -أي 75th percentile- للرواسب الموجودة في كل مجموعة، وكما هو متوقع فإن الوسيط median يقارب الصفر والانحراف الربيعي interquartile range هو حوالي رطلين -أي حوالي 0.9 كيلوغرامًا-، لذا يمكننا تخمين وزن الطفل بخطأ قدره رطلًا واحدًا بحوالي ‎50% من المرات إذا علمنا عمر الأم مسبقًا.
</p>

<p>
	تكون هذه الخطوط في الوضع المثالي مسطحةً ويشير هذا إلى أن الرواسب عشوائية ومتوازية وإلى أن المجموعات متساوية فيما بينهما من حيث تباين الرواسب -أي أن قيم الرواسب متساوية بالنسبة لكل المجموعات-، حيث أن الخطوط قريبة من التوازي وهذا أمر جيد، لكن يوجد بعض الانحناءات في هذه الخطوط مما يدل على أن العلاقة لاخطية، ومع ذلك تُعَدّ الملاءمة الخطية نموذجًا بسيطًا ربما يكون جيدًا بما يكفي لبعض الأغراض.
</p>

<h2>
	التقدير
</h2>

<p>
	يُعَدّ المعامِلان <code>slope</code> و<code>inter</code> تقديرَين بناءً على عينة ما، وهما عُرضة للتحيز في أخذ العينات sampling bias تمامًا مثل التقديرات الأخرى، وكذلك فهما عُرضة للخطأ في القياس measurement error والخطأ في أخذ العينات sampling error، حيث أن التحيُز في أخذ العينات -كما ناقشنا في مقال <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%8A%D8%B1-estimation-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1411/" rel="">التقدير Estimation الإحصائي في بايثون</a>- ناتج عن أخذ عينات غير تمثيلية non-representative sampling، والخطأ في القياس ناتج عن أخطاء في جمع وتسجيل البيانات؛ أما الخطأ في أخذ العينات فهو نتيجة لقياس عينة ما بدلًا من قياس السكان بأكملهم.
</p>

<p>
	سنطرح السؤال التالي من أجل تقييم الخطأ في أخذ العينات: ما مقدار التباين الذي نتوقعه في التقديرات إذا أجرينا هذه التجربة مرةً أخرى؟ حيث سنجيب عن هذا السؤال عن طريق إجراء عدة تجارب محاكاة ومن ثم حساب توزيعات أخذ العينات للتقديرات، وقد طبقنا محاكاةً للتجارب عن طريق إعادة أخذ عينات البيانات، أي نعامل حالات الحمل المرصودة كما لو أنها تمثل السكان جميعهم ومن ثم سحبنا عينات مع الاستبدال من العينة المرصودة، وإليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_18" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">SamplingDistributions</span><span class="pun">(</span><span class="pln">live</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">101</span><span class="pun">):</span><span class="pln">
    t </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">):</span><span class="pln">
        sample </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">ResampleRows</span><span class="pun">(</span><span class="pln">live</span><span class="pun">)</span><span class="pln">
        ages </span><span class="pun">=</span><span class="pln"> sample</span><span class="pun">.</span><span class="pln">agepreg
        weights </span><span class="pun">=</span><span class="pln"> sample</span><span class="pun">.</span><span class="pln">totalwgt_lb
        estimates </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">LeastSquares</span><span class="pun">(</span><span class="pln">ages</span><span class="pun">,</span><span class="pln"> weights</span><span class="pun">)</span><span class="pln">
        t</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">)</span><span class="pln">

    inters</span><span class="pun">,</span><span class="pln"> slopes </span><span class="pun">=</span><span class="pln"> zip</span><span class="pun">(*</span><span class="pln">t</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> inters</span><span class="pun">,</span><span class="pln"> slopes</span></pre>

<p>
	تأخذ الدالة <code>SamplingDistributions</code> إطار بيانات DataFrame بسطر واحد لكل ولادة حية، ويمثِّل المتغير <code>iter</code> عدد التجارب التي سنحاكيها، كما تستخدِم <code>ResampleRows</code> لإعادة أخذ العينات الخاصة بحالات الحمل المرصودة، حيث أننا تطرقنا سابقًا إلى <code>SampleRows</code> التي تختار أسطرًا عشوائية من إطار بيانات، كما يزودنا المستودع <code>thinkstats2</code> بالدالة <code>ResampleRows</code> التي تُعيد عينةً حجمها بحجم العينة الأصلية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_20" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">ResampleRows</span><span class="pun">(</span><span class="pln">df</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">SampleRows</span><span class="pun">(</span><span class="pln">df</span><span class="pun">,</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">df</span><span class="pun">),</span><span class="pln"> replace</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span></pre>

<p>
	استخدمنا العينات المُحاكاة لتقدير المِعاملات بعد عملية إعادة أخذ العينات، إذ تكون النتيجة متسلسلتين هما نقط التقاطع المُقّدَّرة والميول المقدَّرة، كما لخصنا توزيعات أخذ العينات عن طريق طباعة فاصل الثقة والخطأ المعياري، وإليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_22" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Summarize</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">,</span><span class="pln"> actual</span><span class="pun">=</span><span class="kwd">None</span><span class="pun">):</span><span class="pln">
    mean </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Mean</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">)</span><span class="pln">
    stderr </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Std</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">=</span><span class="pln">actual</span><span class="pun">)</span><span class="pln">
    cdf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">)</span><span class="pln">
    ci </span><span class="pun">=</span><span class="pln"> cdf</span><span class="pun">.</span><span class="typ">ConfidenceInterval</span><span class="pun">(</span><span class="lit">90</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'mean, SE, CI'</span><span class="pun">,</span><span class="pln"> mean</span><span class="pun">,</span><span class="pln"> stderr</span><span class="pun">,</span><span class="pln"> ci</span><span class="pun">)</span></pre>

<p>
	يأخذ التابع <code>Summarize</code> متسلسلةً من التقديرات والقيمة الفعلية، حيث تطبع تقديرات المتوسط والخطأ المعياري وفاصل الثقة ‎90%؛ أما بالنسبة لنقطة التقاطع فيكون تقدير المتوسط هو 6.83 مع خطأ معياري قدره 0.07 وفاصل ثقة ‎90% هو (6.71‎- 6.94)؛ أما الميل المُقدَّر فشكله أكثر تراصًا وإحكامًا، حيث أن قيمته هي 0.0174 وقيمة الخطأ المعياري 0.0028، وقيمة فاصل الثقة هي (‎0.0126 - 0.0220)، وفي الواقع الفرق بين النهاية المنخفضة والمرتفعة من فاصل الثقة هو الضعف، لذا يجب اعتباره تقديرًا تقريبيًا.
</p>

<p>
	يمكننا حساب كل الخطوط الملائمة إذا أردنا رسم أخطاء أخذ العينات للتقديرات، لكن إذا أردنا أن يكون تمثيل البيانات أقل تفاوتًا، فيمكننا رسم فاصل الثقة ‎90% لكل عمر، إليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_24" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">PlotConfidenceIntervals</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> inters</span><span class="pun">,</span><span class="pln"> slopes</span><span class="pun">,</span><span class="pln">
                            percent</span><span class="pun">=</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">options</span><span class="pun">):</span><span class="pln">
    fys_seq </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope </span><span class="kwd">in</span><span class="pln"> zip</span><span class="pun">(</span><span class="pln">inters</span><span class="pun">,</span><span class="pln"> slopes</span><span class="pun">):</span><span class="pln">
        fxs</span><span class="pun">,</span><span class="pln"> fys </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">FitLine</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> inter</span><span class="pun">,</span><span class="pln"> slope</span><span class="pun">)</span><span class="pln">
        fys_seq</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">fys</span><span class="pun">)</span><span class="pln">

    p </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">100</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> percent</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pln">
    percents </span><span class="pun">=</span><span class="pln"> p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> p
    low</span><span class="pun">,</span><span class="pln"> high </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">PercentileRows</span><span class="pun">(</span><span class="pln">fys_seq</span><span class="pun">,</span><span class="pln"> percents</span><span class="pun">)</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">FillBetween</span><span class="pun">(</span><span class="pln">fxs</span><span class="pun">,</span><span class="pln"> low</span><span class="pun">,</span><span class="pln"> high</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">options</span><span class="pun">)</span></pre>

<p>
	يمثِّل المتغير <code>xs</code> متسلسلةً لعمر الأم؛ أما <code>inters</code> و<code>slopes</code> فهما معامِلَين مقدَّرَين ولَّدهما التابع <code>SamplingDistributions</code>، في حين يشير المتغير <code>percent</code> إلى فاصل الثقة الذي نريد رسمه، كما يولِّد التابع <code>PlotConfidenceIntervals</code> الخط الملائم لكل زوج من <code>inter</code> و<code>slope</code> ويخزن النتائج في متسلسلة <code>fys_seq</code>، ومن ثم يستخدِم <code>PercentileRows</code> لتحديد مئين العلوي والسفلي من <code>y</code> لكل قيمة <code>x</code>؛ أما بالنسبة لفاصل الثقة ‎90% فيحدد التابع المئين 5 -أي 5th percentile- والمئين 95 -أي 95th percentile-، كما يرسم التابع <code>FillBetween</code> مضلعًا يملأ الفراغ بين خطين اثنين.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="87351" href="https://academy.hsoub.com/uploads/monthly_2021_12/61cc0242d6f6b_10.3.png.bc271da851dc6c63faad8f27d1ab5e43.png" rel=""><img alt="الشكل 10.3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87351" data-unique="xqatxsob2" src="https://academy.hsoub.com/uploads/monthly_2021_12/61cc0242d6f6b_10.3.png.bc271da851dc6c63faad8f27d1ab5e43.png"></a>
</p>

<p>
	يُظهِر الشكل السابق فاصلي الثقة ‎50% و‎90% التباين في الخط الملائم الناتج عن الخطأ في أخذ عينات <code>inter</code> و<code>slope</code>، كما يوضِّح فاصلي الثقة ‎50% و‎90% للمنحنيات الملائمة لأوزان الولادات على أساس دالة عمر الأم؛ أما العرض الرأسي للمنطقة فيمثِّل تأثير الخطأ في أخذ العينات، حيث أن التأثير أصغر على القيم التي تقارب المتوسط mean وأكبر على القيم المتطرفة extremes.
</p>

<h2>
	حسن الملاءمة
</h2>

<p>
	يمكننا قياس جودة النموذج الخطي أو ما يُعرَف بحُسن الملائمة goodness of fit بعدة طرق، وإحدى أبسط هذه الطرق هي الانحراف المعياري للرواسب، فإذا استخدمت نموذجًا خطيًا للتنبؤ، فسيكون<code>Std(res)</code> هو خطأ الجذر التربيعي المتوسط -أو RMSE اختصارًا- لتنبؤاتك.
</p>

<p>
	يكون خطأ الجذر التربيعي المتوسط مثلًا لتخمينك هو 1.14 رطلًا -أي حوالي 0.5 كيلوغرامًا- إذا استخدمت عمر الأم لتخمين وزن الطفل، وإذا خمنت وزن الطفل دون معرفة عمر الأم، فسيكون خطأ الجذر التربيعي المتوسط لتخمينك هو <code>Std(ys)</code> أي 1.14 رطل، لذا لا تؤدي معرفة عمر الأم في مثالنا هذا إلى تحسين التنبؤ إلى حد كبير، كما يمكننا قياس حُسن الملاءمة عن طريق مُعامِل التحديد coefficient of determination أيضًا، علمًا أنه يرمز له R<sup>2</sup> ويدعى مربع R:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_26" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">CoefDetermination</span><span class="pun">(</span><span class="pln">ys</span><span class="pun">,</span><span class="pln"> res</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="typ">Var</span><span class="pun">(</span><span class="pln">res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="typ">Var</span><span class="pun">(</span><span class="pln">ys</span><span class="pun">)</span></pre>

<p>
	يُعَدّ <code>Var(res)</code> الخطأ التربيعي المتوسط -أو MSE اختصارًا- لتنبؤاتك باستخدام النموذج؛ أما الخطأ التربيعي المتوسط للتنبؤات بدون استخدام النموذج فهو <code>Var(ys)</code>، لذا فإن نسبتها هي الجزء المتبقي من الخطأ التربيعي المتوسط إذا استخدمتَ النموذج، وR<sup>2</sup> هو جزء الخطأ التربيع المتوسط الذي يحذفه النموذج، حيث أنّ قيمة R<sup>2</sup> بالنسبة لوزن الولادة وعمر الأم هو 0.0047 أي أن عمر الأم يتنبأ بنصف ‎1% من التباين في وزن الولادة.
</p>

<p>
	توجد علاقة بسيطة بين <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">مُعامِل ترابط بيرسون</a> وبين مُعامِل التحديد وهي تحدَّد بالعلاقة R<sup>2</sup>=ρ<sup>2</sup>، فإذا كان ρ مثلًا 0.8 أو 0.8-، فسيكون R<sup>2</sup>=0.64، وعلى الرغم أنه غالبًا ما يُستخدَم كل من ρ وR<sup>2</sup> لتحديد قوة علاقة، إلا أنه ليس من السهل تفسير هذين المقدارين من حيث القوة التنبؤية، كما يكون أفضل تمثيل لجودة التنبؤ برأينا هو <code>Std(res)</code> خاصةً إذا مُثِّل بالعلاقة مع <code>Std(ys)</code>، فعندما يتحدث الأشخاص مثلًا عن صلاحية اختبار سات SAT -وهو اختبار موحد للالتحاق بالجامعات في الولايات المتحدة الأمريكية-، فإنهم غالبًا ما يتحدثون عن الارتباطات بين درجات سات ومقاييس الذكاء الأخرى.
</p>

<p>
	يوجد - وقفًا لإحدى الدراسات- ارتباط بيرسون ρ=0.72 بين درجات اختبار سات ومعدل اختبار الذكاء IQ، وقد يبدو هذا الارتباط قويًا لكن R<sup>2</sup>=ρ=0.52، لذا فإن درجات سات لا تمثِّل سوى 52‎%‎ من التباين في اختبار الذكاء IQ، كما وُحِّد معدل اختبار الذكاء IQ بالمعادلة <code>Std(ys)=15</code>، لذا يكون:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_28" style="">
<span class="pun">&gt;&gt;&gt;</span><span class="pln"> var_ys </span><span class="pun">=</span><span class="pln"> </span><span class="lit">15</span><span class="pun">**</span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> rho </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0.72</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> r2 </span><span class="pun">=</span><span class="pln"> rho</span><span class="pun">**</span><span class="lit">2</span><span class="pln">
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> var_res </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> r2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> var_ys
</span><span class="pun">&gt;&gt;&gt;</span><span class="pln"> std_res </span><span class="pun">=</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">var_res</span><span class="pun">)</span><span class="pln">
</span><span class="lit">10.4096</span></pre>

<p>
	لذا يقلل استخدام نتيجة اختبار سات لتوقع معدل الذكاء IQ من معدل خطأ الجذر التربيعي المتوسط RMSE بحيث يصبح 10.4 نقطة بعد أن كان 15 نقطة، أي ينتج عن ارتباط 0.72 انخفاضًا في خطأ الجذر التربيعي المتوسط بنسبة ‎31% فقط، فإذا رأيتَ ارتباطًا مذهلًا، فتذكَّر أن R<sup>2</sup> هو مؤشر أفضل للانخفاض في الخطأ التربيعي المتوسط MSE، وكذلك يكون الانخفاض في خطأ الجذر التربيعي المتوسط RMSE مؤشرًا أفضل للقوة التنبؤية.
</p>

<h2>
	اختبار نموذج خطي
</h2>

<p>
	يُعَدّ تأثير عمر الأم على وزن الولادة صغيرًا وتُعَدّ قوته التنبؤية صغيرةً، لذا هل من الممكن أن تكون العلاقة الظاهرة ناتجةً عن الصدفة؟ هناك عدة طرق يمكننا من خلالها اختبار نتيجة الملاءمة الخطية linear fit.
</p>

<p>
	يتمثَّل أحد الخيارات في اختبار كون الانخفاض الظاهر في الخطأ التربيعي المتوسط ناتجًا عن الصدفة، وفي هذه الحالة تكون إحصائية الاختبار هي R<sup>2</sup> وفرضية العدم هنا هي أنه لا توجد علاقة بين المتغيرات، كما يمكننا محاكاة فرضية العدم عن طريق التبديل permutation كما فعلنا في قسم اختبار الارتباط في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">المقال السابق</a> عندما اختبرنا الارتباط بين عمر الأم ووزن الولادة.
</p>

<p>
	يكافئ الاختبار أحادي الجانب للمقدار R<sup>2</sup> للاختبار ثنائي الجانب لارتباط بيرسون ρ في الواقع نظرًا لأن R<sup>2</sup>=ρ<sup>2</sup>، وقد أجرينا هذا الاختبار سابقًا ووجدنا أنّ القيمة الاحتمالية أصغر من 0.001 أي p&lt;0.001، لذا نستنتج أن للعلاقة الظاهرة بين عمر الأم ووزن الولادة دلالة إحصائية.
</p>

<p>
	توجد طريقة أخرى لاختبار فيما إن كان الميل slope الظاهر ناجمًا عن الصدف فحسب، إذ تقول فرضية العدم في هذا الحالة أنّ الميل صفر، ويمكننا في هذه الحالة نمذجة أوزان الولادات على أساس تباينات عشوائية حول قيمة المتوسط الخاصة بهم، وإليك HypothesisTest خاص بهذا النموذج كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_30" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">SlopeTest</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        ages</span><span class="pun">,</span><span class="pln"> weights </span><span class="pun">=</span><span class="pln"> data
        _</span><span class="pun">,</span><span class="pln"> slope </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">LeastSquares</span><span class="pun">(</span><span class="pln">ages</span><span class="pun">,</span><span class="pln"> weights</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> slope

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        _</span><span class="pun">,</span><span class="pln"> weights </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        self</span><span class="pun">.</span><span class="pln">ybar </span><span class="pun">=</span><span class="pln"> weights</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">res </span><span class="pun">=</span><span class="pln"> weights </span><span class="pun">-</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">ybar

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        ages</span><span class="pun">,</span><span class="pln"> _ </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        weights </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">ybar </span><span class="pun">+</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">permutation</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">res</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> ages</span><span class="pun">,</span><span class="pln"> weights</span></pre>

<p>
	البيانات هنا مُمثَّلة بمتسلسلتين هما <code>ages</code> يمثِّل الأعمار و<code>weights</code> يمثِّل الأوزان، وتكون إحصائية الاختبار test statistic هنا الميل المُقدَّر بواسطة <code>LeastSquares</code>، في حين يكون نموذج فرضية العدم ممثَّلًا بمتوسط أوزان جميع الأطفال وبالانحرافات عن المتوسط، كما يمكننا توليد بيانات مُحاكاة عن طريق تبديل الانحرافات وإضافة هذه الانحرافات إلى المتوسط، وإليك الشيفرة التي تنفِّذ اختبار الفرضية كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_32" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    live </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">dropna</span><span class="pun">(</span><span class="pln">subset</span><span class="pun">=[</span><span class="str">'agepreg'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'totalwgt_lb'</span><span class="pun">])</span><span class="pln">
    ht </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SlopeTest</span><span class="pun">((</span><span class="pln">live</span><span class="pun">.</span><span class="pln">agepreg</span><span class="pun">,</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">))</span><span class="pln">
    pvalue </span><span class="pun">=</span><span class="pln"> ht</span><span class="pun">.</span><span class="typ">PValue</span><span class="pun">()</span></pre>

<p>
	تكون القيمة الاحتمالية هنا أقل من 0.001، أي على الرغم من أن الميل المُقدَّر صغير إلا أنه من غير المرجح أن يكون ناجمًا عن الصدفة، ويُعَدّ تقدير القيم الاحتمالية عن طريق إجراء محاكاة لفرضية العدم تقديرًا صحيحًا تمامًا لكن يوجد بديل أبسط من هذه الطريقة، علمًا أننا حسبنا توزيع أخذ العينات الخاص بالميل في قسم التقدير في هذا المقال، لذا افترضنا أنّ الميل المرصود صحيح ثم حاكينا تجاربًا عن طريق إعادة أخذ العينات resampling.
</p>

<p>
	يوضِّح الشكل التالي توزيع أخذ العينات للميل من قسم التقدير في هذا المقال، ويوضح أيضًا توزيع الميول المولَّدة في ظل فرضية العدم، كما يتركَّز توزيع أخذ العينات حول الميل المقدَّر وهو 0.017 رطلًا في العام الواحد، في حين تتركز الميول تحت فرضية العدم حول القيمة 0، لكن بخلاف ذلك فإن التوزيعات متطابقة وهي متماثلة أيضًا لأسباب سنراها في قسم لاحق في مقال لاحق.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="87352" href="https://academy.hsoub.com/uploads/monthly_2021_12/61cc024338907_10.4.png.cb97f91d2dd42aa5ae591ee4ead84770.png" rel=""><img alt="الشكل 10.4.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87352" data-unique="g3d41wmo6" src="https://academy.hsoub.com/uploads/monthly_2021_12/61cc024338907_10.4.png.cb97f91d2dd42aa5ae591ee4ead84770.png"></a>
</p>

<p>
	يوضِّح الشكل السابق توزيع أخذ العينات للميل المُقدَّر وتوزيع الميول المولَّدة في ظل فرضية العدم، حيث أن الخطوط العمودية هي عند القيمة 0 والميل المرصود هو 0.017 رطل في العام الواحد، لذا يمكننا تقدير القيمة الاحتمالية بطريقتين هما:
</p>

<ul>
<li>
		حساب احتمال تخطي قيمة الميل في ظل فرضية العدم لقيمة الميل المرصود.
	</li>
	<li>
		حساب احتمال انخفاض قيمة الميل في توزيع أخذ العينات عن الصفر، فإذا كان الميل المُقدَّر سالبًا، فسنحسب احتمال زيادة قيمة الميل في توزيع أخذ العينات عن الصفر.
	</li>
</ul>
<p>
	يُعَدّ الخيار الثاني خيارًا أسهل لأننا عادةً نريد حساب توزيع أخذ العينات للمعامِلات على أي حال، كما يُعَدّ تقديرًا تقريبيًا جيدًا إلا في حال كان حجم العينة صغيرًا وكان توزيع الرواسب متجانفًا skewed، وحتى حينها يكون هذا الخيار جيدًا بما فيه الكفاية لأنه ليس من الضروري أن تكون القيم الاحتمالية دقيقةً، وإليك الشيفرة التي تقدِّر القيمة الاحتمالية للميل باستخدام توزيع أخذ العينات:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_35" style="">
<span class="pln">    inters</span><span class="pun">,</span><span class="pln"> slopes </span><span class="pun">=</span><span class="pln"> </span><span class="typ">SamplingDistributions</span><span class="pun">(</span><span class="pln">live</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">1001</span><span class="pun">)</span><span class="pln">
    slope_cdf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">slopes</span><span class="pun">)</span><span class="pln">
    pvalue </span><span class="pun">=</span><span class="pln"> slope_cdf</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span></pre>

<p>
	وجدنا مجدَّدًا أنّ القيمة الاحتمالية أصغر من 0.001 أي p&lt;0.001.
</p>

<h2>
	إعادة أخذ العينات مع الأوزان
</h2>

<p>
	عامَلنا في هذا الكتاب بيانات المسح الوطني لنمو الأسرة NSFG على أنها عينة تمثيلية، لكنها ليس تمثيلية كما ذكرنا في مقال <a href="https://academy.hsoub.com/programming/python/%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81%D9%8A%D8%A9-%D9%84%D8%A5%D8%AB%D8%A8%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B8%D8%B1%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1246/" rel="">تحليل البيانات الاستكشافية لإثبات النظريات الإحصائية</a>، حيث تعمَّد هذا المسح الإفراط في أخذ عينات عدة مجموعات من أجل زيادة احتمال ظهور نتائج ذات دلالة إحصائية، أي بهدف تحسين قوة الاختبارات التي تخص هذه المجموعات.
</p>

<p>
	يُعَدّ تصميم الإحصائية هذا مفيدًا لعدة أغراض لكنه يعني أنه لا يمكننا استخدام العينة لتقدير قيم عامة السكان من دون حساب عملية أخذ العينات، كما تحوي بيانات المسح الوطني لنمو الأسرة متغيرًا يُدعى <code>finalwgt</code> لكل مستجيب بحيث يشير إلى عدد الأشخاص الذي يمثلهم هذا المستجيب، وتُدعى هذه القيمة بوزن أخذ العينات sampling weight أو الوزن فقط، فإذا أجريت مسحًا لمائة ألف مستجيب في بلد يحوي 300 مليون نسمة، فسيمثل كل مستجيب 3000 شخص، وإذا بالغت في أخذ عينات مجموعة ما بعامِل 2 -أي الضعف-، فسيكون لكل شخص في المجموعة ذات العينات المبالغة بها وزنًا أقل، أي حوالي 1500.
</p>

<p>
	يمكننا استخدام إعادة أخذ العينات إذا أردنا تصحيح المبالغة في أخذ العينات oversampling، أي يمكننا سحب عينات من المسح باستخدام الاحتمالات المتناسبة مع أوزان أخذ العينات، ثم يمكننا توليد توزيعات أخذ العينات sampling distributions والأخطاء المعيارية standard errors ومجالات الثقة confidence intervals، حيث سنقدِّر مثلًا قيمة متوسط وزن الولادة مع وبدون أوزان أخذ العينات.
</p>

<p>
	رأينا في قسم التقدير في هذا المقال التابع <code>ResampleRows</code> الذي يختار أسطرًا من إطار بيانات معطيًا كل الأسطر الاحتمال نفسه؛ أما الآن فسيتوجب علينا إجراء ذات العمليات لكن مع استخدام احتمالات متناسبة مع أوزان أخذ العينات، في حين يأخذ التابع <code>ResampleRowsWeighted</code> إطار بيانات ويَعيد أخذ عينات resamples الأسطر حسب الوزن في المتغير <code>finalwgt</code>، ثم يُعيد إطار البيانات الذي يحتوي على الأسطر التي أُجري عليها عملية إعادة أخذ عينات.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_37" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">ResampleRowsWeighted</span><span class="pun">(</span><span class="pln">df</span><span class="pun">,</span><span class="pln"> column</span><span class="pun">=</span><span class="str">'finalwgt'</span><span class="pun">):</span><span class="pln">
    weights </span><span class="pun">=</span><span class="pln"> df</span><span class="pun">[</span><span class="pln">column</span><span class="pun">]</span><span class="pln">
    cdf </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">dict</span><span class="pun">(</span><span class="pln">weights</span><span class="pun">))</span><span class="pln">
    indices </span><span class="pun">=</span><span class="pln"> cdf</span><span class="pun">.</span><span class="typ">Sample</span><span class="pun">(</span><span class="pln">len</span><span class="pun">(</span><span class="pln">weights</span><span class="pun">))</span><span class="pln">
    sample </span><span class="pun">=</span><span class="pln"> df</span><span class="pun">.</span><span class="pln">loc</span><span class="pun">[</span><span class="pln">indices</span><span class="pun">]</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> sample</span></pre>

<p>
	يُعَدّ المتغير <code>weights</code> سلسلةً Series، ويؤدي تحويلها إلى قاموس dictionary إلى إنشاء خريطة map من الفهارس إلى الأوزان، علمًا أنّ القيم في <code>cdf</code> هي فهارس والاحتمالات متناسبة مع الأوزان؛ أما <code>indicies</code> فهي متسلسلة sequence من أسطر تحوي فهارس، ويمثِّل <code>sample</code> إطار بيانات يحتوي على الأسطر المُحدَّدة، وقد يظهر السطر نفسه أكثر من مرة لأننا نستخدِم عينات مع الاستبدال، كما يمكننا الآن موازنة تأثير إعادة أخذ العينات مع الأوزان وبدونها، حيث نولِّد توزيعات أخذ العينات بدون أوزان (أي دون إعطاء ترجيح) كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_39" style="">
<span class="pln">   estimates </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="typ">ResampleRows</span><span class="pun">(</span><span class="pln">live</span><span class="pun">).</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
                 </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">)]</span></pre>

<p>
	أما مع أوزان (أي مع ترجيح) فيصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_2129_41" style="">
<span class="pln">   estimates </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="typ">ResampleRowsWeighted</span><span class="pun">(</span><span class="pln">live</span><span class="pun">).</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
                 </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">)]</span></pre>

<p>
	يلخص الجدول التالي النتائج:
</p>

<p>
	<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}	</style></p>

<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				متوسط أوزان الولادات (مقدرةً بالرطل)
			</th>
			<th>
				الخطأ المعياري
			</th>
			<th>
				فاصل الثقة 90%
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				بدون أوزان
			</td>
			<td>
				7.27
			</td>
			<td>
				0.014
			</td>
			<td>
				(7.24, 7.29)
			</td>
		</tr>
<tr>
<td>
				مع أوزان
			</td>
			<td>
				7.35
			</td>
			<td>
				0.014
			</td>
			<td>
				(7.32, 7.37)
			</td>
		</tr>
</tbody>
</table>
<p>
	يُعَدّ أثر الترجيح في هذا المثال صغيرًا لكنه غير مهمل، ويكون الفرق في المتوسطين المُقدَّرَين مع ترجيح وبدون ترجيح هو 0.08 رطلًا تقريبًا -أي حوالي 0.03 كيلوغرامًا- أو 1.3 أوقيةً، وهذا الفرق أكبر بكثير من الخطأ المعياري للتقدير الذي تبلغ قيمته 0.014 رطلًا -أي حوالي 0.006 كيلوغرامًا-، مما يعني أنّ الفرق لم يحدث صدفةً.
</p>

<h2>
	تمارين
</h2>

<p>
	يوجد حل هذا التمرين في <code>chap10soln.ipynb</code>.
</p>

<h3>
	تمرين 1
</h3>

<p>
	استخدِم البيانات الخاصة بنظام مراقبة عوامل المخاطر السلوكية BRFSS واحسب ملاءمة المربعات الصغرى الخطية للوغاريتم الوزن مقابل الطول.
</p>

<p>
	ما هي أفضل طريقة لتمثيل المعامِلات المُقدَّرة لنموذج مثل هذا، أي نموذج طُبق التحويل اللوغاريتم على أحد متغيراته؟ وإلى أي مدى سيساعدك على معرفة وزن شخص ما في حال كنت تحاول تخمينه؟
</p>

<p>
	يُفرِط نظام مراقبة العوامل السلوكية في أخذ العينات لبعض المجموعات تمامًا مثل المسح الوطني لنمو الأسرة ويزودنا بوزن أخذ العينات لكل مستجيب، حيث أنّ اسم متغير هذه الأوزان في بيانات نظام مراقبة العوامل السلوكية هو <code>finalwt</code>.
</p>

<p>
	استخدم إعادة أخذ العينات resampling مع الأوزان وبدونها من أجل تقدير متوسط أطوال المستجيبين في نظام مراقبة العوامل السلوكية، بالإضافة إلى الخطأ المعياري للمتوسط وفاصل الثقة 90‎%‎، وإلى أي مدى يؤثر الترجيح الصحيح على التقديرات؟
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2011.html" rel="external nofollow">Chapter 10 Linear least squares analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">العلاقات بين المتغيرات الإحصائية وكيفية تنفيذها في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-modelling-distributions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1332/" rel="">نمذجة التوزيعات Modelling distributions في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%83%D8%AB%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1335/" rel="">دوال الكثافة الاحتمالية في بايثون</a>
	</li>
</ul>
<p>
	<style type="text/css">
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}	</style></p>
]]></description><guid isPermaLink="false">1412</guid><pubDate>Thu, 23 Dec 2021 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x627;&#x644;&#x641;&#x631;&#x636;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x625;&#x62D;&#x635;&#x627;&#x626;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61cbef9f4d80c_-.png.147eb0391a2c0c84e6c1804bf994dab5.png" /></p>

<p>
	توجد الشيفرة الخاصة بهذا المقال في الملف <code>hypothesis.py</code>.
</p>

<h2>
	الطريقة الكلاسيكية في اختبار الفرضيات
</h2>

<p>
	لاحظنا عند استكشاف بيانات المسح الوطني لنمو الأسرة NSFG تأثيرات واضحةً منها الفرق بين الأطفال الأوائل وبقية الأطفال، ولم نشكك في صحتها حتى الآن، لذا سنخصص هذا المقال  لاختبار صحة الآثار التي ظهرت من عدمها، حيث سنتطرق إلى سؤال أساسي وهو: في حال رأينا تأثيرات في عينة ما، فهل من المرجح أن تظهر هذه التأثيرات في عينة أكبر؟ فقد نرى فرقًا على سبيل المثال في متوسط مدة الحمل للأطفال الأوائل وبقية الأطفال في المسح الوطني لنمو الأسرة، ونود معرفة ما إذا كان هذا التأثير يعكس اختلافًا حقيقيًا في مدة حمل النساء في الولايات المتحدة الأمريكية أم أنه قد يظهر في العينة عن طريق الصدفة.
</p>

<p>
	يمكننا صياغة هذا السؤال بعدة طرق، منها اختبار فرضيات العدم عند فيشر Fisher null hypothesis testing ونظرية قرار نيمان بيرسون Neyman-Pearson decision theory، والاستدلال البايزي Bayesian inference، كما يُعَدّ ما سنقدِّمه في هذا المقال مجموعةً فرعيةً من الطرق الثلاث المذكورة آنفًا وهي ما يستخدِمه معظم الأشخاص في الممارسات العملية وسنسميها الطريقة الكلاسيكية في اختبار الفرضيات classical hypothesis testing.
</p>

<p>
	الهدف من الطريقة الكلاسيكية في اختبار الفرضيات هو الإجابة على السؤال التالي: إذ كان لدينا عينةً ما وهناك تأثير ظاهر فيها، فما هو احتمال رؤية مثل هذا التأثير عن طريق الصدفة؟ إليك كيف نجيب على هذا السؤال:
</p>

<ul>
<li>
		تتمثل الخطوة الأولى في تحديد حجم التأثير الظاهر وذلك عن طريق اختيار إحصائية اختبار test statistic، حيث أنّ التأثير الظاهر في مثال المسح الوطني لنمو الأسرة هو فرق في مدة الحمل بين الأطفال الأوائل وبقية الأطفال، لذا فمن المنطقي أن تكون إحصائية الاختبار هي فرق المتوسطات means بين المجموعتين.
	</li>
	<li>
		تتمثل الخطوة الثانية في تعريف فرضية عدم null hypothesis، وهي نموذج للنظام مبنية على افتراض أن التأثير الظاهر ليس حقيقيًا، وتكون فرضية العدم في مثال المسح الوطني لنمو الأسرة هي أنه لا يوجد فرق بين الأطفال الأوائل وبقية الأطفال، أي أنّ توزيع مدة الحمل للأطفال الأوائل هو توزيع مدة الحمل لبقية الأطفال نفسه.
	</li>
	<li>
		تتمثل الخطوة الثالثة في حساب القيمة الاحتمالية p-value، وهي احتمال وجود التأثير الظاهر في حال كانت فرضية العدم صحيحة، حيث نحسب الفرق الفعلي في المتوسطين في مثال المسح الوطني لنمو الأسرة، ومن ثم نحسب احتمال وجود الفارق بالحجم نفسه أو وجود فارق أكبر وذلك في ظل وجود فرضية العدم.
	</li>
	<li>
		تتمثّل الخطوة الأخيرة في أنها تفسير النتيجة، حيث إذا كانت القيمة الاحتمالية منخفضةً فيُقال عن التأثير أنه ذات دلالة إحصائية statistically significant، أي أنه من غير المحتمل أن يكون قد ظهر بمحض الصدفة، حيث يمكننا في هذه الحالة استنتاج أنه من المرجح ظهور هذا التأثير في عينة أكبر.
	</li>
</ul>
<p>
	يتشابه منطق هذه العملية مع البرهان بالتناقض، أي لإثبات تعبير رياضي A، سنفترض مؤقتًا أنّ A خاطئ، فإذا أدى هذا الافتراض إلى تناقض، فسنستنتج أنّ A صحيحة، وبالمثل لاختبار فرضية مثل "هذا التأثير حقيقي"، سنفترض مؤقتًا أنها خاطئة وهذه هي فرضية العدم، حيث نحسب احتمال التأثير الظاهر بناءً على هذا الافتراض وهذه هي القيمة الاحتمالية p-value، فإذا كانت هذه القيمة الاحتمالية منخفضةً، فسنستنتج أنه من المرجح أن تكون فرضية العدم صحيحة.
</p>

<h2>
	الصنف HypothesisTest
</h2>

<p>
	يزوِّدنا المستودع <code>thinkstats2</code> بالصنف <code>Hypothesis</code> والذي يمثِّل بنية الطريقة الكلاسيكية في اختبار الفرضيات، إليك تعريفه كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_7" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">HypothesisTest</span><span class="pun">(</span><span class="pln">object</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> __init__</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> data
        self</span><span class="pun">.</span><span class="typ">MakeModel</span><span class="pun">()</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">actual </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">PValue</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> iters</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">test_stats </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">self</span><span class="pun">.</span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="typ">RunModel</span><span class="pun">())</span><span class="pln"> 
                           </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">)]</span><span class="pln">

        count </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> x </span><span class="kwd">in</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">test_stats </span><span class="kwd">if</span><span class="pln"> x </span><span class="pun">&gt;=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">actual</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> count </span><span class="pun">/</span><span class="pln"> iters

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">raise</span><span class="pln"> </span><span class="typ">UnimplementedMethodException</span><span class="pun">()</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">pass</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">raise</span><span class="pln"> </span><span class="typ">UnimplementedMethodException</span><span class="pun">()</span></pre>

<p>
	يُعَدّ الصنف <code>HypothesisTest</code> صنفًا مجردًا وهو صنف أب أيضًا، حيث يزوِّدنا بتعريفات كاملة لبعض التوابع وتوابع نائبة عن أخرى place-keepers، كما ترث الأصناف الأبناء المبنية على الصنف <code>HypothesisTest</code> التابعين <code>_init_</code> و<code>PValue</code>، كما تزوِّدنا بالتوابع <code>TestStatistic</code> و<code>RunModel</code> وقد تزودنا اختياريًا بالتابع <code>MakeModel</code>.
</p>

<p>
	يأخذ التابع <code>_init_</code> البيانات بأي صيغة مناسبة، ويستدعي التابع <code>MakeModel</code> الذي يبني تمثيلًا لفرضية العدم، ومن ثم يمرر البيانات إلى التابع <code>TestStatistics</code> الذي يحسب حجم التأثير في العينة، كما يحسب التابع <code>PValue</code> احتمال التأثير الظاهر في ظل فرضية العدم، ويأخذ المتغير <code>iters</code> على أساس معامِل له، حيث يمثل هذا المتغير عدد مرات تكرار المحاكاة.
</p>

<p>
	يولد السطر الأول البيانات التي تمت محاكاتها ويحسب إحصائيات الاختبار test statistics ويخزنها في <code>test_stats</code>، حيث تكون النتيجة جزءًا من العناصر في <code>test_stats</code> مساويًا لإحصائية الاختبار المرصودة <code>self.actual</code> أو أكبر منها، ولنفترض مثلًا أننا رمينا قطعة نقود 250 مرة وظهر لنا الشعار 140 مرة وظهرت لنا الكتابة 110 مرات، فقد نشك أنّ العملة منحازة بناءً على هذه النتيجة أي منحازة لظهور الشعار، ولاختبار هذه الفرضية يمكننا حساب احتمال وجود مثل هذا الفرق إذا كانت قطعة النقود عادلة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_9" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">CoinTest</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        heads</span><span class="pun">,</span><span class="pln"> tails </span><span class="pun">=</span><span class="pln"> data
        test_stat </span><span class="pun">=</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">heads </span><span class="pun">-</span><span class="pln"> tails</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        heads</span><span class="pun">,</span><span class="pln"> tails </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        n </span><span class="pun">=</span><span class="pln"> heads </span><span class="pun">+</span><span class="pln"> tails
        sample </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">random</span><span class="pun">.</span><span class="pln">choice</span><span class="pun">(</span><span class="str">'HT'</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)]</span><span class="pln">
        hist </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Hist</span><span class="pun">(</span><span class="pln">sample</span><span class="pun">)</span><span class="pln">
        data </span><span class="pun">=</span><span class="pln"> hist</span><span class="pun">[</span><span class="str">'H'</span><span class="pun">],</span><span class="pln"> hist</span><span class="pun">[</span><span class="str">'T'</span><span class="pun">]</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> data</span></pre>

<p>
	يُعَدّ المعامِل <code>data</code> زوجًا من الأرقام الصحيحة يحوي عدد مرات ظهور الشعار والكتابة، كما تكون إحصائية الاختبار هي الفرق المطلق بينهما لذا فإن قيمة <code>self.actual</code> هي 30، في حين ينفِّذ التابع <code>RunModel</code> محاكاةً لرمي قطع النقود بافتراض أن القطعة عادلة، حيث يولِّد عينةً من 250 رمية ويستخدِم <code>Hist</code> لحساب عدد مرات ظهور الشعار والكتابة ويُعيد زوجًا من الأعداد الصحيحة، ولا يتعيّن علينا الآن سوى إنشاء نسخة من <code>CoinTest</code> واستدعاء التابع <code>PValue</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_11" style="">
<span class="pln">    ct </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CoinTest</span><span class="pun">((</span><span class="lit">140</span><span class="pun">,</span><span class="pln"> </span><span class="lit">110</span><span class="pun">))</span><span class="pln">
    pvalue </span><span class="pun">=</span><span class="pln"> ct</span><span class="pun">.</span><span class="typ">PValue</span><span class="pun">()</span></pre>

<p>
	النتيحة هي 0.07 تقريبًا أي إذا كانت قطعة النقود عادلةً، فسنتوقَّع وجود فارق بحجم 30 حوالي 7‎‎%‎ من المرات، لذا كيف يمكننا تفسير هذه النتيجة؟ تكون 5‎‎%‎ هي عتبة الأهمية الإحصائية اصطلاحيًا، وإذا كانت القيمة الاحتمالية p-value هي أقل من 5‎‎%‎ يكون التأثير ذا دلالة إحصائية statistically significant وإلا لا يكون ذا دلالة إحصائية، لكن يُعَدّ اختيار ‎5‎‎%‎ اختيارًا اعتباطيًا، وكما سنرى لاحقًا فإن القيمة الاحتمالية تعتمد على اختيار الاختبار الإحصائي ونموذج فرضية العدم، لذلك لا يجب افتراض أنّ القيم الاحتمالية هي مقاييس دقيقة.
</p>

<p>
	نوصي بتفسير القيم الاحتمالية وفقًا لحجمها، أي إذا كانت القيمة الاحتمالية أقل من 1‎%‎ فمن غير المرجح أن يكون التأثير بمحض الصدفة؛ أما إذا كانت أكبر من ‎10%‎، فيمكننا القول أنّ التأثير ربما قد ظهر بمحض الصدفة؛ أما بالنسبة للقيم بين 1‎%‎ و10‎%‎ فلا يمكن عدّها حديةً، لذا فقد استنتجنا في هذا المثال أنّ البيانات لا تقدِّم دليلًا قويًا على انحياز العملة أو عدم انحيازها.
</p>

<h2>
	اختبار الفرق في المتوسطين
</h2>

<p>
	يُعَدّ الفرق بين متوسطي مجموعتين اثنتين من أكثر التأثيرات التي يجري اختبارها. حيث وجدنا في بيانات المسح الوطني لنمو الأسرة أنّ متوسط مدة حمل الأطفال الأوائل أكبر بقليل، ومتوسط الوزن عند ولادة الأطفال الأوائل أقل بقليل، وسنرى الآن فيما إذا كانت هذه التأثيرات ذات دلالة إحصائية، كما تتمثَّل فرضية العدم في هذه الأمثلة في تطابق توزيعي المجموعتين، وتتمثَّل إحدى طرق نمذجة فرضية العدم في التبديل permutation، وهو عبارة عن خلط قيم الأطفال الأوائل وبقية الأطفال ومعاملة المجموعتين على أنهما مجموعة واحدة كبيرة، وإليك الشيفرة الموافقة لذلك كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_13" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DiffMeansPermute</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        group1</span><span class="pun">,</span><span class="pln"> group2 </span><span class="pun">=</span><span class="pln"> data
        test_stat </span><span class="pun">=</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">group1</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> group2</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">())</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        group1</span><span class="pun">,</span><span class="pln"> group2 </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        self</span><span class="pun">.</span><span class="pln">n</span><span class="pun">,</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">m </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">group1</span><span class="pun">),</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">group2</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">pool </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">hstack</span><span class="pun">((</span><span class="pln">group1</span><span class="pun">,</span><span class="pln"> group2</span><span class="pun">))</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">shuffle</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">)</span><span class="pln">
        data </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">[:</span><span class="pln">self</span><span class="pun">.</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">[</span><span class="pln">self</span><span class="pun">.</span><span class="pln">n</span><span class="pun">:]</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> data</span></pre>

<p>
	تُعَدّ <code>data</code> زوجًا من <a href="https://academy.hsoub.com/programming/python/%D8%A2%D9%84%D9%8A%D8%A9-%D9%81%D9%87%D8%B1%D8%B3%D8%A9-%D8%A7%D9%84%D8%B3%D9%84%D8%A7%D8%B3%D9%84-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%88%D8%B7%D8%B1%D9%8A%D9%82%D8%A9-%D8%AA%D9%82%D8%B3%D9%8A%D9%85%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-3-r417/" rel="">المتسلسلات</a>، أي متسلسلة sequence لكل مجموعة، وتكون إحصائية الاختبار هي الفرق المطلق في المتوسطين؛ أما <code>MakeModel</code> فيسجل حجمي المجموعتين <code>n</code> و<code>m</code> ويجمعهما في مصفوفة نمباي NumPy واحدة باسم <code>self.pool</code>، كما يحاكي <code>RunModel</code> فرضية العدم عن طريق خلط القيم المجمَّعة وتقسيمها إلى مجموعتين بحيث يكون حجم الأولى <code>n</code> والثانية <code>m</code>، وكما هو الحال دائمًا تكون القيمة التي يعيدها التابع <code>RunModel</code> بصيغة البيانات المرصودة نفسها، وإليك شيفرة اختبار الفرق في مدة الحمل كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_15" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    data </span><span class="pun">=</span><span class="pln"> firsts</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> others</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">values
    ht </span><span class="pun">=</span><span class="pln"> </span><span class="typ">DiffMeansPermute</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
    pvalue </span><span class="pun">=</span><span class="pln"> ht</span><span class="pun">.</span><span class="typ">PValue</span><span class="pun">()</span></pre>

<p>
	يقرأ التابع <code>MakeFrames</code> بيانات المسح الوطني لنمو الأسرة ويعيد أُطر بيانات DataFrames تمثِّل جميع الولادات الحية للأطفال الأوائل وبقية الأطفال، حيث نستخرِج مدة حالات الحمل على شكل مصفوفات نمباي NumPy ونمررها على أساس بيانات إلى <code>DiffMeansPermute</code> ثم نحسب القيمة الاحتمالية p-value، وتكون النتيجة 0.17 تقريبًا أي أننا نتوقع رؤية فارقًا حجمه بحجم التأثير المرصود حاولي 17‎%‎ من المرات، لذا نستنتج أنه ليس للتأثير دلالةً إحصائيةً.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="87064" href="https://academy.hsoub.com/uploads/monthly_2021_12/61c57de90746a_9.1.png.28e619558b1baa2f01722f57152ced5c.png" rel=""><img alt="الشكل 9.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87064" data-unique="if9kohx8t" src="https://academy.hsoub.com/uploads/monthly_2021_12/61c57de90746a_9.1.png.28e619558b1baa2f01722f57152ced5c.png"></a>
</p>

<p>
	يوضِّح الشكل السابق دالة <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9-%D8%A7%D9%84%D8%AA%D8%B1%D8%A7%D9%83%D9%85%D9%8A-cumulative-distribution-functions-r1331/" rel="">التوزيع التراكمي</a> CDF للفرق في متوسط الحمل في ظل فرضية العدم.
</p>

<p>
	يزودنا الصنف <code>HypothesisTest</code> بالتابع <code>PlotCdf</code> الذي يرسم توزيع إحصائية الاختبار وكذلك فهو يرسم خطًا رماديًا يدل على حجم التأثير المرصود.
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_17" style="">
<span class="pln">    ht</span><span class="pun">.</span><span class="typ">PlotCdf</span><span class="pun">()</span><span class="pln">
    thinkplot</span><span class="pun">.</span><span class="typ">Show</span><span class="pun">(</span><span class="pln">xlabel</span><span class="pun">=</span><span class="str">'test statistic'</span><span class="pun">,</span><span class="pln">
                   ylabel</span><span class="pun">=</span><span class="str">'CDF'</span><span class="pun">)</span></pre>

<p>
	يُظهِر الشكل السابق النتيجة، حيث تتقاطع دالة التوزيع التراكمي مع الفرق المرصود عند 0.83 وهو مكمِّل complement القيمة الاحتمالية وهي 0.17.
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<p>
		توضيح: لحساب مكمل قيمة ما complement نطرح هذه القيمة من 1، حيث يكون المكمِّل في المثال السابق 1-0.17=0.83
	</p>
</blockquote>

<p>
	إذا نفَّذنا التحليل نفسه لأوزان الولادات حيث أن القيمة الاحتمالية المحسوبة هي 0، فلن تسفر المحاكاة بعد 100 محاولة عن تأثير بحجم الفرق المرصود أبدًا، فالفرق المرصود هو 0.12 رطلًا أي ما يعادل حوالي 0.05 كيلوغرامًا، ونقول عندها أنّ القيمة الاحتمالية أصغر من 0.001 أي p&lt;0.001 ونستنتج أنّ الفرق في أوزان الولادات ذو دلالة إحصائية statistically significant.
</p>

<h2>
	إحصائيات اختبار أخرى
</h2>

<p>
	يعتمد اختيار أفضل إحصائية اختبار على السؤال الذي نحاول تناوله، فإذا كان السؤال المطروح مثلًا هو عن كَون مدة حمل الأطفال الأوائل مختلفةً عن بقية الأطفال فمن المنطقي حساب الفرق المطلق في المتوسطين كما فعلنا في القسم السابق، وإذا كنا نعتقد لسبب ما أنه من المرجح أن تتأخر ولادة الأطفال الأوائل، نستخدم إحصائية الاختبار بدلًا من القيمة المطلقة للفرق، وإليك إحصائية الاختبار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_19" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DiffMeansOneSided</span><span class="pun">(</span><span class="typ">DiffMeansPermute</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        group1</span><span class="pun">,</span><span class="pln"> group2 </span><span class="pun">=</span><span class="pln"> data
        test_stat </span><span class="pun">=</span><span class="pln"> group1</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> group2</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat</span></pre>

<p>
	يرث الصنف <code>DiffMeansOneSided</code> التابعين <code>MakeModel</code> و<code>RunModel</code> من <code>DiffMeansPermute</code> والفرق الوحيد هو أن <code>TestStatistic</code> لا يأخذ القيمة المطلقة للفرق، كما يندرج هذا الاختبار تحت نوع الاختبارات أحادية الجانب one-sided لأن هذا الاختبار يحسب جانبًا واحدًا فقط من توزيع الفروق، في حين يستخدِم الاختبار السابق الجانبين، لذا فهو ثنائي الجانب two-sided.
</p>

<p>
	تكون القيمة الاحتمالية في هذه النسخة من الاختبار هي 0.09، في حين تكون القيمة الاحتمالية للاختبار أحادي الجانب عمومًا هي حوالي نصف القيمة الاحتمالية للاختبار ثنائي الجانب وذلك اعتمادًا على شكل التوزيع، وتُعَدّ الفرضية أحادية الجانب التي تقول أنّ الأطفال الأوائل يولدون متأخرين هي أكثر دقة من الفرضية ثنائية الجانب، لذا تكون القيمة الاحتمالية أصغر، لكن ليس للفرق أيّ دلالة إحصائية حتى بالنسبة للفرضية الأقوى.
</p>

<p>
	يمكننا استخدام إطار العمل ذاته لاختبار وجود فرق في <a href="https://academy.hsoub.com/programming/python/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-modelling-distributions-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1332/" rel="">الانحراف المعياري</a>، وقد رأينا في قسم سابق في مقال دوال الكتلة الاحتمالية في <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%AC%D8%B9-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r735/" rel="">بايثون</a> دليلًا على أنه من المرجح ولادة الأطفال الأوائل مبكرين أو متأخرين وأقل احتمالًا أن يولدوا في الوقت المحدد، لذا قد نفترض أنّ الانحراف المعياري أعلى، وإليك الشيفرة التي تنفِّذ الاختبار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_21" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DiffStdPermute</span><span class="pun">(</span><span class="typ">DiffMeansPermute</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        group1</span><span class="pun">,</span><span class="pln"> group2 </span><span class="pun">=</span><span class="pln"> data
        test_stat </span><span class="pun">=</span><span class="pln"> group1</span><span class="pun">.</span><span class="pln">std</span><span class="pun">()</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> group2</span><span class="pun">.</span><span class="pln">std</span><span class="pun">()</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat</span></pre>

<p>
	يُعَدّ هذا اختبارًا أحادي الجانب، إذ لا تقول الفرضية إن قيمة الانحراف المعياري للأطفال الأوائل مختلفة عن القيمة الاحتمالية فحسب بل تحدد أن قيمتها أعلى، حيث أن القيمة الاحتمالية هي 0.09 أي ليس لها دلالة إحصائية.
</p>

<h2>
	اختبار الارتباط
</h2>

<p>
	يمكن لإطار العمل هذا أن يختبر الارتباطات أيضًا، ففي مجموعة بيانات المسح الوطني لنمو الأسرة مثلًا يكون الارتباط بين عمر الأم وبين أوزان الولادات هو حوالي 0.07، أي يبدو أن الأمهات الأكبر عمرًا يلدن أطفالًا أكثر وزنًا، لكن هل يمكن لهذا التأثير أن يكون بسبب الصدفة؟
</p>

<p>
	استخدمنا <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">ارتباط بيرسون</a> لإحصائية الاختبار هذه، علمًا أن ارتباط سبيرمان مناسب أيضًا. حيث يمكن إجراء اختبار أحادي الجانب إذا توقَّعنا لسبب ما أن الارتباط موجب، لكن بما أنه لا يوجب سبب لنعتقد هذا سنُجري اختبارًا ثنائي الجانب باستخدام قيمة الارتباط المطلقة، وتقول فرضية العدم أنه لا يوجد ارتباط بين عمر الأم ووزن الولادة، وإذا خلطنا القيم المرصودة فيمكننا محاكاة عالَم يكون فيه توزيع عمر الأمهات مساويًا لوزن الولادات لكن لا تكون المتغيرات متعلقة ببعضها:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_23" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">CorrelationPermute</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        xs</span><span class="pun">,</span><span class="pln"> ys </span><span class="pun">=</span><span class="pln"> data
        test_stat </span><span class="pun">=</span><span class="pln"> abs</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">Corr</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ys</span><span class="pun">))</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        xs</span><span class="pun">,</span><span class="pln"> ys </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">permutation</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> xs</span><span class="pun">,</span><span class="pln"> ys</span></pre>

<p>
	يُعَدّ المتغير <code>data</code> زوجًا من المتسلسلات sequences، حيث يحسب <code>TestStatistic</code> القيمة المطلقة لارتباط بيرسون، أما <code>RunModel</code> فهو يخلط قيم المصفوفة <code>xs</code> ويعيد البيانات المُحاكاة، وإليك الشيفرة التي تقرأ البيانات وتنفِّذ الاختبار كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_25" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    live </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">dropna</span><span class="pun">(</span><span class="pln">subset</span><span class="pun">=[</span><span class="str">'agepreg'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'totalwgt_lb'</span><span class="pun">])</span><span class="pln">
    data </span><span class="pun">=</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">agepreg</span><span class="pun">.</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> live</span><span class="pun">.</span><span class="pln">totalwgt_lb</span><span class="pun">.</span><span class="pln">values
    ht </span><span class="pun">=</span><span class="pln"> </span><span class="typ">CorrelationPermute</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
    pvalue </span><span class="pun">=</span><span class="pln"> ht</span><span class="pun">.</span><span class="typ">PValue</span><span class="pun">()</span></pre>

<p>
	استخدمنا <code>dropna</code> في هذه الشيفرة مع الوسيط <code>subset</code> لحذف الأسطر التي لا تحوي أحد المتغيرات التي نحتاجها، وتكون قيمة الارتباط الفعلي هي 0.07 والقيمة الاحتمالية المحسوبة هي 0 وبعد حوالي 1000 تكرار يكون أكبر ارتباط مُحاكى هو 0.04، لذا على الرغم من صغر الارتباط المرصود إلا أنه ذو دلالة إحصائية، كما يُعَدّ هذا المثال بمثابة تذكير بأنّ كَون التأثير ذا دلالة إحصائية فلا يعني دائمًا أنه مهم في الناحية العملية، وإنما يعني فقط أنه من غير المرجح أن يكون قد ظهر بمحض الصدفة فقط.
</p>

<h2>
	اختبار النسب
</h2>

<p>
	لنفترض أنك تدير منتدى ترفيهي وشككت في يوم من الأيام أنك أحد الزبائن يستخدِم قطعة نرد ملتوية، أي أنه يُتلاعب بها لكي يكون احتمال ظهور أحد الوجوه أكبر من احتمال ظهور الوجوه الأخرى، لذا تقبض على المتهم وتصادر قطعة النرد لكن يتعيّن عليك عندها إثبات غشه، حيث ترمي قطعة النرد 60 مرة وتحصل على النتائج الموضحة بالجدول التالي:
</p>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<table>
<thead><tr>
<th>
				القيمة
			</th>
			<th>
				1
			</th>
			<th>
				2
			</th>
			<th>
				3
			</th>
			<th>
				4
			</th>
			<th>
				5
			</th>
			<th>
				6
			</th>
		</tr></thead>
<tbody><tr>
<td>
				التردد (عدد مرات التكرار)
			</td>
			<td style="text-align: center;">
				8
			</td>
			<td style="text-align: center;">
				9
			</td>
			<td style="text-align: center;">
				19
			</td>
			<td style="text-align: center;">
				5
			</td>
			<td style="text-align: center;">
				8
			</td>
			<td style="text-align: center;">
				11
			</td>
		</tr></tbody>
</table>
<p>
	نتوقع ظهور كل قيمة 10 مرات وسطيًا، وفي مجموعة البيانات هذه تظهر القيمة 3 أكثر من المتوقع وتظهر القيمة 4 أقل من المتوقع، لكن هل لهذه الفروق دلالة إحصائية؟ يمكننا اختبار هذه الفرضية عن طريق حساب التردد المتوقع لكل قيمة، والفرق بين الترددات المتوقعة والترددات المرصودة والفرق المطلق الكلي، بحيث نتوقع في هذا المثال ظهور كل وجه 10 مرات من أصل 60 مرة، وتكون الانحرافات عن هذا التوقع هي: 2- و1- و9 و5- و2- و1، لذا فإن الفرق المطلق الكلي هو 20، إذًا كم مرة سنرى مثل هذا الاختلاف صدفةً؟ إليك نسخة من الصنف <code>HypothesisTest</code> تجيبنا عن هذا السؤال كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_27" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DiceTest</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        observed </span><span class="pun">=</span><span class="pln"> data
        n </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">observed</span><span class="pun">)</span><span class="pln">
        expected </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">ones</span><span class="pun">(</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> n </span><span class="pun">/</span><span class="pln"> </span><span class="lit">6</span><span class="pln">
        test_stat </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">abs</span><span class="pun">(</span><span class="pln">observed </span><span class="pun">-</span><span class="pln"> expected</span><span class="pun">))</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        n </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">data</span><span class="pun">)</span><span class="pln">
        values </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">]</span><span class="pln">
        rolls </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">choice</span><span class="pun">(</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">,</span><span class="pln"> replace</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span><span class="pln">
        hist </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Hist</span><span class="pun">(</span><span class="pln">rolls</span><span class="pun">)</span><span class="pln">
        freqs </span><span class="pun">=</span><span class="pln"> hist</span><span class="pun">.</span><span class="typ">Freqs</span><span class="pun">(</span><span class="pln">values</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> freqs</span></pre>

<p>
	تُمثَّل هذه البيانات على صورة قائمة من الترددات حيث أن القيم المرصودة هي: <code>[8‎, 9, 19, 5, 8, 11]</code> والترددات المتوقعة هي 10 لكل القيم، كما أن إحصائية الاختبار هي مجموع الفروق المطلقة؛ أما فرضية العدم فتقول أن قطعة النرد عادلة، لذا سنحاكي هذه الفرضية عن طريق أخذ عينات عشوائية من القيم <code>values</code>، ويستخدِم التابع <code>RunModel</code> الصنف <code>Hist</code> لحساب وإعادة قائمة الترددات، كما تكون القيمة الاحتمالية للبيانات هي 0.13 أي أنه إذا كانت قطعة النرد عادلةً، فسنتوقع ظهور الانحراف الكلي المرصود أو قيمة انحراف أكبر من المتوقعة لتكون حوالي ‎13% من المرات، لذا ليس للتأثير الظاهر دلالة إحصائية.
</p>

<h2>
	اختبارات مربع كاي
</h2>

<p>
	استخدمنا في القسم السابق الانحراف الكلي على أساس اختبار إحصائي، لكن يشيع استخدام إحصائية مربع كاي لاختبار النسب، وتكون الصيغة الرياضية لمربع كاي كما يلي:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium">χ</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium"> </span>
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:xx-large">∑</span>
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">i</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium">(<span style="font-style:italic">O</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium"> − <span style="font-style:italic">E</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium">)</span><sup><span style="font-size:medium">2</span></sup>
</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">E</span><sub><span style="font-style:italic;font-size:medium">i</span></sub>
</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	حيث O<sub>i</sub> هي الترددات المرصودة وE<sub>i</sub> هي الترددات المتوقعة، وفيما يلي شيفرة <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> الموافقة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_31" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">DiceChiTest</span><span class="pun">(</span><span class="typ">DiceTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        observed </span><span class="pun">=</span><span class="pln"> data
        n </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">(</span><span class="pln">observed</span><span class="pun">)</span><span class="pln">
        expected </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">ones</span><span class="pun">(</span><span class="lit">6</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> n </span><span class="pun">/</span><span class="pln"> </span><span class="lit">6</span><span class="pln">
        test_stat </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">((</span><span class="pln">observed </span><span class="pun">-</span><span class="pln"> expected</span><span class="pun">)**</span><span class="lit">2</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> expected</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> test_stat</span></pre>

<p>
	يعطي تربيع الانحرافات -بدلًا من أخذ القيم المطلقة- وزنًا أكبر للانحرافات الكبيرة، إذ يوحِّد standardizes التقسيم على <code>expected</code> الانحرافات على الرغم أنه ليس لها في هذه الحالة أثر لأن الترددات المتوقَّعة متساوية فيما بينها، وتكون القيمة الاحتمالية في حال استخدام إحصائية مربع كاي مساويةً لـ 0.04 وهي أصغر بكثير من 0.13 والتي حصلنا عليها عندما استخدمنا الانحراف الكلي، فإذا أخذنا العتبة 5‎‎%‎ على محمل الجد، فسننظر للتأثير على أنه ذو دلالة إحصائية، لكن يمكننا القول إذا أخذنا الاختبارَين بالحسبان فستكون النتائج حديةً borderline، وعلى الرغم أنه لن نستبعد احتمال أن يكون النرد ملتويًا، إلا أننا لن نُدين المتهم بالغش.
</p>

<p>
	يوضِّح هذا المثال نقطة مهمة وهي اعتمادية القيمة الاحتمالية على اختيار إحصائية الاختبار وعلى اختيار نموذج فرضية العدم، وفي بعض الأحيان تحدِّد هذه الاختيارات ما إذا كان التأثير ذا دلالة إحصائية أم لا، وللمزيد من المعلومات حول اختبار مربع كاي يمكنك زيارة <a href="https://ar.wikipedia.org/wiki/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1_%D9%85%D8%B1%D8%A8%D8%B9_%D9%83%D8%A7%D9%8A" rel="external nofollow">صفحة ويكيبيديا</a>.
</p>

<h2>
	عودة إلى الأطفال الأوائل
</h2>

<p>
	ألقينا نظرة في بداية المقال على مدة حالات الحمل للأطفال الأوائل وبقية الأطفال واستنتجنا أن الفروق الظاهرة في المتوسط mean والانحراف المعياري standard deviation ليست ذا دلالة إحصائية، لكن رأينا في قسم سابق في مقال <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%83%D8%AA%D9%84%D8%A9-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1303/" rel="">دوال الكتلة الاحتمالية في بايثون</a> فروقًا واضحةً في توزيع مدة الحمل خاصةً في المجال بين 35 إلى 43 أسبوع، كما يمكننا استخدام اختبار مبني على إحصائية مربع كاي لنتحقق فيما إذا كانت هذه الفروق ذات دلالة إحصائية أم لا، وتجمع الشيفرة هذه عدة عناصر من الأمثلة السابقة:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_34" style="">
<span class="kwd">class</span><span class="pln"> </span><span class="typ">PregLengthTest</span><span class="pun">(</span><span class="pln">thinkstats2</span><span class="pun">.</span><span class="typ">HypothesisTest</span><span class="pun">):</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">MakeModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">data
        self</span><span class="pun">.</span><span class="pln">n </span><span class="pun">=</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">firsts</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">pool </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">hstack</span><span class="pun">((</span><span class="pln">firsts</span><span class="pun">,</span><span class="pln"> others</span><span class="pun">))</span><span class="pln">

        pmf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Pmf</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">values </span><span class="pun">=</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">35</span><span class="pun">,</span><span class="pln"> </span><span class="lit">44</span><span class="pun">)</span><span class="pln">
        self</span><span class="pun">.</span><span class="pln">expected_probs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">array</span><span class="pun">(</span><span class="pln">pmf</span><span class="pun">.</span><span class="typ">Probs</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">values</span><span class="pun">))</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">RunModel</span><span class="pun">(</span><span class="pln">self</span><span class="pun">):</span><span class="pln">
        np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">shuffle</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">)</span><span class="pln">
        data </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">[:</span><span class="pln">self</span><span class="pun">.</span><span class="pln">n</span><span class="pun">],</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">pool</span><span class="pun">[</span><span class="pln">self</span><span class="pun">.</span><span class="pln">n</span><span class="pun">:]</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> data</span></pre>

<p>
	تُمثَّل هذه البيانات على أساس قائمتي مدة حمل، وفرضية العدم هي أنّ العينتين مأخوذتان من التوزيع ذاته، كما ينمذج التابع <code>MakeModel</code> التوزيع عن طريق تجميع العينتَين باستخدام <code>hstack</code>، ومن ثم يولِّد التابع <code>RunModel</code> البيانات المُحاكاة عن طريق خلط العينة المجمَّعة وقسمها إلى جزئين اثنين، كما يُعرِّف <code>MakeModel</code> المتغير <code>values</code> وهو مجال الأسابيع الذي سنستخدمه، والمتغير <code>expected_probs</code> وهو احتمال كل قيمة في التوزيع المجمَّع، وفيما يلي الشيفرة التي تحسب إحصائية الاختبار:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_36" style="">
<span class="com"># class PregLengthTest:</span><span class="pln">

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">TestStatistic</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">):</span><span class="pln">
        firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> data
        stat </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">ChiSquared</span><span class="pun">(</span><span class="pln">firsts</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> self</span><span class="pun">.</span><span class="typ">ChiSquared</span><span class="pun">(</span><span class="pln">others</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> stat

    </span><span class="kwd">def</span><span class="pln"> </span><span class="typ">ChiSquared</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> lengths</span><span class="pun">):</span><span class="pln">
        hist </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Hist</span><span class="pun">(</span><span class="pln">lengths</span><span class="pun">)</span><span class="pln">
        observed </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">array</span><span class="pun">(</span><span class="pln">hist</span><span class="pun">.</span><span class="typ">Freqs</span><span class="pun">(</span><span class="pln">self</span><span class="pun">.</span><span class="pln">values</span><span class="pun">))</span><span class="pln">
        expected </span><span class="pun">=</span><span class="pln"> self</span><span class="pun">.</span><span class="pln">expected_probs </span><span class="pun">*</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">lengths</span><span class="pun">)</span><span class="pln">
        stat </span><span class="pun">=</span><span class="pln"> sum</span><span class="pun">((</span><span class="pln">observed </span><span class="pun">-</span><span class="pln"> expected</span><span class="pun">)**</span><span class="lit">2</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> expected</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> stat</span></pre>

<p>
	يحسب <code>TestStatistics</code> إحصائية مربع كاي للأطفال الأوائل وبقية الأطفال وتحسب مجموعها، في حين يأخذ التابع <code>ChiSquared</code> متسلسلةً من مدة الحمل ويحسب مدرَّجه التكراري histogram ويحسب <code>observed</code> التي هي قائمة من الترددات الموافقة للقيم <code>self.values</code>، كما يضرب <code>ChiSquared</code> الاحتمالات المحسوبة مسبقًا <code>expected_probs</code> بحجم العينة لحساب قائمة الترددات المتوقعة ويُعيد إحصائية مربع كاي<code>stat</code>.
</p>

<p>
	تكون إحصائية مربع كاي الكليّة لبيانات المسح الوطني لنمو الأسرة هي 102 وليس لهذه القيمة معنى لوحدها، لكن بعد 1000 تكرار تكون قيمة أكبر إحصائية اختبار مولدة في ظل فرضية العدم 32، وبالتالي نستنتج أنه من غير المرجح أن تكون إحصائية مربع كاي مرصودةً في ظل فرضية العدم -أي من غير المرجح أن تكون مرصودة بافتراض أن فرضية العدم صحيحة-، لذا فإن التأثير الظاهر ذو دلالة إحصائية، كما يوضح هذا المثال وجود قيود على اختبارات مربع كاي، فهي تشير إلى وجود اختلاف بين المجموعتين، لكنها لا تذكر أي معلومة محددة حول ماهية الاختلاف.
</p>

<h2>
	الأخطاء
</h2>

<p>
	يكون التأثير ذا دلالة إحصائية في الطريقة الكلاسيكية في اختبار الفرضيات إذا كانت القيمة الاحتمالية أقل من عتبة معينة وغالبًا ما تكون ‎5%، كما يثير هذا النهج سؤالين هما:
</p>

<ul>
<li>
		إذا كان التأثير قد ظهر صدفةً، فما هو احتمال أن نعده ذو دلالة إحصائية؟ يدعى هذا الاحتمال بمعدل السلبية الكاذبة false negative rate.
	</li>
	<li>
		إذا كان التأثير حقيقيًا، فما احتمال فشل اختبار الفرضية؟ يدعى هذا الاحتمال بمعدل الإيجابية الكاذبة false positive rate.
	</li>
</ul>
<p>
	من السهل نسبيًا حساب معدل الإيجابية الكاذبة، فهو 5‎‎%‎ إذا كانت العتبة 5‎%‎، وإليك السبب كما يلي:
</p>

<ul>
<li>
		إذا لم يكن هناك تأثير حقيقي، فستكون فرضية العدم صحيحةً، لذا يمكننا حساب توزيع إحصائية الاختبار عن طريق محاكاة فرضية العدم، وندعو هذا التوزيع CDF<sub>T</sub>.
	</li>
	<li>
		نحصل على إحصائية اختبار <code>t</code> مأخوذة من CDF<sub>T</sub> في كل مرة ننفِّذ فيها تجربة، ومن ثم نحسب القيمة الاحتمالية التي هي احتمال تجاوز قيمة عشوائية مأخوذة من CDF<sub>T</sub> الإحصائية <code>t</code>، أي 1-CDF<sub>T</sub>(t).
	</li>
	<li>
		إذا كانت CDF<sub>T</sub>(t) أكبر من 95‎%‎، فستكون القيمة الاحتمالية أصغر من ‎5% وذلك إذا تجاوزت <code>t</code> المئين 95 -أي 95th percentile، وكم مرة تتجاوز قيمة مختارة ما من CDF<sub>T</sub> المئين 95؟ حوالي 5‎%‎ من المرات الكلية.
	</li>
</ul>
<p>
	لذا إن أدّيت اختبار فرضية واحدًا مع عتبة 5%، فستتوقع حصول إيجابية كاذبة مرة واحدة من كل 20 مرة.
</p>

<h2>
	القوة
</h2>

<p>
	يعتمد معدل السلبية الكاذبة على حجم التأثير الفعلي الذي لا يكون معلومًا عادةً، لذا فمن الصعب حسابه، لكن يمكننا حساب المعدل عن طريق حساب معدل مشروط بحجم تأثير افتراضي، فإذا افترضنا مثلًا أن الفارق المرصود بين المجموعتين دقيقًا، فيمكننا استخدام العينات المرصودة على أساس نموذج للسكان ومن ثم استخدام البيانات المُحاكاة من أجل تنفيذ اختبارات الفرضيات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_38" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">FalseNegRate</span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> num_runs</span><span class="pun">=</span><span class="lit">100</span><span class="pun">):</span><span class="pln">
    group1</span><span class="pun">,</span><span class="pln"> group2 </span><span class="pun">=</span><span class="pln"> data
    count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

    </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">num_runs</span><span class="pun">):</span><span class="pln">
        sample1 </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Resample</span><span class="pun">(</span><span class="pln">group1</span><span class="pun">)</span><span class="pln">
        sample2 </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Resample</span><span class="pun">(</span><span class="pln">group2</span><span class="pun">)</span><span class="pln">

        ht </span><span class="pun">=</span><span class="pln"> </span><span class="typ">DiffMeansPermute</span><span class="pun">((</span><span class="pln">sample1</span><span class="pun">,</span><span class="pln"> sample2</span><span class="pun">))</span><span class="pln">
        pvalue </span><span class="pun">=</span><span class="pln"> ht</span><span class="pun">.</span><span class="typ">PValue</span><span class="pun">(</span><span class="pln">iters</span><span class="pun">=</span><span class="lit">101</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> pvalue </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0.05</span><span class="pun">:</span><span class="pln">
            count </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> count </span><span class="pun">/</span><span class="pln"> num_runs</span></pre>

<p>
	يأخذ التابع <code>FalseNegRate</code> بيانات على صورة متسلسلتين بحيث يكون لكل مجموعة متسلسلة، إذ يحاكي التابع تجربةً في كل تكرار من الحلقة عن طريق سحب عينة عشوائية من كل مجموعة وتنفيذ اختبار فرضية، ومن ثم يتحقق التابع من النتيجة ويحسب عدد مرات السلبية الكاذبة، في حين يأخذ التابع <code>Resample</code> متسلسلةً ويسحب عينةً من الطول نفسه مع استبدال كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_40" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Resample</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">):</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">choice</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">),</span><span class="pln"> replace</span><span class="pun">=</span><span class="kwd">True</span><span class="pun">)</span></pre>

<p>
	إليك الشيفرة التي تختبر مدة حالات الحمل:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" id="ips_uid_8383_42" style="">
<span class="pln">    live</span><span class="pun">,</span><span class="pln"> firsts</span><span class="pun">,</span><span class="pln"> others </span><span class="pun">=</span><span class="pln"> first</span><span class="pun">.</span><span class="typ">MakeFrames</span><span class="pun">()</span><span class="pln">
    data </span><span class="pun">=</span><span class="pln"> firsts</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">values</span><span class="pun">,</span><span class="pln"> others</span><span class="pun">.</span><span class="pln">prglngth</span><span class="pun">.</span><span class="pln">values
    neg_rate </span><span class="pun">=</span><span class="pln"> </span><span class="typ">FalseNegRate</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span></pre>

<p>
	تكون النتيجة حوالي 70‎%‎، أي نتوقع أن تؤدي تجربة بحجم العينة هذا إلى اختبار سلبي 70‎%‎ من المرات إذا كان الفرق الفعلي في متوسط مدة الحمل 0.078 أسبوعًا، لكن غالبًا ما تُقدَّم هذه النتيجة بالطريقة المعاكسة أي نتوقع أن تؤدي تجربة بحجم العينة هذا إلى اختبار إيجابي ‎30% من المرات إذا كان الفرق الفعلي في متوسط مدة الحمل 0.078 أسبوعًا، حيث يدعى معدل الإيجابية الصحيحة هذا بقوة power الاختبار، أو يدعى أحيانًا بحساسية sensitivity الاختبار، إذ تعكس هذه التسمية قدرة الاختبار على تحديد تأثير بحجم معين مُعطى سابقًا. كان احتمال أن يعطي الاختبار نتيجةً إيجابيةً في هذا المثال هو 30‎%‎ -في حال كان الفرق هو 0.078 أسبوعًا كما ذكرنا سابقًا-، وتقول القاعدة العامة أنه تُعَدّ قوة ‎80% قيمةً مقبولةً، لذلك يمكننا القول أنّ هذا الاختبار كان ضعيفًا أو يفتقر للقوة underpowered، وبصورة عامة لا يعني اختبار الفرضية السلبي negative hypothesis test أنه لا يوجد فرق بين المجموعات، وإنما يقترح أنه إذا كان هناك فارقًا، فهو صغير جدًا لتحديده باستخدام حجم العينة هذا.
</p>

<h2>
	التكرار
</h2>

<p>
	تُعَدّ عملية اختبار الفرضيات الموضحة في هذا المقال ليست ممارسة جيدة بالمعنى الدقيق للكلمة.
</p>

<p>
	أولًا، أدينا عدة اختبارات، حيث أنك إذا نفَّذت اختبار فرضية واحد، فسيكون احتمال ظهور إيجابية كاذبة هو 1 من 20 وقد يكون هذا مقبولًا، لكن إن أجريت 20 اختبارًا تتوقع ظهور إيجابية كاذبة مرة واحدة على الأقل في معظم الأوقات.
</p>

<p>
	ثانيًا، استخدمنا مجموعة البيانات ذاتها لعمليتي الاستكشاف والاختبار، وإذا استكشفتَ مجموعة بيانات ضخمة ووجدت تأثيرًا مفاجئًا ثم اختبرته لتعرف فيما إذا كان ذا دلالة إحصائية أم لا، فسيكون من المرجح توليد إيجابية كاذبة.
</p>

<p>
	يمكنك ضبط عتبة القيمة الاحتمالية للتعويض عن الاختبارات المتعددة، كما هو وارد في <a href="https://en.wikipedia.org/wiki/Holm-Bonferroni_method" rel="external nofollow">صفحة ويكيبيديا</a>، أو يمكنك معالجة كلا المشكلتين عن طريق تقسيم البيانات باستخدام مجموعة للاستكشاف ومجموعة أخرى للاختبار، كما تكون بعض هذه الممارسات إجباريةً في بعض المجالات أو مستحسنَةً على الأقل، لكن من الشائع أيضًا معالجة هذه المشاكل ضمنيًا عن طريق تكرار النتائج المنشورة، وعادةً ما تُعَدّ الورقة البحثية الأولى التي تقدم نتيجة جديدة أنها استكشافية exploratory، كما تُعَدّ الأوراق اللاحقة التي تكرر النتيجة باستخدام بيانات جديدة أنها مؤكِدة confirmatory.
</p>

<p>
	صدف وأن أتُيحت لنا الفرصة لتكرار النتائج في هذا المقال، حيث أن النسخة الأولى من الكتاب مبنية على الدورة السادسة من المسح الوطني لنمو الأسرة التي صدرت عام 2002، لكن أصدرت مراكز السيطرة على الأمراض والوقاية منها CDC في الشهر 10 من عام 2010 بيانات إضافية مبنية على المقابلات التي أجريت بين عامي 2006-2010، كما يحتوي <code>nsfg2.py</code> على تعليمات برمجية لقراءة هذه البيانات وتنظيفها، حيث يكون في مجموعة البيانات الجديدة ما يلي:
</p>

<ul>
<li>
		الفرق في متوسط مدة الحمل هو 0.16 أسبوع وله دلالة إحصائية وقيمته الاحتمالية أصغر من 0.001 أيp&lt;0.001 موازنةً بفارق 0.078 أسبوع في مجموعة البيانات الأصلية.
	</li>
	<li>
		الفرق في وزن الولادة هو 0.17 رطل مع قيمة احتمالية أصغر من 0.001 أي p&lt;0.001 موازنةً بفارق 0.12 رطل في مجموعة البيانات الأصلية.
	</li>
	<li>
		الارتباط بين وزن الولادة وعمر الأم هو 0.08 مع قيمة احتمالية أصغر من 0.001 أي p&lt;0.001 موازنةً بفارق 0.07.
	</li>
	<li>
		اختبار مربع كاي فهو ذو دلالة إحصائية مع قيمة احتمالية أصغر من 0.001 أي p&lt;0.001 كما كان في مجموعة البيانات الأصلية.
	</li>
</ul>
<p>
	باختصار، تكررت التأثيرات في مجموعة البيانات الجديدة والتي تمتعت بدلالة إحصائية في مجموعة البيانات الأصلية، وكذلك فإن الفرق في طول الحمل أكبر في مجموعة البيانات الجديدة وهو ذو دلالة إحصائية أيضًا مع أنه لم يكن ذا دلالة إحصائية في مجموعة البيانات الأصلية.
</p>

<h2>
	تمارين
</h2>

<p>
	يوجد حل التمارين في <code>chap09soln.py</code>.
</p>

<h3>
	تمرين 1
</h3>

<p>
	تزداد قوة اختبار الفرضية كلما ازداد حجم العينة، أي أنه من المرجح أن يكون موجبًا إن كان التأثير حقيقيًا، والعكس صحيح فعندما ينقص حجم العينة يصبح من غير المرجح أن يكون الاختبار موجبًا حتى ولو كان التأثير حقيقيًا، وللتحقق من هذا الأمر نفِّذ الاختبارات التي ذكرناها في هذا المقال على مجموعات فرعية من بيانات المسح الوطني لنمو الأسرة، علمًا أنه يمكنك استخدام <code>thinkstats2.SampleRows</code> لاختيار مجموعة فرعية عشوائية من الأسطر في إطار بيانات.
</p>

<p>
	ماذا سيكون مصير القيم الاحتمالية لهذه الاختبارات عندما ينقص حجم العينة؟ وما هو أصغر حجم عينة ينتج عنه اختبار إيجابي؟
</p>

<h3>
	تمرين 2
</h3>

<p>
	أجرينا في قسم اختبار الفرق في المتوسطين محاكاةً لفرضية العدم عن طريق التبديل permutation، أي أننا عاملنا القيم المرصودة على أساس جمع السكان ومن ثم قسمنا الأشخاص إلى مجموعتين عشوائيًا، كما يوجد بديل آخر وهو استخدام العينة لتقدير توزيع السكان ومن ثم سحب عينة عشوائية من هذا التوزيع، وتدعى هذه العملية بإعادة أخذ عينات resampling، حيث أنه هناك عدة طرق لإعادة أخذ العينات، وإحدى أبسط هذه الطريق هي سحب عينة مع استبدال with replacement من القيم المرصودة كما فعلنا في قسم "القوة".
</p>

<p>
	اكتب صنفًا class باسم <code>DiffMeansResample</code> يرث من الصنف <code>DiffMeansPermute</code> وأعِد تعريف التابع <code>RunModel</code> لتنفيذ إعادة أخذ عينات resampling بدلًا من التبديل permutation، ثم استخدم هذا النموذج لاختبار الفروق في مدة الحمل ووزن الولادة، وما مدى تأثير النموذج على النتائج؟
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2010.html" rel="external nofollow">Chapter 9 Hypothesis testing analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e/" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">العلاقات بين المتغيرات الإحصائية وكيفية تنفيذها في بايثون</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%83%D8%AB%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1335/" rel="">دوال الكثافة الاحتمالية في بايثون</a>
	</li>
</ul>
<style type="text/css">
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}</style>
]]></description><guid isPermaLink="false">1408</guid><pubDate>Mon, 20 Dec 2021 16:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x642;&#x62F;&#x64A;&#x631; Estimation &#x627;&#x644;&#x625;&#x62D;&#x635;&#x627;&#x626;&#x64A; &#x641;&#x64A; &#x628;&#x627;&#x64A;&#x62B;&#x648;&#x646;</title><link>https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%82%D8%AF%D9%8A%D8%B1-estimation-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1411/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_12/61cbed7d3497c_(2).png.3b5e2542639e44a59f50ab964429c545.png" /></p>

<p>
	توجد الشيفرة الخاصة بهذا المقال في ملف <code>estimation.py.</code>
</p>

<h2>
	لعبة التقدير
</h2>

<p>
	ستكون بداية هذا المقال مع لعبة، حيث سنعطيك <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1302/" rel="">توزيعًا</a> ومهمتك هي تخمين ماهية هذا التوزيع، كما سنزوِّدك بتلميحَين هما أنّ التوزيع طبيعي، وأن هناك عينةً عشوائيةً منه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_7" style="">
<span class="pun">[-</span><span class="lit">0.441</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.774</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.101</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1.138</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.975</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2.138</span><span class="pun">]</span></pre>

<p>
	برأيك ما هو معامل المتوسط μ لهذا التوزيع؟
</p>

<p>
	يمكننا استخدام متوسط العينة x̄ على أساس تقدير لـ μ، حيث يكون متوسط العينة في هذا المثال هو 0.155 أي x̄=0.155، لذا من المنطقي قولنا أنّ μ=0.155، حيث تدعى هذه العملية بالتقدير estimation، وتُدعى الإحصائية التي استخدمناها -أي متوسط العينة- بالمقدِّر estimator.
</p>

<p>
	في الواقع يُعَدّ استخدام متوسط العينة لتقدير واضحًا لدرجة أنه من الصعب علينا تخيل وجود بديل منطقي، لكننا سنعدِّل اللعبة الآن عن طريق إضافة <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D9%88%D8%B2%D9%8A%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1302/" rel="">قيم شاذة outliers</a>، ويكون التوزيع الجديد توزيعًا طبيعيًا أيضًا، وإليك عينةً جمَعها مسّاح غير موثوق به يضع الفاصلة العشرية في المكان الخطأ أحيانًا:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_9" style="">
<span class="pun">[-</span><span class="lit">0.441</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.774</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.101</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1.138</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.975</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">213.8</span><span class="pun">]</span></pre>

<p>
	ما هو تقديرك لقيمة μ؟ إذا استخدمت متوسط العينة في عملية التقدير، فسيكون تخمينك هو 35.12-، لكن هل هذا هو الخيار الأفضل؟ وما هي البدائل؟
</p>

<p>
	تُعَدّ عملية تحديد القيم الشاذة والتخلص منها ومن ثم حساب متوسط العينة لما تبقى هي إحدى هذه البدائل، كما يمكننا استخدام الوسيط median على أساس مُقدِّر، لكن يعتمد تحديد المُقدِّر الأفضل على الظروف مثل ما إذا كانت هناك قيم شاذة، وعلى الهدف مثل هل تحاول تقليل الأخطاء أو زيادة فرصتك في الحصول على الإجابة الصحيحة؟
</p>

<p>
	يقلل متوسط العينة من متوسط الخطأ التربيعي mean squared error - أو MSE اختصارًا- إذا لم تكن هناك أيّ قيم شاذة، أي إذا لعبنا اللعبة ذاتها عدة مرات وحسبنا الخطأ x̄-μ، فسيقِل متوسط العينة.
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-size:medium"><span style="font-style:italic">MSE</span> = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium">1</span>
							</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">m</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> <span style="font-size:xx-large">∑</span>(<span style="text-decoration:overline">x</span> − µ)</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	حيث أنّ <code>m</code> هو عدد مرات لعبك للعبة التقدير، ومن المهم التفريق بينها وبين <code>n</code> حجم العينة المستخدَم في حساب x̄، وإليك الدالة التي تحاكي لعبة التقدير ومن ثم تحسب خطأ الجذر التربيعي المتوسط RSME وهو الجذر التربيعي لمتوسط الخطأ التربيعي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_12" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Estimate1</span><span class="pun">(</span><span class="pln">n</span><span class="pun">=</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
    mu </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    sigma </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

    means </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    medians </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">m</span><span class="pun">):</span><span class="pln">
        xs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">random</span><span class="pun">.</span><span class="pln">gauss</span><span class="pun">(</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)]</span><span class="pln">
        xbar </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        median </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">median</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        means</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">xbar</span><span class="pun">)</span><span class="pln">
        medians</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">median</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'rmse xbar'</span><span class="pun">,</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">means</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'rmse median'</span><span class="pun">,</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">medians</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">))</span></pre>

<p>
	نؤكد على أنّ <code>n</code> هو حجم العينة و<code>m</code> هو عدد مرات لعب اللعبة، في حين يكون <code>means</code> قائمة التقديرات المبنية على x̄ ويكون <code>medians</code> قائمة وسطاء medians، وفيما يلي الدالة التي تحسب خطأ الجذر التربيعي المتوسط RSME:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_14" style="">
<span class="kwd">def</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">,</span><span class="pln"> actual</span><span class="pun">):</span><span class="pln">
    e2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[(</span><span class="pln">estimate</span><span class="pun">-</span><span class="pln">actual</span><span class="pun">)**</span><span class="lit">2</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> estimate </span><span class="kwd">in</span><span class="pln"> estimates</span><span class="pun">]</span><span class="pln">
    mse </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(</span><span class="pln">e2</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">mse</span><span class="pun">)</span></pre>

<p>
	تُعَدّ <code>estimates</code> قائمة التقديرات و<code>actual</code> القيمة الفعلية التي يتم تقديرها، لكن في التطبيق العملي تكون قيمة <code>actual</code> غير معروفة، إذ لسنا بحاجة إلى تقديرها في حال كنا نعرفها مسبقًا، حيث أن الغرض من هذه التجربة هو موازنة أداء المقدِّرات.
</p>

<p>
	ظهرت نتيجة خطأ الجذر التربيعي المتوسط لمتوسط العينة 0.41 عندما نفّذنا هذه الشيفرة، مما يعني أنه إذا استخدمنا x̄ لتقدير متوسط هذا التوزيع بناءً على عينة تحوي 7 قيم أي n=7، فيجب علينا التوقع أن يكون الخطأ وسطيًا 0.41، وفي حال استخدام الوسيط median من أجل تقدير المتوسط فستنتج لدينا قيمة للخطأ الجذر التربيعي المتوسط، وهي 0.53، مما يعني أنه وعلى الأقل بالنسبة لهذا المثال ينتج عن x̄ خطأ الجذر التربيعي المتوسط الأدنى.
</p>

<p>
	يُعَدّ تقليل الخطأ التربيعي المتوسط MSE خاصيةً جيدةً لكنها ليست الاستراتيجية الأفضل في كل الأوقات، فلنفترض مثلًا أننا نقدِّر توزيع سرعات الرياح في موقع بناء، فإذا كان التقدير مرتفعًا جدًا، فقد نبالغ في بناء الهيكل مما يزيد من تكلفته، لكن إذا كان التقدير منخفضًا للغاية، فقد ينهار البناء بسبب عدم الحذر أثناء البناء، كما أنّ تقليل الخطأ التربيعي المتوسط MSE ليس أفضل استراتيجية ممكنة، وذلك لأن التكلفة في حال كانت <a href="https://academy.hsoub.com/programming/python/%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D9%83%D8%AA%D9%84%D8%A9-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%85%D8%A7%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1303/" rel="">دالة</a> خطأ ليست متناظرةً.
</p>

<p>
	افترض أيضًا أننا ألقينا ثلاثة أحجار نرد سداسية الجوانب وطلبنا أن تتوقع مجموع الناتج الكلي، فإذا كان تقديرك صحيحًا تمامًا، فستحصل على جائزة؛ وإلا فلن تحصل على أيّ شيء، لذا تكون القيمة التي تقلل الخطأ التربيعي المتوسط MSE في هذه الحالة هي 10.5، لكن سيكون هذا تخمينًا سيئًا لأنه لا يمكن أن يكون مجموع الأرقام التي ظهرت على أحجار النرد الثلاثة 10.5 في أي حال من الأحوال، وبالنسبة لهذه اللعبة أنت تريد مُقدِّرًا لديه أعلى فرصة ليكون صحيحًا وهو مُقدِّر الاحتمال الأعظم maximum likelihood estimator -أو MLE اختصارًا-، فإذا اخترت 10 أو 11، فستكون فرصتك في الفوز هي 1 من 8 وهذا أفضل ما يمكنك الوصول إليه.
</p>

<h2>
	خمن التباين
</h2>

<p>
	إليك هذا التوزيع الطبيعي المألوف:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_16" style="">
<span class="pun">[-</span><span class="lit">0.441</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1.774</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.101</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1.138</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.975</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2.138</span><span class="pun">]</span></pre>

<p>
	ما هي برأيك قيمة التباين σ<sup>2</sup> الخاصة بالتوزيع السابق؟ بالطبع يُعَد الخيار الواضح هو استخدام تباين العينة S<sup>2</sup> على أساس مُقدِّر estimator.
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-style:italic;font-size:medium">S</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium">1</span>
							</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-style:italic;font-size:medium">n</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> <span style="font-size:xx-large">∑</span>(<span style="font-style:italic">x</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium"> − <span style="text-decoration:overline">x</span>)</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	لكن يكون S<sup>2</sup> مقدِّرًا مناسبًا بالنسبة للعينات الضخمة إلا أنه يميل إلى أن يكون منخفضًا جدًا بالنسبة للعينات الصغيرة، حيث تطلق عليه تسمية المقدِّر المتحيز biased بسبب هذه الخاصية المؤسفة، لكن يكون المقدِّر غير متحيز unbiased إذا كان الخطأ المتوقع الكلي -أو المتوسط- هو 0 وذلك بعد عدة تكرارات للعبة التقدير، وتوجد لحسن الحظ إحصائية بسيطة أخرى غير متحيزة للتباين σ<sup>2</sup> كما يلي:
</p>

<table class="display" style="direction: ltr; margin: auto;"><tbody><tr style="vertical-align:middle">
<td class="dcell">
				<span style="font-style:italic;font-size:medium">S</span><sub><span style="font-size:medium"><span style="font-style:italic">n</span>−1</span></sub><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> = </span>
			</td>
			<td class="dcell">
				<table class="display"><tbody>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium">1</span>
							</td>
						</tr>
<tr>
<td class="hbar">
								 
							</td>
						</tr>
<tr>
<td class="dcell" style="text-align:center">
								<span style="font-size:medium"><span style="font-style:italic">n</span>−1</span>
							</td>
						</tr>
</tbody></table>
</td>
			<td class="dcell">
				<span style="font-size:medium"> <span style="font-size:xx-large">∑</span>(<span style="font-style:italic">x</span></span><sub><span style="font-style:italic;font-size:medium">i</span></sub><span style="font-size:medium"> − <span style="text-decoration:overline">x</span>)</span><sup><span style="font-size:medium">2</span></sup><span style="font-size:medium"> </span>
			</td>
		</tr></tbody></table>
<p>
	إذا كنت تريد شرحًا عن سبب تحيز S<sup>2</sup> وبرهانًا على عدم تحيز S<sub>n-1</sub><sup>2</sup> ، فيمكنك الاطلاع على <a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%AD%D9%8A%D8%B2_%D8%A7%D9%84%D9%85%D9%82%D8%AF%D8%B1" rel="external nofollow">الانحياز المقدر</a>.
</p>

<p>
	تتمثل أكبر مشكلة لهذا المُقدِّر في كون الاسم والرمز غير متناسقَين، حيث يمكن أن يشير الاسم "تباين العينة" إلى S<sup>2</sup> أو S<sub>n-1</sub><sup>2</sup>، كما أن فإن الرمز S<sup>2</sup> يستخدَم للمصطلحين، وفيما يلي دالة تُحاكي لعبة التقدير وتختبر أداء كل من S<sup>2</sup> وS<sub>n-1</sub><sup>2</sup>:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_18" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Estimate2</span><span class="pun">(</span><span class="pln">n</span><span class="pun">=</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
    mu </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">
    sigma </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln">

    estimates1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    estimates2 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">m</span><span class="pun">):</span><span class="pln">
        xs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">random</span><span class="pun">.</span><span class="pln">gauss</span><span class="pun">(</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)]</span><span class="pln">
        biased </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">var</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        unbiased </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">var</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">,</span><span class="pln"> ddof</span><span class="pun">=</span><span class="lit">1</span><span class="pun">)</span><span class="pln">
        estimates1</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">biased</span><span class="pun">)</span><span class="pln">
        estimates2</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">unbiased</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'mean error biased'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MeanError</span><span class="pun">(</span><span class="pln">estimates1</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">**</span><span class="lit">2</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'mean error unbiased'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MeanError</span><span class="pun">(</span><span class="pln">estimates2</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">**</span><span class="lit">2</span><span class="pun">))</span></pre>

<p>
	يشير <code>n</code> إلى حجم العينة و<code>m</code> إلى عدد مرات لعب اللعبة، كما يحسب التابع <code>np.var</code> المقدار S<sup>2</sup> اقتراضيًا، إلى جانب أنه يمكن أن يحسب S<sub>n-1</sub><sup>2</sup> إذا زُوِّد بالوسيط <code>ddof=1</code> الذي يشير إلى "درجة حرية دلتا"، وعلى الرغم من أنه لن نشرح هذا المصطلح إلا أنه يمكنك معرفة تفاصيله عن طريق الاطلاع على <a href="https://ar.wikipedia.org/wiki/%D8%AF%D8%B1%D8%AC%D8%A9_%D8%AD%D8%B1%D9%8A%D8%A9_(%D8%A5%D8%AD%D8%B5%D8%A7%D8%A1)" rel="external nofollow">صفحة درجة الحرية على ويكيبيديا</a>.
</p>

<p>
	يحسب التابع <code>MeanError</code> متوسط الفرق بين التقديرات والقيمة الفعلية:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_20" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">MeanError</span><span class="pun">(</span><span class="pln">estimates</span><span class="pun">,</span><span class="pln"> actual</span><span class="pun">):</span><span class="pln">
    errors </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">estimate</span><span class="pun">-</span><span class="pln">actual </span><span class="kwd">for</span><span class="pln"> estimate </span><span class="kwd">in</span><span class="pln"> estimates</span><span class="pun">]</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(</span><span class="pln">errors</span><span class="pun">)</span></pre>

<p>
	كان متوسط خطأ S<sup>2</sup> عندما نفّذنا هذه الشيفرة هو -0.13، وكما هو متوقع، يميل المُقدِّر المتحيز إلى أن يكون منخفضًا للغاية، كما كانت قيمة S<sub>n-1</sub><sup>2</sup> هي 0.014 أي أقل بعشر مرات، وكلما ازدادت قيمة <code>m</code> توقعنا مقاربة متوسط خطأ S<sub>n-1</sub><sup>2</sup> من الصفر.
</p>

<p>
	تُعَدّ خاصتَي الخطأ التربيعي المتوسط MSE والتحيز bias والخواص المشابهة توقعات للمدى الطويل وهي مبنية على عدة تكرارات للعبة التقدير، بحيث يمكننا موازنة المُقدِّرات والتحقق فيما إذا كان لها خصائصًا مرغوبًا بها أم لا عن طريق تشغيل عمليات محاكاة مثل الموجودة في هذا المقال، لكنك لا تحصل إلا على تقدير واحد حينما تطبق المُقدِّر على البيانات الحقيقية، إذ لن يكون قولنا بأنّ التقدير غير متحيز ذا أهمية أو معنى، وذلك لأن خاصية عدم التحيز هي خاصية المُقدِّر لا التقدير، وتكون الخطوة التالية بعد اختيارك المُقدِّر الذي يمتلك الخصائص المناسبة لك واستخدامه لتوليد التقدير هي توصيف عدم استقرار التقدير الذي سنتناوله في القسم التالي.
</p>

<h2>
	توزيعات أخذ العينات sampling distributions
</h2>

<p>
	لنفترض أنك عالم تهتم بدراسة الغوريلا في محمية للحياة البرية مثلًا، وتريد معرفة متوسط وزن أنثى حيوان الغوريلا في هذه المحمية، لكن سيتوجب عليك تهدئتها أولًا، وهذا أمر خطير ومُكلف وقد يضر بصحة الحيوان نفسه، لكن إذا كان الحصول على المعلومات أمرًا هامًا، فقد يكون من المقبول وزن تسعة منها إذا افترضنا أنّ العدد الكامل للمحمية معلوم مسبقًا ويمكننا عندها اختيار عينة تمثيلية للإناث البالغات، كما يمكننا استخدام متوسط العينة x̄ لتقدير متوسط عدد الحيوانات المجهولين μ.
</p>

<p>
	قد تجد عند وزن 9 إناث أن x̄=90 kg وأن الانحراف المعياري للعينة هو S = 7.5 kg، حيث أن متوسط العينة هو مُقدِّر غير متحيز للمقدار μ ويقلل متوسط الخطأ التربيعي MSE على المدى الطويل، لذا إن كنت تريد الخروج بتقدير واحد يلخص النتيجة، فيمكنك اختيار القيمة 90 kg، لكن ما مدى ثقتك بهذا التقدير؟ إذا وزنت 9 إناث فقط n=9 من أصل عدد كبير، فقد لا يكون الحظ حليفك، فربما تكون قد اخترت أكثر الإناث وزنًا -أو أقلها وزنًا- عن طريق الصدفة وحسب.
</p>

<p>
	يُعرَف تباين التقدير الناتج عن الاختيار العشوائي <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B1%D8%A8%D8%B9%D8%A7%D8%AA-%D8%A7%D9%84%D8%B5%D8%BA%D8%B1%D9%89-%D8%A7%D9%84%D8%AE%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1412/" rel="">بخطأ أخذ العينات sampling error</a>، حيث يمكننا حساب خطأ أخذ العينات عن طريق محاكاة عملية أخذ العينات بقيم افتراضية للمقدارين σ وμ، ومن ثم مراقبة مدى تباين x̄، كما سنستخدِم تقديرات كلًا من x̄ وS لعدم معرفتنا القيم الفعلية للمقدارين σ وμ، لذا يكون السؤال المطروح هو إذا كانت القيمة الفعلية هي σ=90 kg وμ = 7.5 kg ونفّذنا التجربة عدة مرات، فكيف سيتغير المتوسط المُقدَّر x̄؟ إليك الدالة التي تجيب عن هذا السؤال:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_23" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">SimulateSample</span><span class="pun">(</span><span class="pln">mu</span><span class="pun">=</span><span class="lit">90</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">=</span><span class="lit">7.5</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">=</span><span class="lit">9</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
    means </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> j </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">m</span><span class="pun">):</span><span class="pln">
        xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">normal</span><span class="pun">(</span><span class="pln">mu</span><span class="pun">,</span><span class="pln"> sigma</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">)</span><span class="pln">
        xbar </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        means</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">xbar</span><span class="pun">)</span><span class="pln">

    cdf </span><span class="pun">=</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Cdf</span><span class="pun">(</span><span class="pln">means</span><span class="pun">)</span><span class="pln">
    ci </span><span class="pun">=</span><span class="pln"> cdf</span><span class="pun">.</span><span class="typ">Percentile</span><span class="pun">(</span><span class="lit">5</span><span class="pun">),</span><span class="pln"> cdf</span><span class="pun">.</span><span class="typ">Percentile</span><span class="pun">(</span><span class="lit">95</span><span class="pun">)</span><span class="pln">
    stderr </span><span class="pun">=</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">means</span><span class="pun">,</span><span class="pln"> mu</span><span class="pun">)</span></pre>

<p>
	تُعَدّ كلًا من <code>mu</code> و<code>sigma</code> قيمًا افتراضيةً للمعامِلات، و<code>n</code> هي حجم العينة أي عدد إناث الغوريلا التي وزناها، و<code>m</code> هي عدد المرات التي ننفِّذ فيها المحاكاة.
</p>

<p style="text-align: center;">
	<img alt="الشكل 8.1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="87347" data-unique="wo5w3dmdr" src="https://academy.hsoub.com/uploads/monthly_2021_12/61cbeeb2ce6eb_8.1.png.be3e6901faed89969f06d97d12f109e8.png" style=""></p>

<p>
	يوضِّح الشكل السابق توزيع أخذ عينات x̄ مع مجال الثقة.
</p>

<p>
	نختار في كل تكرار <code>n</code> قيمةً من التوزيع الطبيعي مع المعامِلات المعطاة ونحسب متوسط العينة <code>xbar</code> وننفِّذ 1000 محاكاةً للتجربة، ثم نحسب توزيع التقديرات <code>cdf</code>، ويُظهر الشكل السابق النتيجة ويُدعى هذا التوزيع بتوزيع أخذ العينات sampling distributions للمُقدِّر، وهو يُظهر مدى تنوّع التقديرات إذا نفّذنا التجربة عدة مرات.
</p>

<p>
	تقترب قيمة متوسط توزيع أخذ العينات من القيمة الافتراضية للمقدار μ، مما يعني أن التجربة تعطي الإجابة الصحيحة وسطيًا، حيث أنّ أقل نتيجة بعد 1000 محاولة هي 82 كيلوغرامًا وأعلى قيمة هي 98 كيلوغرامًا، ويشير هذا المجال إلى احتمالية ابتعاد التقدير عن القيمة الحقيقية بمقدار 8 كيلوغرام.
</p>

<p>
	توجد طريقتان شائعتان لتلخيص توزيع أخذ العينات وهما:
</p>

<ul>
<li>
		<strong>الخطأ المعياري standard error -أو SE اختصارًا-</strong>: هو مقياس لمدى توقعنا أن يكون التقدير بعيدًا عن القيمة الحقيقية وسطيًا، حيث نحسب الخطأ x̄-μ لكل تجربة محاكاة ومن ثم نحسب خطأ الجذر التربيعي المتوسط RSME، إذ تكون قيمة الخطأ في هذا المثال 2.5 كيلو غرامًا.
	</li>
	<li>
		<strong>lمجال الثقة confidence interval -أو IC اختصارًا-</strong>: هو مجال يتضمن جزءًا من توزيع أخذ العينات، أي مجال الثقة 90% مثلًا هو المجال من المئين رقم 5 5th percentile إلى المئين رقم 95 95th percentile؛ أما في هذا المثال فيكون مجال الثقة 90% هو (94 ,86) كيلوغرامًا.
	</li>
</ul>
<p>
	غالبًا ما تكون الأخطاء المعيارية ومجالات الثقة مصدرًا للالتباس كما يلي:
</p>

<ul>
<li>
		غالبًا ما يحصل خلط بين الخطأ المعياري والانحراف المعياري، لذا تذكَّر أنّ الانحراف المعياري يصف التباين في عينة مُقاسة، حيث يكون الانحراف المعياري لوزن إناث الغوريلا في هذا المثال هو 7.5 كيلو غرامًا؛ أما الخطأ المعياري فيصف التباين في التقدير، حيث يكون الخطأ المعياري للمتوسط بناءً على عينة من 9 قياسات هي 2.5 كيلو غرامًا، كما يمكنك تذكُّر الفرق بين المفهومين عن طريق حفظ القاعدة التي تقول أنه كلما ازداد حجم العينة، صغر الخطأ المعياري، على عكس الانحراف المعياري الذي لا يقل.
	</li>
	<li>
		غالبًا ما يعتقد الأشخاص أنه هناك احتمال بنسبة 90% لوقوع المعامِل الفعلي في مجال الثقة، لكن هذا ليس صحيحًا لأنه سيتوجب عليك استخدام التوابع البايزية -ويمكنك الاطلاع على كتابنا Think Bayes- في حال كنت تريد تقديم ادّعاءً من هذا القبيل، كما يجيب توزيع أخذ العينات عن سؤال آخر، فهو يمنحك معلومات عن مدى تغير التقدير إذا كررت التجربة، لذا تستطيع من خلاله معرفة ما إذا كان التقدير هذا موثوقًا أم لا.
	</li>
</ul>
<p>
	من المهم أن تتذكر أن مجالات الثقة confidence intervals والأخطاء المعيارية standard errors تحسب خطأ أخذ العينات sampling error فقط، أي أنها لا تحسب سوى الأخطاء الناتجة عن معاينة جزء من العدد الكلي وحسب، كما لا يأخذ توزيع العينات في الحسبان مصادر الخطأ الأخرى خاصةً تحيز أخذ العينات sampling bias وخطأ القياس measurement error وهما موضوعا القسم التالي.
</p>

<h2>
	تحيز أخذ العينات sampling bias
</h2>

<p>
	إذا افترضنا أنك تريد معرفة متوسط أوزان النساء في المدينة التي تعيش فيها بدلًا عن وزن الغوريلات في محمية طبيعية، فسيكون من غير المرجح السماح لك باختيار عينة تمثيلية من النساء وتسجيل أوزانهن، لذا ستحتاج إلى بديل وسيكون البديل البسيط هو أخذ العينات عن طريق المكالمة الهاتفية telephone sampling، أي يمكنك اختيار أرقام عشوائية من دليل الهاتف ومن ثم الاتصال وسؤال امرأة بالغة عن وزنها.
</p>

<p>
	تملك طريقة أخذ العينات عن طريق المكالمة الهاتفية بالطبع قيودًا وحدودًا واضحةً، أي تُعَدّ هذه الطريقة مثلًا محدودةً بالأشخاص الذين يملكون أرقامًا هاتفية ومسجلةً في دليل الهاتف، لذا فهي تستبعد الأشخاص الذين لا يملكون هواتفًا -أي مَن قد يكونوا أفقر من المتوسط- ومَن رقمه ليس مسجلًا -أي مَن قد يكونوا أثرى من المتوسط-، كما أنه إذا اتصلت على المنازل في النهار، فسيكون احتمال إيجاد الأشخاص الذين يمتلكون عملًا احتمالًا ضعيفًا إلى حد ما، وإذا لم تسأل سوى أول مَن يجيب على الهاتف، فسيكون احتمال أخذ عينة من الأشخاص الذين يشتركون معه في الهاتف ضعيفًا أيضًا.
</p>

<p>
	ستتأثر نتيجة المسح هذا بطريقة أو بأخرى إذا وُجدت بعض العوامل، مثل الدخل والحالة الوظيفية وعدد أفراد الأسرة التي تُعَدّ أمورًا متعلقةً بالوزن -ومن المنطقي أن تكون كذلك-، حيث تُدعى هذه المشكلة بتحيز أخذ العينات sampling bias لأنها خاصية من عملية أخذ العينات، كذلك فإنّ عملية أخذ العينات مُعرّضة لما يُعرَف بالاختيار الذاتي self-selection الذي يُعَدّ نوعًا من أنواع تحيز أخذ العينات.
</p>

<div aria-label="blockquote widget" contenteditable="false" role="region" tabindex="-1">
	<blockquote class="ipsQuote" data-ipsquote="" data-widget="ipsquote">
		<div class="ipsQuote_citation">
			اقتباس
		</div>

		<p>
			<strong>توضيح</strong>: الاختيار الذاتي في الإحصاء أو البحث هو تحيز ناتج عن أن الأشخاص يستطيعون أن يرفضوا الإجابة عن السؤال المطروح، أي أنهم يختارون فيما إن كانوا سينتمون إلى مجموعة المشاركين أم لا، حيث أنه من الجائز أن يرفض البعض الإجابة عن السؤال، لذا ستتأثر النتائج إذا كان احتمال الرفض متعلقًا بالوزن.
		</p>
	</blockquote>
</div>

<p>
	أخيرًا، قد تكون النتائج غير دقيقة في حال سألت الأشخاص عن وزنهم بدلًا من قيامك أنت بعملية الوزن، حيث سترى في أفضل الحالات -أي إذا كان المستجيبون متعاونِين- أنه قد يلجأ المشاركون إلى تقريب وزنهم من أقرب قيمة عليا صحيحة أو أقرب قيمة دنيا صحيحة إن كانت لديهم بعض المشاكل المتعلقة بالثقة أو عدم الراحة تجاه وزنهم الفعلي، فضلًا عن أنّ بعض المستجيبين لا يكونون متعاونِين كثيرًا، وتُعَدّ هذه الأخطاء في الدقة أمثلةً عن الخطأ في القياس measurement error، وفي حال كان تقريرك يحوي قيمةً مُقدَّرةً، فقد يكون من المفيد حساب الخطأ المعياري أو مجال الثقة أو كلاهما معًا، وذلك من أجل تحديد خطأ أخذ العينات sampling error، ولكن من المهم أيضًا التذكُّر أنّ خطأ أخذ العينات هو أحد مصادر الخطأ -أي ليس المصدر الوحيد-، وغالبًا لا يكون المصدر الأكبر.
</p>

<h2>
	التوزيعات الأسية
</h2>

<p>
	دعنا نلعب لعبة التقدير مرةً أخرى، بحيث يكون التوزيع هنا توزيعًا أسيًا، وإليك عينةً منه:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_25" style="">
<span class="pun">[</span><span class="lit">5.384</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4.493</span><span class="pun">,</span><span class="pln"> </span><span class="lit">19.198</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.790</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6.122</span><span class="pun">,</span><span class="pln"> </span><span class="lit">12.844</span><span class="pun">]</span></pre>

<p>
	برأيك ما هو معامِل λ الخاص بهذا التوزيع؟
</p>

<p>
	تقول القاعدة إنه بصورة عامة فإن متوسط التوزيع الأسي هو 1/λ، لذا إذا عكسنا العملية فقد نختار:
</p>

<p style="text-align: center;">
	L=1/ x̄
</p>

<p>
	يُعَدّ L مُقدِّرًا للـ λ كما أنه ليس مقدرًا عاديًا بل هو مُقدِّر الاحتمال الأعظم maximum likelihood estimator -أو MLE اختصارًا-، ويمكنك قراءة المزيد من المعلومات عنه في صفحة <a href="https://ar.wikipedia.org/wiki/%D8%AA%D9%88%D8%B2%D9%8A%D8%B9_%D8%A3%D8%B3%D9%8A" rel="external nofollow">الويكيبيديا</a>، فإذا كنت ترغب بزيادة فرصتك إلى أعلى حد ممكن في تخمين λ تخمينًا دقيقًا، فعليك اللجوء إلى L، لكننا نعلم أنه في حال وجود قيم شاذة، فلن يكون x̄ متينًا؛ لذا من المتوقع أن يكون للمقدر L المشكلة ذاتها، كما يمكننا اختيار بديل بناءً على وسيط العينة sample median، حيث أن الصيغة الرياضية لوسيط التوزيع الأسي هو ln(2)/λ، لذا إذا عكسنا العملية، فيمكننا تعريف مُقدِّر كما يلي:
</p>

<p style="text-align: center;">
	L<sub>m</sub>=ln(2)/m
</p>

<p>
	حيث <code>m</code> وسيط العينة sample median.
</p>

<p>
	يمكننا إجراء محاكاة لعملية أخذ العينات إذا أردنا اختبار أداء هذه المُقدِّرات، وإليك الشيفرة الموافقة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-py prettyprinted" data-widget="ipscode" id="ips_uid_1788_27" style="">
<span class="kwd">def</span><span class="pln"> </span><span class="typ">Estimate3</span><span class="pun">(</span><span class="pln">n</span><span class="pun">=</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> m</span><span class="pun">=</span><span class="lit">1000</span><span class="pun">):</span><span class="pln">
    lam </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pln">

    means </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    medians </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[]</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">m</span><span class="pun">):</span><span class="pln">
        xs </span><span class="pun">=</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">random</span><span class="pun">.</span><span class="pln">exponential</span><span class="pun">(</span><span class="lit">1.0</span><span class="pun">/</span><span class="pln">lam</span><span class="pun">,</span><span class="pln"> n</span><span class="pun">)</span><span class="pln">
        L </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> np</span><span class="pun">.</span><span class="pln">mean</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        </span><span class="typ">Lm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> math</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> thinkstats2</span><span class="pun">.</span><span class="typ">Median</span><span class="pun">(</span><span class="pln">xs</span><span class="pun">)</span><span class="pln">
        means</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">L</span><span class="pun">)</span><span class="pln">
        medians</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="typ">Lm</span><span class="pun">)</span><span class="pln">

    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'rmse L'</span><span class="pun">,</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">means</span><span class="pun">,</span><span class="pln"> lam</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'rmse Lm'</span><span class="pun">,</span><span class="pln"> RMSE</span><span class="pun">(</span><span class="pln">medians</span><span class="pun">,</span><span class="pln"> lam</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'mean error L'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MeanError</span><span class="pun">(</span><span class="pln">means</span><span class="pun">,</span><span class="pln"> lam</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">print</span><span class="pun">(</span><span class="str">'mean error Lm'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">MeanError</span><span class="pun">(</span><span class="pln">medians</span><span class="pun">,</span><span class="pln"> lam</span><span class="pun">))</span></pre>

<p>
	كان خطأ الجذر التربيعي المتوسط RMSE للمقدر L هو 1.1 عندما نفَّذنا هذه التجربة من أجل λ=2 ؛ أما بالنسبة للمُقدِّر L<sub>m</sub> المبني على الوسيط median-based، فإن خطأ الجذر التربيعي المتوسط RMSE هو 1.8، وفي الواقع لا يمكننا الاستنتاج من هذه التجربة ما إذا كان L يقلل من الخطأ التربيعي المتوسط MSE أم لا، لكن يبدو لنا أن L هو على الأقل أفضل من L<sub>m</sub>، لكن لسوء الحظ يبدو أنّ المُقدِّران متحيزان، حيث أن الخطأ المتوسط للـ L هو 0.33 والخطأ المتوسط للمقدر L<sub>m</sub> هو 0.45، ولا يتقارب أيّ منهما إلى الصفر مع ازدياد قيمة <code>m</code>، وبالتالي يتضح أنّ x̄ هي مُقدِّر غير متحيز لمتوسط التوزيع 1/λ، في حين L ليس مُقدِّرًا غير متحيز لـ λ.
</p>

<h2>
	التمارين
</h2>

<p>
	قد يكون من المفيد لك أخذ نسخة من الملف <code>estimation.py</code> على أساس نقطة انطلاق لهذه التمارين، مع العلم أنّ الحلول موجودة في <code>chap08soln.py</code>.
</p>

<h3>
	تمرين 1
</h3>

<p>
	استخدمنا في هذا المقال كلًا من x̄ والوسيط من أجل تقدير μ، ووجدنا أنّ الخطأ التربيعي المتوسط الأدنى ينتج عن x̄، كما استخدمنا S<sup>2</sup> وS<sub>n-1</sub><sup>2</sup> لتقدير الانحراف المعياري σ ووجدنا أنّ S<sup>2</sup> متحيز وأنّ S<sub>n-1</sub><sup>2</sup> غير متحيز، لذا وانطلاقًا من هذا نفِّذ بعض التجارب المماثلة لترى فيما إذا كان x̄ والوسيط هي تقديرات متحيزة للمتوسط μ، وتحقق فيما إذا كان ينتج عن S<sup>2</sup> أو S<sub>n-1</sub><sup>2</sup> خطأً تربيعيًا متوسطًا أدنى أم لا.
</p>

<h3>
	تمرين 2
</h3>

<p>
	لنفترض أنك رسمت عينةً تحوي 10 قيم أي n=10 وبتوزيع أسي، بحيث يكون λ=2.
</p>

<p>
	نفِّذ محاكاةً لهذه التجربة 1000 مرة وارسم توزيع أخذ العينات للتقدير L، واحسب الخطأ المعياري للتقدير ومجال الثقة 90%، ثم كرر التجربة مع تغيير قيمة <code>n</code> بضع مرات، وارسم مخطط الخطأ المعياري مقابل n.
</p>

<h3>
	تمرين 3
</h3>

<p>
	عادةً ما يكون الوقت بين الأهداف في رياضات مثل الهوكي وكرة القدم أسيًا، لذا يمكنك تقدير معدل تسجيل الفريق للأهداف عن طريق رصد عدد الأهداف التي يسجلونها في مباراة ما، حيث تختلف عملية التقدير هذه اختلافًا صغيرًا عن عملية أخذ عينات من الوقت بين الأهداف، لذا سنرى التطبيق العملي لهذا.
</p>

<p>
	اكتب دالةً تأخذ معدل تسجيل الأهداف <code>lam</code> والذي وحدة قياسه أهداف لكل مباراة، ثم حاكي مباراةً بتوليد الوقت بين الأهداف إلى أن يتخطى الوقت زمن مباراة واحدة، ومن ثم تُعيد هذه الدالة عدد الأهداف المسجلة، واكتب دالةً أخرى تُحاكي عدة مباريات وتسجِّل تقديرات لـ <code>lam</code>، ثم تحسب خطأ المتوسط وخطأ الجذر التربيعي المتوسط RMSE.
</p>

<p>
	برأيك هل تُعَدّ هذه الطريقة في إنشاء تقدير متحيزة؟ ارسم توزيع أخذ العينات sampling distribution الذي يحوي التقديرات ومجال الثقة 90%؛ ما هو الخطأ المعياري في هذه الحالة؟ وكيف تؤثر زيادة قيم <code>lam</code> على خطأ أخذ العينات sampling error؟
</p>

<p>
	ترجمة -وبتصرف- للفصل <a href="https://greenteapress.com/thinkstats2/html/thinkstats2009.html" rel="external nofollow">Chapter 8 Estimate analysis</a> من كتاب <a href="https://greenteapress.com/wp/think-stats-2e" rel="external nofollow">Think Stats: Exploratory Data Analysis in Python</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%B9%D9%84%D8%A7%D9%82%D8%A7%D8%AA-%D8%A8%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%BA%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r1381/" rel="">العلاقات بين المتغيرات الإحصائية وكيفية تنفيذها في بايثون</a>
	</li>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/python/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%81%D8%B1%D8%B6%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D8%AD%D8%B5%D8%A7%D8%A6%D9%8A%D8%A9-r1408/" rel="">اختبار الفرضيات الإحصائية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/files/15-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A8%D9%84%D8%BA%D8%A9-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">البرمجة بلغة بايثون</a>
	</li>
</ul>
<style type="text/css">
div table{margin-left:inherit;margin-right:inherit;margin-bottom:2px;margin-top:2px}
td p{margin:0px;}
.vbar{border:none;width:2px;background-color:black;}
.hbar{display: block;border:none;height:2px;width:100%;background-color:black;}
.display{border-collapse:separate;border-spacing:2px;width:auto;border:none;}
.dcell{white-space:nowrap;padding:0px; border:none;}
.dcenter{margin:0ex auto;}
.theorem{text-align:left;margin:1ex auto 1ex 0ex;}
table{border-collapse:collapse;}
td{padding:0;}
.cellpadding0 tr td{padding:0;}
.cellpadding1 tr td{padding:1px;}
.center{text-align:center;margin-left:auto;margin-right:auto;}</style>
]]></description><guid isPermaLink="false">1411</guid><pubDate>Sat, 11 Dec 2021 16:00:00 +0000</pubDate></item></channel></rss>
