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.
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.
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.
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)
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
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
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%
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')
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.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')
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.
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
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
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