[PYTHON] Ingéniosité pour gérer les données avec Pandas de manière à économiser la mémoire

Si vous souhaitez traiter une grande quantité de données avec la mémoire de votre ordinateur personnel, mais que vous voulez survivre avec des pandas, je vais résumer la méthode de sauvegarde de la mémoire et les idées associées qui peuvent être faites dans le post final. Si vous l'utilisez consciemment sur une base régulière, vous pouvez l'analyser sans gaspiller de ressources informatiques, donc je pense qu'il y a plusieurs avantages. Cependant, s'il existe une limite et que cela ne fonctionne pas, il est recommandé d'augmenter la mémoire ou d'utiliser des services cloud tels que AWS et GCP en premier lieu.

Données de validation

Cette fois, j'utiliserai les fameuses Titanic Survivor Prediction Data car il n'est pas réaliste d'écrire uniquement la méthode. La quantité de données n'est pas du tout importante, mais pardonnez-moi. Si vous ne vous souciez que de la façon de le faire, vous pouvez ignorer cette partie.

Environnement d'analyse

OS: Windows 10 Home dans un ordinateur portable bon marché sur le marché Environnement: ordinateur portable Jupyter lancé en montant Kaggle Docker sur le système d'exploitation ci-dessus python:Python 3.6.6 :: Anaconda, Inc. La structure des dossiers au moment de l'analyse est la suivante, le dossier dans lequel le notebook est placé et le dossier dans lequel les données d'utilisation sont placées. image.png

Tout d'abord, placez les données Titanic téléchargées dans le dossier d'entrée et préparez-vous à lire les données. Exécutez la commande suivante sur le notebook jupyter.

#Importer la bibliothèque
import os, gc, pickle, datetime, sys
import numpy as np
import pandas as pd

#Chemin des données d'utilisation et confirmation des données
INPUTPATH = '../input'
print(os.listdir(INPUTPATH))

#Résultat d'exécution
#> ['gender_submission.csv', 'test.csv', 'train.csv']

Ensuite, j'ai pu confirmer les données. Chargez train.csv.


df = pd.read_csv(f'{INPUTPATH}/train.csv')
df.head(1)

Changer le type de données

S'il n'y a pas une grande demande de précision maximale, minimale et fractionnaire des données, vous pouvez réduire la consommation de mémoire en passant du type de données double précision au type de données simple précision. Prenons donc les données de vérification comme exemple. Tout d'abord, vérifiez le type de données chargé de train.csv. Sauf indication contraire, les valeurs entières sont lues comme int64 et les fractions sont lues comme float64.


df.dtypes
#Résultat de sortie ci-dessous
#PassengerId      int64
#Survived         int64
#Pclass           int64
#Name            object
#Sex             object
#Age            float64
#SibSp            int64
#Parch            int64
#Ticket          object
#Fare           float64
#Cabin           object
#Embarked        object
#dtype: object

Modifions immédiatement le type de données. .astype() Le moyen le plus simple est d'utiliser .astype (). Par exemple, modifions le tarif du billet Titanic de float64 à float32. En conséquence, la taille des données est divisée par deux.


dfare1 = df.Fare.nbytes
print('La taille des données du tarif à float64 est:{:.2f} KB'.format(dfare1/1024))
dfare2 = df.Fare.astype('float32').nbytes
print('La taille des données du tarif à float32 est:{:.2f} KB'.format(dfare2/1024))
print('En changeant le type de données{:.2f}%J'ai pu réduire la taille du fichier'.format(100*(1-dfare2/dfare1)))

##Résultat de sortie ci-dessous
#La taille des données de Fare lorsque float64 est: 6.96 KB
#La taille des données du tarif à float32 est: 3.48 KB
#50 en changeant le type de données.00%J'ai pu réduire la taille du fichier

Cependant, lors du changement de type de données, il est nécessaire de s'assurer que la précision des valeurs maximale, minimale et décimale des données d'origine n'est pas affectée analytiquement. Par exemple, dans le cas du type float, l'influence sur l'analyse peut être ignorée même si le nombre de chiffres après la virgule décimale diminue, et dans le cas du type int, la plage d'entiers doit être fermement prise en compte même si le type de données est modifié. est nécessaire. Sinon, c'est un exemple extrême, mais si vous modifiez le type de données même si la valeur maximale d'origine de la colonne appelée PassengerId est 891 comme indiqué ci-dessous, la [valeur maximale qui peut être exprimée par le type de données](https: //) Veuillez noter qu'il restera fidèle à docs.scipy.org/doc/numpy-1.10.0/user/basics.types.html) et les nombres eux-mêmes changeront. Personnellement, sauf si vous avez affaire à des nombres très petits ou très grands, changez d'abord la double précision (64 bits) en simple précision (32 bits). Si cela ne s'améliore pas, vérifiez individuellement la plage de nombres et l'exigence de précision de la virgule décimale et apportez quelques corrections. Vous pouvez également faire un jugement ici en utilisant directement la fonction décrite plus loin.


df.PassengerId.max()
#Résultat de sortie
#891

df.PassengerId.astype('int8').max()
#Résultat de sortie
#127

Spécifiez le type de données à la fois avec read_csv

Il est pratique d'utiliser read_csv des pandas car il existe une option appelée dtype qui vous permet de spécifier le type de données de chaque colonne lors de la lecture des données. Cependant, pour ce faire, vous devez créer au préalable un dictionnaire définissant le nom de la colonne et le type de données correspondant. Par conséquent, une fois que vous avez lu les données et vérifié que changer le type de données avec df.describe () n'a aucun effet, le résultat de df.dtypes est transformé en dictionnaire. Remplacez simplement 64 par 32 et remplacez dtype par np.dtype (lors de l'affichage, il est affiché comme dtype, mais lors de la saisie, il doit être np.dtype), et le dictionnaire peut être créé relativement facilement. peut faire.


dtype_dict=df.dtypes.to_dict()
print(dtype_dict)
dtype_dict ={'PassengerId': np.dtype('int32'),
 'Survived': np.dtype('int32'),
 'Pclass': np.dtype('int32'),
 'Name': np.dtype('O'),
 'Sex': np.dtype('O'),
 'Age': np.dtype('float32'),
 'SibSp': np.dtype('int32'),
 'Parch': np.dtype('int32'),
 'Ticket': np.dtype('O'),
 'Fare': np.dtype('float32'),
 'Cabin': np.dtype('O'),
 'Embarked': np.dtype('O')}
df = pd.read_csv(f'{INPUTPATH}/train.csv',dtype=dtype_dict)
df.dtypes
##Résultat de sortie
#PassengerId      int32
#Survived         int32
#Pclass           int32
#Name            object
#Sex             object
#Age            float32
#SibSp            int32
#Parch            int32
#Ticket          object
#Fare           float32
#Cabin           object
#Embarked        object
#dtype: object

Utilisez des fonctions pratiques

Vous pouvez également utiliser les fonctions pratiques utilisées dans le concours Kaggle (https://www.kaggle.com/fabiendaniel/elo-world) il y a environ un an. J'ai également participé à ce concours et je suis reconnaissant de l'avoir utilisé. Il est également présenté dans cet article. Des détails peuvent être trouvés dans cet article, mais ils prennent des décisions basées sur les valeurs minimales et maximales des données. Le code ci-dessous est en partie personnalisé.

―― Tout d'abord, la double précision est modifiée par rapport au type numérique dont le type de données n'est pas le type d'objet.


#L'importation suivante est requise lors de l'utilisation de la fonction
from pandas.api.types import is_datetime64_any_dtype as is_datetime
from pandas.api.types import is_categorical_dtype
#Définition des fonctions
def reduce_mem_usage(df, use_float16=False):
    """ iterate through all the columns of a dataframe and modify the data type
        to reduce memory usage.        
    """
    start_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
    for col in df.columns:
        if is_datetime(df[col]) or is_categorical_dtype(df[col]):
            # skip datetime type or categorical type
            continue
        col_type = df[col].dtype
        if col_type != object:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if use_float16 and c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
        #else:
            #df[col] = df[col].astype('category')
    end_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))
    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))
    return df

Utilisons la fonction. Dans ce cas, l'utilisation de la mémoire du train.csv chargé pourrait être réduite d'environ 44%.

df = reduce_mem_usage(df, use_float16=False)
#Résultat de sortie
#Memory usage of dataframe is 0.08 MB
#Memory usage after optimization is: 0.05 MB
#Decreased by 43.7%

Passer au type de catégorie

Le passage au type de catégorie du dernier point de la partie personnalisée de la fonction reduction_mem_usage () introduite ci-dessus économise également de la mémoire. Même si vous essayez de changer le sexe des données titanesques du type d'objet au type de catégorie, vous pouvez le réduire d'environ la moitié dans ce cas.

dsex1 = df.Sex.nbytes
print('La taille des données du tarif au moment de l'objet est:{:.2f} KB'.format(dsex1/1024))
dsex2 = df.Fare.astype('category').nbytes
print('La taille des données du tarif au moment de la catégorie est:{:.2f} KB'.format(dsex2/1024))
print('En changeant le type de données{:.2f}%J'ai pu réduire la taille du fichier'.format(100*(1-dsex2/dsex1)))
##Résultat de sortie
#La taille des données du tarif au moment de l'objet est: 6.96 KB
#La taille des données de Tarif dans la catégorie est: 3.68 KB
#En changeant le type de données 47.17%J'ai pu réduire la taille du fichier

Cependant, il y a quelques précautions lors du passage au type de catégorie, et s'il y a des valeurs manquantes, un traitement supplémentaire sera nécessaire. Par exemple, la colonne Cabine de la cabine passagers contient des valeurs manquantes.

df.isnull().sum()
##Résultat de sortie
#PassengerId      0
#Survived         0
#Pclass           0
#Name             0
#Sex              0
#Age            177
#SibSp            0
#Parch            0
#Ticket           0
#Fare             0
#Cabin          687
#Embarked         2
#dtype: int64

Supposons que vous le changiez en type de catégorie, puis que vous remplissiez les valeurs manquantes. Ensuite, j'obtiens une erreur indiquant qu'il n'y a pas de catégorie correspondant à "null" que j'essaie de remplacer par "ValueError: fill value must be in categories".

df.Cabin = df.Cabin.astype('category')
df.Cabin = df.Cabin.fillna('null')

image.png Vous n'êtes pas obligé de réduire complètement la mémoire, ou si le type d'objet est un petit nombre de données, vous pouvez simplement utiliser le type d'objet ou omettre la partie conversion de la fonction reduction_mem_usage () vers le type de catégorie. De plus, il n'est pas toujours possible de réduire la mémoire en en faisant un type de catégorie. D'un autre côté, si vous souhaitez convertir le type d'objet en type de catégorie et réduire complètement la mémoire, il existe deux façons de traiter l'erreur de complétion de valeur manquante. De manière générale, si vous complétez la valeur manquante à l'avance, puis la convertissez dans le type de catégorie comme dans la méthode 1, aucune erreur ne se produira, mais si vous souhaitez compléter la valeur manquante au besoin pendant l'analyse, vous pouvez utiliser l'étape 2. Il semble qu'il soit efficace de répondre par une seule personne.

  1. Enregistrez les valeurs manquantes, puis convertissez-les en type de catégorie
  2. S'il a été converti au préalable dans le type de catégorie, ajoutez une nouvelle catégorie correspondant à null pour compléter la valeur manquante. Utilisez les add_categories de pandas (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.cat.add_categories.html).
#1.la méthode de
df.Cabin = df.Cabin.fillna('null')
df.Cabin = df.Cabin.astype('category')

#2.la méthode de
df.Cabin = df.Cabin.cat.add_categories('null').fillna('null')

Utiliser une structure de données fragmentée

Utilisez la structure de données fragmentée de pandas (https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html) pour réduire la mémoire lors de l'encodage de variables catégorielles en variables factices On peut s'attendre à ce que cela ait un effet de réduction de la mémoire. Si le nombre de colonnes d'une variable factice composée uniquement de 0 et 1 données augmente, l'effet de réduction de la mémoire peut être obtenu à l'aide de la structure de données éparse. Dans Sparse Data Structure, la position de certaines données avec 1 est enregistrée et les autres parties 0 sont compressées sans être enregistrées en tant que données. Cependant, il existe certains inconvénients causés par l'utilisation de la structure de données clairsemée, donc cela sera décrit plus tard.

Essayez le codage factice normalement

Avant cela, osons sélectionner les variables catégorielles des données Titanic avec un grand nombre de niveaux (cardinalité élevée) et voyons l'effet.

for var in df.dtypes[df.dtypes =='object'].index.tolist():
    print('Unique level of '+var+' is {:}'.format(len(df[var].unique())))
#Résultat de sortie
#Unique level of Name is 891
#Unique level of Sex is 2
#Unique level of Ticket is 681
#Unique level of Cabin is 148
Unique level of Embarked is 4

Comme prévu, Name est un nom, donc excluons-le de la cible (rires) Essayons d'encoder Ticket, Cabin, Embarked. L'encodage factice est facile avec get_dummies () de pandas [https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html). Même si le nombre de lignes de données est de 891, le nombre de colonnes est de 834, ce qui est des données terriblement rares. L'utilisation de la mémoire est d'environ 726 Ko lorsqu'elle est vérifiée avec df_dummies.info (). (Vous pouvez également obtenir la taille des données en Ko avec sys.getsizeof (df_dummies) / 1024 et df_dummies.memory_usage (). Sum () / 1024)

dummy_list = ['Ticket', 'Cabin', 'Embarked']
df_dummies = pd.get_dummies(df[dummy_list], dummy_na=True, sparse=False, prefix = dummy_list)
df_dummies.shape

##Résultat de sortie
#(891, 834)

df_dummies.info()
##Résultat de sortie
#<class 'pandas.core.frame.DataFrame'>
#RangeIndex: 891 entries, 0 to 890
#Columns: 834 entries, Ticket_110152 to Embarked_nan
#dtypes: uint8(834)
#memory usage: 725.8 KB

Encoder en structure de données éparse

Ensuite, essayez d'utiliser Sparse Data Structure pour réduire la mémoire. get_dummies () a une option appelée Sparse, qui est normalement False, mais changez-la en True. En conséquence, nous avons pu réduire la mémoire d'environ 98% de 726 Ko à 13 Ko.

df_dummies2 = pd.get_dummies(df[dummy_list], dummy_na=True, sparse=True, prefix = dummy_list)
df_dummies2.info()
##Résultat de sortie
#<class 'pandas.core.frame.DataFrame'>
#RangeIndex: 891 entries, 0 to 890
#Columns: 834 entries, Ticket_110152 to Embarked_nan
#dtypes: Sparse[uint8, 0](834)
#memory usage: 13.2 KB

En utilisant Sparse Data Structure, j'ai pu compresser la mémoire de manière significative. Cependant, il y a quelque chose à faire attention. Le fait est que la compression peut rendre les méthodes pandas utilisées dans les structures de données normales inutilisables. Par exemple, supposons que vous ayez une variable factice appelée Ticket_110152 et que vous souhaitiez additionner le nombre de 1 dans toutes les données. Dans le cas d'un DataFrame normal, .sum () est suffisant, mais dans le cas de Sparse Data Structure, une erreur se produit car les données sont compressées.

#Comment le faire normalement
df_dummies.Ticket_110152.sum()
#Résultat de sortie
#3

#Sparse Data Structure (J'ai créé df avec Sparse Data Structure_dummies2)
df_dummies2.Ticket_110152.sum()
#Résultat de sortie
#TypeError: sum() got an unexpected keyword argument 'min_count'

Pour éviter de telles erreurs, il est préférable de revenir de la structure de données fragmentée à la structure de données d'origine. Cette fois, c'est python3.6, donc je vais le restaurer avec np.asarray, mais depuis ptyhon3.7 il peut être simplifié avec une méthode appelée .to_dense ().

np.asarray(df_dummies2.Ticket_110152).sum()
#Résultat de sortie
#3
#python 3.Après 7
#df_dummies2.Ticket_110152.to_dense().sum()Mais tu devrais pouvoir y aller

Résumé

Voici un résumé des idées que vous pouvez faire lors de la gestion des dataframes pandas de manière à économiser la mémoire et des points à noter à ce moment-là.

Merci de visiter notre site. S'il existe une autre meilleure façon, veuillez laisser un commentaire. C'est facile, mais j'ai téléchargé les données et le code sur github. J'ai également publié un échantillon de groupe en utilisant Sparse Data Structure.

Recommended Posts

Ingéniosité pour gérer les données avec Pandas de manière à économiser la mémoire
<Pandas> Comment gérer les données de séries chronologiques dans le tableau croisé dynamique
Essayez de convertir en données ordonnées avec les pandas
Gérez les structures de données 3D avec les pandas
Essayez d'agréger les données de musique doujin avec des pandas
Supprimer des données dans un modèle avec Redis Cluster
Gérer les types entiers avec des valeurs manquantes dans Pandas
Transformez les données de vacances en une trame de données avec les pandas
Convertir 202003 en 2020-03 avec les pandas
Visualisation des données avec les pandas
Manipulation des données avec les Pandas!
Mélangez les données avec les pandas
Je veux donner un group_id à une trame de données pandas
Entraînez les données MNIST avec PyTorch en utilisant un réseau neuronal
Je veux faire la transition avec un bouton sur le ballon
Comment convertir des données détenues horizontalement en données détenues verticalement avec des pandas
Comment accéder avec cache lors de la lecture_json avec pandas
Comment extraire des données qui ne manquent pas de valeur nan avec des pandas
Je veux travailler avec un robot en python.
[Python] Un mémo pour écrire du CSV verticalement avec Pandas
Convertissez les variables numériques en variables catégorielles avec les pandas en définissant un seuil
Comment extraire des données qui ne manquent pas de valeur nan avec des pandas
Comment utiliser fixture dans Django pour saisir des exemples de données associés au modèle utilisateur
Enregistrez les données pandas dans des actifs de données au format Excel avec Cloud Pak for Data (Watson Studio)
J'ai essayé de créer un cadre de données pandas en grattant les informations de rappel d'aliments avec Python
Gérer les données ambiantes en Python
Comment sortir un document au format pdf avec Sphinx
Combinez des listes dans un DataFrame avec des pandas
Fonction pratique pour ajouter des colonnes n'importe où dans Pandas DataFrame
Une collection de méthodes utilisées lors de l'agrégation de données avec des pandas
Comment obtenir un aperçu de vos données dans Pandas
Comment gérer les trames de données
Entrer simultanément des données spécifiques sur une feuille spécifique dans de nombreux Excel
Comment créer une trame de données et jouer avec des éléments avec des pandas
Compagnon de science des données en python, comment spécifier des éléments dans les pandas
Conseils de traitement des données avec Pandas
La méthode minimale à retenir lors de l'agrégation de données avec Pandas
Si vous souhaitez devenir data scientist, commencez par Kaggle
[Analyse du cours de l'action] Apprenez les pandas avec la moyenne Nikkei (004: Changer les données lues en moyenne Nikkei)
Organisez les données d'achat individuelles dans un tableau avec le MultiLabel Binarizer de scikit-learn
Les ingénieurs de données apprennent DevOps en vue de MLOps. ① Prise en main
[Pandas] J'ai essayé d'analyser les données de ventes avec Python [Pour les débutants]
Comment créer une grande quantité de données de test dans MySQL? ??
Je vais vous expliquer comment utiliser Pandas d'une manière facile à comprendre.
Comment gérer une session dans SQLAlchemy
Gérez les données au format NetCDF avec Python
Comment gérer les données déséquilibrées
Gérer les demandes dans un processus distinct
Traçage de données polyvalent avec pandas + matplotlib
Essayez de mettre des données dans MongoDB
Comment augmenter les données avec PyTorch
Dessinez un graphique avec des pandas + XlsxWriter
Je veux faire ○○ avec les Pandas
Comment gérer le japonais avec Python
Gère divers formats de date avec des pandas
[Petite histoire] Comment enregistrer des graphiques matplotlib dans un lot avec Jupyter
SE, débutant en analyse de données, apprend avec l'équipe de science des données vol.1
Comment obtenir un nom de colonne et un nom d'index spécifiques avec Pandas DataFrame
(Matplotlib) Je veux dessiner un graphique avec une taille spécifiée en pixels