تاس انداختن با پلات‌لی

در این بخش از بسته‌ی پلات‌لیِ پایتون برای ساختن بصری‌سازی‌های تعاملی استفاده می‌کنیم. به‌کارگیری پلات‌لی به ویژه زمانی سودمند است که بخواهید بصری‌سازی را در مرورگر وب نمایش دهید زیرا مقیاس خروجی‌های آن به طور خودکار تغییر می‌کند تا در صفحه‌ی بیننده جا شوند. همچنین بصری‌سازی‌های ساخته‌شده با پلات‌لی تعاملی هستند؛ زمانی که کاربر روی اجزای خاصّی روی صفحه بایستد، اطّلاعاتی درباره‌ی آن نمایش داده می‌شود.

در این پروژه نتایج حاصل از پرتاب تاس را تحلیل می‌کنیم. زمانی که شما تاسی معمولی و شش‌وجهی را پرت می‌کنید، احتمال رخ دادن هر یک از اعداد ۱ تا ۶ یکسان است. با این حال زمانی که از دو تاس استفاده می‌کنید احتمال رخ دادن برخی عددها از اعداد دیگر بیشتر می‌شود. تلاش می‌کنیم که با تولید مجموعه‌داده‌ای که بیانگر پرتاب تاس‌ها است، معلوم کنیم که کدام عددها احتمال بیشتری برای رخ دادن دارند. سپس نتایج تعداد زیادی پرتاب را رسم خواهیم کرد تا ببینیم کدام نتیجه‌ها از دیگر نتایج محتمل‌ترند.

مطالعه‌ی پرتاب تاس‌ها اغلب در ریاضیّات برای توضیح انواع مختلفی از تحلیل داده‌ها به کار می‌رود. امّا به جز این کاربردهایی هم در دنیای واقعی دارد مانند کازینوها و دیگر روش‌های شرط‌بندی، و همچنین در روش بازی بازی‌هایی مثل مونوپولی و بسیاری از بازی‌های تاس‌محور.

نصب کردن پلات‌لی

پلات‌لی را هم مانند Matplotlib به کمک pip نصب کنید:

1
$ python -m pip install --user plotly

اگر هنگام نصب Matplotlib از دستور python3 یا مشابه آن استفاده کردید، اکنون نیز از همان استفاده کنید.

برای بازدید از بصری‌سازی‌های قابل ساخت با پلات‌لی از گالری انواع نمودارهای آن در https://plot.ly/python/ دیدن کنید. هر مثال شامل کد برنامه نیز هست بنابراین می‌توانید ببینید که پلات‌لی چگونه آن بصری‌سازی را تولید کرده است.

ساختن کلاس تاس

کلاس Die زیر را برای شبیه‌سازی پرتاب یک تاس می‌سازیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from random import randint

class Die:
    """A class representing a single die."""

    def __init__(self, num_sides=6):
        """Assume a six-sided die."""
        self.num_sides = num_sides

    def roll(self):
        """Return a random value between 1 and number of sides."""
        return randint(1, self.num_sides)

متد __init__() یک نشان‌وند اختیاری می‌پذیرد. در کلاس Die هنگامی که نمونه‌ای از تاس ساخته می‌شود، اگر نشان‌وندی وارد نشود، تعداد وجه‌ها همیشه شش خواهد بود. امّا اگر نشان‌وندی ارائه شود، مقدار آن تعداد وجه‌های تاس را معلوم می‌کند. (تاس‌ها با تعداد وجه‌های آن‌ها نام‌گذاری می‌شوند: تاس شش‌وجهی D6 و تاس هشت‌وجهی D8 نامیده می‌شود و …)

متد roll() از تابع randint() برای برگرداندن یک عدد تصادفی بین ۱ و تعداد وجه‌های تاس استفاده می‌کند. این تابع ممکن است مقدار آغازین (۱)، مقدار پایانی (num_sides) و یا هر عددی صحیح بین این دو را برگرداند.

پرتاب تاس

پیش از آن که بر پایه‌ی کلاس Die بصری‌سازی تولید کنیم بیایید یک تاس D6 را پرتاب کنیم، نتایج را چاپ کنیم و ببینیم که آیا نتایج منطقی است یا نه:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from die import Die

# Create a D6.
die = Die()

# Make some rolls, and store results in a list.
results = []
for roll_num in range(100):
    result = die.roll()
    results.append(result)

print(results)

نمونه‌ای از Die با تعداد پیش‌فرض شش وجه می‌سازیم. این تاس را ۱۰۰ بار می‌اندازیم و نتایج هر پرتاب را در لیست results ذخیره می‌کنیم. این نمونه‌ای از مجموعه‌ی نتایج است:

1
2
3
4
5
[4, 6, 5, 6, 1, 5, 6, 3, 5, 3, 5, 3, 2, 2, 1, 3, 1, 5, 3, 6, 3, 6, 5, 4,
1, 1, 4, 2, 3, 6, 4, 2, 6, 4, 1, 3, 2, 5, 6, 3, 6, 2, 1, 1, 3, 4, 1, 4,
3, 5, 1, 4, 5, 5, 2, 3, 3, 1, 2, 3, 5, 6, 2, 5, 6, 1, 3, 2, 1, 1, 1, 6,
5, 5, 2, 2, 6, 4, 1, 4, 5, 1, 1, 1, 4, 5, 3, 3, 1, 3, 5, 4, 5, 6, 5, 4,
1, 5, 1, 2]

یک نگاه سریع به نتایج نشان می‌دهد که کلاس Die به نظر درست کار می‌کند. مقدارهای ۱ تا ۶ را می‌بینیم که می‌دانیم آن‌ها کوچک‌ترین و بزرگ‌ترین مقدارهای برگشتی ممکن هستند و چون اعداد ۱ یا ۷ را نمی‌بینیم می‌فهمیم که تمام نتایج در بازه‌ی مناسب قرار دارند. همچنین تمام اعداد بین ۱ تا ۶ را در خروجی می‌بینیم که نشان می‌دهد تمام رخ‌دادهای ممکن نمایش داده شده است. بیایید تعداد دقیق نمایان شدن هر عدد را معلوم کنیم.

تحلیل نتایج

نتایج حاصل از پرتاب تاس D6 را با شمردن تعداد رخ دادن هر عدد تحلیل می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
--snip--

# Make some rolls, and store results in a list.
results = []
for roll_num in range(1000):
    result = die.roll()
    results.append(result)

# Analyze the results.
frequencies = []
for value in range(1, die.num_sides+1):
    frequency = results.count(value)
    frequencies.append(frequency)

print(frequencies)

چون دیگر نتایج پرتاب تاس را چاپ نمی‌کنیم می‌توانیم تعداد پرتاب‌های تاس شبیه‌سازی‌شده را به ۱۰۰۰ افزایش دهیم. برای تحلیل پرتاب‌ها لیستی خالی به نام frequencies می‌سازیم تا تعداد رخ دادن هر عدد را در آن ذخیره کنیم. در نتایج ممکن (در این مورد ۱ تا ۶) حلقه می‌زنیم، تعداد نمایان شدن هر عدد را در results می‌شماریم و سپس این مقدار را به لیست frequencies اضافه می‌کنیم. سپس نتایج را پیش از ساختن بصری‌سازی چاپ می‌کنیم:

1
[155, 167, 168, 170, 159, 181]

این نتایج به نظر قابل قبول‌اند: شش بسامد داریم، هر کدام برای هر عدد ممکن در پرتاب تاس D6، و می‌بینیم که هیچ کدام به طرز محسوسی از دیگر بسامدها بیشتر نیستند. حالا بیایید این نتایج را بصری کنیم.

ساختن یک بافت‌نگار

با وجود لیست بسامدها می‌توانیم بافت‌نگار یا هیستوگرام128 نتایج را درست کنیم. بافت‌نگار نموداری میله‌ای است که تعداد رخ دادن هر نتیجه را نمایش می‌دهد. این کد ساختن بافت‌نگار است:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from plotly.graph_objs import Bar, Layout
from plotly import offline
from die import Die

--snip--

# Analyze the results.
frequencies = []
for value in range(1, die.num_sides+1):
    frequency = results.count(value)
    frequencies.append(frequency)

# Visualize the results.
x_values = list(range(1, die.num_sides+1))
data = [Bar(x=x_values, y=frequencies)]

x_axis_config = {'title': 'Result'}
y_axis_config = {'title': 'Frequency of Result'}
my_layout = Layout(title='Results of rolling one D6 1000 times',
    xaxis=x_axis_config, yaxis=y_axis_config)

offline.plot({'data': data, 'layout': my_layout}, filename='d6.html')

برای ساختن یک بافت‌نگار برای هر نتیجه‌ی ممکن یک میله نیاز داریم. این میله‌ها را در لیستی به نام x_values ذخیره می‌کنیم که از ۱ آغاز و به تعداد وجه‌های تاس ختم می‌شود. پلات‌لی خروجی مستقیم تابع range() را نمی‌پذیرد پس باید این بازه را صریحاً به کمک تابع list() به لیست تبدیل کنیم. کلاس Bar() پلات‌لی بیانگر مجموعه‌داده‌ای است که به شکل نمودار میله‌ای درخواهد آمد. این کلاس به لیستی شامل مقدارهای x و لیستی شامل مقدارهای y نیاز دارد. این کلاس باید داخل کروشه قرار بگیرد زیرا یک مجموعه‌ی داده ممکن است عنصرهای متعدّدی داشته باشد.

هر محور می‌تواند به چند روش تنظیم شود و هر گزینه‌ی پیکربندی به عنوان عضوی از یک دیکشنری ذخیره می‌شود. فعلاً تنها عنوان هر محور را تعیین می‌کنیم. کلاس Layout() شی‌ای برمی‌گرداند که طرح و پیکربندی کلّی نمودار را مشخّص می‌کند. اینجا عنوان نمودار را معلوم کرده و دیکشنری‌های مربوط به محورهای x و y را وارد می‌کنیم.

برای تولید نمودار تابع offline.plot() را فرامی‌خوانیم. این تابع یک دیکشنری شامل داده و شیءهای طرح‌بندی و همچنین نام فایل ذخیره‌سازی نمودار را می‌پذیرد. خروجی را در فایلی به نام d6.html ذخیره می‌کنیم.

زمانی که برنامه‌ی die_visual.py را اجرا کنید احتمالاً مرورگری باز می‌شود که فایل d6.html را نشان می‌دهد. اگر چنین نشد برگه‌ی جدیدی در یک مرورگر باز کنید و سپس فایل d6.html را در آن باز کنید (فایل در همان پوشه‌ای است که die_visual.py را ذخیره کرده‌اید.) باید نموداری شبیه به آنچه در شکل ۱۵-۱۲ آمده ببینید.

![][image27]

شکل ۱۵-۱۲: نمودار میله‌ای ساده‌ای که با پلات‌لی درست شده است.

توجّه کنید که پلات‌لی نمودار را تعاملی کرده است: نشانگر ماوس را روی هر کدام از میله‌های نمودار نگه دارید و داده‌ی مربوط به آن را خواهید دید. این ویژگی به خصوص زمانی سودمند است که چند مجموعه‌داده را روی یک نمودار رسم کنید. همچنین به آیکون‌های بالا سمت راست توجّه کنید که به شما اجازه‌ی جابه‌جا کردن و بزرگنمایی این بصری‌سازی و ذخیره کردن آن به صورت یک عکس را می‌دهد.