الباندا دليل لتحليل البيانات الكبيرة

عند استخدام مكتبة الباندا لتحليل مجموعات البيانات الصغيرة ، التي لا يتجاوز حجمها 100 ميجابايت ، نادراً ما يصبح الأداء مشكلة. ولكن عندما يتعلق الأمر بدراسة مجموعات البيانات ، التي يمكن أن تصل أحجامها إلى عدة غيغابايت ، يمكن أن تؤدي مشاكل الأداء إلى زيادة كبيرة في مدة تحليل البيانات ويمكن أن تؤدي إلى عدم القدرة على إجراء التحليل بسبب نقص الذاكرة.

بينما تستطيع أدوات مثل Spark معالجة مجموعات البيانات الكبيرة بكفاءة (من مئات الجيجابايت إلى عدة تيرابايت) ، من أجل الاستفادة الكاملة من قدراتها ، تحتاج عادة إلى أجهزة قوية ومكلفة للغاية. وبالمقارنة مع حيوانات الباندا ، فإنها لا تختلف في مجموعات غنية من الأدوات للتنظيف والبحث والبيانات عالية الجودة. بالنسبة إلى مجموعات البيانات متوسطة الحجم ، من الأفضل تجربة استخدام حيوانات الباندا بشكل أكثر كفاءة ، بدلاً من التبديل إلى أدوات أخرى.



في المادة التي نترجمها اليوم ، سنتحدث عن ميزات العمل مع الذاكرة عند استخدام الباندا ، وكيفية تقليل استهلاك الذاكرة بنسبة 90٪ تقريبًا عن طريق تحديد أنواع البيانات المناسبة المخزنة في أعمدة هياكل بيانات الجدول في DataFrame .

العمل مع البيانات على ألعاب البيسبول


سنعمل مع البيانات حول ألعاب البيسبول للرابطة الكبرى التي تم جمعها على مدار 130 عامًا وأخذت من Retrosheet .

في البداية ، تم تقديم هذه البيانات في صورة 127 ملفًا بتنسيق CSV ، لكننا قمنا بدمجها في مجموعة بيانات واحدة باستخدام ملف csvkit وأضفنا صفًا يحتوي على أسماء الأعمدة كصف أول من الجدول الناتج. إذا كنت تريد ، يمكنك تنزيل إصدارنا من هذه البيانات وتجربتها ، وقراءة المقال.

لنبدأ باستيراد مجموعة بيانات ونلقي نظرة على الأسطر الخمسة الأولى. يمكنك العثور عليها في هذا الجدول ، على ورقة .

 import pandas as pd gl = pd.read_csv('game_logs.csv') gl.head() 

يوجد أدناه معلومات حول أهم أعمدة الجدول مع هذه البيانات. إذا كنت ترغب في قراءة التفسيرات لجميع الأعمدة ، يمكنك هنا العثور على قاموس بيانات لمجموعة البيانات بأكملها.

  • date - تاريخ اللعبة.
  • v_name - اسم فريق الضيف.
  • v_league - دوري الفريق الزائر.
  • h_name - اسم الفريق المضيف.
  • h_league - دوري الفريق المضيف.
  • v_score - نقاط من الفريق الضيف.
  • h_score - نقاط الفريق المضيف.
  • v_line_score - ملخص لنقاط فريق الضيف ، على سبيل المثال - 010000(10)00 .
  • h_line_score - ملخص لنقاط الفريق المضيف ، على سبيل المثال - 010000(10)0X .
  • park_id - معرف الحقل الذي تم لعب اللعبة عليه.
  • attendance - عدد المشاهدين.

لمعرفة معلومات عامة حول كائن DataFrame ، يمكنك استخدام الأسلوب DataFrame.info () . بفضل هذه الطريقة ، يمكنك معرفة حجم الكائن ، وأنواع البيانات ، واستخدام الذاكرة.

بشكل افتراضي ، توفر الباندا ، من أجل توفير الوقت ، DataFrame تقريبية حول استخدام الذاكرة بواسطة DataFrame . نحن مهتمون بالمعلومات الدقيقة ، لذلك سنقوم بتعيين المعلمة memory_usage على 'deep' .

 gl.info(memory_usage='deep') 

فيما يلي المعلومات التي تمكنا من الحصول عليها:

 <class 'pandas.core.frame.DataFrame'> RangeIndex: 171907 entries, 0 to 171906 Columns: 161 entries, date to acquisition_info dtypes: float64(77), int64(6), object(78) memory usage: 861.6 MB 

كما اتضح ، لدينا 171،907 صفًا و 161 عمودًا. اكتشفت مكتبة الباندا أنواع البيانات تلقائيًا. هناك 83 عمودًا يحتوي على بيانات رقمية و 78 عمودًا به كائنات. تُستخدم أعمدة الكائنات لتخزين بيانات السلسلة ، وفي الحالات التي يحتوي فيها العمود على بيانات من أنواع مختلفة.

الآن ، من أجل فهم أفضل لكيفية تحسين استخدام الذاكرة باستخدام DataFrame ، دعنا نتحدث عن كيفية تخزين الباندا للبيانات في الذاكرة.

عرض داخلي من DataFrame


داخل الباندا ، يتم تجميع أعمدة البيانات في كتل ذات قيم من نفس النوع. فيما يلي مثال على كيفية تخزين أول 12 عمودًا من DataFrame في الباندا.


التمثيل الداخلي لأنواع مختلفة من البيانات في الباندا

قد تلاحظ أن الكتل لا تخزن معلومات اسم العمود. هذا يرجع إلى حقيقة أن الكتل محسّنة لتخزين القيم المتوفرة في خلايا الجدول لكائن DataFrame . فئة BlockManager هي المسؤولة عن تخزين المعلومات حول المراسلات بين فهارس الصف والعمود لمجموعة البيانات وما يتم تخزينه في كتل من نفس النوع من البيانات. إنه يلعب دور API الذي يوفر الوصول إلى البيانات الأساسية. عندما نقرأ القيم أو DataFrame أو DataFrame تتفاعل فئة DataFrame مع فئة BlockManager لتحويل طلباتنا إلى استدعاءات الوظائف والأسلوب.

يحتوي كل نوع بيانات على فئة متخصصة في الوحدة النمطية pandas.core.internals . على سبيل المثال ، يستخدم الباندا فئة ObjectBlock لتمثيل الكتل التي تحتوي على أعمدة السلسلة وفئة FloatBlock لتمثيل الكتل التي تحتوي على أعمدة تحتوي على أرقام FloatBlock العائمة. بالنسبة للكتل التي تمثل القيم الرقمية التي تشبه الأعداد الصحيحة أو أرقام ndarray العائمة ، تجمع الباندا الأعمدة ndarray بيانات ndarray . تعتمد بنية البيانات هذه على الصفيف C ، ويتم تخزين القيم في كتلة مستمرة من الذاكرة. بفضل نظام تخزين البيانات ، أصبح الوصول إلى أجزاء البيانات سريعًا للغاية.

نظرًا لأن بيانات الأنواع المختلفة يتم تخزينها بشكل منفصل ، فإننا نفحص استخدام الذاكرة لأنواع مختلفة من البيانات. لنبدأ باستخدام متوسط ​​استخدام الذاكرة لأنواع مختلفة من البيانات.

 for dtype in ['float','int','object']:   selected_dtype = gl.select_dtypes(include=[dtype])   mean_usage_b = selected_dtype.memory_usage(deep=True).mean()   mean_usage_mb = mean_usage_b / 1024 ** 2   print("Average memory usage for {} columns: {:03.2f} MB".format(dtype,mean_usage_mb)) 

نتيجة لذلك ، اتضح أن متوسط ​​مؤشرات استخدام الذاكرة للبيانات من أنواع مختلفة يبدو كما يلي:

 Average memory usage for float columns: 1.29 MB Average memory usage for int columns: 1.12 MB Average memory usage for object columns: 9.53 MB 

هذه المعلومات تجعلنا نفهم أن معظم الذاكرة تنفق على 78 عمودًا في تخزين قيم الكائنات. سنتحدث أكثر عن هذا لاحقًا ، لكن الآن دعونا نفكر فيما إذا كان بإمكاننا تحسين استخدام الذاكرة مع الأعمدة التي تخزن البيانات الرقمية.

أنواع فرعية


كما قلنا سابقًا ، تمثل الباندا قيمًا رقمية مثل هياكل البيانات ndarray وتخزنها في كتل متجاورة من الذاكرة. يسمح لك نموذج تخزين البيانات هذا بحفظ الذاكرة والوصول إلى القيم بسرعة. نظرًا لأن الباندا تمثل كل قيمة من نفس النوع باستخدام نفس عدد البايتات ، ndarray هياكل ndarray معلومات حول عدد القيم ، يمكن ndarray أن تعرض بسرعة وبدقة معلومات حول مقدار الذاكرة التي تستهلكها أعمدة تخزين القيم العددية.

تحتوي العديد من أنواع البيانات في الباندا على العديد من الأنواع الفرعية التي يمكنها استخدام عدد أقل من البايتات لتمثيل كل قيمة. على سبيل المثال ، يحتوي نوع float على أنواع فرعية float16 و float32 و float64 . يشير الرقم في اسم النوع إلى عدد البتات التي يستخدمها النوع الفرعي لتمثيل القيم. على سبيل المثال ، في الأنواع الفرعية المدرجة للتو ، يتم استخدام 2 و 4 و 8 و 16 بايت على التوالي لتخزين البيانات. يعرض الجدول التالي الأنواع الفرعية لأنواع البيانات الأكثر استخدامًا في حيوانات الباندا.
استخدام الذاكرة ، بايت
رقم النقطة العائمة
عدد صحيح
عدد صحيح غير موقعة
التاريخ والوقت
القيمة المنطقية
كائن
1
int8
uint8
منطقي
2
float16
int16
uint16
4
float32
int32
uint32
8
float64
int64
uint64
datetime64
سعة الذاكرة المتغيرة
كائن

تستخدم قيمة النوع int8 بايت واحد (8 بت) لتخزين رقم ويمكن أن تمثل 256 قيمة ثنائية (2 إلى 8 طاقة). هذا يعني أنه يمكن استخدام هذا النوع الفرعي لتخزين القيم في النطاق من -128 إلى 127 (بما في ذلك 0).

للتحقق من الحد الأدنى والحد الأقصى للقيم المناسبة للتخزين باستخدام كل نوع فرعي صحيح ، يمكنك استخدام طريقة numpy.iinfo() . النظر في مثال:

 import numpy as np int_types = ["uint8", "int8", "int16"] for it in int_types:   print(np.iinfo(it)) 

عن طريق تنفيذ هذا الرمز ، نحصل على البيانات التالية:

 Machine parameters for uint8 --------------------------------------------------------------- min = 0 max = 255 --------------------------------------------------------------- Machine parameters for int8 --------------------------------------------------------------- min = -128 max = 127 --------------------------------------------------------------- Machine parameters for int16 --------------------------------------------------------------- min = -32768 max = 32767 --------------------------------------------------------------- 

هنا يمكنك الانتباه إلى الفرق بين أنواع uint (عدد صحيح غير موقّع) و int (عدد صحيح موقّع). يتمتع كلا النوعين بنفس السعة ، ولكن عند تخزين القيم الإيجابية فقط في الأعمدة ، تسمح الأنواع غير الموقعة باستخدام أكثر كفاءة للذاكرة.

تعظيم تخزين البيانات العددية باستخدام الأنواع الفرعية


يمكن استخدام الدالة pd.to_numeric() تحويل أنواع رقمية. لتحديد أعمدة عدد صحيح ، نستخدم طريقة DataFrame.select_dtypes() ، ثم نقوم DataFrame.select_dtypes() ومقارنة استخدام الذاكرة قبل وبعد التحسين.

 #     ,   , #   ,      . def mem_usage(pandas_obj):   if isinstance(pandas_obj,pd.DataFrame):       usage_b = pandas_obj.memory_usage(deep=True).sum()   else: #     ,     DataFrame,   Series       usage_b = pandas_obj.memory_usage(deep=True)   usage_mb = usage_b / 1024 ** 2 #       return "{:03.2f} MB".format(usage_mb) gl_int = gl.select_dtypes(include=['int']) converted_int = gl_int.apply(pd.to_numeric,downcast='unsigned') print(mem_usage(gl_int)) print(mem_usage(converted_int)) compare_ints = pd.concat([gl_int.dtypes,converted_int.dtypes],axis=1) compare_ints.columns = ['before','after'] compare_ints.apply(pd.Series.value_counts) 

هذه هي نتيجة دراسة استهلاك الذاكرة:

7.87 MB
1.48 MB

إلى
بعد
uint8
NaN
5.0
uint32
NaN
1.0
int64
6.0
NaN

نتيجةً لذلك ، يمكنك رؤية انخفاض في استخدام الذاكرة من 7.9 إلى 1.5 ميجابايت ، أي أننا خفضنا استهلاك الذاكرة بأكثر من 80٪. التأثير الكلي لهذا التحسين على DataFrame الأصلي ، ومع ذلك ، ليست قوية بشكل خاص لأنه يحتوي على عدد قليل جداً من الأعمدة عدد صحيح.

دعونا نفعل الشيء نفسه مع الأعمدة التي تحتوي على أرقام الفاصلة العائمة.

 gl_float = gl.select_dtypes(include=['float']) converted_float = gl_float.apply(pd.to_numeric,downcast='float') print(mem_usage(gl_float)) print(mem_usage(converted_float)) compare_floats = pd.concat([gl_float.dtypes,converted_float.dtypes],axis=1) compare_floats.columns = ['before','after'] compare_floats.apply(pd.Series.value_counts) 

والنتيجة هي ما يلي:

100.99 MB
50.49 MB

إلى
بعد
float32
NaN
77.0
float64
77.0
NaN

ونتيجة لذلك ، فإن جميع الأعمدة التي تخزن أرقام float64 بنوع البيانات float64 تخزن الآن أرقام النوع float32 ، مما أعطانا تخفيضًا بنسبة 50٪ في استخدام الذاكرة.

قم بإنشاء نسخة من DataFrame الأصلي ، واستخدم هذه الأعمدة الرقمية المحسنة بدلاً من تلك التي كانت موجودة في الأصل ، وانظر في استخدام الذاكرة الكلي بعد التحسين.

 optimized_gl = gl.copy() optimized_gl[converted_int.columns] = converted_int optimized_gl[converted_float.columns] = converted_float print(mem_usage(gl)) print(mem_usage(optimized_gl)) 

إليك ما حصلنا عليه:

861.57 MB
804.69 MB


على الرغم من أننا خفضنا استهلاك الذاكرة بشكل كبير عن طريق أعمدة تخزين البيانات العددية ، بشكل عام ، عبر DataFrame بأكمله ، انخفض استهلاك الذاكرة بنسبة 7٪ فقط. يمكن أن يصبح تحسين تخزين أنواع الكائنات مصدرًا للتحسين الأكثر خطورة للموقف.

قبل أن نقوم بهذا التحسين ، سوف نلقي نظرة فاحصة على كيفية تخزين السلاسل في الباندا ، ومقارنتها بكيفية تخزين الأرقام هنا.

مقارنة آليات تخزين الأرقام والسلاسل


يمثل نوع object القيم باستخدام كائنات سلسلة Python. هذا جزئيًا لأن NumPy لا يدعم تمثيل قيم السلسلة المفقودة. نظرًا لأن Python هي لغة عالية المستوى يتم تفسيرها ، فهي لا تزود المبرمج بأدوات لضبط كيفية تخزين البيانات في الذاكرة.

يؤدي هذا القيد إلى حقيقة أنه لا يتم تخزين السلاسل في أجزاء متجاورة من الذاكرة ؛ يتم تجزئة تمثيلها في الذاكرة. هذا يؤدي إلى زيادة في استهلاك الذاكرة وإلى تباطؤ في سرعة العمل مع قيم السلسلة. في الواقع ، كل عنصر في العمود الذي يخزن نوع بيانات الكائن هو مؤشر يحتوي على "العنوان" الذي توجد به القيمة الحقيقية في الذاكرة.

فيما يلي رسم تخطيطي يستند إلى هذه المادة يقارن تخزين البيانات الرقمية باستخدام أنواع بيانات NumPy وتخزين السلاسل باستخدام أنواع البيانات المضمنة في Python.


تخزين البيانات الرقمية وسلسلة

هنا يمكنك أن تتذكر أنه في أحد الجداول أعلاه ، تبين أنه يتم استخدام مقدار متغير من الذاكرة لتخزين بيانات أنواع الكائنات. على الرغم من أن كل مؤشر يشغل بايت واحد من الذاكرة ، إلا أن كل قيمة سلسلة معينة تشغل نفس مقدار الذاكرة التي سيتم استخدامها لتخزين سلسلة واحدة في Python. لتأكيد هذا ، sys.getsizeof() طريقة sys.getsizeof() . أولاً ، ألق نظرة على الأسطر الفردية ، ثم على كائن Series الباندا الذي يخزن بيانات السلسلة.

لذلك ، أولاً نفحص الخطوط المعتادة:

 from sys import getsizeof s1 = 'working out' s2 = 'memory usage for' s3 = 'strings in python is fun!' s4 = 'strings in python is fun!' for s in [s1, s2, s3, s4]:   print(getsizeof(s)) 

هنا ، تبدو بيانات استخدام الذاكرة كما يلي:

60
65
74
74


الآن دعونا نلقي نظرة على كيفية ظهور استخدام السلاسل في كائن Series :

 obj_series = pd.Series(['working out',                         'memory usage for',                         'strings in python is fun!',                         'strings in python is fun!']) obj_series.apply(getsizeof) 

هنا نحصل على ما يلي:

 0    60 1    65 2    74 3    74 dtype: int64 

هنا يمكنك أن ترى أن أحجام الخطوط المخزنة في كائنات Series الباندا تشبه أحجامها عند العمل معها في بيثون وعند تمثيلها ككيانات منفصلة.

تعظيم تخزين بيانات نوع الكائن باستخدام المتغيرات الفئوية


ظهرت المتغيرات الفئوية في الباندا الإصدار 0.15. يستخدم النوع المطابق ، category ، قيم عدد صحيح في آلياته الداخلية ، بدلاً من القيم الأصلية المخزنة في أعمدة الجدول. يستخدم Pandas قاموس منفصل يحدد مراسلات عدد صحيح وقيم أولية. هذا النهج مفيد عندما تحتوي الأعمدة على قيم من مجموعة محدودة. عندما يتم تحويل البيانات المخزنة في عمود إلى نوع category ، تستخدم الباندا النوع الفرعي int ، والذي يسمح باستخدام أكثر كفاءة للذاكرة ويمكنه تمثيل جميع القيم الفريدة الموجودة في العمود.


مصدر البيانات والبيانات الفئوية باستخدام النوع الفرعي int8

لكي نفهم بالضبط أين يمكننا استخدام البيانات الفئوية لتقليل استهلاك الذاكرة ، نجد عدد القيم الفريدة في الأعمدة التي تخزن قيم أنواع الكائنات:

 gl_obj = gl.select_dtypes(include=['object']).copy() gl_obj.describe() 

يمكنك العثور على ما لدينا في هذا الجدول ، على الورقة .

على سبيل المثال ، في العمود day_of_week ، وهو يوم الأسبوع الذي تم لعب اللعبة فيه ، هناك 171907 قيمة. من بينها ، فقط 7 فريدة من نوعها. بشكل عام ، يكفي إلقاء نظرة واحدة على هذا التقرير لفهم أنه يتم استخدام عدد قليل من القيم الفريدة في العديد من الأعمدة لتمثيل بيانات حوالي 172000 لعبة.

قبل أن نقوم بالتحسين على نطاق واسع ، دعونا نختار عمودًا واحدًا يخزن بيانات الكائن ، على الأقل day_of_week ، ونرى ما يحدث داخل البرنامج عند تحويله إلى نوع قاطع.

كما ذكرنا سابقًا ، يحتوي هذا العمود على 7 قيم فريدة فقط. لتحويله إلى نوع قاطع ، نستخدم طريقة .astype() .

 dow = gl_obj.day_of_week print(dow.head()) dow_cat = dow.astype('category') print(dow_cat.head()) 

إليك ما حصلنا عليه:

 0    Thu 1    Fri 2    Sat 3    Mon 4    Tue Name: day_of_week, dtype: object 0    Thu 1    Fri 2    Sat 3    Mon 4    Tue Name: day_of_week, dtype: category Categories (7, object): [Fri, Mon, Sat, Sun, Thu, Tue, Wed] 

كما ترى ، على الرغم من تغير نوع العمود ، فإن البيانات المخزنة فيه تبدو كما كانت من قبل. الآن دعونا نلقي نظرة على ما يحدث داخل البرنامج.

في التعليمة البرمجية التالية ، نستخدم السمة Series.cat.codes لمعرفة القيم الصحيحة التي يستخدمها نوع category لتمثيل كل يوم من أيام الأسبوع:

 dow_cat.head().cat.codes 

نتمكن من معرفة ما يلي:

 0    4 1    0 2    2 3    1 4    5 dtype: int8 

هنا يمكنك أن ترى أن كل قيمة فريدة يتم تعيين قيمة عددية لها ، وأن العمود الآن من النوع int8 . لا توجد قيم مفقودة ، ولكن إذا كان الأمر كذلك ، فسيتم استخدام -1 للإشارة إلى هذه القيم.

الآن دعنا نقارن استهلاك الذاكرة قبل وبعد تحويل العمود day_of_week إلى نوع category .

 print(mem_usage(dow)) print(mem_usage(dow_cat)) 

هذه هي النتيجة:

9.84 MB
0.16 MB


كما ترون ، في البداية تم استهلاك 9.84 ميغابايت من الذاكرة ، وبعد التحسين فقط 0.16 ميجابايت ، مما يعني تحسن بنسبة 98٪ في هذا المؤشر. يرجى ملاحظة أن العمل مع هذا العمود ربما يوضح أحد أكثر سيناريوهات التحسين ربحية عندما يتم استخدام 7 قيم فريدة فقط في عمود يحتوي على حوالي 172000 عنصر.

على الرغم من أن فكرة تحويل جميع الأعمدة إلى نوع البيانات هذا تبدو جذابة ، قبل القيام بذلك ، يجب مراعاة الآثار الجانبية السلبية لهذا التحويل. لذلك ، فإن أخطر من هذا التحول هو استحالة إجراء العمليات الحسابية على البيانات الفئوية. ينطبق هذا أيضًا على العمليات الحسابية العادية ، واستخدام أساليب مثل Series.min() و Series.max() دون تحويل البيانات أولاً إلى نوع رقم حقيقي.

يجب أن نقصر استخدام نوع category على الأعمدة التي تخزن بيانات object النوع بشكل أساسي ، حيث يكون أقل من 50٪ من القيم فريدة من نوعها. إذا كانت جميع القيم الموجودة في العمود فريدة ، فسيؤدي استخدام نوع category إلى زيادة مستوى استخدام الذاكرة. هذا يرجع إلى حقيقة أنه في الذاكرة لديك لتخزين ، بالإضافة إلى رموز الفئة الرقمية ، وقيم السلسلة الأصلية. يمكن الاطلاع على تفاصيل قيود أنواع category في وثائق الباندا.

دعنا ننشئ حلقة تتكرر على جميع الأعمدة التي تخزن بيانات object الكتابة ، ومعرفة ما إذا كان عدد القيم الفريدة في الأعمدة يتجاوز 50 ٪ ، وإذا كان الأمر كذلك ، يحولها إلى كتابة category .

 converted_obj = pd.DataFrame() for col in gl_obj.columns:   num_unique_values = len(gl_obj[col].unique())   num_total_values = len(gl_obj[col])   if num_unique_values / num_total_values < 0.5:       converted_obj.loc[:,col] = gl_obj[col].astype('category')   else:       converted_obj.loc[:,col] = gl_obj[col] 

قارن الآن ما حدث بعد التحسين بما حدث من قبل:

 print(mem_usage(gl_obj)) print(mem_usage(converted_obj)) compare_obj = pd.concat([gl_obj.dtypes,converted_obj.dtypes],axis=1) compare_obj.columns = ['before','after'] compare_obj.apply(pd.Series.value_counts) 

نحصل على ما يلي:

752.72 MB
51.67 MB

إلى
بعد
كائن
78.0
NaN
الفئة
NaN
78.0

category , , , , , , , , .

, , , object , 752 52 , 93%. , . , , , , 891 .

 optimized_gl[converted_obj.columns] = converted_obj mem_usage(optimized_gl) 

:

'103.64 MB'

. - . , datetime , , , .

 date = optimized_gl.date print(mem_usage(date)) date.head() 

:

0.66 MB

:

 0    18710504 1    18710505 2    18710506 3    18710508 4    18710509 Name: date, dtype: uint32 

, uint32 . - datetime , 64 . datetime , , , .

to_datetime() , format , YYYY-MM-DD .

 optimized_gl['date'] = pd.to_datetime(date,format='%Y%m%d') print(mem_usage(optimized_gl)) optimized_gl.date.head() 

:

104.29 MB

:

 0   1871-05-04 1   1871-05-05 2   1871-05-06 3   1871-05-08 4   1871-05-09 Name: date, dtype: datetime64[ns] 


DataFrame . , , , , , , , . , . , , , . , , DataFrame , .

, . pandas.read_csv() , . , dtype , , , , — NumPy.

, , . , .

 dtypes = optimized_gl.drop('date',axis=1).dtypes dtypes_col = dtypes.index dtypes_type = [i.name for i in dtypes.values] column_types = dict(zip(dtypes_col, dtypes_type)) #    161 ,  #  10  /   #     preview = first2pairs = {key:value for key,value in list(column_types.items())[:10]} import pprint pp = pp = pprint.PrettyPrinter(indent=4) pp.pprint(preview)     : {   'acquisition_info': 'category',   'h_caught_stealing': 'float32',   'h_player_1_name': 'category',   'h_player_9_name': 'category',   'v_assists': 'float32',   'v_first_catcher_interference': 'float32',   'v_grounded_into_double': 'float32',   'v_player_1_id': 'category',   'v_player_3_id': 'category',   'v_player_5_id': 'category'} 

, , .

- :

 read_and_optimized = pd.read_csv('game_logs.csv',dtype=column_types,parse_dates=['date'],infer_datetime_format=True) print(mem_usage(read_and_optimized)) read_and_optimized.head() 

:

104.28 MB

, .

, , , , . pandas 861.6 104.28 , 88% .


, , , . .

 optimized_gl['year'] = optimized_gl.date.dt.year games_per_day = optimized_gl.pivot_table(index='year',columns='day_of_week',values='date',aggfunc=len) games_per_day = games_per_day.divide(games_per_day.sum(axis=1),axis=0) ax = games_per_day.plot(kind='area',stacked='true') ax.legend(loc='upper right') ax.set_ylim(0,1) plt.show() 


,

, 1920- , , 50 , .

, , , 50 , .

, .

 game_lengths = optimized_gl.pivot_table(index='year', values='length_minutes') game_lengths.reset_index().plot.scatter('year','length_minutes') plt.show() 




, 1940- .

النتائج


pandas, , DataFrame , 90%. :

  • , , , , .
  • .

, , , , , , pandas, , .

أعزائي القراء! eugene_bb . - , — .

Source: https://habr.com/ru/post/ar442516/


All Articles