در فرایند «تحلیل داده‌ها» (Data Analytics) و استخراج دانش از آن‌ها، نیاز به پیاده‌سازی راهکارها با بهره‌گیری از ابزارهای کامپیوتری است. یکی از روش‌های متداولی که در این راستا و برای پیاده‌سازی مدل مورد استفاده قرار می‌گیرد، «زبان برنامه‌نویسی پایتون» (Python Programming Language) است. «نرمان نیمر» (Norman Niemer)، یک «دانشمند داده ارشد» (Senior Data Scientist) محسوب می‌شود که به دلیل کدهای پایتونی که تاکنون توسعه داده و تعداد زیاد دانشمندان داده تازه‌کاری که با او کار کرده‌اند، جزو ٪۱ برتر «استک‌اورفلو» (Stack Overflow) به شمار می‌آید. در این مطلب، نرمان نیمار به بیان اشتباهات رایج دانشمندان داده (به ویژه تازه‌کار)، که طی سال‌های فعالیت خود با آن‌ها مواجه شده، می‌پردازد.

یک «دانشمند داده» (Data Scientist)، شخصی است که نسبت به مهندس‌های کامپیوتر دانش آماری بسیار بهتر و نسبت به آماردان‌ها، مهارت‌های مهندسی کامپیوتری بالاتری دارد. بسیاری از دانشمندان داده، دارای پیش‌زمینه آماری و تجربیات کمی در مهندسی کامپیوتر هستند.

من (نرمان نیمر) یک دانشمند هستم که به خاطر کدهای پایتونم و و کار کردن با دانشمندان داده تازه کار بسیار متعدد، در لیست ٪۱ برتر «استک‌اورفلو» (Stack Overflow) قرار گرفته‌ام. در ادامه، لیستی از ده اشتباه متداول که معمولا آن‌ها را در کدهای دانشمندان داده مشاهده می‌کنم، بیان کرده‌ام.

۱. عدم به اشتراک‌گذاری داده‌های استفاده شده

در پیاده‌سازی راهکار برای حل مسائل «علم داده» (Data Science)، نیاز به کد پیاده‌سازی و مجموعه داده‌ای است که مورد استفاده قرار می‌گیرد (و در واقع، مساله حول محور آن است و پیاده‌سازی روی آن انجام می‌شود). بدین شکل، افراد دیگر نیز قادر خواهند بود نتایجی که تولید شده‌اند را بازتولید کرده و یا در صورت نیاز، برای آن داده‌ها و مساله، به دنبال راهکارهای بهتری باشند.

این یعنی ممکن است افراد دیگری نیز به داده‌هایی که در کد و در واقع پیاده‌سازی مورد استفاده قرار گرفته‌اند، نیاز داشته باشند. اما، متاسفانه بسیاری از افراد فراموش می‌کنند که مجموعه داده‌ای را که از آن استفاده کرده‌اند را همراه با کدهایشان منتشر کنند.

import pandas as pd
df1 = pd.read_csv('file-i-dont-have.csv') # fails
do_stuff(df)

راهکار: برای حل این مساله، می‌توان از کتابخانه پایتون d6tpipe و یا سرویس‌های ابری مانند Amazon S3 و گوگل درایو برای به اشتراک‌گذاری فایل‌های داده‌ها - همراه با کد - استفاده شود. همچنین، می‌توان از پایگاه داده استفاده کرد تا دریافت‌کننده بتواند فایل‌ها را بازیابی کند (اما توصیه می‌شود آن‌ها را به گیت اضافه نکنند. دلیل این امر در ادامه توضیح داده شده است).

۲. مسیرهای غیرقابل دسترس در هاردکد

همچون اشتباهی که در بالا بیان شد، اگر فرد «مسیرهایی» (Path) که افراد دیگر به آن دسترسی ندارند را به صورت «هاردکد» (hardcode) بنویسد، سایرین نمی‌توانند کد را اجرا کنند و بنابراین، باید مکان‌های زیادی را جستجو کنند تا این مسیرها را به صورت دستی تغییر دهند.

import pandas as pd
df = pd.read_csv('/path/i-dont/have/data.csv') # fails
do_stuff(df)

# or 
import os
os.chdir('c:\\Users\\yourname\\desktop\\python') # fails

راهکار: استفاده از مسیرهای مرتبط، «متغیرهای پیکربندی مسیر سراسری» (global path config variables) و یا d6tpipe برای آنکه داده‌ها به سادگی در دسترس قرار بگیرند.

۳. عدم سازمان‌دهی فایل‌ها

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

این امر، موجب می‌شود تا یک شلختگی اساسی به وقوع بپیوندد.

├── data.csv
├── ingest.py
├── other-data.csv
├── output.png
├── report.html
└── run.py

راهکار: دایرکتوری‌ها را باید در دسته‌هایی مانند داده‌ها، گزارش‌ها، کد و دیگر موارد سازمان‌دهی کرد. در این راستا، می‌توان از قالب‌های پروژه Cookiecutter Data Science یا d6tflow (بند ۵ مشاهده شود) و ابزارهای بیان شده در بند ۱ برای ذخیره‌سازی و به اشتراک‌گذاری داده‌ها استفاده کرد.

۴. قرار دادن مجموعه داده در گیت

در حال حاضر، اغلب افراد از «کنترل نسخه» (Version Control) برای کدهای خود استفاده می‌کنند. به منظور به اشتراک‌گذاری مجموعه داده مورد استفاده، افراد ممکن است تصمیم بگیرند که مجموعه داده خود را نیز در گیت قرار دهند.

این کار برای فایل‌های کوچک بی‌اشکال است، اما گیت برای قرار دادن داده به ویژه فایل‌های خیلی بزرگ بهینه‌سازی نشده است.

git add data.csv

راهکار: از ابزارهای بیان شده در بند ۱ برای ذخیره‌سازی و به اشتراک‌گذاری فایل‌ها استفاده کنید. برای انجام کنترل نسخه واقعی روی داده‌ها، می‌توان از مواردی مانند d6tpipe ،DVC و Git Large File Storage استفاده کرد.

۵. نوشتن تابع به جای DAG

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

def process_data(data, parameter):
    data = do_stuff(data)
    data.to_pickle('data.pkl')

data = pd.read_csv('data.csv')
process_data(data)
df_train = pd.read_pickle(df_train)
model = sklearn.svm.SVC()
model.fit(df_train.iloc[:,:-1], df_train['y'])

راهکار: به جای توابع خطی که به صورت زنجیروار به یکدیگر متصل شده‌اند، کدهای مورد استفاده در حوزه علم داده، بهتر است به صورت مجموعه‌ای از وظایف با وابستگی‌های بین آن‌ها نوشته شود. در این راستا، می‌توان از d6tflow یا airflow استفاده کرد.

6. نوشتن حلقه‌های for

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

x = range(10)
avg = sum(x)/len(x); std = math.sqrt(sum((i-avg)**2 for i in x)/len(x));
zscore = [(i-avg)/std for x]
# should be: scipy.stats.zscore(x)

# or
groupavg = []
for i in df['g'].unique():
	dfg = df[df[g']==i]
	groupavg.append(dfg['g'].mean())
# should be: df.groupby('g').mean()

راهکار: کتابخانه‌های «نام‌پای» (NumPy)، «سای‌پای» (scipy) و «پانداس» (Pandas) برای اغلب مواردی که کاربر ممکن است فکر کند به حلقه for نیاز دارد، دارای توابع برداری شده هستند.

۷. ننوشتن تست واحد

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

راهکار: از دستورات assert برای بررسی کیفیت داده‌ها استفاده شود. کتابخانه Pandas دارای تست‌های کیفیت، d6tstack چک کردن برای دریافت داده و d6tjoin برای اتصالات داده است. کد مثال زیر، برای چک کردن داده‌ها است.

assert df['id'].unique().shape[0] == len(ids) # have data for all ids?
assert df.isna().sum()<0.9 # catch missing values
assert df.groupby(['g','date']).size().max() ==1 # no duplicate values/date?
assert d6tjoin.utils.PreJoin([df1,df2],['id','date']).is_all_matched() # all ids matched?

۸. مستندسازی کدها

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

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

def some_complicated_function(data):
	data = data[data['column']!='wrong']
	data = data.groupby('date').apply(lambda x: complicated_stuff(x))
	data = data[data['value']<0.9]
	return data

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

۹. ذخیره‌سازی داده‌ها به صورت csv یا pickle

با توجه به زمینه‌ای (علم داده) که برنامه‌نویسی در آن صورت می‌گیرد، علاوه بر توابع و حلقه‌ها و چنین مواردی، استفاده از فایل‌های CSV و pickle ضمن کار بسیار متداول است. اما این کار خیلی خوب نیست. CSV‌ها دارای Schema نیستند، بنابراین همه باید اعداد و تاریخ‌ها را تجزیه کنند. Pickle‌ها مساله را حل می‌کنند اما متاسفانه فقط در پایتون کار می‌کنند و فشرده نیستند. هیچ یک از دو مورد، قالب‌های خوبی برای ذخیره‌سازی مجموعه داده‌های بزرگ محسوب نمی‌شوند.

def process_data(data, parameter):
    data = do_stuff(data)
    data.to_pickle('data.pkl')

data = pd.read_csv('data.csv')
process_data(data)
df_train = pd.read_pickle(df_train)

راهکار: استفاده از فرمت ذخیره‌سازی داده ستون‌محور، آزاد و متن‌باز Apache Parquet که دارای شمای داده‌ها است و داده‌ها را فشرده می‌کند. d6tflow به طور خودکار خروجی‌های داده وظایف را به صورت parquet ذخیره می‌کند، بنابراین نیاز به کلنجار رفتن با آن نیست.

۱۰. استفاده از نوت‌بوک ژوپیتر

اما در نهایت می‌توان یک نتیجه بحث برانگیز داشت: استفاده از «ژوپیتر نوت‌بوک» (Jupyter Notebook) به اندازه استفاده از فایل‌های CSV متداول است. افراد زیادی از این نرم‌افزار کاربردی وب آن استفاده می‌کنند.

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

  1. فرد وسوسه می‌شود که همه فایل‌های خود را در یک مسیر قرار دهد.
  2. فرد کدهایی می‌نویسد که به صورت بالا به پایین کار می‌کنند، به جای آنکه به صورت DAG باشند.
  3. فرد کد خود را ماژولار نمی‌کند.
  4. خطایابی کدها دشوارتر است.
  5. کد و خروجی آن در یک فایل ترکیب می‌شوند.
  6. به خوبی کنترل نسخه ندارند.

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