Оптимизация размера памяти dataframe (python, pandas) путем преобразования числовых типов

Обычно данные для анализа довольно большого объема сами по себе и при загрузке занимают от сотен мегабайт до гигабайт. Помимо этого при загрузке данных в dataframe (pandas) числовые значения не всегда получают наиболее подходящие типы, например вместо int8 назначается тип int64 из-за чего объем используемой под dataframe памяти существенно возрастает.
Приведенная в статье функция позволит быстро переопределить указанные типы в случае такой возможности и в некоторых случаях существенно сократить размер памяти под dataframe.



Далее сама функция и пример применения.

In [25]:
import pandas as pd
import numpy as np
In [26]:
def optimize_memory_usage(df, print_size=True):
    # Function optimizes memory usage in dataframe.
    # (RU) Функция оптимизации типов в dataframe.
    
    # Types for optimization.
    # Типы, которые будем проверять на оптимизацию.
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    # Memory usage size before optimize (Mb).
    # (RU) Размер занимаемой памяти до оптимизации (в Мб).
    before_size = df.memory_usage().sum() / 1024**2    
    for column in df.columns:
        column_type = df[column].dtypes
        if column_type in numerics:
            column_min = df[column].min()
            column_max = df[column].max()
            if str(column_type).startswith('int'):
                if column_min > np.iinfo(np.int8).min and column_max < np.iinfo(np.int8).max:
                    df[column] = df[column].astype(np.int8)
                elif column_min > np.iinfo(np.int16).min and column_max < np.iinfo(np.int16).max:
                    df[column] = df[column].astype(np.int16)
                elif column_min > np.iinfo(np.int32).min and column_max < np.iinfo(np.int32).max:
                    df[column] = df[column].astype(np.int32)
                elif column_min > np.iinfo(np.int64).min and column_max < np.iinfo(np.int64).max:
                    df[column] = df[column].astype(np.int64)  
            else:
                if column_min > np.finfo(np.float32).min and column_max < np.finfo(np.float32).max:
                    df[column] = df[column].astype(np.float32)
                else:
                    df[column] = df[column].astype(np.float64)    
    # Memory usage size after optimize (Mb).
    # (RU) Размер занимаемой памяти после оптимизации (в Мб).
    after_size = df.memory_usage().sum() / 1024**2
    if print_size: print('Memory usage size: before {:5.4f} Mb - after {:5.4f} Mb ({:.1f}%).'.format(before_size, after_size, 100 * (before_size - after_size) / before_size))
    return df
In [30]:
def import_data_from_csv(filePath):
    # Load a dataframe from csv-file and optimize its memory usage.
    # (RU) Загрузка данных из csv-файла и оптимизация числовых типов для оптимизации использования памяти
    df = pd.read_csv(filePath, parse_dates=True, keep_date_col=True)
    # Show dataframe info before optimize.
    # (RU) Показать информацию о таблице до оптимизации.
    print('-' * 80)
    print(df.info())
    print('-' * 80)
    # (RU) Оптимизация типов в dataframe.
    df = optimize_memory_usage(df)
    # Show dataframe info after optimize.
    # (RU) Показать информацию о таблице после оптимизации.
    print('-' * 80)
    print(df.info())
    print('-' * 80)
    return df
In [31]:
df = import_data_from_csv('data.csv')
------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 157 entries, 0 to 156
Data columns (total 3 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   Passengers  145 non-null    float64
 1   Year        157 non-null    int64  
 2   Month       157 non-null    int64  
dtypes: float64(1), int64(2)
memory usage: 3.8 KB
None
-----------------------------------------------------------------------
Memory usage size: before 0.0037 Mb - after 0.0012 Mb (68.5%).
-----------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 157 entries, 0 to 156
Data columns (total 3 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   Passengers  145 non-null    float32
 1   Year        157 non-null    int16  
 2   Month       157 non-null    int8   
dtypes: float32(1), int16(1), int8(1)
memory usage: 1.2 KB
None
------------------------------------------------------------------------
In [32]:
df.head()
Out[32]:
PassengersYearMonth
01235.75000020074
11487.53002920073
21563.95996120077
31575.27002020070
41762.00000020078


Видно, что при загрузке dataframe были назначены типы - float64(1) и int64(2), а после оптимизации они преобразованы в float32(1), int16(1) и int8(1).


(с) Ella S.
Если Вам понравилась статья, пожалуйста, поставьте лайк, сделайте репост или оставьте комментарий. Если у Вас есть какие-либо замечания, также пишите комментарии.

1 комментарий :