1 - شکل فایل CSV

در این فصل مجموعه‌داده‌ها را از منابع آنلاین بارگیری کرده و بر اساس آن‌ها بصری‌سازی‌های کاربردی‌ای خواهید ساخت. شما می‌توانید گستره‌ی عظیمی از داده‌ها را به صورت آنلاین پیدا کنیم که بسیاری از آن‌ها هنوز کاملاً بررسی نشده‌اند. توانایی تحلیل این داده‌ها به شما امکان یافتن الگوها و ارتباطاتی در آن‌ها می‌دهد که هیچ‌کس پیدا نکرده است.

دسترسی به داده‌ها و بصری‌سازی آن‌ها از طریق دو گون129 ه‌ی متداول ذخیره‌سازی داده، CSV و JSON خواهد بود. از ماژول csv پایتون برای پردازش داده‌های هواشناسی ذخیره‌شده به شکل CSV (مقدارهای جداشده با کاما) استفاده کرده و کمینه و بیشینه‌ی دما را در طول زمان و در دو موقعیّت مختلف تحلیل می‌کنیم. از Matplotlib برای تولید نمودارهایی بر اساس داده‌های بارگیری‌شده بهره می‌گیریم تا تغییرات دما را در دو محیط متفاوت، Sitka, Alaska و Death Valley, California نشان دهیم. در ادامه‌ی فصل از ماژول json برای دریافت داده‌های لرزه‌نگاری ذخیره‌شده به شکل JSON استفاده می‌کنیم و به کمک پلات‌لی یک نقشه‌ی جهان رسم می‌کنیم که موقعیّت‌ها و بزرگی‌های زمین‌لرزه‌های اخیر را نشان می‌دهد.

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

شکل فایل CSV

یک راه ساده برای ذخیره کردن داده‌های در فایل‌های متنی این است که آن‌ها در قالب دنباله‌ای از مقدارهای جداشده با کاما بنویسیم که مقدارهای جداشده با کاما یا CSV نامیده می‌شوند. به فایل‌های حاصل به این روش فایل‌های CSV گویند. برای مثال اینجا تکه‌ای از داده‌های هواشناسی را به شکل CSV می‌بینیم:

1
"USW00025333","SITKA AIRPORT, AK US","2018-01-01","0.45",,"48","38"

این گزیده‌ای از داده‌های هواشناسی تاریخ اول ژانویه‌ی ۲۰۱۸ در شهر سیتکای آلاسکا است و شامل بیشینه و کمینه‌ی دما و تعداد دیگری اندازه‌گیری در طول روز است. خواندن فایل‌های CSV ممکن است برای انسان‌ها مشکل باشد امّا برنامه‌های به سادگی مقدارها را از آن خوانده و پردازش می‌کنند که این امر موجب سرعت یافتن فرآیند تحلیل داده‌ها می‌شود.

ما کار را از مجموعه‌ی کوچکی از داده‌های هواشناسی به شکل CSV مربوط به شهر سیتکای آلاسکا آغاز می‌کنیم که در منابع کتاب به آدرس https://nostarch.com/pythoncrashcourse2e/ در دسترس است. پوشه‌ای به نام data داخل پوشه‌ی برنامه‌های این فصل بسازید. فایل sitka_weather_07-2018_simple.csv را در این پوشه‌ی جدید کپی کنید. (پس از بارگیری منابع کتاب به تمام فایل‌های مورد نیاز در این فصل دسترسی خواهید داشت.)

نکته
داده‌های هواشناسی این پروژه در اصل از http://ncdc.noaa.gov/cdo-web/ گرفته شده‌اند.

جداسازی عنوان‌های فایل CSV

ماژول csv پایتون کتابخانه‌ای استاندارد برای جداسازی خط‌های فایل130 CSV است که به ما امکان می‌دهد تا مقدارهایی را مد نظر داریم از آن استخراج کنیم. از بررسی اوّلین خط فایل شروع می‌کنیم که شامل دنباله‌ای از عنوان‌ها برای داده‌هاست. این عنوان‌ها به ما می‌گویند که چه اطّلاعاتی در داده‌ها وجود دارد:

1
2
3
4
5
6
7
8
9
import csv

filename = 'data/sitka_weather_07-2018_simple.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

print(header_row)

پس از وارد کردن ماژول csv نام فایلی را که روی آن کار می‌کنیم به متغیّر filename نسبت می‌دهیم. سپس فایل را باز کرده و شیء فایل حاصل را در f می‌گذاریم. سپس csv.reader() را فرامی‌خوانیم و شیء فایل را به عنوان نشان‌وند به آن می‌ده131 یم تا برای آن یک شیء خواننده بسازد. این شیء را به reader نسبت می‌دهیم.

ماژول csv تابعی به نام next() دارد که هنگام تعیین شدن شیء خواننده خط بعدی فایل را برمی‌گرداند. در برنامه‌ی قبلی next() را فقط یک بار فراخواندیم تا بتوانیم خط اوّل فایل را که شامل عنوان‌های فایل است استخراج کنیم. داده‌هایی را که برگردانده شده در header_row ذخیره می‌کنیم. چنانکه می‌بینید header_row شامل عنوان‌هایی معنادار و مربوط به هواشناسی است که به ما می‌گوید در هر خط داده چه اطّلاعاتی ذخیره شده است:

1
['STATION', 'NAME', 'DATE', 'PRCP', 'TAVG', 'TMAX', 'TMIN']

شیء reader خط اول مقدارهای جداشده با کاما را در فایل پردازش می‌کند و هر کدام را به عنوان عضو یک لیست ذخیره می‌کند. عنوان STATION بیانگر کد ایستگاه هواشناسی‌ای‌ست که این داده‌ها را ثبت کرده است. موقعیّت این عنوان به ما می‌گوید که اوّلین مقدار هر خط کد ایستگاه هواشناسی خواهد بود. عنوان بعدی NAME است که نام ایستگاه هواشناسی ثبت‌کننده‌ی داده را معلوم می‌کند. باقی عنوان‌ها انواع مختلف اطّلاعاتی که در هر قرائت ذخیره شده نشان می‌دهد. داده‌هایی که فعلاً مورد نظر ماست تاریخ، بیشینه‌ی دما (TMAX) و کمینه‌ی دما (TMIN) است. این مجموعه‌داده ساده‌ای است که تنها شامل داده‌های مربوط به دما و بارندگی است. اگر خودتان داده‌های هواشناسی را بارگیری کنید می‌توانید اندازه‌گیری‌های دیگری همچون سرعت و جهت باد و داده‌های جزئی‌تر بارندگی را نیز داشته باشید.

چاپ کردن عنوان‌ها و موقعیّت آن‌ها

برای درک ساده‌تر عنوان‌های فایل هر کدام را به همراه موقعیّت آن‌ها را در لیست چاپ می‌کنیم:

1
2
3
4
5
6
7
8
--snip--

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    for index, column_header in enumerate(header_row):
        print(index, column_header)

تابع enumerate شماره و مقدار هر عضو لیستی را که در آن حلقه می‌زنید برمی‌گرداند. (توجّه کنید که خط print(header_row) را حذف کرده‌ایم چون جزئیات بیشتر آن را اینجا می‌بینیم.)

اینجا شاخص هر عنوان را در خروجی می‌بینیم:

1
2
3
4
5
6
7
0 STATION
1 NAME
2 DATE
3 PRCP
4 TAVG
5 TMAX
6 TMIN

چنان‌که می‌بینیم تاریخ‌ها و بیشینه‌ی دمای آن‌ها در ستون‌های ۲ تا ۵ ذخیره شده است. برای بررسی این داده‌ها هر سطر داده‌های sitka_weather_07-2018_simple.csv را پردازش و مقدار شاخص‌های ۲ تا ۵ را استخراج می‌کنیم.

استخراج و خواندن داده‌ها

اکنون که می‌دانیم به کدام ستون‌ها نیاز داریم، بیایید چند داده را بخوانیم. ابتدا بیشینه‌ی دمای هر روز را می‌خوانیم:

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

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    # Get high temperatures from this file.
    highs = []
    for row in reader:
        high = int(row[5])
        highs.append(high)

print(highs)

لیستی خالی به نام highs می‌سازیم و سپس در باقیمانده‌ی سطرهای فایل حلقه می‌زنیم. شیء خواننده از جای قبلی در فایل CSV ادامه می‌دهد و به طور خودکار هر خط را به همراه جایگاه آن برمی‌گرداند. چون قبلاً سطر عنوان را خوانده‌ایم حلقه از دومین خط آغاز می‌شود یعنی جایی که اصل داده‌ها شروع می‌شوند. در هر اجرای حلقه پنجمین داده را از سطر بیرون می‌کشیم که متناظر با عنوان TMAX است و سپس آن را در متغیّر high می‌گذاریم. به کمک تابع int() داده را که به صورت رشته ذخیره شده به شکل عددی تبدیل می‌کنیم تا بتوانیم از آن استفاده کنیم. سپس آن را به لیست highs الحاق می‌کنیم.

در ادامه داده‌هایی را که اکنون در highs ذخیره شده‌اند می‌بینیم:

1
2
[62, 58, 70, 70, 67, 59, 58, 62, 66, 59, 56, 63, 65, 58, 56, 59, 64, 60, 60,
61, 65, 65, 63, 59, 64, 65, 68, 66, 64, 67, 65]

ما بیشینه‌ی دمای هر روز را استخراج و در لیستی ذخیره کرده‌ایم. اکنون بیایید این داده‌ها را بصری کنیم.

2 - رسم داده‌ها در یک نمودار دما

برای بصری کردن داده‌های دمایی که در اختیار داریم، ابتدا مانند زیر به کمک Matplotlib نمودار ساده‌ای از بیشینه‌های دما می‌سازیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import csv
import matplotlib.pyplot as plt

filename = 'data/sitka_weather_07-2018_simple.csv'

with open(filename) as f:
    --snip--

    # Plot the high temperatures.
    plt.style.use('seaborn')
    fig, ax = plt.subplots()
    ax.plot(highs, c='red')

    # Format plot.
    plt.title("Daily high temperatures, July 2018", fontsize=24)
    plt.xlabel('', fontsize=16)
    plt.ylabel("Temperature (F)", fontsize=16)
    plt.tick_params(axis='both', which='major', labelsize=16)

plt.show()

لیست بیشینه‌ی دما را به plot() می‌دهیم و c='red' را تعیین می‌کنیم تا نقطه‌ها را به رنگ قرمز رسم کند. (بیشینه‌ی دما را قرمز و کمینه‌ی آن را آبی رسم خواهیم کرد.) سپس تعدادی دیگر از جزئیات شکل‌دهی مانند عنوان، اندازه‌ی متن و برچسب‌ها را تعیین می‌کنیم که آن‌ها را از فصل ۱۵ بشناسید. چون هنوز تاریخ‌ها را اضافه نکرده‌ایم عنوانی برای محور x تعیین نمی‌کنیم امّا اندازه‌ی متن آن را برای افزایش خوانایی برچسب‌های پیش‌فرض تغییر می‌دهیم. شکل ۱۶-۱ نمودار حاصل را نشان می‌دهد: یک نمودار خطّی ساده مربوط به بیشینه‌های دما در ژوئیه‌ی ۲۰۱۸ در سیتکای آلاسکا.

شکل ۱۶-۱- نموداری خطّی که بیشینه‌ی دمای روزانه در ماه ژوئیه‌ی سال ۲۰۱۸ در سیتکای آلاسکا نشان می‌دهد.

3 - ماژول datetime

حال تاریخ‌ها را به نمودار خود اضافه می‌کنیم تا آن را کاربردی‌تر کنیم. اوّلین تاریخ داده‌های هواشناسی در دومین سطر فایل قرار دارد.

داده به صورت رشته خوانده خواهد شد پس باید رشته‌ی "2018-07-01" را به شی‌ای که بیانگر این تاریخ است تبدیل کنیم. می‌توانیم شیء بیانگر تاریخ اول ژوئیه‌ی ۲۰۱۸ را به کمک متد strptime() از ماژول datetime بسازیم. ابتدا در نشست ترمینال ببینیم که strptime() چگونه کار می‌کند:

1
2
3
4
from datetime import datetime

first_date = datetime.strptime('2018-07-01', '%Y-%m-%d')
print(first_date)
1
2018-07-01 00:00:00

ابتدا کلاس datetime را از ماژول datetime وارد می‌کنیم. سپس متد strptime() را فراخوانی می‌کنیم و رشته‌ی حاوی تاریخ را به عنوان یکمین نشان‌وند به آن می‌دهیم. دومین نشان‌وند به پایتون می‌گوید که تاریخ را به چه شکلی درآورد. در این مثال پایتون '%Y-' را اینجور تفسیر می‌کند که قسمتی از رشته که پشت اوّلین خطّ فاصله قرار دارد چهار رقمِ سال است؛ '%m-' به این معنی است که آن بخش از رشته که قبل از دومین خطّ فاصله قرار گرفته عددی بیانگر ماه است و '%d' می‌گوید که آخرین قسمت رشته چندمین روز ماه را نشان می‌دهد، از ۱ تا ۳۱.

متد strptime() می‌تواند گسترده‌ای از نشان‌وندها را برای تفسیر تاریخ بپذیرد. جدول ۱۶-۱ برخی از این نشان‌وندها را نشان می‌دهد.

جدول ۱۶-۱- نشان‌وندهای شکل‌دهی تاریخ و ساعت از ماژول datetime

نشان‌وند معنی
%A نام روز هفته مثل Monday
%B نام ماه مانند January
%m ماه، به صورت عددی از 01 تا 12
%d روز ماه به صورت عددی از 01 تا 31
%Y سال چهاررقمی مثل 2019
%y سال دورقمی مثل 19
%H ساعت در شکل ۲۴ ساعتی از 00 تا 23
%I ساعت در شکل ۱۲ ساعتی از 01 تا 12
%p AM یا PM
%M دقیقه‌ها (00 تا 59)
%S ثانیه‌ها (00 تا 61)

4 - رسم تاریخ‌ها

اکنون می‌توانیم نمودار داده‌های دمایی خود را با استخراج تاریخِ بیشینه‌های روزانه بهبود بدهیم و آن‌ها را مانند زیر به همراه بیشینه‌ی دما به plot() بدهیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import csv
from datetime import datetime
import matplotlib.pyplot as plt

filename = 'data/sitka_weather_07-2018_simple.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    # Get dates and high temperatures from this file.
    dates, highs = [], []
    for row in reader:
        current_date = datetime.strptime(row[2], '%Y-%m-%d')
        high = int(row[5])
        dates.append(current_date)
        highs.append(high)

# Plot the high temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, highs, c='red')

# Format plot.
plt.title("Daily high temperatures, July 2018", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)

plt.show()

دو لیست خالی برای ذخیره‌سازی تاریخ و بیشینه‌ی دما می‌سازیم. سپس داده‌های شامل اطّلاعات تاریخی (row[2]) را به شیء datetime تبدیل می‌کنیم و آن را به dates الحاق می‌کنیم. تاریخ‌ها و بیشینه‌های دما را به plot() می‌دهیم. فراخوانی fig.autofmt_xdate() برچسب‌های تاریخ را به صورت مورّب رسم می‌کند تا از هم‌پوشانی آن‌ها را جلوگیری کند. شکل ۱۶-۲ نمودار بهبودیافته را نشان می‌دهد.

شکل ۱۶-۲- نمودار که اکنون تاریخ‌ها را بر محور x خود دارد معنادارتر است.

5 - رسم یک دوره‌ی زمانی طولانی‌تر

حال که نمودار ما راه افتاده اجازه دهید داده‌های بیشتر اضافه کنیم تا تصویر کامل‌تری از آب و هوای سیتکا داشته باشیم. فایل sitka_weather_2018_simple.csv را که شامل اطّلاعات کامل آب و هوایی یک‌ساله‌ی سیتکا است در پوشه‌ای کپی کنید که داده‌های برنامه‌های این فصل را نگه می‌دارید.

اکنون می‌توانیم نموداری برای آب و هوای یک سال کامل را رسم کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
--snip--

filename = 'data/sitka_weather_2018_simple.csv'

with open(filename) as f:
    --snip--

    # Format plot.
    plt.title("Daily high temperatures - 2018", fontsize=24)
    plt.xlabel('', fontsize=16)
    --snip--

نام فایل را تغییر می‌دهیم تا از فایل داده‌ی جدید sitka_weather_2018_simple.csv استفاده کنیم و عنوان نمودار خود را عوض می‌کنیم تا بیانگر تغییر محتویّات آن باشد. شکل ۱۶-۳ نمودار حاصل را نشان می‌دهد.

شکل ۱۶-۳- داده‌های یک ساله

6 - رسم یک دنباله‌ی داده‌ی دیگر

می‌توانیم با اضافه کردن کمینه‌ی دما نمودار گویای خود را از این هم سودمندتر کنیم. باید داده‌های مربوط به کمینه‌ی دما را از فایل داده‌ها استخراج کنیم و آن‌ها را نمودار خود بیفراییم، مانند زیر:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--snip--

filename = 'sitka_weather_2018_simple.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    # Get dates, and high and low temperatures from this file.
    dates, highs, lows = [], [], []
    for row in reader:
        current_date = datetime.strptime(row[2], '%Y-%m-%d')
        high = int(row[5])
        low = int(row[6])
        dates.append(current_date)
        highs.append(high)
        lows.append(low)

# Plot the high and low temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, highs, c='red')
ax.plot(dates, lows, c='blue')

# Format plot.
plt.title("Daily high and low temperatures - 2018", fontsize=24)
--snip--

در ابتدا لیست lows را برای نگهداری دماهای کمینه اضافه می‌کنیم و سپس این داده را از موقعیّت هفتم هر سطر (row[6]) استخراج و ذخیره می‌کنیم. سپس یک فراخوانی plot() دیگر برای دماهای کمینه اضافه می‌کنیم و این مقادیر را آبی می‌کنیم. در نهایت عنوان را به‌روز می‌کنیم. شکل ۱۶-۴ نمودار حاصل را نشان می‌دهد.

شکل ۱۶-۴- دو دنباله‌ی داده در یک نمودار

7 - سایه زدن سطحی در نمودار

اکنون با اضافه کردن دو دنباله‌ی داده می‌توانیم گستره‌ی دمایی هر روز را بررسی کنیم. اجازه دهید آخرین تغییر را در نمودار ایجاد کنیم و با سایه زدن محدوده‌ی بین بالاترین و پایین‌ترین دمای هر روز را نشان دهیم. برای این کار از متد fill_between() استفاده خواهیم کرد که دنباله‌ای از مقدارهای x و دو دنباله‌ی مقدارهای y را دریافت کرده و فاصله‌ی بین آن‌ها را پر می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--snip--

# Plot the high and low temperatures.
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.plot(dates, highs, c='red', alpha=0.5)
ax.plot(dates, lows, c='blue', alpha=0.5)
plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)

--snip--

نشان‌وند alpha میزان محو بودن یک رنگ را کنترل می‌کند. مقدار alphaی ۰ کاملاً شفّاف و ۱ (مقدار پیش‌فرض) کاملاً مات است. با تنظیم مقدار ۰٫۵ برای alpha خط‌های آبی و قرمز نمودار را روشن‌تر می‌کنیم.

لیست dates را به عنوان مقدارهای x و سپس دو لیست highs و lows را به جای مقدارهای y به fill_between() بدهید. نشان‌وند facecolor رنگ ناحیه‌ی سایه‌خورده را مشخّص می‌کند؛ مقدار alphaی آن را ۰٫۱ می‌گذاریم تا ناحیه‌ی پرشده بدون منحرف کردن توجّه از اطّلاعات نمایش‌داده‌شده، دو منحنی را به هم وصل کند. شکل ۱۶-۵ نمودار را با ناحیه‌ی سایه‌خورده بین مقدارهای کمینه و بیشینه نشان می‌دهد.

شکل ۱۶-۵- ناحیه‌ی بین دو مجموعه داده سایه خورده است.

این سایه‌زنی کمک می‌کند که ناحیه‌ی بین دو مجموعه‌داده بلافاصله به چشم بیاید.

8 - بررسی خطاها

ما باید بتوانیم کد sitka_highs_lows.py را برای اطّلاعات هر نقطه‌ی جغرافیایی استفاده کنیم. امّا برخی ایستگاه‌های هواشناسی داده‌های متفاوتی را جمع‌آوری می‌کنند و برخی ممکن است گاهی نتوانند داده‌های لازم را گرد هم آوردند. فقدان داده‌ها به وقوع استثنا منجر می‌شوند و برنامه‌های ما را از کار می‌اندازند مگر این که آن‌ها را به درستی مدیریت کنیم.

برای مثال ببینیم که در صورت تلاش برای تولید نمودار دما برای دره‌ی مرگ در کالیفرنیا چه رخ می‌دهد. فایل death_valley_2018_simple.csv را به پوشه‌ای که داده‌های برنامه‌های این فصل قرار داد منتقل کنید.

ابتدا برای دیدن عنوان داده‌هایی که در این فایل آمده کد زیر را اجرا کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import csv

filename = 'data/death_valley_2018_simple.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    for index, column_header in enumerate(header_row):
        print(index, column_header)

خروجی چنین است:

1
2
3
4
5
6
7
0 STATION
1 NAME
2 DATE
3 PRCP
4 TMAX
5 TMIN
6 TOBS

جایگاه تاریخ همانند قبل شاخص ۲ است. امّا جایگاه دماهای کمینه و بیشینه ۴ و ۵ است پس باید شماره‌ی شاخص‌ها را در برنامه‌ی خود تغییر دهیم تا این جایگاه‌های جدید اعمال شوند. به جای قرائت دمای میانگین برای یک روز، این ایستگاه TOBS را ارسال می‌کند که بیانگر زمان خاص مشاهده و خواندن داده است.

من یکی از قرائت‌های دما را از این فایل حذف کرده‌ام تا نشان دهم که با فقدان داده‌ای در فایل چه رخ می‌دهد. با در نظر گرفتن تغییراتی که در شماره‌ی شاخص‌ها در بالا اشاره شد، برنامه‌ی sitka_highs_lows.py را برای تولید نموداری برای دره‌ی مرگ تغییر دهید و ببینید چه رخ می‌دهد:

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

filename = 'data/death_valley_2018_simple.csv'

with open(filename) as f:
    --snip--

    # Get dates, and high and low temperatures from this file.
    dates, highs, lows = [], [], []
    for row in reader:
        current_date = datetime.strptime(row[2], '%Y-%m-%d')
        high = int(row[4])
        low = int(row[5])
        dates.append(current_date)
        --snip--

شاخص‌ها را برای تطابق با جایگاه TMAX و TMIN این فایل تغییر می‌دهیم.

زمانی که دوباره برنامه را اجرا می‌کنیم به خطا برمی‌خوریم؛ چنان‌که در آخرین خط خروجی زیر نمایان است:

1
2
3
4
Traceback (most recent call last):
  File "death_valley_highs_lows.py", line 15, in <module>
      high = int(row[4])
ValueError: invalid literal for int() with base 10: ''

این خروجی نشان می‌دهد که پایتون نمی‌تواند بیشینه‌ی دمای یکی از روزها را پردازش کند زیرا تبدیل رشته‌ی خالی ('') به عدد صحیح ممکن نیست. به جای گشتن به دنبال داده‌ی مفقود و فهمیدن این که کدام قرائت جا افتاده، موارد فقدان داده را مستقیماً مدیریّت می‌کنیم.

زمانی که مقدارها را از فایل CSV می‌خوانیم کد بررسی خطا را اجرا می‌کنیم تا استثناهای احتمالی را مدیریت کنیم. این کار به این صورت است:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--snip--

filename = 'data/death_valley_2018_simple.csv'

with open(filename) as f:
    --snip--

    for row in reader:
        current_date = datetime.strptime(row[2], '%Y-%m-%d')
        try:
            high = int(row[4])
            low = int(row[5])
        except ValueError:
            print(f"Missing data for {current_date}")
        else:
            dates.append(current_date)
            highs.append(high)
            lows.append(low)

    # Plot the high and low temperatures.
    --snip--

    # Format plot.
    title = "Daily high and low temperatures - 2018\nDeath Valley, CA"
    plt.title(title, fontsize=20)
    plt.xlabel('', fontsize=16)
    --snip--

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

اگر اکنون death_valley_highs_lows.py را اجرا کنید خواهید دید که تنها یک داده از دست رفته:

1
Missing data for 2018-02-18 00:00:00

چون خطا به درستی مدیریّت شده کد ما قادر است که نموداری تولید کند و از داده‌ی مفقود بگذرد. شکل ۱۶-۶ نمودار حاصل را نشان می‌دهد.

شکل ۱۶-۶- دماهای بیشینه و کمینه‌ی روزانه برای دره‌ی مرگ

مقایسه‌ی این نمودار با نمودار سیتکا نشان می‌دهد همان‌طور که انتظار داشتیم که دره‌ی مرگ به طور کلی از جنوب شرق آلاسکا گرم‌تر است. علاوه بر این گستره‌ی دمایی هر روز در بیابان بزرگ‌تر است. ارتفاع ناحیه‌ی سایه خورده این موضوع را روشن می‌کند.

بسیاری از مجموعه‌داده‌های مورد نظر شما شامل داده‌های مفقود، داده‌هایی که شکل درست ندارند یا داده‌های نادرست‌اند. شما می‌توانید به کمک ابزارهایی که در نیمه‌ی اوّل این کتاب آموختید این شرایط را مدیریّت کنیم. اینجا از بلوک try-except-else برای رسیدگی به داده‌ی مفقود استفاده کردیم. گاهی شما از continue برای پریدن از روی برخی داده‌ها یا از remove() یا del() برای حذف کردن برخی از داده‌های استخراج‌شده استفاده می‌کنید. هر رویکردی را که کار می‌کند پی بگیرید تا زمانی که نتیجه‌ی آن بصری‌سازی دقیق و معنادار باشد.

9 - بارگذاری داده‌هایی برای خود

اگر می‌خواهید داده‌های هواشناسی خود را بارگذاری کنید این گام‌ها را دنبال کنید:

۱- از سایت داده‌های اقلیمی آنلاین NOAA را به آدرس https://www.ncdc.noaa.gov/cdo-web/ بازدید کنید. در بخش Discover Data By روی Search Tool کلیک کنید. در جعبه‌ی Select a Dataset گزینه‌ی Daily Summaries را برگزینید.

۲- بازه‌ی زمانی دلخواه را انتخاب کنید و در بخش Search For، ZIP Codes را انتخاب کنید. کد ZIP مورد نظر خود را وارد کنید و روی Search کلیک کنید.

۳- در صفحه‌ی بعد یک نقشه و اطّلاعاتی در مورد محل مورد نظر شما خواهید دید. پایین نام محل روی View Full Details کلیک کنید یا روی نقشه کلیک کرده و سپس Full Details را انتخاب کنید.

۴- پایین‌تر بروید و روی Station List کلیک کنید تا ایستگاه‌های هواشناسی موجود در آن منطقه را ببینید. یکی از آن‌ها را انتخاب کنید و روی Add to Cart کلیک کنید. هرچند این سایت از نمایه‌ی سبد خرید استفاده کرده است، امّا این داده‌ها رایگان است. روی سبد خرید در گوشه‌ی بالا سمت راست کلیک کنید.

۵- در Select the Output، گزینه‌ی Custom GHCN-Daily CSV را انتخاب کنید. مطمئین شوید که بازه‌ی تاریخ درست است و روی Continue کلیک کنید.

۶- در صفحه‌ی بعد می‌توانید انواع داده‌های مورد نیاز خود را انتخاب کنید. می‌توان تنها یک نوع داده، مثلاً با تمرکز بر دمای هوا را بگیرید یا کل داده‌های موجود یک ایستگاه را بارگذاری کنید. انتخاب‌های خود را انجام دهید و روی Continue کلیک کنید.

۷- در آخرین صفحه خلاصه‌ای از سفارش خود خواهید دید. آدرس ایمیل خود را وارد کرده و Submit Order را بزنید. تاییدیه‌ای دریافت می‌کنید که نشان می‌دهد سفارش دریافت شده و ظرف چند دقیقه ایمیل دیگری شامل پیوندی برای بارگذاری داده‌های خود خواهید گرفت.

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

خودآزمایی

۱۶-۱- بارندگی در سیتکا: سیتکا جنگلی بارانی و معتدل است بنابراین بارندگی در آن نسبتاً زیاد است. در فایل داده‌ی sitka_weather_2018_simple.csv عنوانی به نام PRCP دارد که بیانگر میزان بارش روزانه است. بصری‌سازی‌ای با تمرکز بر داده‌های این ستون درست کنید. اگر درباره‌ی کم بودن میزان بارندگی در بیابان کنجکاوید، می‌توانید تمرین را برای دره‌ی مرگ هم تکرار کنید.

۱۶-۲- مقایسه‌ی سیتکا و دره‌ی مرگ: مقیاس‌های دما در نمودارهای سیتکا و دره‌ی مرگ بازتاب‌دهنده‌ی تفاوت گسترده‌های دمایی است. برای مقایسه‌ی دقیق گستره‌ی دمایی بین سیتکا و دره‌ی مرگ به مقیاس‌های مشابه بر روی محور y نیاز دارید. تنظیمات محور y را در یکی یا هر دوی نمودارهای ۱۶-۵ و ۱۶-۶ تغییر دهید. سپس مقایسه‌ای مستقیم بین گستره‌ی دمایی سیتکا و دره‌ی مرگ (یا هر دو نقطه‌ی دیگر که می‌خواهید) انجام دهید.

۱۶-۳- سان فرانسیسکو: دماها در سان‌فرانسیسکو بیشتر به سیتکا شبیه است یا به دره‌ی مرگ؟ داده‌هایی در مورد سان‌فرانسیسکو بارگذاری کنید و یک نمودار بیشینه‌-کمینه‌ی دما برای آن تولید کنید تا بتوان مقایسه‌ای انجام داد.

۱۶-۴- شاخص‌های خودکار: در این بخش شاخص‌های مربوط به ستون‌های TMIN و TMAX را دستی تعیین کردیم. از سطر عنوان‌ها برای تعیین این شاخص‌ها برای مقدارها استفاده کنید تا برنامه‌ی شما برای هر دو جا، سیتکا یا دره‌ی مرگ، کار کند. از نام ایستگاه هواشناسی هم برای تولید خودکار عنوان مناسب برای نمودار خود استفاده کنید.

۱۶-۵- کند و کاو: چند بصری‌سازی دیگر بسازید تا دیگر جنبه‌های آب و هوایی مورد علاقه‌ی خود را در هر موقعیّتی که درباره‌ی آن کنجکاو هستید، بررسی کنید.

10 - رسم نقشه‌ی مجموعه‌ی داده‌های جهانی: قالب JSON

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

بارگذاری داده‌های زمین‌لرزه

فایل eq_1_day_m1.json را به پوشه‌ای منتقل کنید که در آن داده‌های این فصل را ذخیر132 ه کرده‌اید. زمین‌لرزه‌ها بر اساس بزرگی آن‌ها در مقیاس ریشتر دسته‌بندی می‌شوند. این فایل شامل داده‌های تمام زمین‌لرزه‌ها با بزرگی بزرگتر از M1 است که در ۲۴ ساعت گذشته رخ داده‌اند. (در زمان نگارش این متن) این داده از یکی منابع داده‌ی زمین‌لرزه مربوط به مطالعات زمین‌شناسی ایالات متحده آمده است که می‌توانید آن را در آدرس https://earthquake.usgs.gov/earthquakes/feed/ پیدا کنید.

بررسی داده‌ی JSON

زمانی که فایل eq_1_day_m1.json را باز می‌کنید خواهید دیدید که محتویّات آن بسیار فشرده و خواندن آن‌ها مشکل است:

1
2
3
4
5
6
7
{"type":"FeatureCollection","metadata":{"generated":1550361461000,...
{"type":"Feature","properties":{"mag":1.2,"place":"11km NNE of Nor...
{"type":"Feature","properties":{"mag":4.3,"place":"69km NNW of Ayn...
{"type":"Feature","properties":{"mag":3.6,"place":"126km SSE of Co...
{"type":"Feature","properties":{"mag":2.1,"place":"21km NNW of Teh...
{"type":"Feature","properties":{"mag":4,"place":"57km SSW of Kakto...
--snip--

این فایل بیشتر برای ماشین‌ها قالب‌بندی شده تا برای انسان‌ها. امّا می‌بینیم که این فایل شامل چند دیکشنری و همچنین اطّلاعاتی است که به آن‌ها علاقه‌مندیم مانند بزرگی زمین‌لرزه‌ها و موقعیّت‌های آن.

کار را با خواندن داده‌ها شروع می‌کنیم و آن‌ها را به شکلی نمایش می‌دهیم که راحت‌تر خوانده شوند. این فایل داده طولانی است پس به جای چاپ کردن، داده‌ی آن را در فایل جدیدی بازنویسی می‌کنیم. سپس می‌توانید این فایل باز کنیم و داده‌ها را به راحتی پیمایش کنیم.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import json

# Explore the structure of the data.
filename = 'data/eq_data_1_day_m1.json'

with open(filename) as f:
    all_eq_data = json.load(f)

readable_file = 'data/readable_eq_data.json'

with open(readable_file, 'w') as f:
    json.dump(all_eq_data, f, indent=4)

ابتدا ماژول json را وارد می‌کنیم تا داده‌ها را به درستی از فایل بخوانیم و کل این مجموعه‌داده را در all_eq_data ذخیره کنیم. تابع json.load() داده را به شکلی تبدیل می‌کند که پایتون بتواند با آن کار کند: در این مورد یک دیکشنری عظیم. سپس فایلی می‌سازیم که همان داده‌ها را به شکلی خواناتر در آن ذخیره کنیم. تابع json.dump() یک شیء داده‌ی JSON و یک شیء فایل دریافت می‌کند و داده را در فایل می‌نویسد. نشان‌وند indent=4 به تابع dump() می‌گوید که داده‌ها را با تورفتگی شکل دهد تا با ساختار داده جور دربیاید.

اکنون که در پوشه‌ی data فایل readable_eq_data.json را یافته و باز کنید، بخش اوّل آن را مانند زیر خواهید دید:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "type": "FeatureCollection",
    "metadata": {
        "generated": 1550361461000,
        "url": "https://earthquake.usgs.gov/earthquakes/.../1.0_day.geojson",
        "title": "USGS Magnitude 1.0+ Earthquakes, Past Day",
        "status": 200,
        "api": "1.7.0",
        "count": 158
    },
    "features": [
--snip--

بخش اوّل فایل قسمتی را با کلید "metadata" دربرمی‌گیرد. این قسمت به ما می‌گوید که فایل داده چه زمانی تولید شده و کجا می‌توانیم آن را به صورت آنلاین پیدا کنیم. همچنین عنوانی قابل خواندن و تعداد زمین‌لرزه‌های موجود در فایل هم در این بخش مشخّص می‌شود. در یک دوره‌ی ۲۴ ساعته ۱۵۸ زمین‌لرزه ثبت شده است.

این فایل geoJSON ساختاری دارد که برای داده‌های موقعیّت‌محور سودمند است. اطّلاعات در لیستی با کلید "features" ذخیره شده است. از آن‌جایی که این فایل حاوی داده‌های زمین‌لرزه‌ای است، داده‌های آن به شکل لیست آمده‌اند و هر عضو لیست مربوط به یک زمین‌لرزه است. این ساختار ممکن است گیج‌کننده به نظر بیاید امّا بسیار قدرتمند است و به زمین‌شناسان امکان می‌دهد که هر تعداد اطّلاعات که بخواهید در قالب یک دیکشنری برای هر زمین‌لرزه ذخیره کنند و سپس تمام دیکشنری‌ها را در یک لیست بزرگ قرار دهند.

بیایید به یکی از دیکشنری‌های مربوط به یک زمین‌لرزه نگاهی بیندازیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--snip--
{
    "type": "Feature",
    "properties": {
        "mag": 0.96,
        --snip--
        "title": "M 1.0 - 8km NE of Aguanga, CA"
    },
    "geometry": {
        "type": "Point",
        "coordinates": [
            -116.7941667,
            33.4863333,
            3.22
        ]
    },
    "id": "ci37532978"
},

کلید "properties" اطّلاعات بسیاری درباره‌ی هر زمین‌لرزه دارد. ما بیشتر به بزرگی هر زمین‌لرزه علاقه‌مندیم که در کلید مربوط به آن، "mag" قرار دارد. همچنین عنوان هر زمین‌لرزه هم برای ما جالب است که شامل خلاصه‌ای زیبا از بزرگی و موقعیّت آن است.

کلید "geometry" به ما کمک می‌کند که محل وقوع زمین‌لرزه را پیدا کنیم. این اطّلاعات برای رسم هر رخداد ضروری است. می‌توانیم طول و عرض جغرافیایی هر زمین‌لرزه را در لیستی با کلید "coordinates" پیدا کنیم.

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

نکته
زمانی که درباره‌ی موقعیت‌ها صحبت می‌کنیم غالباً عرض جغرافیایی را ابتدا و سپس طول جغرافیایی را می‌آوریم. این قرارداد احتمالاً به این دلیل به وجود آمده که انسان‌ها عرض جغرافیایی را بسیار پیش‌تر از مفهوم طول جغرافیایی توسعه داده‌اند. با این حال بسیاری از چارچوب‌های جغرافیایی ابتدا طول و سپس عرض جغرافیایی را می‌آورند تا با قرارداد (x,y) در بیان‌های ریاضیاتی متناظر باشد. قالب geoJSON از قرارداد (طول، عرض) پیروی می‌کند و اگر شما از چارچوب دیگری استفاده می‌کنید مهم است که قرارداد مورد استفاده‌ی آن را یاد بگیرید.

11 - ساختن لیست تمام زمین‌لرزه‌ها

ابتدا لیستی می‌سازیم که شامل تمام اطّلاعات هر زمین‌لرزه‌ی رخ‌داده باشد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import json

# Explore the structure of the data.
filename = 'data/eq_data_1_day_m1.json'

with open(filename) as f:
    all_eq_data = json.load(f)

all_eq_dicts = all_eq_data['features']

print(len(all_eq_dicts))

ما داده‌های متناظر با کلید 'features' را گرفته و همه را در all_eq_dicts ذخیره می‌کنیم. می‌دانیم که این فایل داده‌های ۱۵۸ زمین‌لرزه را در بر گرفته و خروجی تایید می‌کند که داده‌ی تمام زمین‌لرزه‌های داخل فایل را گرفته‌ایم:

1
158

توجّه کنید که این کد چه‌قدر کوتاه است. فایل readable_eq_data.json که آن را شکلی مرتّب درآوردیم بیشتر از ۶۰۰۰ خط است. امّا توانستیم در تنها چند خط کد تمام آن داده‌ها را خوانده و در یک لیست پایتون ذخیره کنیم. در ادامه بزرگی هر زمین‌لرزه را بیرون می‌کشیم.

استخراج بزرگی زمین‌لرزه‌ها

می‌توانیم در لیستی که شامل داده‌های هر زمین‌لرزه است، حلقه بزنیم و هر اطّلاعاتی را که بخواهیم استخراج کنیم. اکنون بزرگی هر زمین‌لرزه را بیرون می‌کشیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--snip--

all_eq_dicts = all_eq_data['features']

mags = []
for eq_dict in all_eq_dicts:
    mag = eq_dict['properties']['mag']
    mags.append(mag)

print(mags[:10])

لیستی خالی برای ذخیره کردن بزرگی‌ها می‌سازیم و سپس در دیکشنری all_eq_dicts حلقه می‌زنیم. داخل این حلقه هر زلزله با دیکشنری eq_dict نمایش داده می‌شود. بزرگی هر زلزله در بخش 'properties' دیکشنری ذیل کلید 'mag' ذخیره شده است. هر بزرگی را در متغیّر mag ذخیره و سپس آن را به لیست mags الحاق می‌کنیم.

۱۰ بزرگی اوّل را چاپ می‌کنیم تا ببینیم که داده‌های صحیح را گرفته‌ایم یا نه:

1
[0.96, 1.2, 4.3, 3.6, 2.1, 4, 1.06, 2.3, 4.9, 1.8]

در ادامه داده‌های موقعیّت هر زمین‌لرزه را به دست می‌آوریم تا پس از آن بتوانیم نقشه‌ی زمین‌لرزه‌ها را بسازیم.

استخراج داده‌ی موقعیّت

داده‌ی موقعیّت ذیل کلید "geometry" ذخیره شده است. داخل این دیکشنری کلید "coordinates" وجود دارد و دو مقدار اوّل این لیست طول و عرض جغرافیایی‌ست.

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

all_eq_dicts = all_eq_data['features']

mags, lons, lats = [], [], []
for eq_dict in all_eq_dicts:
    mag = eq_dict['properties']['mag']
    lon = eq_dict['geometry']['coordinates'][0]
    lat = eq_dict['geometry']['coordinates'][1]
    mags.append(mag)
    lons.append(lon)
    lats.append(lat)

print(mags[:10])
print(lons[:5])
print(lats[:5])

دو لیست خالی برای طول و عرض جغرافیایی می‌سازیم. کد eq_dict['geometry'] به دیکشنری‌ای دست می‌یابد که بیانگر عنصر هندسی زمین‌لرزه است. کلید دوم، 'coordinates' لیست مقدارهای مرتبط با 'coordinates' را بیرون می‌کشد. سرانجام شاخص صفرم اوّلین مقدار در لیست مختصّات را درخواست می‌کند که متناظر با طول جغرافیایی زمین‌لرزه است.

زمانی که پنج طول و عرض جغرافیایی اوّل را چاپ می‌کنیم خروجی نشان از استخراج صحیح داده‌ها دارد:

1
2
3
4
5
[0.96, 1.2, 4.3, 3.6, 2.1, 4, 1.06, 2.3, 4.9, 1.8]

[-116.7941667, -148.9865, -74.2343, -161.6801, -118.5316667]

[33.4863333, 64.6673, -12.1025, 54.2232, 35.3098333]

با این داده‌ها می‌توانیم به سمت نگاشت هر زمین‌لرزه حرکت کنیم.

12 - ساختن یک نقشه‌ی جهان

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

این نقشه‌ی اوّلیّه است:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import json
from plotly.graph_objs import Scattergeo, Layout
from plotly import offline

--snip--

for eq_dict in all_eq_dicts:
    --snip--

# Map the earthquakes.
data = [Scattergeo(lon=lons, lat=lats)]

my_layout = Layout(title='Global Earthquakes')

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

نمودار Scattergeo و کلاس Layout و سپس ماژول offline را برای ساختن نقشه وارد می‌کنیم. مشابه کاری که برای ساختن نمودار میله‌ای کردیم، لیستی به نام data تعریف می‌کنیم. داخل این لیست شیء Scattergeo را می‌سازیم زیرا شما می‌توانید در هر بصری‌سازی بیش از یک مجموعه‌داده را رسم کنید. نمودار از نوع Scattergeo به شما امکان می‌دهد که نمودار پراکندگی داده‌های جغرافیایی را روی یک نقشه بیندازید. این ساده‌ترین روش استفاده از این نوع نمودار است، کافی‌ست شما لیستی از طول‌ها و لیستی از عرض‌های جغرافیایی را ارائه کنید.

به نمودار عنوانی مناسب می‌دهیم و یک دیکشنری به نام fig شامل داده و چیدمان آن می‌سازیم. در نهایت fig را به همراه یک نام فایل گویا به plot() می‌دهیم. زمانی که این فایل را اجرا می‌کنید باید نقشه‌ای مشابه شکل ۱۶-۷ ببینید. زمین‌لرزه‌ها معمولاً در نزدیکی مرز صفحه‌های زمین رخ می‌دهند و این با آن‌چه در نمودار می‌بینیم مطابقت دارد.

شکل ۱۶-۷: نقشه‌ی ساده‌ای که محلّ وقوع تمام زمین‌لرزه‌های ۲۴ ساعت گذشته را نشان می‌دهد.

تغییرات بسیاری برای معنادار و خواناتر کردن این نمودار می‌توان انجام داد پس اجازه دهید برخی از آن‌ها را پیاده کنیم.

روش دیگر تعیین داده‌های نمودار

پیش از تنظیم نمودار بیایید نگاهی به روش کمی متفاوتی برای تعیین داده‌های نمودارهای پلات‌لی بیندازیم. در نمودار فعلی لیست data در یک خط تعریف می‌شود:

1
data = [Scattergeo(lon=lons, lat=lats)]

این یکی از آسان‌ترین راه‌ها برای تعریف داده‌های یک نمودار در پلات‌لی است. امّا وقتی بخواهید ارائه‌ی خود را شخصی کنید این بهترین روش نیست. روشی معادل برای تعریف داده‌ها برای نمودار فعلی به شکل زیر است:

1
2
3
4
5
data = [{
    'type': 'scattergeo',
    'lon': lons,
    'lat': lats,
}]

در این رویکرد دانسته‌ها در مورد داده‌ها به صورت جفت‌های کلید-مقدار در یک دیکشنری درمی‌آیند. اگر این کد را در eq_plot.py قرار دهید خواهید دید که نمودار مشابهی حاصل خواهد شد. این شکل تعریف به ما امکان می‌دهد که شخصی‌سازی‌ها را نسبت به حالت قبلی آسان‌تر تعیین کنیم.

13 - شخصی‌سازی اندازه‌ی نشان‌گرها

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

برای انجام این کار اندازه‌ی نشان‌گرها را بسته به بزرگی هر زمین‌لرزه تغییر می‌دهیم:

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

# Map the earthquakes.
data = [{
    'type': 'scattergeo',
    'lon': lons,
    'lat': lats,
    'marker': {
        'size': [5*mag for mag in mags],
    },
}]

my_layout = Layout(title='Global Earthquakes')

--snip--

پلات‌لی طیف گسترده‌ای از شخصی‌سازی‌ها را برای یک دنباله‌ی داده فراهم می‌کند که هر کدام را می‌توان به صورت یک جفت کلید-مقدار بیان کرد. اینجا از کلید 'marker' استفاده می‌کنیم تا میزان بزرگی هر نشان‌گر را روی نقشه مشخّص کنیم. ما از دیکشنری تودرتو به عنوان مقدار مرتبط با 'marker' استفاده می‌کنیم چون می‌توان تعدادی تنظیمات را همزمان برای تمام نشان‌گرهای نقشه تعیین کرد.

هدف این است که اندازه‌ی نشان‌گر متناسب با بزرگی از زمین‌لرزه باشد. امّا اگر تنها لیست mag را ملاک قرار دهیم نشان‌گرها بسیار کوچک خواهند شد و تشخیص تفاوت آن‌ها مشکل است. باید بزرگی را در ضریب مقیاسی ضرب کنیم تا به اندازه‌ی مطلوب نشان‌گر برسیم. در صفحه نمایش من مقدار ۵ به خوبی کار می‌کند؛ مقداری کمی کوچک‌تر یا بزرگتر ممکن است برای نقشه‌ی شما نتیجه‌ی مطلوب‌تر بدهد. ما از دربرداری لیست استفاده می‌کنیم که اندازه‌ی مناسب نشان‌گر را برای هر مقدار در لیست mags تولید می‌کند.

زمانی که این کد را اجرا می‌کنید باید نقشه‌ای شبیه شکل ۱۶-۸ ببینید. این نقشه به مراتب بهتر است امّا هنوز کارهای بیشتری برای انجام مانده است.

شکل ۱۶-۸- اکنون نقشه بزرگی هر زمین‌لرزه را نشان می‌دهد.

شخصی‌سازی رنگ نشان‌گرها

ما همچنین می‌توانیم رنگ هر نشان‌گر را برای ارائه‌ی برخی دسته‌بندی‌ها در مورد شدّت هر زمین‌لرزه تغییر دهیم. برای این کار از مقیاس‌های رنگی پلات‌لی استفاده می‌کنیم. پیش از هر تغییری فایل eq_data_30_day_m1.json را به پوشه‌ی داده‌ی خود کپی کنید. این فایل شامل داده‌های زمین‌لرزه‌ای یک دوره‌ی ۳۰ روزه است و نقشه با وجود این مجموعه‌داده‌ی بزرگ‌تر جالب‌تر به نظر خواهد رسید.

این روش استفاده از مقیاس رنگی برای نمایش بزرگی هر زمین‌لرزه است:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
--snip--

filename = 'data/eq_data_30_day_m1.json'

--snip--

# Map the earthquakes.
data = [{
    --snip--

    'marker': {
        'size': [5*mag for mag in mags],
        'color': mags,
        'colorscale': 'Viridis',
        'reversescale': True,
        'colorbar': {'title': 'Magnitude'},
    },
}]

--snip--

مطمئن شوید که نام فایل را به‌روز می‌کنید تا از مجموعه‌داده‌ی ۳۰روزه استفاده کنید. اینجا تمام تغییرات مهم در دیکشنری 'marker' رخ می‌دهد چون تنها می‌خواهیم ظاهر نشان‌گرها را تغییر دهیم. تنظیمات 'color' به پلات‌لی می‌گوید که باید از چه مقدارهایی استفاده کند تا محل قرارگیری هر نشان‌گر در مقیاس رنگی تعیین شود. از لیست mags برای تعیین رنگی که استفاده شده است استفاده می‌کنیم. تنظیمات 'colorscale' به پلات‌لی می‌گوید که از کدام گستره‌ی رنگی استفاده کند: 'Viridis' مقیاسی رنگی است که آبی تیره تا زرد روشن تغییر می‌کند و برای چنین مجموعه‌داده‌ای کارآمد است. 'reversescale' را برابر True می‌گذاریم زیرا می‌خواهیم زرد روشن را برای کمترین مقدارها و آبی تیره را برای شدیدترین زمین‌لرزه‌ها استفاده کنیم. تنظیمات 'colorbar' به ما امکان کنترل ظاهر مقیاس رنگی نمایش‌داده‌شده در کنار نقشه را می‌دهد. این عنوان مقیاس رنگی را 'Magnitude' قرار می‌دهید تا آن‌چه را رنگ‌ها بیان می‌کنند شفّاف کنیم.

اگر اکنون برنامه را اجرا کنید نقشه‌ای بسیار زیباتر133 خواهید دید. در شکل ۱۶-۹ مقیاس رنگی شدت یک‌یک زمین‌لرزه‌ها را نشان می‌دهد. رسم این تعداد زمین‌لرزه به طور واضح نشان می‌دهد که مرزهای صفحه‌های تکتونیکی کجاست!

شکل ۱۶-۹: در زمین‌لرزه‌های ۳۰ روزه رنگ و اندازه برای نمایش بزرگی هر زمین‌لرزه به کار می‌رود.

14 - دیگر مقیاس‌های رنگی

شما می‌توانید از تعدادی مقیاس رنگی دیگر نیز استفاده کنید. برای دیدن مقیاس‌های موجود برنامه‌ی کوتاه زیر را به عنوان show_color_scales.py ذخیره کنید:

1
2
3
4
from plotly import colors

for key in colors.PLOTLY_SCALES.keys():
    print(key)

پلات‌لی مقیاس‌های رنگی را در ماژول colors ذخیره می‌کند. این مقیاس‌ها در دیکشنری PLOTLY_SCALES تعریف می‌شوند و نام آن‌ها به عنوان کلید این دیکشنری‌ها می‌آیند. این خروجی شامل تمام مقیاس‌های رنگی موجود است:

1
2
3
4
5
Greys
YlGnBu
Greens
--snip--
Viridis

این مقیاس‌ها را امتحان کنید و به یاد داشته باشید که می‌توانید آن‌ها را با تنظیم reversescale وارونه کنید.

نکته
اگر دیکشنری PLOTLY_SCALES را چاپ کنید می‌بینید که مقیاس‌های رنگی چگونه تعریف شده‌اند. هر مقیاس یک رنگ آغازین و یک رنگ پایانی دارد و برخی از آن‌ها یک یا چند رنگ میانی نیز دارند. پلات‌لی سایه‌های بین هر کدام از این رنگ‌ها را درون‌یابی می‌کند.

تعیین متن شناور

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

برای ایجاد چنین تغییری اطّلاعات بیشتری از فایل بیرون می‌کشیم و آن‌ها را به دیکشنری data اضافه می‌کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
--snip--

mags, lons, lats, hover_texts = [], [], [], []

for eq_dict in all_eq_dicts:
    --snip--

    lat = eq_dict['geometry']['coordinates'][1]
    title = eq_dict['properties']['title']
    mags.append(mag)
    lons.append(lon)
    lats.append(lat)
    hover_texts.append(title)

--snip--

# Map the earthquakes.
data = [{
    'type': 'scattergeo',
    'lon': lons,
    'lat': lats,
    'text': hover_texts,
    'marker': {
        --snip--
    },
}]

--snip--

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

سپس کلید 'text' را به شیء data اضافه می‌کنیم، پلات‌لی از این مقدار به عنوان برچسب هر نشان‌گر استفاده می‌کند که هنگام ایستادن روی آن نمایش داده می‌شود. زمانی که لیستی که طول آن به تعداد نشان‌گر می‌خورد، ارائه کنیم پلات‌لی هر برچسب را به یک نشان‌وند نسبت می‌دهد. زمانی که این برنامه را اجرا کنید باید بتوانید روی هر نشان‌گر بایستید و شرحی از محل وقوع زمین‌لرزه و بزرگی دقیق آن ببینید.

این تاثیرگذار است! در تقریباً ۴۰ خط کد نقشه‌ای زیبا و پرمعنی از فعالیّت‌های زمین‌لرزه‌ای جهانی ساختیم که ساختار زمین‌شناختی سیّاره را هم به تصویر می‌کشد. پلات‌لی چندین راه برای شخصی‌سازی ظاهر و رفتار بصری‌سازی‌ها به شما ارائه می‌کند. با استفاده از این گزینه‌های پلات‌لی می‌توانید جدول‌ها و نقشه‌هایی بسازید که دقیقاً مطابق با خواسته‌های شما هستند.

15 - خودآزمایی

۱۶-۶- بازآرایی: حلقه‌ای که داده‌ها را از all_eq_dicts بیرون می‌کشد پیش از الحاق موارد به لیست‌های متناظر، از متغیّرهایی برای بزرگی، طول و عرض جغرافیایی و عنوان هر زمین‌لرزه استفاده می‌کند. این رویکرد به این دلیل انتخاب شده که نحوه‌ی استخراج داده از فایل JSON شفّاف باشد امّا این در کد شما الزامی نیست. به جای استفاده از این متغیّرهای موقّت، هر مقدار را از eq_dict بیرون بکشید و آن را در یک خط به لیست متناظر اضافه کنید. این کار باعث می‌شود کل بدنه‌ی حلقه به چهار خط کاهش یابد.

۱۶-۷- عنوان خودکار: در این بخش هنگام تعریف my_layout عنوان را به صورت دستی تعیین کردیم که نشان می‌دهد باید با134 هر بار تغییر فایل منبع عنوان را نیز عوض کنیم. به جای این می‌توانید از عنوان مجموعه‌داده استفاده کنید که در بخش فراداده‌ی فایل JSON آمده است. این مقدار را بیرون بکشید، به متغیّری نسبت دهید و آن را به عنوان عنوان نقشه هنگام تعریف my_layout به کار ببرید.

۱۶-۸- زمین‌لرزه‌های اخیر: شما می‌توانید فایل‌های داده‌ی حاوی اطّلاعات را در مورد جدیدترین زمین‌لرزه‌ها در بازه‌های ۱ ساعته، ۱ روزه، ۷ روزه و ۳۰ روزه به صورت آنلاین پیدا کنید. به https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php بروید. در اینجا لیستی از پیوندها برای مجموعه‌داده‌ها در بازه‌های زمانی مختلف خواهید دید که برای زمین‌لرزه‌ها با بزرگی‌های متفاوت تمرکز دارد. یکی از این مجموعه‌داده‌ها را بگیرید و بصری‌سازی‌ای در مورد جدیدترین فعّالیّت‌های زمین‌لرزه‌ای بسازید.

۱۶-۹- آتش‌سوزی‌های جهانی: در منابع این فصل فایلی به نام world_fires_1_day.csv خواهید یافت که شامل اطّلاعاتی در مورد آتش‌سوزی‌های نقاط مختلف کره‌ی زمین است و طول و عرض جغرافیایی و شدّت آن را نیز شامل می‌شود. به کمک کارهای مربوط به پردازش داده در بخش اوّل این فصل و نقشه‌هایی که در این بخش ساختیم، نقشه‌ای درست کنید که نشان دهد کدام بخش جهان تحت تاثیر آتش‌سوزی قرار گرفته است.

می‌توانید نسخه‌های جدیدتری از این داده را از https://earthdata.nasa.gov/earth-observation-data/near-real-time/firms/active-fire-data/ دریافت کنید. می‌توانید پیوندهایی به داده‌های به شکل CSV در بخش TXT پیدا کنید.

16 - خلاصه

در این فصل کار کردن با مجموعه‌داده‌های دنیای واقعی را آموختید. فایل‌های CSV و JSON را پردازش و داده‌های مورد نظر خود را از آن‌ها استخراج کردید. با استفاده از داده‌های تاریخی آب و هوا درباره‌ی کار کردن با Matplotlib بیشتر آموختید، مانند چگونگی کار با ماژول datetime و نحوه‌ی رسم چند دنباله‌ی داده بر روی یک نمودار. شما در پلات‌لی داده‌های جغرافیایی را روی نقشه‌ی جهان رسم کردید و نقشه‌ها و نمودارهای پلات‌لی را نیز شکل دادیم.

همچنان‌که در کار با فایل‌های CSV و JSON تبحّر پیدا می‌کنید قادر خواهید بود که هر نوع داده‌ای که می‌خواهید پردازش کنید. می‌توانید بیشتر داده‌های آنلاین را به یکی از این دو شکل یا هر دوی آن دریافت کنید. با کار کردن با این قالب‌ها خواهید آموخت که چگونه با دیگر اشکال داده نیز کار کنید.

در فصل آینده برنامه‌هایی می‌نویسید که داده‌های خود را به طور خودکار از منابع اینترنتی دریافت می‌کنند و سپس از این داده‌ها بصری‌سازی می‌سازید. اگر برنامه‌نویسی را به عنوان تفریح در نظر بگیرید این مهارت‌ها سرگرم‌کننده خواهند بود و اگر علاقه‌مند به برنامه‌نویسی حرفه‌ای باشید این‌ها مهارت‌هایی حیاتی هستند.