با رشد مجموعههای داده از هزاران ردیف قابل مدیریت به میلیونها یا حتی میلیارد، رویکردهای ساده دستکاری داده در پایتون تحت وزن خود فرو میریزند. اگرچه کتابخانه پانداس سنگ بنای علم داده است، اما به طور ذاتی برای هر نوع عملیات سنگین بهینه نشده است. تفاوت بین اسکریپتی که در چند ثانیه اجرا میشود و آنکه ساعتها طول میکشد، اغلب در رویکرد بنیادین به تبدیل داده نهفته است. به طور خاص، بهرهگیری از برداریسازی—انجام عملیات روی کل آرایهها به جای تکرار ردیف به ردیف—تک تغییر تأثیرگذارترین است که یک توسعهدهنده متوسط میتواند برای بهبود کارایی کد انجام دهد.
هزینه تکرار: چرا apply همیشه پاسخ نیست
یک دام رایج برای توسعهدهندگانی که از R یا SQL به پایتون مهاجرت میکنند، استفاده بیش از حد از روش DataFrame.apply() است. اگرچه apply یک رابط کاربری راحت با ظاهر برداری ارائه میدهد، اما در واقعیت از طریق ردیفها یا ستونها در بایتکد پایتون حلقه میزند. این موضوع مزایای بهینهسازیهای مبتنی بر C در NumPy را خنثی میکند. برای تبدیلهای مقیاس بزرگ، جایگزینی apply با روشهای برداریسازی بومی پانداس یا توابع NumPy میتواند منجر به افزایش سرعت بین ۱۰ تا ۱۰۰ برابر شود.
سناریویی را در نظر بگیرید که نیاز دارید یک ستون متنی را با حذف زیررشتههای خاص تمیز کنید. یک رویکرد غیر برداری ممکن است به این شکل باشد:
def clean_text(row):
return row['comment'].replace('bad_word', 'good_word')
df['clean_comment'] = df.apply(clean_text, axis=1) # Slow!
به جای آن، باید از دسترسیگر رشتهای داخلی پانداس استفاده کنیم که در Cython پیادهسازی شده و روی کل ستون به صورت همزمان عمل میکند:
# Fast Vectorized Approach
df['clean_comment'] = df['comment'].str.replace('bad_word', 'good_word')
این تغییر ساده، هزینه بالای حلقه در سطح پایتون را حذف میکند و اجازه میدهد عملیات با سرعتی نزدیک به C اجرا شود.
بهرهگیری از np.select برای منطق شرطی پیچیده
هنگام کار با منطق شرطی پیچیدهای که فراتر از ماسکبندی بولی ساده است، np.select یک ابزار قدرتمند است که برداریسازی را حفظ میکند. بسیاری از توسعهدهندگان به بیانیههای زنجیرهای if-else با استفاده از apply یا فراخوانیهای تو در تو np.where روی میآورند که به سرعت غیرقابل خواندن و کند میشوند.
فرض کنید سطوح اولویت را بر اساس معیارهای متعدد اختصاص دهید: اولویت بالا اگر مقدار > ۱۰۰ باشد، متوسط اگر مقدار > ۵۰ باشد و در غیر این صورت پایین.
import numpy as np
conditions = [
df['value'] > 100,
df['value'] > 50
]
choices = ['high', 'medium']
# Vectorized and readable
df['priority'] = np.select(conditions, choices, default='low')
این روش به طور قابل توجهی سریعتر از استفاده از apply با یک تابع سفارشی است و مقیاسپذیری بسیار بهتری دارد زیرا تعداد شرایط افزایش مییابد. همچنین منطق را اعلانی (declarative) نگه میدارد به جای اینکه دستوری (imperative) باشد.
بهینهسازی عملیات رشتهای با str.extract و Regex
دستکاری رشتهها اغلب گلوگاه در پایپلاینهای داده است، به ویژه هنگام کار با لاگهای متنی ساختارنیافته یا محتوای تولید شده توسط کاربر. پانداس یک دسترسیگر str قوی ارائه میدهد که از عبارات منظم پشتیبانی میکند. استفاده از str.extract() به شما اجازه میدهد چندین قطعه اطلاعات را از یک ستون رشتهای در یک گذر واحد استخراج کنید و چندین ستون جدید را به طور کارآمد ایجاد نمایید.
data = {'log': ['User 123 login success', 'User 456 error 404']}
df = pd.DataFrame(data)
# Extract user ID and status in one vectorized call
df[['user_id', 'status']] = df['log'].str.extract(r'User (\d+) (\w+)', expand=True)
با اجتناب از حلقهها یا کامپایل regex در داخل apply، این روش اطمینان حاصل میکند که کارهای سنگین به کتابخانههای C زیرین واگذار میشود که زمان اجرا را به شدت کاهش میدهد.
نقش انواع دادههای دستهای (Categorical)
در نهایت، برداریسازی فقط درباره روشهایی است که استفاده میکنید، بلکه درباره انواع دادهای است که به کار میبرید. ستونهای رشتهای در پانداس معمولاً کندتر از دادههای دستهای پردازش میشوند. اگر یک ستون تعداد محدودی مقدار منحصر به فرد داشته باشد (مثلاً 'شمال'، 'جنوب'، 'شرق'، 'غرب')، تبدیل آن به نوع Categorical میتواند استفاده از حافظه را به شدت کاهش دهد و عملیات گروهبندی و ادغام را سرعت بخشد. پانداس دادههای دستهای را در داخل با استفاده از کدهای عدد صحیح مدیریت میکند که برای محاسبات و مقایسههای برداری بسیار کارآمدتر از اشیاء رشتهای هستند.
نتیجهگیری
دستیابی به عملکرد بالا در پانداس نیازمند تغییر نگرش است. این موضوع تنها درباره نوشتن کد کمتر نیست، بلکه درباره درک نحوه تعامل کتابخانه با NumPy و Cython در پشت صحنه است. با کنار گذاشتن تکرار ردیف به ردیف به نفع روشهای برداریسازی بومی مانند str.replace، np.select و کدگذاری دستهای، میتوانید پایپلاینهای دادهای بسازید که نه تنها سریعتر، بلکه قابل خواندنتر و قابل نگهداریتر هستند. با ادامه انفجار حجم دادهها، تسلط بر این تکنیکهای پیشرفته برداریسازی مهارتی ضروری برای هر توسعهدهنده پایتونی خواهد بود که با دادههای مقیاس بزرگ کار میکند.